Compare commits

..

278 Commits

Author SHA1 Message Date
UnicornFreedom
cf78c278af
Update brain to 0.24.2 and fix TPS counter disappearing 2025-12-03 03:31:48 +01:00
Fingercomp
3b82541da6
Have the SoundSource object store factories rather than sources
Closes #182.
2025-09-17 22:32:14 +03:00
UnicornFreedom
2aba4a7969
Fix incorrect base format + swapped texture arg names 2025-09-13 05:48:01 +02:00
UnicornFreedom
876043bf2f
Replace GL11.GL_CLAMP with GL12.GL_CLAMP_TO_EDGE to fix GL_INVALID_ENUM 2025-09-13 05:15:33 +02:00
UnicornFreedom
22ee84d74c
Update ocelot-brain to fix setting loading 2025-09-13 01:05:09 +02:00
Fingercomp
cbc80baf71
Intercept mouse/key events when a context menu is open
Closes #180.
2025-09-05 18:10:29 +03:00
Fingercomp
479d13cc0c
Treat _ as a word character in word boundary search 2025-09-05 00:57:46 +03:00
Fingercomp
51c4494628
Reformat TextInput 2025-09-05 00:25:56 +03:00
Fingercomp
cbd3927cc5
Use punctuation boundaries for word selection on double-click
Closes #181.
2025-09-05 00:20:38 +03:00
Fingercomp
8f291c4a80
Implement text navigation via Ctrl keybings 2025-09-04 01:46:43 +03:00
Fingercomp
7f0fccae80
Update selection when the LMB is pressed instead of released 2025-09-04 00:36:15 +03:00
Fingercomp
c0eec1fffc
Select all text on triple-click 2025-09-04 00:24:05 +03:00
Fingercomp
4cee456454
Double-click is NOT a speedrunning strat, I said 2025-09-03 23:29:25 +03:00
Fingercomp
dfbcb0418c
Remove MouseEvent.StateChanged because it's redundant
I don't know why I decided that mouse events are level-triggered.
Sometimes I'm just dumb like that. Now here I am, cleaning up after my
stupidity.

Oh, this also makes double-clicks seem less like a speedrunning strat.
2025-09-03 23:25:40 +03:00
Fingercomp
5e172ee804
Merge branch 'feature/qol-ocelot-interface' into develop 2025-09-03 19:33:38 +03:00
Fingercomp
052f61ad43
Remember the desired cursor position when changing the text 2025-09-03 19:26:44 +03:00
Fingercomp
d676a4a5bf
Fix how LogWidget is laid out 2025-09-03 18:52:40 +03:00
Fingercomp
2c77f8cba9
Clarify the purpose of removedOffset 2025-09-03 18:39:56 +03:00
UnicornFreedom
6a252d86fd
Core review corrections 2025-09-03 11:39:43 +02:00
UnicornFreedom
667a74519a
Fix FPS counter "frame" skip 2025-09-03 11:06:41 +02:00
UnicornFreedom
b03078ff27
Persist LogWidget "scroll to the end" checkbox value 2025-09-03 11:06:41 +02:00
UnicornFreedom
04bbf626a1
Fix Ocelot interface relayout on clear 2025-09-03 11:06:40 +02:00
UnicornFreedom
9a3ee8e95c
Add navigation through Ocelot component log history 2025-09-03 11:06:40 +02:00
UnicornFreedom
cf088ce9f7
Add context menu to Ocelot component log entries 2025-09-03 11:06:39 +02:00
Dmitry Zhidenkov
6db5ff3f37 Merge branch 'feature/better-text-edit' into 'develop'
Better text edit

See merge request cc-ru/ocelot/ocelot-desktop!120
2025-09-03 09:04:41 +00:00
Fingercomp
68350f8d62
Take mouse.y into account when computing the selection endpoint 2025-09-03 01:05:00 +03:00
Fingercomp
ca8ef6eee1
Reimplement TextInput's selection state 2025-09-03 00:52:44 +03:00
Fingercomp
c9f8f4a123
Don't spam drag events if asked nicely 2025-08-31 19:45:56 +03:00
UnicornFreedom
b17efa3aa2
Fix cursor movement when mouse is pressed 2025-08-31 18:13:40 +02:00
UnicornFreedom
7316baa390
Simplify code, allow Shift+Click selections 2025-08-31 17:55:49 +02:00
Fingercomp
b6e68d40bd
Make the event capture phase more sophisticated 2025-08-22 21:19:56 +03:00
UnicornFreedom
b67ad92273
Fix ChangeSimulationSpeedDialog 2025-08-22 15:24:40 +02:00
UnicornFreedom
313e5c8b6a
Remove unnecessary keyboard event repeats 2025-08-22 15:21:36 +02:00
UnicornFreedom
6f5f84e5fd
Fix cursor positioning on TextInput text set 2025-08-22 15:20:01 +02:00
UnicornFreedom
4e9d7c96e2
Allow selecting current word by double click 2025-08-22 11:19:18 +02:00
UnicornFreedom
a8dc52f1b2
Add context menu to the TextInput 2025-08-22 03:46:01 +02:00
UnicornFreedom
c796b74ea3
Add support for TextInput text selection 2025-08-22 02:59:24 +02:00
UnicornFreedom
9511f586a6
Fix hash collisions in Watcher + more discreet scroll updates 2025-08-21 16:47:25 +02:00
UnicornFreedom
90a01493db
Support non-BMP codepoints in TextInput 2025-08-21 16:21:08 +02:00
UnicornFreedom
349280d802
Split text state and cursor state 2025-08-21 13:23:21 +02:00
UnicornFreedom
605023118f
Split TextInput state from presentation 2025-08-21 13:11:58 +02:00
UnicornFreedom
3bee61832f
Small corrections and refactoring 2025-08-21 11:34:08 +02:00
UnicornFreedom
841733f6fe
Simplify TextInput and fix few small bugs 2025-08-21 11:34:08 +02:00
UnicornFreedom
81840e4fab
Initial refactoring of TextInput 2025-08-21 11:34:06 +02:00
Fingercomp
8724c4a3ad
Merge branch 'refactor/hover-capture-phase' into develop 2025-08-20 20:28:43 +03:00
Fingercomp
99af664d84
Make HoverEvent a CapturingEvent 2025-08-20 18:25:24 +03:00
Dmitry Zhidenkov
448089ccb9 Merge branch 'feature/pause-resume-emulation' into 'develop'
Allow to pause and resume emulation

See merge request cc-ru/ocelot/ocelot-desktop!118
2025-08-20 09:06:59 +00:00
UnicornFreedom
8fc3759d88
Stop TickUpdatable stuff during the pause 2025-08-20 03:21:44 +02:00
UnicornFreedom
d533e33f2b
Tooltips should not clip through screen edges 2025-08-20 02:32:25 +02:00
UnicornFreedom
e4aca89f92
Add hover effect to the pause/resume button 2025-08-20 02:31:22 +02:00
UnicornFreedom
8416a46b5c
Avoid sudden jumps in TPS counter due to pause 2025-08-20 01:48:54 +02:00
UnicornFreedom
3f9ad3c710
Replace magical constant with a less magical constant 2025-08-20 01:48:30 +02:00
UnicornFreedom
d9e65fc67e
Allow to pause and resume emulation 2025-08-20 00:59:55 +02:00
Fingercomp
eb8665970d
Merge branch 'master' into develop 2025-08-19 21:09:22 +03:00
Fingercomp
9db2416d23
Merge branch 'develop' 2025-08-19 21:01:27 +03:00
Fingercomp
3ada4c6845
Version 1.14.1 2025-08-19 21:01:18 +03:00
Fingercomp
25cabf1d0a
Fix a typo in doc/releases.md 2025-08-19 20:57:24 +03:00
UnicornFreedom
fd31074d0d
Fix default config resource loading 2025-08-19 19:38:51 +02:00
Fingercomp
97e05f2895
Fix the node selector always spawning in the same place 2025-08-19 16:32:32 +03:00
Fingercomp
110e0b85f1
Merge branch 'master' into develop 2025-08-18 19:57:57 +03:00
Fingercomp
b53311aa27
Merge branch 'develop' 2025-08-18 19:01:55 +03:00
Fingercomp
33a3cf4773
Version 1.14.0 2025-08-18 19:01:35 +03:00
Fingercomp
e74a3d5264
Merge branch 'feature/edit-raid' into develop 2025-08-18 18:55:57 +03:00
Fingercomp
745aac646f
Don't change things if editing is disabled 2025-08-18 15:50:44 +03:00
Fingercomp
f2234677a0
Update ocelot-brain 2025-08-18 15:41:34 +03:00
Fingercomp
604d7f156d
Add a DiskEditWindow for RAIDs 2025-08-18 01:02:36 +03:00
Fingercomp
c73a0ab9d1
Remove Vector2D.*(Double) 2025-08-17 23:22:49 +03:00
Fingercomp
d769b0bde3
Merge branch 'refactor/icon-source-everywhere' into develop 2025-08-17 19:51:42 +03:00
Fingercomp
4edfd8fff2
Rename iconSource properties to icon 2025-08-17 18:43:42 +03:00
Fingercomp
d28572124f
Use all-caps for key hints to be consistent 2025-08-17 18:39:40 +03:00
Fingercomp
cbc16a3d23
Introduce IconScope 2025-08-17 17:07:07 +03:00
Fingercomp
ce2eeaec0a
Require IconSource in all Graphics.sprite overloads
Well, almost. The smoke animation is too weird.
2025-08-17 16:00:50 +03:00
Fingercomp
210f4b1a0c
Merge branch 'refactor/icon-button' into develop 2025-08-17 00:18:40 +03:00
Fingercomp
f6c41f0184
Dispose of items when they become inaccessible
Closes #63.
2025-08-16 20:18:00 +03:00
Fingercomp
5bcc005143
Close context menus after opening a modal dialog 2025-08-16 00:40:18 +03:00
Fingercomp
498df9ed09
Merge branch 'refactor/event-capturing' into develop 2025-08-15 15:07:31 +03:00
Fingercomp
1c74adc329
Intercept mouse press events when dragging scroll bar thumbs 2025-08-14 21:16:50 +03:00
Fingercomp
87b13e66b1
Remove the window focusing hack 2025-08-14 19:15:53 +03:00
Fingercomp
1c19df7cf3
Implement event capturing 2025-08-14 19:13:56 +03:00
Fingercomp
9ced3b99b4
Merge branch 'feature/scrollable-context-menu' into develop 2025-08-14 17:30:34 +03:00
Fingercomp
5dda7c2b0d
Put context menus in a scroll view and try harder to fit them
Closes #149.
2025-08-13 23:17:13 +03:00
Fingercomp
c789722d4c
Use PartialFunction.applyOrElse when dispatching 2025-08-13 22:47:15 +03:00
Fingercomp
22d854ed34
Merge branch 'refactor/scroll-view' into develop 2025-08-13 14:57:35 +03:00
Fingercomp
34537bd76c
Lay the ScrollView's children out using a Layout like the rest
This fixes a long-standing problem with the LogWidget not picking up the
latest scroll offset.
2025-08-13 01:03:39 +03:00
Fingercomp
c1020ff1cb
Refactor ScrollView 2025-08-13 00:51:04 +03:00
Fingercomp
79e3d8ca73
Replace a redundant field with a def 2025-08-12 18:29:51 +03:00
Fingercomp
ed6ff78956
Batch changes to Entry.y when removing entries
This improves performance significantly (by more than an order of
magnitude) when spamming messages.

Closes #177.
2025-08-12 16:36:51 +03:00
Fingercomp
9ce89c2b58
Continue IconSource refactoring 2025-08-12 15:20:27 +03:00
Fingercomp
b759846505
Use IconSources and override IconButton.onClicked 2025-08-12 02:58:41 +03:00
Fingercomp
055671abc1
Add IconButton.onClicked for regular buttons 2025-08-12 02:58:20 +03:00
Fingercomp
47ea0e7d9a
Update scalafmt settings
This is the docstring style we have decided we prefer.
2025-08-12 01:57:04 +03:00
Fingercomp
6b1da4162c
Fix the window state update logic 2025-08-12 00:00:48 +03:00
Fingercomp
094ac8cb48
Fix brain event snapshotting; remove obsolete synchronization 2025-08-11 23:49:25 +03:00
Fingercomp
fd6400f39a
Process a snapshot of the BrainEvent queue on update
Closes #177.
2025-08-11 01:43:49 +03:00
Fingercomp
a042c76b0f
Update ocelot-brain 2025-08-11 01:18:38 +03:00
Fingercomp
eb6f6b0d8d
Emulate clipboard limits
See ocelot-brain#44.
2025-08-09 00:16:56 +03:00
Fingercomp
d0682ea063
Remove debug print statements left by mistake 2025-08-08 00:45:24 +03:00
Fingercomp
9192d7fc48
Downmix SDC SFX sounds to mono to allow positioning to work
Closes #174.
2025-08-05 01:04:39 +03:00
Fingercomp
09277887a6
Indicate the current tier in the tier selection submenu
This is useful with microcontrollers, whose apperance does not change
depending on the tier, unlike, say, computer cases.
2025-08-05 00:20:43 +03:00
Fingercomp
4bcdebedfa
Have each sound card create its own sound stream
In addition to that, fixes a long-standing bug concerning brain event
dispatch to inventory items: items could receive events coming from a
component with a different address.

Fixes #175.
2025-08-04 01:06:57 +03:00
Fingercomp
d1d2deddff
if is an expression; use that 2025-08-03 22:36:37 +03:00
Fingercomp
c06b048393
Fix the initial configuration of μCs
Closes #173.
2025-08-03 22:30:05 +03:00
Fingercomp
d05aa8d89d
Merge branch 'feature/self-destructing-card-enhancements' into develop 2025-08-03 22:28:01 +03:00
Fingercomp
7c68939d54
Adjust SDC effects 2025-08-03 22:12:20 +03:00
Fingercomp
901d81aa8b
Make the SDC's tooltip more descriptive when the fuse is lit 2025-08-03 18:40:35 +03:00
Fingercomp
a5a9b18771
Make BoomCardFxHandler a supertrait of ComputerAwareNode 2025-08-03 18:39:41 +03:00
Fingercomp
7ecb9a6fe7
Refactor so as to handle SDC FX in racks too 2025-08-03 18:25:34 +03:00
Fingercomp
f6e81d6752
Refactor the boom glow effect 2025-08-03 17:55:58 +03:00
UnicornFreedom
09f688cbb3
Rebase implementation on a new particle system 2025-08-03 16:35:23 +02:00
smok1e
89da2e9f39
Yeah, the code won't work if i don't add comma after the last argument... 2025-08-03 16:35:23 +02:00
smok1e
b697d3c0c4
Applied scalafmt (probably) 2025-08-03 16:35:22 +02:00
smok1e
a9393ab2be
Added glow indicating soon explosion 2025-08-03 16:35:22 +02:00
smok1e
4209a05c62
Added explosion animation 2025-08-03 16:35:22 +02:00
smok1e
549833f3e9
Added countdown beep sound for Self-Destructing card 2025-08-03 16:35:21 +02:00
Fingercomp
82d98ff4f8
Merge branch 'refactor/sync-brain-dispatch' into develop 2025-08-03 16:28:11 +03:00
Fingercomp
d64314a9db
Dispatch BrainEvents synchronously from the main thread
Closes #172.
2025-08-03 16:18:24 +03:00
Dmitry Zhidenkov
c3fcf7ed52 Merge branch 'feature/particle-system' into 'develop'
Refactor and unify particle system

See merge request cc-ru/ocelot/ocelot-desktop!109
2025-08-03 12:59:04 +00:00
Fingercomp
e3ccfeba9f
Fix a race when spawning ocelot log particles 2025-08-03 15:43:27 +03:00
Fingercomp
69dcca71dc
Make ParticleSystem.count O(1) 2025-08-03 15:42:47 +03:00
UnicornFreedom
7466b7d4db
Move particle speed into Particle class 2025-08-03 01:46:42 +02:00
Fingercomp
6554a1ef84
Deduplicate code by using the newly added side property 2025-08-03 02:36:59 +03:00
UnicornFreedom
c510cbb834
Refactor and unify particle system 2025-08-03 01:35:50 +02:00
Fingercomp
b248c4beeb
Highlight nodes in the rack window
Closes #167.
2025-08-03 02:32:27 +03:00
Fingercomp
88f6870aa1
Rip out scalafmt from the CI pipeline
How can Scala people be satisfied with such a defective tool? It takes
all the bad parts of clang-format and none of the good ones. It can't
even enfore the official Scala style guide!

Anyway, I'm open to adding it back as long as it's set up to only check
files that have been modified in a MR or a commit. But I, personally,
don't feel like digging through giant piles of yaml to accomplish that.
2025-08-03 00:20:21 +03:00
UnicornFreedom
e0b8f61b6e
Get rid of assembly errors and warnings 2025-08-02 23:05:56 +02:00
Fingercomp
e23db58c4c
Remove audio sources before destroying their buffers
Fixes the OpenAL error messages printed on exit.
2025-08-02 21:57:15 +03:00
UnicornFreedom
c0dad8daed
Update ocelot-brain to v0.24.0 2025-08-02 16:51:30 +02:00
UnicornFreedom
ee8ff464ad
Use immutable IntMap in Font class to avoid boxed numeric operations 2025-08-02 00:27:15 +02:00
Fingercomp
24678b803b
Merge branch 'feature/more-tooltips' into develop 2025-08-01 21:47:14 +03:00
Fingercomp
8d3e71c8ad
Lighten the front/south side color 2025-08-01 21:27:27 +03:00
UnicornFreedom
fe86cb22de
Do not show side hint text on Cable nodes 2025-08-01 20:12:33 +02:00
Fingercomp
401e347227
Adjust the position of the node port legend by 1px 2025-08-01 21:04:17 +03:00
Fingercomp
7cdc316d24
Add a fade-in and fade-out animation to the port legend 2025-08-01 21:02:05 +03:00
Fingercomp
0dac2b2f1f
Deduplicate code 2025-08-01 20:51:39 +03:00
Fingercomp
3315764951
Choose the side or the direction in connection hints as appropriate too 2025-08-01 20:48:45 +03:00
Fingercomp
189d26c4a1
Only show the node port legend on hover 2025-08-01 20:44:27 +03:00
Fingercomp
223bbd5048
Show the cardinal direction or the side as appropriate for each node 2025-08-01 20:42:05 +03:00
UnicornFreedom
87f8aaaed5
Remove Hub side tooltip, add new connection hint overlay 2025-08-01 19:36:16 +02:00
Fingercomp
bdcbdc3fd8
Draw a node port legend on hover 2025-08-01 20:11:05 +03:00
UnicornFreedom
424c5fc202
Add a tooltip displaying connected Hub sides 2025-08-01 17:19:03 +02:00
Dmitry Zhidenkov
0d0046fd05 Merge branch 'feature/configurable-transparency' into 'develop'
Make inactive windows transparency configurable

See merge request cc-ru/ocelot/ocelot-desktop!106
2025-08-01 11:56:19 +00:00
UnicornFreedom
a1cd8cab00
The terrible crime of a bracketless if was averted 2025-08-01 13:40:17 +02:00
UnicornFreedom
00be88f571
Make the "hide when unfocused" feature more explicit 2025-08-01 13:40:17 +02:00
UnicornFreedom
18fc7b46ac
Refactor window refresh method to be less hacky 2025-08-01 13:40:16 +02:00
UnicornFreedom
47f92b06c1
Make inactive windows transparency configurable 2025-08-01 13:40:16 +02:00
Fingercomp
a0754f4cd8
Merge branch 'refactor/screen-window' into develop 2025-08-01 13:24:25 +03:00
Fingercomp
af3e887a1d
Adjust ScreenWindow's drag regions
The union of new regions is the window area sans the screen view and the
bottom right corner. This differs from the old regions when the
resolution is too small for the screen view to occupy the entirety of
the window's inner width.
2025-07-31 22:57:59 +03:00
Fingercomp
04550de173
Make the screen window look sane even when it's ridiculously small 2025-07-31 22:51:14 +03:00
Fingercomp
182d42a843
Pull ScreenView out of ScreenWindow 2025-07-31 18:29:53 +03:00
Dmitry Zhidenkov
6babdcf6d8 Merge branch 'feature/key-mappings' into 'develop'
Configurable key mappings

See merge request cc-ru/ocelot/ocelot-desktop!105
2025-07-29 22:57:55 +00:00
UnicornFreedom
3c604b976a
Add key mapping tab to the settings dialog 2025-07-30 00:29:03 +02:00
UnicornFreedom
98d352a9e6
Implement a serializable Keymap class 2025-07-29 20:21:37 +02:00
UnicornFreedom
73a63e75aa
Change item serialization logging level to DEBUG 2025-07-29 16:39:30 +02:00
UnicornFreedom
c9434089ea
Refactor Setting loading method 2025-07-29 14:14:13 +02:00
Fingercomp
da17dc81a8
Make scalafmt optional and more permissive 2025-07-28 22:05:43 +03:00
Fingercomp
35f060451b
Merge branch 'feature/insert-key-remapping' into develop 2025-07-18 14:44:09 +03:00
Fingercomp
34158d2408
Reformat with scalafmt 2025-07-18 14:38:38 +03:00
UnicornFreedom
eafcb3c16d
Add a file with default keymap constants 2025-07-16 17:47:03 +02:00
UnicornFreedom
816d4ea992
Make it possible to re-map OpenComputers Insert key 2025-07-16 17:35:01 +02:00
Fingercomp
0f333a6f26 Merge branch 'feature/apple-silicon-support' into 'develop'
Introduce support for Apple Silicon platforms

See merge request cc-ru/ocelot/ocelot-desktop!102
2025-02-17 18:25:29 +00:00
AtomicScience
9a90c64291 Added the LWGL compilation manual 2025-02-17 17:05:02 +03:00
AtomicScience
6f4bfc0a6e Make a aarch64 check more lenient 2025-02-17 15:12:08 +03:00
AtomicScience
deb0365d87 Remove unnecessary Java 8 enforcement 2025-02-17 12:49:45 +03:00
AtomicScience
bbfb840430 Bring back platform lwjgl deps 2025-02-17 12:46:53 +03:00
AtomicScience
cb902180c6 Fix scalafmt errors, remove unneded logging 2025-02-17 12:14:47 +03:00
AtomicScience
6f1f1fffc7 Bump OpenGL version to allow using inverse()
Perhaps Metal is more strict about versions, or vendors provide this function in 140 on other platforms
2025-02-17 11:30:05 +03:00
AtomicScience
7b89069c40 Fix issue with alpha channel blending on Metal 2025-02-17 11:29:57 +03:00
AtomicScience
b67cd2a7f2 Add support for manually compiled LWJGL instance 2025-02-17 09:20:05 +03:00
Fingercomp
1a69b55f5e
Merge branch 'master' into develop 2025-02-13 22:00:26 +03:00
Fingercomp
34bcb0bd4c
Merge branch 'develop' 2025-02-13 21:59:20 +03:00
Fingercomp
5fa0cdcb0b
Version 1.13.1 2025-02-13 21:59:10 +03:00
Fingercomp
3f4f32d71c
Fix a glitch when removing a screen node while its window is open
Fixes #161.
2025-02-13 21:23:13 +03:00
Fingercomp
8d80e41a2f
Merge branch 'master' into develop 2025-02-09 17:48:39 +03:00
Fingercomp
23eadac441
Merge branch 'develop' 2025-02-09 16:50:12 +03:00
Fingercomp
21f9d25371
Version 1.13.0 2025-02-09 16:49:51 +03:00
Fingercomp
46ca2709af
Update ocelot-brain 2025-02-09 16:48:43 +03:00
Fingercomp
dbbe289af8
Perform η-reduction of plot constructors
Closes #157.
2025-02-09 02:02:20 +03:00
Fingercomp
e29555e592
Add a hint for the home key to the status bar 2025-02-02 01:13:01 +03:00
Fingercomp
f62acc1cb8
Don't show null addresses in item tooltips
Fixes #159.
2025-02-01 16:11:32 +03:00
Fingercomp
6f9558bed1
Merge branch 'refactor/entity-id' into develop 2025-02-01 16:03:11 +03:00
Fingercomp
e37a910d69
Allow non-Environment entities in SyncedInventories
To that end, entities are identified by their entityIds instead of their
environment's node addresses.
2025-02-01 02:38:41 +03:00
LeshaInc
709acb268a
scalafmt: Set docstrings.forceBlankLineBefore = false 2025-01-31 21:36:34 +03:00
LeshaInc
70bdbcd630 Merge branch 'refactor/add-scalafmt' into 'develop'
Add scalafmt

Closes #88

See merge request cc-ru/ocelot/ocelot-desktop!100
2025-01-30 20:29:55 +00:00
LeshaInc
ee4f2dcc3b Add scalafmt 2025-01-30 20:29:55 +00:00
LeshaInc
edd9916905 Merge branch 'feature/scalatest' into 'develop'
Add scalatest

Closes #153

See merge request cc-ru/ocelot/ocelot-desktop!98
2025-01-27 21:09:19 +00:00
LeshaInc
234442f5ea Add scalatest 2025-01-27 21:09:19 +00:00
Fingercomp
9d323138d3
Merge branch 'feature/relay-gui' into develop 2025-01-27 00:20:25 +03:00
Fingercomp
7a6d62efbe
Use semantic naming for relay window colors 2025-01-27 00:16:13 +03:00
Fingercomp
cd86902dd5
Add a relay GUI
Closes #74.
2025-01-26 22:32:54 +03:00
Fingercomp
63030f505f
Merge branch 'refactor/click-sounds' into develop 2025-01-26 00:09:33 +03:00
Fingercomp
d2845d9320
Create a new iterator for every event dispatch
Previously, if the same `dispatchEvent(...)` callback was reused for
different events, only the first call actually delivered the event,
whereas the following calls would find the widget iterator exhausted.
2025-01-25 23:13:36 +03:00
Fingercomp
79284270bc
Dispatch events to items in any kind of Inventory 2025-01-25 22:49:39 +03:00
Fingercomp
68643be2b8
Make focused text fields and screen windows consume handled events 2025-01-25 22:19:48 +03:00
Fingercomp
bdf0037146
Split click sounds into two, played on press and release 2025-01-25 18:33:56 +03:00
Fingercomp
477b3586f0
Use Option[T] instead of nulls 2025-01-25 16:48:51 +03:00
Fingercomp
3374053a5a
Merge branch 'refactor/click-and-drag' into develop 2025-01-25 16:05:35 +03:00
Fingercomp
766f0e5ae6
Remove ComponentSelector*
Nothing uses these classes, apparently.
2025-01-25 02:37:30 +03:00
Fingercomp
11e3254925
Make IconButtons behave more like regular buttons 2025-01-25 02:20:33 +03:00
Fingercomp
47a858b788
Register clicks as long as the button is released within the bounds 2025-01-25 01:57:50 +03:00
Fingercomp
1a64e949dd
Unify ClickHandler and DragHandler
This fixes long-standing annoyances regarding click detection: if a
widget handles both the click and the drag events, pressing down a
button, dragging the cursor away, moving it back, and releasing the
button would trigger a click event. No other UI behaves like that.
2025-01-25 01:35:25 +03:00
Fingercomp
8efa8e84c1
Synchronize access to screen buffers
Also fix screen viewport rendering, which previously used "live" screen
data instead of the buffer copies updated every tick.
2025-01-24 21:47:32 +03:00
Fingercomp
e6ac48862e
Open the simulation speed dialog on left-click
See #143.
2025-01-24 19:23:16 +03:00
Fingercomp
6df90c0fca
Highlight the TPS counter on mouse hover
Closes #143.
2025-01-24 19:20:49 +03:00
Fingercomp
34337b2cc3
Merge branch 'feature/icon-button-hover-highlighting' into develop 2025-01-24 13:19:38 +03:00
Fingercomp
5b6e378000
Highlight the rest of IconButtons on mouse hover
See #143.
2025-01-24 01:39:18 +03:00
Fingercomp
aa635a6d1c
Leave words of wisdom for whoever touches TextInput code again
Let's hope it won't be me who does that.
2025-01-23 21:10:58 +03:00
Fingercomp
a81dbb3ba2
Merge branch 'feature/screenshot' into develop 2025-01-23 20:27:16 +03:00
Fingercomp
b1a1cef365
Allow both left and right modifiers in key bindings 2025-01-23 19:53:36 +03:00
UnicornFreedom
a65caad242 Add shutter sound and effect 2025-01-23 17:36:34 +01:00
Fingercomp
08ef6f9075
Merge branch 'refactor/node-icon-sources' into develop 2025-01-23 13:31:40 +03:00
Fingercomp
82b35d9dbc
Load workspaces in the main thread
Fixes #147.
2025-01-23 02:08:01 +03:00
Fingercomp
33b08dcef8
Remove an obsolete TODO 2025-01-23 01:54:44 +03:00
Fingercomp
a69f04bcf3
Use IconSources to refer to node sprites 2025-01-23 01:46:43 +03:00
Fingercomp
a898e04dc3
Set the WM_CLASS explicitly so it doesn't depend on the version
Fixes #148.
2025-01-22 23:48:02 +03:00
UnicornFreedom
2772b3ad67 Refactor the screenshot function in UiHandler 2025-01-16 11:49:38 +01:00
UnicornFreedom
9c5bc3189a Make screenshot function more robust 2025-01-16 11:33:40 +01:00
UnicornFreedom
584bb1be9c Make screenshots with F12 2025-01-15 05:44:01 +01:00
UnicornFreedom
300c5dfe5b Update ocelot-brain to v0.22.0 2025-01-12 12:40:26 +01:00
UnicornFreedom
78fc31abbc Remove duplicate LuaJ license, add LWJGL2 license 2025-01-11 01:04:16 +01:00
Dmitry Zhidenkov
5518f1941b Merge branch 'feature/autosave' into 'develop'
Add optional workspace autosave

See merge request cc-ru/ocelot/ocelot-desktop!90
2025-01-10 22:22:58 +00:00
Dmitry Zhidenkov
05c4abbd66 Code review correction: better config comment wording 2025-01-10 22:20:47 +00:00
UnicornFreedom
0e4b6364bf Small README correction 2025-01-03 03:44:08 +01:00
UnicornFreedom
73d41071ee Add hover animation to icon buttons 2025-01-03 03:23:13 +01:00
UnicornFreedom
7a255ec631 Add hover animation to sliders 2025-01-03 03:08:24 +01:00
UnicornFreedom
d12fdee3d7 Add hover animation to text inputs 2025-01-03 03:04:51 +01:00
UnicornFreedom
e68fd4f65e Add hover animation to checkboxes 2025-01-03 02:49:47 +01:00
UnicornFreedom
7fdfdf5f3b Make festive decorations slightly smaller 2025-01-03 01:29:16 +01:00
UnicornFreedom
aea28d3548 Add hover animation to buttons 2025-01-03 00:58:06 +01:00
UnicornFreedom
c48404d654 Add optional workspace autosave 2025-01-02 15:51:24 +01:00
UnicornFreedom
b1a5fe680a Fix a bug with root widget scaling 2025-01-02 13:58:33 +01:00
UnicornFreedom
51771e4c99 Add some tooltips to System settings tab to make it clearer 2025-01-02 12:12:34 +01:00
UnicornFreedom
150d324777 Allow to set text input "placeholder" value 2025-01-02 12:12:04 +01:00
UnicornFreedom
50a4e1c7e4 Fix note block particle effects on non-standard pitches 2024-12-03 20:30:47 +01:00
UnicornFreedom
a611b02bfa Make festive decorations toggleable 2024-12-03 20:24:36 +01:00
UnicornFreedom
ad92266709 Add HOME button to reset camera position 2024-12-03 19:57:24 +01:00
Dmitry Zhidenkov
5722f671aa Merge branch 'feature/extend-tooltips' into 'develop'
Extended item tooltips with additional info

See merge request cc-ru/ocelot/ocelot-desktop!89
2024-09-22 19:58:06 +00:00
UnicornFreedom
3526586eff Code review corrections 2024-09-22 21:16:36 +02:00
UnicornFreedom
78c022dd14 Extended item tooltips with additional info 2024-09-22 17:25:00 +02:00
Dmitry Zhidenkov
6c1d41671a Merge branch 'feature/configurable-tooltips' into 'develop'
Allow to configure tooltip delays

See merge request cc-ru/ocelot/ocelot-desktop!88
2024-09-22 11:15:28 +00:00
UnicornFreedom
8ee5b0d5ef Align tooltip delay sliders in one row 2024-09-22 13:01:17 +02:00
UnicornFreedom
2d7912c16f Allow to configure tooltip delays 2024-09-22 08:26:21 +02:00
UnicornFreedom
9dca251543 Update ocelot-brain to v0.21.0 2024-09-21 16:22:43 +02:00
Fingercomp
8938242fff
Merge branch 'master' into develop 2024-08-30 20:07:32 +07:00
Fingercomp
ac5bfc693d
Merge branch 'develop' 2024-08-30 19:27:43 +07:00
Fingercomp
d55fe35e20
Version 1.12.0 2024-08-30 19:26:42 +07:00
Fingercomp
3d6ceaf219
Update ocelot-brain to v0.20.4 2024-08-30 19:25:41 +07:00
Fingercomp
c15193a78d
Merge branch 'feature/screen-mipmaps' into develop 2024-08-30 19:06:02 +07:00
Fingercomp
8c371f3f63
Merge branch 'feature/ocelot-block' into develop 2024-08-28 12:59:39 +07:00
Fingercomp
5e7777bd4c
Generate and use mipmaps for screen textures 2024-08-28 02:49:20 +07:00
Fingercomp
acb1374140
Copy the screen buffer during initialization 2024-08-28 00:49:10 +07:00
Fingercomp
e994011cd9
Add an ocelot block
Closes #131.
2024-08-28 00:37:53 +07:00
Fingercomp
a609ae8cb4
Merge branch 'fix/screen-texture' into develop 2024-08-27 00:01:41 +07:00
Fingercomp
8ed3350fca
Enable screen previews by default 2024-08-26 20:06:57 +07:00
Fingercomp
a483aff078
Revert "Pixel-perfect rendering, 2024 take"
This reverts commit 27f7db8b4651afa1fda83f2c46e02ce5dc4f6f35.
2024-08-26 19:57:35 +07:00
Fingercomp
27f7db8b46
Pixel-perfect rendering, 2024 take 2024-08-26 19:42:44 +07:00
Fingercomp
811d8e0a09
Fix screen preview positioning 2024-08-26 19:10:00 +07:00
Fingercomp
9741f70e42
Reuse the screen shader among instances 2024-08-26 18:25:14 +07:00
Fingercomp
e7a2a1c6ee
Clear things like a normal person 2024-08-26 16:03:59 +07:00
Fingercomp
2efec95103
Update screen contents once per tick
Fixes #117. Closes #103.
2024-08-25 22:50:52 +07:00
Fingercomp
6bc51ba1a9
Render screen contents to a texture
See #103.
2024-08-25 22:09:11 +07:00
Smok1e
22f9216890 Fix note blocks pitch calculation 2024-07-10 15:37:38 +03:00
UnicornFreedom
fed823eefc Allow to attach/detach keyboards to the screens 2024-03-11 17:55:13 +01:00
UnicornFreedom
d72c666a9e Add status bar hint for changing TPS 2024-03-11 07:42:03 +01:00
UnicornFreedom
d272befce5 Play note block sound samples on LMB 2024-03-11 07:36:25 +01:00
Dmitry Zhidenkov
9072778c1c Merge branch 'feature/component-counter' into 'develop'
Component usage counter / tooltip

See merge request cc-ru/ocelot/ocelot-desktop!84
2024-02-22 11:47:43 +00:00
UnicornFreedom
c5b2cf5136 Code review corrections 2024-02-22 12:39:27 +01:00
UnicornFreedom
afd468763c Add component usage color indication 2024-02-22 10:45:46 +01:00
UnicornFreedom
bfd9197340 Correct histograms not resetting 2024-02-22 09:51:49 +01:00
UnicornFreedom
e9d5a5a71a Add computer component usage graph 2024-02-22 01:47:00 +01:00
UnicornFreedom
5d8a73371b Add the number of supported components to CPU-like item tooltips 2024-02-21 23:23:49 +01:00
Fingercomp
344eacd40f
Fix a typo in TaskQueue's name 2024-01-21 01:12:13 +07:00
Fingercomp
35e64752fd
Add ocelot.clearLog()
Closes #130.
2024-01-21 00:41:31 +07:00
Fingercomp
117aa0d2d2
Synchronize access to OcelotCardItem._entries
Fixes #129.
2024-01-18 19:53:29 +07:00
Fingercomp
05819d4eba
Update ocelot-brain 2024-01-10 20:23:05 +07:00
Fingercomp
4616056add
Update ocelot-brain 2024-01-10 15:47:06 +07:00
Igor Timofeev
d7a6e75ab4 Merge branch 'refactor/lwjgl-threading' into 'develop'
Unified cross-threading lambdas to prevent setTitle()-like troubles

See merge request cc-ru/ocelot/ocelot-desktop!83
2024-01-07 13:13:43 +00:00
Igor Timofeev
a0bdcfd17a Unified cross-threading lambdas to prevent setTitle()-like troubles 2024-01-07 13:13:43 +00:00
Fingercomp
ff628c7829
Merge branch 'master' into develop 2024-01-05 22:06:41 +07:00
345 changed files with 8844 additions and 4735 deletions

View File

@ -17,10 +17,27 @@ variables:
stages:
- build
- test
- upload
- deploy
- release
test:
stage: test
before_script:
- sbt -v sbtVersion
script:
- sbt test
# Rest in piece.
#scalafmt:
# stage: test
# allow_failure: true
# before_script:
# - sbt -v sbtVersion
# script:
# - sbt scalafmtCheckAll
build:
stage: build
before_script:

35
.scalafmt.conf Normal file
View File

@ -0,0 +1,35 @@
version = 3.9.9
runner.dialect = scala213
preset = default
maxColumn = 120
align = {
preset = none
openParenDefnSite = false
}
newlines = {
source = keep
}
rewrite = {
rules = [SortModifiers, Imports]
sortModifiers.preset = styleGuide
imports.sort = scalastyle
trailingCommas.style = multiple
insertBraces.minLines = 3
}
docstrings = {
style = SpaceAsterisk
blankFirstLine = unfold
oneline = unfold
wrap = keep
forceBlankLineBefore = false
}
indent {
defnSite = 2
extendSite = 2
withSiteRelativeToExtends = 2
}

View File

@ -61,7 +61,7 @@ We'll make sure your setups are grounded in reality
by having the maximum card tier in a slot depend on your computer case.
Oh, did I forget to mention Ocelot Desktop has APUs as well?
Explore the wonders of distributed computing by adding a couple of other
Explore the wonders of distributed computing by adding a couple of extra
computers to your workspace.
(Or a thousand — if you think your host can handle this.)
Network cards allow these newly spawned machines to talk to each other.
@ -82,7 +82,7 @@ drives.
Manage your screen real estate to avoid distractions.
All nodes are draggable, as are windows.
And screen windows in particular are also resizeable —
Screen windows in particular are also resizeable —
click and drag the bottom-right corner if they take up too much space.
Or hold <kbd>Shift</kbd> and let the window consume it all.
@ -125,6 +125,8 @@ A few smaller features are worth mentioning, too:
shows performance graphs: the memory, processor time, and call budget.
- Hold the <kbd>Ctrl</kbd> key while dragging blocks to snap them to the grid.
Full list of hotkeys and supported features is available in the [FAQ][ocelot-desktop-faq].
## Download
Decided to give Ocelot Desktop a shot?
@ -170,7 +172,7 @@ home directory.
## Credits
- **LeshaInc:** the author of Ocelot Desktop and the graphics guru.
- **Totoro:** the creator of [ocelot-brain][] and [ocelot.online][ocelot-online].
- **bpm140:** produced a marvelous Ocelot Desktop landing page.
- **bpm140:** produced the marvelous Ocelot Desktop [landing page][ocelot-desktop].
- **rason:** stirred development at the critical moment!
- **NE0:** the bug extermination specialist.
- **ECS:** introduced the exciting realm of cloud computing.
@ -201,5 +203,6 @@ home directory.
[ocelot-brain]: https://gitlab.com/cc-ru/ocelot/ocelot-brain
[ocelot-online]: https://ocelot.fomalhaut.me/
[ocelot-desktop]: https://ocelot.fomalhaut.me/desktop/
[ocelot-desktop-faq]: https://ocelot.fomalhaut.me/faq.html
[irc]: ircs://irc.esper.net:6697/cc.ru
[discord]: https://discord.com/invite/FM9qWGm

View File

@ -1,5 +1,5 @@
name := "ocelot-desktop"
version := "1.11.1"
version := "1.14.1"
scalaVersion := "2.13.10"
lazy val root = project.in(file("."))
@ -18,9 +18,12 @@ lazy val brain = ProjectRef(file("lib/ocelot-brain"), "ocelot-brain")
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value
libraryDependencies += "org.apache.logging.log4j" % "log4j-core" % "2.20.0"
libraryDependencies += "org.apache.logging.log4j" % "log4j-api" % "2.20.0"
libraryDependencies += "org.apache.logging.log4j" % "log4j-slf4j-impl" % "2.20.0"
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test"
libraryDependencies += "org.scalatest" %% "scalatest-funsuite" % "3.2.19" % "test"
libraryDependencies += "org.apache.logging.log4j" % "log4j-core" % "2.25.1"
libraryDependencies += "org.apache.logging.log4j" % "log4j-api" % "2.25.1"
libraryDependencies += "org.apache.logging.log4j" % "log4j-slf4j-impl" % "2.25.1"
val lwjglVersion = "2.9.3"
@ -29,7 +32,9 @@ libraryDependencies += "org.lwjgl.lwjgl" % "lwjgl-platform" % lwjglVersion class
libraryDependencies += "org.lwjgl.lwjgl" % "lwjgl-platform" % lwjglVersion classifier "natives-windows"
libraryDependencies += "org.lwjgl.lwjgl" % "lwjgl-platform" % lwjglVersion classifier "natives-osx"
libraryDependencies += "com.github.stephengold" % "j-ogg-all" % "1.0.3"
Compile / unmanagedResourceDirectories += baseDirectory.value / "lib" / "native"
libraryDependencies += "com.github.stephengold" % "j-ogg-all" % "1.0.6"
libraryDependencies += "com.github.wendykierp" % "JTransforms" % "3.1"
libraryDependencies += "com.github.sarxos" % "webcam-capture" % "0.3.12"

View File

@ -0,0 +1,25 @@
# How to compile LWJGL2 for Apple Silicon
LWJGL2 does not provide official native ARM support, therefore Ocelot uses a specially modified version by **shadowfacts**
Article: https://shadowfacts.net/2022/lwjgl-arm64/
Repository: https://github.com/shadowfacts/lwjgl2-arm64
This procedure is completely optional, as the precompiled library is already checked into the repository at `lib/native/liblwjgl-arm64.dylib`
## Compilation
This assumes that you are running macOS on **Apple Silicon** *(cross-compiling LWJGL seems to be impossible)*
1. Acquire a JDK8 built for ARM - for example, [Zulu 8 JDK](https://www.azul.com/downloads/?version=java-8-lts&os=macos&architecture=arm-64-bit&package=jdk#zulu), and add it to your `JAVA_HOME`
2. Get [`maven`](https://maven.apache.org/) and [`ant`](https://ant.apache.org/) - you may install them via `brew`, but be careful, as they will try to install a JDK as a dependency
3. Clone the LWJGL repository:
```bash
% git clone https://github.com/shadowfacts/lwjgl-arm64.git
```
4. Run the following commands in the repo:
```bash
% ant generate-all
% ant jars
% ant compile-native
```
5. Copy the compiled library from `libs/macosx/liblwjgl.dylib` into the `ocelot-desktop` project as `lib/native/liblwjgl-arm64.dylib`

View File

@ -9,7 +9,7 @@ Let's assume you want to release a new version, say, `1.33.7`.
3. Update the version of Ocelot Desktop in `build.sbt`; commit the changes.
The commit message should just say "Version 1.33.7".
4. Switch to `master` and merge `develop` into it.
5. Create an annotated tag: `git -a v1.33.7`.
5. Create an annotated tag: `git tag -a v1.33.7`.
On the first line, write "Version 1.33.7", followed by a blank line.
Then describe the changes in Markdown.
Make sure not to use `#` in the text, since git will treat it as a comment.

Binary file not shown.

@ -1 +1 @@
Subproject commit 072ad32d0e512829c9ef50d12f56df7664e19b4b
Subproject commit bec1cc6b1e9e588692f753e9c617063c74967fed

View File

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

View File

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

View File

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

3
project/plugins.sbt Normal file
View File

@ -0,0 +1,3 @@
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.13.1")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.1")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.4")

0
spritepack/spritepack.sh Normal file → Executable file
View File

BIN
sprites/icons/Cut.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

BIN
sprites/icons/Keyboard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

BIN
sprites/icons/Paste.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

BIN
sprites/icons/Pause.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 B

BIN
sprites/icons/Play.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

BIN
sprites/icons/SideAny.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 B

BIN
sprites/icons/SideDown.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

BIN
sprites/icons/SideEast.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 B

BIN
sprites/icons/SideNone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

BIN
sprites/icons/SideNorth.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

BIN
sprites/icons/SideSouth.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

BIN
sprites/icons/SideUp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

BIN
sprites/icons/SideWest.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 644 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 B

BIN
sprites/particles/Smoke.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 546 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2002-2007 Lightweight Java Game Library Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'Light Weight Java Game Library' nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

View File

@ -6,11 +6,12 @@ AboutForegroundSubtle = #eeeeeeee
AboutButtonForeground = #222222
AboutButtonBorder = #444444
AboutButtonBackground = #777777
AboutButtonBackgroundActive = #888888
PortDown = #8382d8
PortUp = #75bdc1
PortNorth = #c8ca5f
PortSouth = #990da3
PortSouth = #ed8ef4
PortEast = #7ec95f
PortWest = #db7d75
PortAny = #9b9b9b
@ -22,6 +23,7 @@ Tier3 = #c354cd
Label = #333333
LabelError = #aa0000
LabelDisabled = #888888
Scrollbar = #e5e5e526
ScrollbarThumb = #cc3f72
@ -41,15 +43,14 @@ ContextMenuHover = #363636
ContextMenuSeparator = #444444
ContextMenuIcon = #7F7F7F
ComponentSelectorBackground = #222222cc
ComponentSelectorBorder = #444444
ComponentSelectorText = #b0b0b0
ComputerAddress = #333333
ScreenOff = #000000
WindowBackground = #c6c6c6
StatusBar = #181818
StatusBarActive = #ffffff04
StatusBarBorder = #222222
StatusBarForeground = #777777
StatusBarIcon = #bbbbbb
@ -66,14 +67,17 @@ TextInputBorder = #888888
TextInputBorderDisabled = #666666
TextInputBorderFocused = #336666
TextInputBackground = #aaaaaa
TextInputBackgroundDisabled = #aaaaaa
TextInputBackgroundActive = #bbbbbb
TextInputBackgroundSelected = #336666
TextInputForeground = #333333
TextInputForegroundSelected = #aaaaaa
TextInputForegroundDisabled = #888888
TextInputBorderError = #aa8888
TextInputBorderErrorDisabled = #aa8888
TextInputBorderErrorFocused = #cc6666
ButtonBackground = #aaaaaa
ButtonBackgroundActive = #bbbbbb
ButtonBorder = #888888
ButtonForeground = #333333
ButtonForegroundPressed = #444444
@ -89,7 +93,11 @@ HistogramBarTop = #ccfdcc
HistogramBarFill = #64cc65
HistogramGrid = #336633
HistogramFill = #73ff7360
HistogramFillWarning = #ffda7360
HistogramFillError = #ff737f60
HistogramEdge = #ccfdcc
HistogramEdgeWarning = #fdeacc
HistogramEdgeError = #fdccd0
VerticalMenuBackground = #c6c6c6
VerticalMenuEntryIcon = #888888
@ -98,12 +106,14 @@ VerticalMenuEntryForeground = #333333
VerticalMenuBorder = #dfdfdf
SliderBackground = #aaaaaa
SliderBackgroundActive = #bbbbbb
SliderBorder = #888888
SliderTick = #989898
SliderHandler = #bbbbbb
SliderForeground = #333333
CheckboxBackground = #aaaaaa
CheckboxBackgroundActive = #bbbbbb
CheckboxBorder = #888888
CheckboxForeground = #333333
CheckboxLabel = #333333
@ -177,3 +187,14 @@ LogEntryTxForeground = #cccccc
LogEntryTxBorder = #183a41
OcelotCardTooltip = #c1905f
RackRelayButtonText = #e0e0e0
Flash = #ffffff
RelayTextLow = #009900
RelayTextMid = #999900
RelayTextHigh = #990000
BoomCardGlowStart = #ff4010
BoomCardGlowEnd = #ff1010

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 150 KiB

View File

@ -1,322 +1,347 @@
BackgroundPattern 0 0 304 304
BarSegment 385 434 16 4
Empty 249 655 16 16
EmptySlot 220 707 18 18
BarSegment 308 305 16 4
Empty 134 632 16 16
EmptySlot 246 567 18 18
Knob 203 434 50 50
KnobCenter 254 434 50 50
KnobLimits 305 434 50 50
Loading 0 305 48 448
Logo 305 0 168 200
ShadowBorder 279 305 1 24
ShadowCorner 216 674 24 24
TabArrow 134 634 8 14
blocks/Generic 266 655 16 16
blocks/HologramEffect 287 314 4 4
blocks/HologramProjector1Top 283 655 16 16
blocks/HologramProjector2Top 300 655 16 16
blocks/HologramProjectorSide 317 655 16 16
buttons/BottomDrawerClose 239 707 18 18
buttons/BottomDrawerOpen 258 707 18 18
buttons/OpenFMRadioCloseOff 404 445 7 8
buttons/OpenFMRadioCloseOn 412 445 7 8
buttons/OpenFMRadioRedstoneOff 359 445 8 8
buttons/OpenFMRadioRedstoneOn 368 445 8 8
buttons/OpenFMRadioStartOff 241 674 24 24
buttons/OpenFMRadioStartOn 266 674 24 24
buttons/OpenFMRadioStopOff 291 674 24 24
buttons/OpenFMRadioStopOn 316 674 24 24
buttons/OpenFMRadioVolumeDownOff 143 634 10 10
buttons/OpenFMRadioVolumeDownOn 154 634 10 10
buttons/OpenFMRadioVolumeUpOff 165 634 10 10
buttons/OpenFMRadioVolumeUpOn 176 634 10 10
buttons/PowerOff 277 707 18 18
buttons/PowerOn 296 707 18 18
buttons/RackRelayOff 117 655 65 18
buttons/RackRelayOn 183 655 65 18
icons/Antenna 334 655 16 16
icons/ArrowRight 351 655 16 16
icons/AspectRatio 368 655 16 16
icons/Book 385 655 16 16
icons/ButtonCheck 341 674 17 17
icons/ButtonClipboard 359 674 17 17
icons/ButtonRandomize 377 674 17 17
icons/CPU 402 655 16 16
icons/Card 419 655 16 16
icons/Close 134 601 15 14
icons/Code 436 655 16 16
icons/ComponentBus 453 655 16 16
icons/Copy 470 655 16 16
icons/Cross 487 655 16 16
icons/Delete 504 655 16 16
icons/DragLMB 483 707 21 14
icons/DragRMB 505 707 21 14
icons/EEPROM 521 655 16 16
icons/Edit 538 655 16 16
icons/Eject 555 655 16 16
icons/File 572 655 16 16
icons/Floppy 589 655 16 16
icons/Folder 606 655 16 16
icons/FolderSlash 623 655 16 16
icons/Grid 151 707 22 22
icons/GridOff 174 707 22 22
icons/Guitar 640 655 16 16
icons/HDD 657 655 16 16
icons/Help 674 655 16 16
icons/Home 197 707 22 22
icons/LMB 218 616 11 14
icons/Label 691 655 16 16
icons/LinesHorizontal 708 655 16 16
icons/Link 725 655 16 16
icons/LinkSlash 742 655 16 16
icons/Memory 759 655 16 16
icons/Microchip 776 655 16 16
icons/NA 793 655 16 16
icons/NotificationError 242 616 11 11
icons/NotificationInfo 254 616 11 11
icons/NotificationWarning 266 616 11 11
icons/Ocelot 810 655 16 16
icons/Pin 173 616 14 14
icons/Plus 827 655 16 16
icons/Power 844 655 16 16
icons/RMB 230 616 11 14
icons/Restart 861 655 16 16
icons/Save 878 655 16 16
icons/SaveAs 895 655 16 16
icons/Server 912 655 16 16
icons/SettingsSound 134 616 12 17
icons/SettingsSystem 147 616 12 17
icons/SettingsUI 160 616 12 17
icons/Tier0 929 655 16 16
icons/Tier1 946 655 16 16
icons/Tier2 963 655 16 16
icons/Tiers 980 655 16 16
icons/Unpin 188 616 14 14
icons/WaveLFSR 799 584 24 10
icons/WaveNoise 824 584 24 10
icons/WaveSawtooth 849 584 24 10
icons/WaveSine 874 584 24 10
icons/WaveSquare 899 584 24 10
icons/WaveTriangle 924 584 24 10
icons/Window 997 655 16 16
icons/WireArrowLeft 281 305 4 8
icons/WireArrowRight 286 305 4 8
ShadowBorder 279 344 1 24
ShadowCorner 233 674 24 24
TabArrow 356 452 8 14
blocks/Generic 151 632 16 16
blocks/HologramEffect 291 344 4 4
blocks/HologramProjector1Top 168 632 16 16
blocks/HologramProjector2Top 185 632 16 16
blocks/HologramProjectorSide 202 632 16 16
buttons/BottomDrawerClose 265 567 18 18
buttons/BottomDrawerOpen 284 567 18 18
buttons/OpenFMRadioCloseOff 327 316 7 8
buttons/OpenFMRadioCloseOn 335 316 7 8
buttons/OpenFMRadioRedstoneOff 282 316 8 8
buttons/OpenFMRadioRedstoneOn 291 316 8 8
buttons/OpenFMRadioStartOff 258 674 24 24
buttons/OpenFMRadioStartOn 283 674 24 24
buttons/OpenFMRadioStopOff 308 674 24 24
buttons/OpenFMRadioStopOn 333 674 24 24
buttons/OpenFMRadioVolumeDownOff 365 452 10 10
buttons/OpenFMRadioVolumeDownOn 376 452 10 10
buttons/OpenFMRadioVolumeUpOff 387 452 10 10
buttons/OpenFMRadioVolumeUpOn 398 452 10 10
buttons/PowerOff 303 567 18 18
buttons/PowerOn 322 567 18 18
buttons/RackRelayOff 134 655 65 18
buttons/RackRelayOn 200 655 65 18
icons/Antenna 219 632 16 16
icons/ArrowRight 236 632 16 16
icons/AspectRatio 253 632 16 16
icons/Book 270 632 16 16
icons/ButtonCheck 143 600 17 17
icons/ButtonClipboard 161 600 17 17
icons/ButtonRandomize 179 600 17 17
icons/CPU 287 632 16 16
icons/Card 304 632 16 16
icons/Close 218 600 15 14
icons/Code 321 632 16 16
icons/ComponentBus 338 632 16 16
icons/Copy 355 632 16 16
icons/Cross 372 632 16 16
icons/Cut 389 632 16 16
icons/Delete 406 632 16 16
icons/DragLMB 509 567 21 14
icons/DragRMB 531 567 21 14
icons/EEPROM 423 632 16 16
icons/Edit 440 632 16 16
icons/Eject 457 632 16 16
icons/File 474 632 16 16
icons/Floppy 491 632 16 16
icons/Folder 508 632 16 16
icons/FolderSlash 525 632 16 16
icons/Grid 177 567 22 22
icons/GridOff 200 567 22 22
icons/Guitar 542 632 16 16
icons/HDD 559 632 16 16
icons/Help 576 632 16 16
icons/Home 223 567 22 22
icons/Keyboard 593 632 16 16
icons/KeyboardOff 610 632 16 16
icons/LMB 453 434 11 14
icons/Label 627 632 16 16
icons/LinesHorizontal 644 632 16 16
icons/Link 661 632 16 16
icons/LinkSlash 678 632 16 16
icons/Memory 695 632 16 16
icons/Microchip 712 632 16 16
icons/NA 729 632 16 16
icons/NotificationError 477 434 11 11
icons/NotificationInfo 489 434 11 11
icons/NotificationWarning 501 434 11 11
icons/Ocelot 746 632 16 16
icons/Paste 763 632 16 16
icons/Pause 780 632 16 16
icons/Pin 408 434 14 14
icons/Play 797 632 16 16
icons/Plus 814 632 16 16
icons/Power 831 632 16 16
icons/RMB 465 434 11 14
icons/Restart 848 632 16 16
icons/Save 865 632 16 16
icons/SaveAs 882 632 16 16
icons/Server 899 632 16 16
icons/SettingsKeymap 356 434 12 17
icons/SettingsSound 369 434 12 17
icons/SettingsSystem 382 434 12 17
icons/SettingsUI 395 434 12 17
icons/SideAny 513 434 11 11
icons/SideDown 525 434 11 11
icons/SideEast 537 434 11 11
icons/SideNone 549 434 11 11
icons/SideNorth 561 434 11 11
icons/SideSouth 573 434 11 11
icons/SideUndefined 585 434 11 11
icons/SideUp 597 434 11 11
icons/SideWest 609 434 11 11
icons/Tier0 916 632 16 16
icons/Tier1 933 632 16 16
icons/Tier2 950 632 16 16
icons/Tiers 967 632 16 16
icons/Unpin 423 434 14 14
icons/WaveLFSR 288 540 24 10
icons/WaveNoise 313 540 24 10
icons/WaveSawtooth 338 540 24 10
icons/WaveSine 363 540 24 10
icons/WaveSquare 388 540 24 10
icons/WaveTriangle 413 540 24 10
icons/Window 984 632 16 16
icons/WireArrowLeft 281 344 4 8
icons/WireArrowRight 286 344 4 8
items/APU0 49 655 16 96
items/APU1 66 655 16 96
items/APU2 83 655 16 96
items/CPU0 201 540 16 16
items/CPU1 218 540 16 16
items/CPU2 235 540 16 16
items/CardBase 252 540 16 16
items/CircuitBoard 269 540 16 16
items/ComponentBus0 286 540 16 16
items/ComponentBus1 303 540 16 16
items/ComponentBus2 320 540 16 16
items/ComponentBus3 337 540 16 16
items/CPU0 1001 632 16 16
items/CPU1 358 674 16 16
items/CPU2 375 674 16 16
items/CardBase 392 674 16 16
items/CircuitBoard 409 674 16 16
items/ComponentBus0 426 674 16 16
items/ComponentBus1 443 674 16 16
items/ComponentBus2 460 674 16 16
items/ComponentBus3 477 674 16 16
items/DataCard0 49 526 16 128
items/DataCard1 66 526 16 128
items/DataCard2 83 526 16 128
items/DebugCard 354 540 16 16
items/DiskDriveMountable 371 540 16 16
items/EEPROM 388 540 16 16
items/FloppyDisk_dyeBlack 405 540 16 16
items/FloppyDisk_dyeBlue 422 540 16 16
items/FloppyDisk_dyeBrown 439 540 16 16
items/FloppyDisk_dyeCyan 456 540 16 16
items/FloppyDisk_dyeGray 473 540 16 16
items/FloppyDisk_dyeGreen 490 540 16 16
items/FloppyDisk_dyeLightBlue 507 540 16 16
items/FloppyDisk_dyeLightGray 524 540 16 16
items/FloppyDisk_dyeLime 541 540 16 16
items/FloppyDisk_dyeMagenta 558 540 16 16
items/FloppyDisk_dyeOrange 575 540 16 16
items/FloppyDisk_dyePink 592 540 16 16
items/FloppyDisk_dyePurple 609 540 16 16
items/FloppyDisk_dyeRed 626 540 16 16
items/FloppyDisk_dyeWhite 643 540 16 16
items/FloppyDisk_dyeYellow 660 540 16 16
items/GraphicsCard0 677 540 16 16
items/GraphicsCard1 694 540 16 16
items/GraphicsCard2 711 540 16 16
items/HardDiskDrive0 728 540 16 16
items/HardDiskDrive1 745 540 16 16
items/HardDiskDrive2 762 540 16 16
items/InternetCard 117 707 16 32
items/DebugCard 494 674 16 16
items/DiskDriveMountable 511 674 16 16
items/EEPROM 528 674 16 16
items/FloppyDisk_dyeBlack 545 674 16 16
items/FloppyDisk_dyeBlue 562 674 16 16
items/FloppyDisk_dyeBrown 579 674 16 16
items/FloppyDisk_dyeCyan 596 674 16 16
items/FloppyDisk_dyeGray 613 674 16 16
items/FloppyDisk_dyeGreen 630 674 16 16
items/FloppyDisk_dyeLightBlue 647 674 16 16
items/FloppyDisk_dyeLightGray 664 674 16 16
items/FloppyDisk_dyeLime 681 674 16 16
items/FloppyDisk_dyeMagenta 698 674 16 16
items/FloppyDisk_dyeOrange 715 674 16 16
items/FloppyDisk_dyePink 732 674 16 16
items/FloppyDisk_dyePurple 749 674 16 16
items/FloppyDisk_dyeRed 766 674 16 16
items/FloppyDisk_dyeWhite 783 674 16 16
items/FloppyDisk_dyeYellow 800 674 16 16
items/GraphicsCard0 817 674 16 16
items/GraphicsCard1 834 674 16 16
items/GraphicsCard2 851 674 16 16
items/HardDiskDrive0 868 674 16 16
items/HardDiskDrive1 885 674 16 16
items/HardDiskDrive2 902 674 16 16
items/InternetCard 143 567 16 32
items/LinkedCard 100 655 16 96
items/Memory0 779 540 16 16
items/Memory1 796 540 16 16
items/Memory2 813 540 16 16
items/Memory3 830 540 16 16
items/Memory4 847 540 16 16
items/Memory5 864 540 16 16
items/Memory6 881 540 16 16
items/NetworkCard 898 540 16 16
items/Memory0 919 674 16 16
items/Memory1 936 674 16 16
items/Memory2 953 674 16 16
items/Memory3 970 674 16 16
items/Memory4 987 674 16 16
items/Memory5 1004 674 16 16
items/Memory6 134 707 16 16
items/NetworkCard 151 707 16 16
items/OcelotCard 100 526 16 128
items/RedstoneCard0 915 540 16 16
items/RedstoneCard1 932 540 16 16
items/SelfDestructingCard 134 707 16 32
items/Server0 949 540 16 16
items/Server1 966 540 16 16
items/Server2 983 540 16 16
items/Server3 1000 540 16 16
items/RedstoneCard0 168 707 16 16
items/RedstoneCard1 185 707 16 16
items/SelfDestructingCard 160 567 16 32
items/Server0 202 707 16 16
items/Server1 219 707 16 16
items/Server2 236 707 16 16
items/Server3 253 707 16 16
items/SoundCard 117 526 16 128
items/TapeCopper 134 567 16 16
items/TapeDiamond 151 567 16 16
items/TapeGold 168 567 16 16
items/TapeGreg 185 567 16 16
items/TapeIg 202 567 16 16
items/TapeIron 219 567 16 16
items/TapeNetherStar 236 567 16 16
items/TapeSteel 253 567 16 16
items/WirelessNetworkCard0 270 567 16 16
items/WirelessNetworkCard1 287 567 16 16
light-panel/BookmarkLeft 780 584 18 14
light-panel/BookmarkRight 395 674 20 14
light-panel/BorderB 292 314 4 4
light-panel/BorderL 382 314 4 2
light-panel/BorderR 297 314 4 4
light-panel/BorderT 302 314 4 4
light-panel/CornerBL 307 314 4 4
light-panel/CornerBR 312 314 4 4
light-panel/CornerTL 317 314 4 4
light-panel/CornerTR 322 314 4 4
light-panel/Fill 285 325 2 2
light-panel/Vent 356 434 2 38
nodes/Cable 377 445 8 8
nodes/Camera 304 567 16 16
nodes/Chest 203 616 14 14
nodes/HologramProjector0 321 567 16 16
nodes/HologramProjector1 338 567 16 16
nodes/IronNoteBlock 355 567 16 16
nodes/Lamp 372 567 16 16
nodes/LampFrame 389 567 16 16
items/TapeCopper 270 707 16 16
items/TapeDiamond 287 707 16 16
items/TapeGold 304 707 16 16
items/TapeGreg 321 707 16 16
items/TapeIg 338 707 16 16
items/TapeIron 355 707 16 16
items/TapeNetherStar 372 707 16 16
items/TapeSteel 389 707 16 16
items/WirelessNetworkCard0 406 707 16 16
items/WirelessNetworkCard1 423 707 16 16
light-panel/BookmarkLeft 269 540 18 14
light-panel/BookmarkRight 197 600 20 14
light-panel/BorderB 296 344 4 4
light-panel/BorderL 284 353 4 2
light-panel/BorderR 301 344 4 4
light-panel/BorderT 306 344 4 4
light-panel/CornerBL 311 344 4 4
light-panel/CornerBR 316 344 4 4
light-panel/CornerTL 321 344 4 4
light-panel/CornerTR 326 344 4 4
light-panel/Fill 410 344 2 2
light-panel/Vent 279 305 2 38
nodes/Cable 300 316 8 8
nodes/Camera 440 707 16 16
nodes/Chest 438 434 14 14
nodes/HologramProjector0 457 707 16 16
nodes/HologramProjector1 474 707 16 16
nodes/IronNoteBlock 491 707 16 16
nodes/Lamp 508 707 16 16
nodes/LampFrame 525 707 16 16
nodes/LampGlow 49 305 128 128
nodes/NewNode 406 567 16 16
nodes/NoteBlock 423 567 16 16
nodes/OpenFMRadio 440 567 16 16
nodes/Relay 457 567 16 16
nodes/TapeDrive 474 567 16 16
nodes/computer/Default 491 567 16 16
nodes/computer/DiskActivity 508 567 16 16
nodes/computer/Error 525 567 16 16
nodes/computer/On 542 567 16 16
nodes/disk-drive/Default 559 567 16 16
nodes/disk-drive/DiskActivity 576 567 16 16
nodes/disk-drive/Floppy 593 567 16 16
nodes/holidays/Christmas 117 674 32 32
nodes/holidays/Halloween 150 674 32 32
nodes/holidays/Valentines 183 674 32 32
nodes/microcontroller/Default 610 567 16 16
nodes/microcontroller/Error 627 567 16 16
nodes/microcontroller/On 644 567 16 16
nodes/rack/Default 661 567 16 16
nodes/rack/Empty 678 567 16 16
nodes/rack/drive/0/Default 695 567 16 16
nodes/rack/drive/0/DiskActivity 712 567 16 16
nodes/rack/drive/0/Floppy 729 567 16 16
nodes/rack/drive/1/Default 746 567 16 16
nodes/rack/drive/1/DiskActivity 763 567 16 16
nodes/rack/drive/1/Floppy 780 567 16 16
nodes/rack/drive/2/Default 797 567 16 16
nodes/rack/drive/2/DiskActivity 814 567 16 16
nodes/rack/drive/2/Floppy 831 567 16 16
nodes/rack/drive/3/Default 848 567 16 16
nodes/rack/drive/3/DiskActivity 865 567 16 16
nodes/rack/drive/3/Floppy 882 567 16 16
nodes/rack/drive/Floppy 899 567 16 16
nodes/rack/server/0/Default 916 567 16 16
nodes/rack/server/0/DiskActivity 933 567 16 16
nodes/rack/server/0/Error 950 567 16 16
nodes/rack/server/0/NetworkActivity 967 567 16 16
nodes/rack/server/0/On 984 567 16 16
nodes/rack/server/1/Default 1001 567 16 16
nodes/rack/server/1/DiskActivity 134 584 16 16
nodes/rack/server/1/Error 151 584 16 16
nodes/rack/server/1/NetworkActivity 168 584 16 16
nodes/rack/server/1/On 185 584 16 16
nodes/rack/server/2/Default 202 584 16 16
nodes/rack/server/2/DiskActivity 219 584 16 16
nodes/rack/server/2/Error 236 584 16 16
nodes/rack/server/2/NetworkActivity 253 584 16 16
nodes/rack/server/2/On 270 584 16 16
nodes/rack/server/3/Default 287 584 16 16
nodes/rack/server/3/DiskActivity 304 584 16 16
nodes/rack/server/3/Error 321 584 16 16
nodes/rack/server/3/NetworkActivity 338 584 16 16
nodes/rack/server/3/On 355 584 16 16
nodes/raid/0/DiskActivity 372 584 16 16
nodes/raid/0/Error 389 584 16 16
nodes/raid/1/DiskActivity 406 584 16 16
nodes/raid/1/Error 423 584 16 16
nodes/raid/2/DiskActivity 440 584 16 16
nodes/raid/2/Error 457 584 16 16
nodes/raid/Default 474 584 16 16
nodes/screen/BottomLeft 491 584 16 16
nodes/screen/BottomMiddle 508 584 16 16
nodes/screen/BottomRight 525 584 16 16
nodes/screen/ColumnBottom 542 584 16 16
nodes/screen/ColumnMiddle 559 584 16 16
nodes/screen/ColumnTop 576 584 16 16
nodes/screen/Middle 593 584 16 16
nodes/screen/MiddleLeft 610 584 16 16
nodes/screen/MiddleRight 627 584 16 16
nodes/screen/PowerOnOverlay 644 584 16 16
nodes/screen/RowLeft 661 584 16 16
nodes/screen/RowMiddle 678 584 16 16
nodes/screen/RowRight 695 584 16 16
nodes/screen/Standalone 712 584 16 16
nodes/screen/TopLeft 729 584 16 16
nodes/screen/TopMiddle 746 584 16 16
nodes/screen/TopRight 763 584 16 16
panel/BorderB 327 314 4 4
panel/BorderL 387 314 4 2
panel/BorderR 332 314 4 4
panel/BorderT 337 314 4 4
panel/CornerBL 342 314 4 4
panel/CornerBR 347 314 4 4
panel/CornerTL 352 314 4 4
panel/CornerTR 357 314 4 4
panel/Fill 288 325 2 2
particles/Note 377 434 7 10
screen/BorderB 284 314 2 8
screen/BorderT 281 314 2 10
screen/CornerBL 386 445 8 8
screen/CornerBR 395 445 8 8
screen/CornerTL 359 434 8 10
screen/CornerTR 368 434 8 10
window/BorderDark 281 325 1 4
window/BorderLight 283 325 1 4
window/CornerBL 362 314 4 4
window/CornerBR 367 314 4 4
window/CornerTL 372 314 4 4
window/CornerTR 377 314 4 4
nodes/NewNode 542 707 16 16
nodes/NoteBlock 559 707 16 16
nodes/OpenFMRadio 576 707 16 16
nodes/Relay 593 707 16 16
nodes/TapeDrive 610 707 16 16
nodes/computer/Default 627 707 16 16
nodes/computer/DiskActivity 644 707 16 16
nodes/computer/Error 661 707 16 16
nodes/computer/On 678 707 16 16
nodes/disk-drive/Default 695 707 16 16
nodes/disk-drive/DiskActivity 712 707 16 16
nodes/disk-drive/Floppy 729 707 16 16
nodes/holidays/Christmas 134 674 32 32
nodes/holidays/Halloween 167 674 32 32
nodes/holidays/Valentines 200 674 32 32
nodes/microcontroller/Default 746 707 16 16
nodes/microcontroller/Error 763 707 16 16
nodes/microcontroller/On 780 707 16 16
nodes/ocelot-block/Default 117 655 16 80
nodes/ocelot-block/Rx 797 707 16 16
nodes/ocelot-block/Tx 814 707 16 16
nodes/rack/Default 831 707 16 16
nodes/rack/Empty 848 707 16 16
nodes/rack/drive/0/Default 865 707 16 16
nodes/rack/drive/0/DiskActivity 882 707 16 16
nodes/rack/drive/0/Floppy 899 707 16 16
nodes/rack/drive/1/Default 916 707 16 16
nodes/rack/drive/1/DiskActivity 933 707 16 16
nodes/rack/drive/1/Floppy 950 707 16 16
nodes/rack/drive/2/Default 967 707 16 16
nodes/rack/drive/2/DiskActivity 984 707 16 16
nodes/rack/drive/2/Floppy 1001 707 16 16
nodes/rack/drive/3/Default 266 655 16 16
nodes/rack/drive/3/DiskActivity 283 655 16 16
nodes/rack/drive/3/Floppy 300 655 16 16
nodes/rack/drive/Floppy 317 655 16 16
nodes/rack/server/0/Default 334 655 16 16
nodes/rack/server/0/DiskActivity 351 655 16 16
nodes/rack/server/0/Error 368 655 16 16
nodes/rack/server/0/NetworkActivity 385 655 16 16
nodes/rack/server/0/On 402 655 16 16
nodes/rack/server/1/Default 419 655 16 16
nodes/rack/server/1/DiskActivity 436 655 16 16
nodes/rack/server/1/Error 453 655 16 16
nodes/rack/server/1/NetworkActivity 470 655 16 16
nodes/rack/server/1/On 487 655 16 16
nodes/rack/server/2/Default 504 655 16 16
nodes/rack/server/2/DiskActivity 521 655 16 16
nodes/rack/server/2/Error 538 655 16 16
nodes/rack/server/2/NetworkActivity 555 655 16 16
nodes/rack/server/2/On 572 655 16 16
nodes/rack/server/3/Default 589 655 16 16
nodes/rack/server/3/DiskActivity 606 655 16 16
nodes/rack/server/3/Error 623 655 16 16
nodes/rack/server/3/NetworkActivity 640 655 16 16
nodes/rack/server/3/On 657 655 16 16
nodes/raid/0/DiskActivity 674 655 16 16
nodes/raid/0/Error 691 655 16 16
nodes/raid/1/DiskActivity 708 655 16 16
nodes/raid/1/Error 725 655 16 16
nodes/raid/2/DiskActivity 742 655 16 16
nodes/raid/2/Error 759 655 16 16
nodes/raid/Default 776 655 16 16
nodes/screen/BottomLeft 793 655 16 16
nodes/screen/BottomMiddle 810 655 16 16
nodes/screen/BottomRight 827 655 16 16
nodes/screen/ColumnBottom 844 655 16 16
nodes/screen/ColumnMiddle 861 655 16 16
nodes/screen/ColumnTop 878 655 16 16
nodes/screen/Middle 895 655 16 16
nodes/screen/MiddleLeft 912 655 16 16
nodes/screen/MiddleRight 929 655 16 16
nodes/screen/PowerOnOverlay 946 655 16 16
nodes/screen/RowLeft 963 655 16 16
nodes/screen/RowMiddle 980 655 16 16
nodes/screen/RowRight 997 655 16 16
nodes/screen/Standalone 201 540 16 16
nodes/screen/TopLeft 218 540 16 16
nodes/screen/TopMiddle 235 540 16 16
nodes/screen/TopRight 252 540 16 16
panel/BorderB 331 344 4 4
panel/BorderL 289 353 4 2
panel/BorderR 336 344 4 4
panel/BorderT 341 344 4 4
panel/CornerBL 346 344 4 4
panel/CornerBR 351 344 4 4
panel/CornerTL 356 344 4 4
panel/CornerTR 361 344 4 4
panel/Fill 413 344 2 2
particles/Note 300 305 7 10
particles/Smoke 134 567 8 64
screen/InnerBorderB 281 360 2 4
screen/InnerBorderT 284 360 2 4
screen/InnerCornerBL 366 344 4 4
screen/InnerCornerBR 371 344 4 4
screen/InnerCornerTL 376 344 4 4
screen/InnerCornerTR 381 344 4 4
screen/OuterBorderT 281 353 2 6
screen/OuterCornerBL 309 316 8 8
screen/OuterCornerBR 318 316 8 8
screen/OuterCornerTL 282 305 8 10
screen/OuterCornerTR 291 305 8 10
window/BorderDark 406 344 1 4
window/BorderLight 408 344 1 4
window/CornerBL 386 344 4 4
window/CornerBR 391 344 4 4
window/CornerTL 396 344 4 4
window/CornerTR 401 344 4 4
window/OpenFMRadio 474 0 232 105
window/case/Motherboard 123 434 79 70
window/rack/Lines 49 434 73 91
window/rack/Motherboard 178 305 100 78
window/rack/NetworkBack 299 319 1 2
window/rack/NetworkBottom 301 319 1 2
window/rack/NetworkConnector 303 319 1 2
window/rack/NetworkLeft 305 319 1 2
window/rack/NetworkRight 307 319 1 2
window/rack/NetworkTop 309 319 1 2
window/rack/NodeBack 392 314 5 1
window/rack/NodeBottom 398 314 5 1
window/rack/NodeLeft 404 314 5 1
window/rack/NodeRight 410 314 5 1
window/rack/NodeTop 416 314 5 1
window/rack/SideBack 287 319 1 3
window/rack/SideBottom 289 319 1 3
window/rack/SideConnector 291 319 1 3
window/rack/SideLeft 293 319 1 3
window/rack/SideRight 295 319 1 3
window/rack/SideTop 297 319 1 3
window/rack/NetworkBack 293 365 1 2
window/rack/NetworkBottom 295 365 1 2
window/rack/NetworkConnector 297 365 1 2
window/rack/NetworkLeft 299 365 1 2
window/rack/NetworkRight 301 365 1 2
window/rack/NetworkTop 303 365 1 2
window/rack/NodeBack 287 360 5 1
window/rack/NodeBottom 293 360 5 1
window/rack/NodeLeft 299 360 5 1
window/rack/NodeRight 305 360 5 1
window/rack/NodeTop 311 360 5 1
window/rack/SideBack 281 365 1 3
window/rack/SideBottom 283 365 1 3
window/rack/SideConnector 285 365 1 3
window/rack/SideLeft 287 365 1 3
window/rack/SideRight 289 365 1 3
window/rack/SideTop 291 365 1 3
window/raid/Slots 134 540 66 26
window/tape/Back 315 707 20 15
window/tape/BackPressed 336 707 20 15
window/tape/Forward 357 707 20 15
window/tape/ForwardPressed 378 707 20 15
window/tape/Play 399 707 20 15
window/tape/PlayPressed 420 707 20 15
window/tape/Back 341 567 20 15
window/tape/BackPressed 362 567 20 15
window/tape/Forward 383 567 20 15
window/tape/ForwardPressed 404 567 20 15
window/tape/Play 425 567 20 15
window/tape/PlayPressed 446 567 20 15
window/tape/Screen 134 526 146 13
window/tape/Stop 441 707 20 15
window/tape/StopPressed 462 707 20 15
window/tape/Stop 467 567 20 15
window/tape/StopPressed 488 567 20 15

View File

@ -46,11 +46,31 @@ ocelot {
# Otherwise, Ocelot doesn't bother asking and saves the workspace right away if it knows where.
saveOnExit: true
# If enabled, Ocelot will automatically save the current workspace every `autosavePeriod` seconds.
# This will overwrite an existing save if it exists, or create a temporary save that will be loaded at the next Ocelot startup.
autosave: true
# Delay before the next autosave happens (in seconds).
autosavePeriod: 300
# If enabled, Ocelot loads the most recently opened workspace automatically on startup.
openLastWorkspace: true
# If true, screen nodes will render mini-previews of their content in realtime
# Otherwise, content will be shown only in windows
renderScreenDataOnNodes: false
renderScreenDataOnNodes: true
# Delay before a tooltip appears when hovering over items (in seconds)
tooltipDelayItem: 0.001
# Delay before a tooltip appears when hovering over UI elements (in seconds)
tooltipDelayUI: 0.3
# Whether to show small holiday decorations (pumpkins on Halloween for example)
enableFestiveDecorations: true
}
render {
# Whether mipmap scaling is enabled for screen windows (when shrinking).
screenWindowMipmap: true
}
}

View File

@ -1,4 +1,4 @@
#version 140
#version 150
in vec3 inPos;
in vec3 inNormal;

View File

@ -28,7 +28,7 @@ class ColorScheme extends Logging {
val bytes = java.lang.Long.parseLong(value.substring(1), 16)
val color = if (value.length == 9) {
val rgb = bytes >> 8
IntColor(rgb.toInt).toRGBANorm.withAlpha((bytes & 0xFF).toFloat / 255f)
IntColor(rgb.toInt).toRGBANorm.withAlpha((bytes & 0xff).toFloat / 255f)
} else IntColor(bytes.toInt).toRGBANorm
entries.addOne((key, color))
}

View File

@ -1,6 +1,7 @@
package ocelot.desktop
import buildinfo.BuildInfo
import ocelot.desktop.geometry.Size2D
import ocelot.desktop.inventory.Items
import ocelot.desktop.ui.UiHandler
import ocelot.desktop.ui.swing.SplashScreen
@ -29,13 +30,11 @@ import scala.io.Source
import scala.jdk.CollectionConverters._
import scala.util.{Failure, Success, Try, Using}
object OcelotDesktop
// This configures Log4j appenders & loggers, it should come before Logging in inheritance hierarchy
extends LoggingConfiguration
with Logging
{
// LoggingConfiguration configures Log4j appenders & loggers, it should come before Logging in inheritance hierarchy
object OcelotDesktop extends LoggingConfiguration with Logging {
System.setProperty("awt.useSystemAAFontSettings", "on")
System.setProperty("swing.aatext", "true")
System.setProperty("LWJGL_WM_CLASS", "Ocelot Desktop")
// Required to make all subsequent swing components look "native"
try UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName)
@ -49,6 +48,16 @@ object OcelotDesktop
val tpsCounter = new FPSCalculator
val ticker = new Ticker
private var _emulationPaused = false
def emulationPaused: Boolean = _emulationPaused
def emulationPaused_=(paused: Boolean): Unit = {
_emulationPaused = paused
// avoid sudden jumps of TPS counter after a pause
if (!paused) tpsCounter.skipSecond()
}
private val TickerIntervalHistorySize = 5
val tickerIntervalHistory = new mutable.Queue[Duration](TickerIntervalHistorySize)
@ -68,11 +77,10 @@ object OcelotDesktop
splashScreen.setStatus("Loading configuration...", 0.10f)
val customConfigPath = args.get(CommandLine.ConfigPath).flatten
val desktopConfigPath: Path =
val desktopConfigPath: Path = {
if (customConfigPath.isDefined) {
Paths.get(customConfigPath.get)
}
else {
} else {
// TODO: migration for old locations of ocelot.conf, can be safely removed later
// TODO: uncomment this line and delete everything below it when you're ready!
// OcelotPaths.desktopConfig
@ -81,23 +89,24 @@ object OcelotDesktop
try {
if (!Files.exists(newConfigPath)) {
val oldConfigPath =
val oldConfigPath = {
if (SystemUtils.IS_OS_WINDOWS)
Paths.get(OcelotPaths.windowsAppDataDirectoryName, "Ocelot", "ocelot.conf")
else
Paths.get(OcelotPaths.linuxHomeDirectoryName, ".config", "ocelot", "ocelot.conf")
}
if (Files.exists(oldConfigPath))
Files.move(oldConfigPath, newConfigPath)
}
}
catch {
} catch {
case _: Throwable =>
}
// TODO: end of upper todo <3
newConfigPath
}
}
Settings.load(desktopConfigPath)
@ -123,8 +132,7 @@ object OcelotDesktop
val loadRecentWorkspace = Settings.get.recentWorkspace.isDefined && Settings.get.openLastWorkspace
root = new RootWidget(!loadRecentWorkspace)
root.width = Display.getWidth
root.height = Display.getHeight
root.size = Size2D(Display.getWidth / Settings.get.scaleFactor, Display.getHeight / Settings.get.scaleFactor)
UiHandler.setRoot(root)
splashScreen.setStatus("Loading workspace...", 0.90f)
@ -156,22 +164,30 @@ object OcelotDesktop
}
}
val updateThread = new Thread(() => try {
val updateThread = new Thread(
() =>
try {
val currentThread = Thread.currentThread()
while (!currentThread.isInterrupted) {
if (!emulationPaused) {
Profiler.measure("tick") {
withTickLockAcquired {
workspace.update()
updateThreadTasks.run()
tpsCounter.tick()
}
}
}
ticker.waitNext()
ticker.waitNext(!emulationPaused)
}
} catch {
case _: InterruptedException => // ignore
}, "update-thread")
},
"update-thread",
)
updateThread.start()
splashScreen.dispose()
@ -181,7 +197,8 @@ object OcelotDesktop
logger.info("Cleaning up")
updateThread.interrupt()
try updateThread.join() catch {
try updateThread.join()
catch {
case _: InterruptedException =>
}
@ -231,7 +248,29 @@ object OcelotDesktop
root.workspaceView.load(frontendNBT)
if (frontendNBT.hasKey("players")) {
players.clear()
players.addAll(frontendNBT.getTagList("players", NBT.TAG_STRING).map((player: NBTTagString) => User(player.getString)))
players.addAll(
frontendNBT.getTagList("players", NBT.TAG_STRING).map((player: NBTTagString) => User(player.getString))
)
}
}
private var lastAutosave: Long = -1
def resetAutosave(): Unit = {
if (Settings.get.autosave) {
lastAutosave = System.currentTimeMillis()
logger.debug("Autosave interval was reset.")
}
}
def updateAutosave(): Unit = {
if (Settings.get.autosave) {
if (lastAutosave == -1 || System.currentTimeMillis() - lastAutosave > Settings.get.autosavePeriod * 1000) {
val path = savePath.getOrElse(tmpPath)
logger.debug(s"Autosave to $path...")
saveTo(path)
lastAutosave = System.currentTimeMillis()
}
}
}
@ -241,31 +280,37 @@ object OcelotDesktop
private def savePath_=(path: Option[Path]): Unit = {
_savePath = path
UiHandler.requestWindowTitle(UiHandler.baseWindowTitle + path.map(" - " + _).getOrElse(""))
UiHandler.windowTitleSuffix = path.map(_.toString)
}
private val tmpPath = Files.createTempDirectory("ocelot-save")
Runtime.getRuntime.addShutdownHook(new Thread(() => {
// if autosave is turned on and the project is not saved properly, we would like to keep the temporary project
if (!Settings.get.autosave || savePath.isEmpty) {
FileUtils.deleteDirectory(tmpPath.toFile)
}
}))
def newWorkspace(): Unit = showCloseConfirmationDialog("Save workspace before opening a new one?") {
root.workspaceView.newWorkspace()
savePath = None
Settings.get.recentWorkspace = None
resetAutosave()
}
private def saveTo(outputPath: Path): Unit = {
val oldPath = workspace.path
if (oldPath != outputPath) {
val (oldFiles, newFiles) =
val (oldFiles, newFiles) = {
Using.resources(Files.list(oldPath), Files.list(outputPath)) { (oldDirStream, newDirStream) =>
val oldFiles = oldDirStream.iterator.asScala.toArray
val newFiles = newDirStream.iterator.asScala.toArray
(oldFiles, newFiles)
}
}
val toRemove = newFiles.intersect(oldFiles)
@ -283,8 +328,7 @@ object OcelotDesktop
if (Files.isDirectory(path)) {
FileUtils.copyDirectory(oldFile, newFile)
}
else {
} else {
FileUtils.copyFile(oldFile, newFile)
}
}
@ -327,11 +371,12 @@ object OcelotDesktop
def saveAs(): Unit = showSaveDialog()
def showOpenDialog(): Unit =
def showOpenDialog(): Unit = {
showFileChooserDialog(JFileChooser.OPEN_DIALOG, JFileChooser.DIRECTORIES_ONLY) {
case Some(dir) => load(dir)
case None => Success(())
}
}
def load(dir: File): Try[Unit] = {
val path = Paths.get(dir.getCanonicalPath, "workspace.nbt")
@ -348,6 +393,9 @@ object OcelotDesktop
Settings.get.recentWorkspace = Some(dir.getCanonicalPath)
workspace.path = dir.toPath
loadWorld(nbt)
resetAutosave()
logger.info(s"Workspace successfully loaded from: $path")
}
}
} else Failure(new FileNotFoundException("Specified directory does not contain 'workspace.nbt'"))
@ -374,13 +422,16 @@ object OcelotDesktop
val selectedFile =
Option.when(chooser.showDialog(null, null) == JFileChooser.APPROVE_OPTION)(chooser.getSelectedFile)
// users of [showFileChooserDialog] expect that the continuation is run on the main thread.
// let's meet this expectation.
UiHandler.UiThreadTasks.add(() => {
val result = f(selectedFile)
result match {
case f @ Failure(_) => showFailureMessage(f)
case Success(_) =>
}
})
}).start()
}
@ -391,13 +442,13 @@ object OcelotDesktop
new NotificationDialog(
s"Something went wrong!\n($exception)\nCheck the log file for a full stacktrace.",
NotificationType.Error
NotificationType.Error,
).addCloseButton().show()
}
def showAddPlayerDialog(): Unit = new InputDialog(
"Add new player",
text => OcelotDesktop.selectPlayer(text)
text => OcelotDesktop.selectPlayer(text),
).show()
def player: User = if (players.nonEmpty) players.head else User("myself")
@ -424,19 +475,23 @@ object OcelotDesktop
}
var workspace: Workspace = _
val updateThreadTasks = new TaskQueue()
private def createWorkspace(): Unit = {
workspace = new Workspace(tmpPath)
}
private def prepareSavePath(path: Path)(continuation: => Unit): Unit = {
val nonEmpty = try Using.resource(Files.list(path))(_.iterator.asScala.nonEmpty) catch {
val nonEmpty = {
try Using.resource(Files.list(path))(_.iterator.asScala.nonEmpty)
catch {
case _: FileNotFoundException | _: NoSuchFileException =>
logger.info(s"Save path $path does not exist: creating a new directory")
Files.createDirectory(path)
false
}
}
if (nonEmpty) {
new NotificationDialog(
@ -444,7 +499,7 @@ object OcelotDesktop
|Files in the save directory will be included in the workspace.
|They may be overwritten, causing loss of data.
|Proceed with saving anyway?""".stripMargin,
NotificationType.Warning
NotificationType.Warning,
) {
addButton("Cancel") {
close()
@ -457,7 +512,7 @@ object OcelotDesktop
} else continuation
}
private def showSaveDialog(continuation: => Unit): Unit =
private def showSaveDialog(continuation: => Unit): Unit = {
showFileChooserDialog(JFileChooser.SAVE_DIALOG, JFileChooser.DIRECTORIES_ONLY) { dir =>
Try {
if (dir.nonEmpty) {
@ -471,6 +526,9 @@ object OcelotDesktop
save(continuation)
Settings.get.recentWorkspace = dir.map(_.getCanonicalPath)
resetAutosave()
}
}
}
}

View File

@ -5,11 +5,9 @@ import ocelot.desktop.Settings.ExtendedConfig
import ocelot.desktop.util.{Logging, SettingsData}
import org.apache.commons.lang3.SystemUtils
import java.io.InputStream
import java.nio.charset.StandardCharsets
import java.nio.file.{Files, Path}
import java.util
import scala.io.{Codec, Source}
class Settings(val config: Config) extends SettingsData {
// TODO: refactor this mess (having to declare every field 3 times is extremely error-prone)
@ -44,12 +42,24 @@ class Settings(val config: Config) extends SettingsData {
windowSize.y -= 16
}
if (config.hasPath("ocelot.keymap")) {
keymap.load(config.getConfig("ocelot.keymap"))
}
recentWorkspace = config.getOptionalString("ocelot.workspace.recent")
pinNewWindows = config.getBooleanOrElse("ocelot.workspace.pinNewWindows", default = true)
unfocusedWindowTransparency = config.getDoubleOrElse("ocelot.workspace.unfocusedWindowTransparency", 0.5)
unfocusedWindowTransparency = config.getDoubleOrElse("ocelot.workspace.unfocusedWindowTransparency", 0.5).toFloat
unfocusedWindowHide = config.getBooleanOrElse("ocelot.workspace.unfocusedWindowHide", default = false)
saveOnExit = config.getBooleanOrElse("ocelot.workspace.saveOnExit", default = true)
autosave = config.getBooleanOrElse("ocelot.workspace.autosave", default = true)
autosavePeriod = config.getIntOrElse("ocelot.workspace.autosavePeriod", default = 300)
openLastWorkspace = config.getBooleanOrElse("ocelot.workspace.openLastWorkspace", default = true)
renderScreenDataOnNodes = config.getBooleanOrElse("ocelot.workspace.renderScreenDataOnNodes", default = false)
renderScreenDataOnNodes = config.getBooleanOrElse("ocelot.workspace.renderScreenDataOnNodes", default = true)
tooltipDelayItem = (config.getDoubleOrElse("ocelot.workspace.tooltipDelayItem", 0.001) max 0.001 min 2.0).toFloat
tooltipDelayUI = (config.getDoubleOrElse("ocelot.workspace.tooltipDelayUI", 0.3) max 0.001 min 2.0).toFloat
enableFestiveDecorations = config.getBooleanOrElse("ocelot.workspace.enableFestiveDecorations", default = true)
screenWindowMipmap = config.getBooleanOrElse("ocelot.render.screenWindowMipmap", default = true)
}
object Settings extends Logging {
@ -66,23 +76,31 @@ object Settings extends Logging {
if (config.hasPath(path)) config.getBoolean(path)
else default
def getIntOrElse(path: String, default: Int): Int =
if (config.hasPath(path)) config.getInt(path)
else default
def getDoubleOrElse(path: String, default: Double): Double =
if (config.hasPath(path)) config.getDouble(path)
else default
def withValuePreserveOrigin(path: String, value: Any): Config =
config.withValue(path,
def withValuePreserveOrigin(path: String, value: Any): Config = {
config.withValue(
path,
if (config.hasPath(path))
ConfigValueFactory.fromAnyRef(value).withOrigin(config.getValue(path).origin())
else ConfigValueFactory.fromAnyRef(value)
else ConfigValueFactory.fromAnyRef(value),
)
}
def withValuePreserveOrigin(path: String, value: Option[Any]): Config =
config.withValue(path,
def withValuePreserveOrigin(path: String, value: Option[Any]): Config = {
config.withValue(
path,
if (config.hasPath(path))
ConfigValueFactory.fromAnyRef(value.orNull).withOrigin(config.getValue(path).origin())
else ConfigValueFactory.fromAnyRef(value.orNull)
else ConfigValueFactory.fromAnyRef(value.orNull),
)
}
def withValue(path: String, value: Int2D): Config = {
config.withValue(path, ConfigValueFactory.fromIterable(util.Arrays.asList(value.x, value.y)))
@ -90,12 +108,16 @@ object Settings extends Logging {
def withValue(path: String, value: Option[Any]): Config =
config.withValue(path, ConfigValueFactory.fromAnyRef(value.orNull))
def withValue(path: String, value: Int): Config =
config.withValue(path, ConfigValueFactory.fromAnyRef(value))
}
class Int2D(var x: Int, var y: Int) {
var isSet: Boolean = false
def this() = this(0, 0)
def this(list: util.List[Integer]) = {
this()
if (list.size() == 2) {
@ -116,41 +138,18 @@ object Settings extends Logging {
def get: Settings = settings
def load(path: Path): Unit = {
import java.lang.System.{lineSeparator => EOL}
if (Files.exists(path)) {
var stream: InputStream = null
try {
stream = Files.newInputStream(path)
val source = Source.fromInputStream(stream)(Codec.UTF8)
val plain = source.getLines().mkString("", EOL, EOL)
val config = ConfigFactory.parseString(plain)
settings = new Settings(config)
source.close()
settings = new Settings(ConfigFactory.parseFile(path.toFile))
logger.info(s"Loaded Ocelot Desktop configuration from: $path")
return
}
catch {
case _: Throwable =>
logger.info(s"Failed to parse $path, using default Ocelot Desktop configuration.")
}
finally {
if (stream != null)
stream.close()
} catch {
case t: Throwable => logger.error(s"Failed to parse $path!", t)
}
}
val defaults = {
val in = getClass.getResourceAsStream("/ocelot/desktop/ocelot.conf")
val config = Source.fromInputStream(in)(Codec.UTF8).getLines().mkString("", EOL, EOL)
in.close()
ConfigFactory.parseString(config)
}
settings = new Settings(defaults)
logger.info(s"Using default Ocelot Desktop configuration...")
settings = new Settings(ConfigFactory.parseResources(OcelotDesktop.getClass, "/ocelot/desktop/ocelot.conf"))
}
def save(path: Path): Unit = {
@ -172,12 +171,19 @@ object Settings extends Logging {
.withValuePreserveOrigin("ocelot.window.fullscreen", settings.windowFullscreen)
.withValuePreserveOrigin("ocelot.window.disableVsync", settings.disableVsync)
.withValuePreserveOrigin("ocelot.window.debugLwjgl", settings.debugLwjgl)
.withValue("ocelot.keymap", settings.keymap.save())
.withValue("ocelot.workspace.recent", settings.recentWorkspace)
.withValuePreserveOrigin("ocelot.workspace.pinNewWindows", settings.pinNewWindows)
.withValuePreserveOrigin("ocelot.workspace.unfocusedWindowTransparency", settings.unfocusedWindowTransparency)
.withValuePreserveOrigin("ocelot.workspace.unfocusedWindowHide", settings.unfocusedWindowHide)
.withValuePreserveOrigin("ocelot.workspace.saveOnExit", settings.saveOnExit)
.withValuePreserveOrigin("ocelot.workspace.autosave", settings.autosave)
.withValuePreserveOrigin("ocelot.workspace.autosavePeriod", settings.autosavePeriod)
.withValuePreserveOrigin("ocelot.workspace.openLastWorkspace", settings.openLastWorkspace)
.withValuePreserveOrigin("ocelot.workspace.renderScreenDataOnNodes", settings.renderScreenDataOnNodes)
.withValuePreserveOrigin("ocelot.workspace.tooltipDelayItem", settings.tooltipDelayItem)
.withValuePreserveOrigin("ocelot.workspace.tooltipDelayUI", settings.tooltipDelayUI)
.withValuePreserveOrigin("ocelot.workspace.enableFestiveDecorations", settings.enableFestiveDecorations)
if (!Files.exists(path.getParent))
Files.createDirectory(path.getParent)

View File

@ -21,7 +21,7 @@ object AL10W extends Logging {
val exc = OpenAlException(func, errName, err)
if (Settings.get.logAudioErrorStacktrace) {
logger.error(exc)
logger.error(exc.getMessage, exc)
} else {
logger.error(exc.getMessage)
}

View File

@ -15,9 +15,7 @@ object Audio extends Logging {
private val sources = new mutable.HashMap[SoundSource, Int]
private var _disabled = true
/**
* Should be called _before_ initializing any sound-related resources
*/
/** Should be called _before_ initializing any sound-related resources */
def init(): Unit = {
try {
AL.create()
@ -32,14 +30,10 @@ object Audio extends Logging {
def isDisabled: Boolean = _disabled
def numSources: Int = synchronized {
sources.size
}
def newStream(
soundCategory: SoundCategory.Value,
pitch: Float = 1f,
volume: Float = 1f
volume: Float = 1f,
): (SoundStream, SoundSource) = {
var source: SoundSource = null
@ -83,7 +77,7 @@ object Audio extends Logging {
}
}
source = SoundSource.fromStream(stream, soundCategory, looping = false, pitch, volume)
source = SoundSource.fromStream(stream, soundCategory, looping = false, pitch, volume).build()
(stream, source)
}
@ -120,7 +114,7 @@ object Audio extends Logging {
tx.onFailure { AL10W.alDeleteSources(sourceId) }
source.kind match {
case SoundSource.KindSoundBuffer(buffer) =>
case SoundSource.Kind.Buffer(buffer) =>
buffer.bufferId match {
case Some(bufferId) =>
AL10W.alSourcei(sourceId, AL10.AL_BUFFER, bufferId)
@ -130,14 +124,14 @@ object Audio extends Logging {
tx.abort()
}
case SoundSource.KindSoundSamples(samples) =>
case SoundSource.Kind.Samples(samples) =>
Transaction.run { innerTx =>
val bufferId = samples.genBuffer().getOrElse { tx.abort() }
innerTx.onFailure { AL10W.alDeleteBuffers(bufferId) }
AL10W.alSourceQueueBuffers(sourceId, bufferId)
}
case SoundSource.KindStream(_) =>
case SoundSource.Kind.Stream(_) =>
}
AL10W.alSourcef(sourceId, AL10.AL_PITCH, source.pitch)
@ -188,7 +182,7 @@ object Audio extends Logging {
AL10W.alSourcef(
sourceId,
AL10.AL_GAIN,
source.volume * SoundCategory.getSettingsValue(source.soundCategory) * Settings.get.volumeMaster
source.volume * SoundCategory.getSettingsValue(source.soundCategory) * Settings.get.volumeMaster,
)
}
@ -248,4 +242,12 @@ object Audio extends Logging {
AL10W.alDeleteBuffers(buf.get(i))
}
}
def removeAllSources(): Unit = synchronized {
for (sourceId <- sources.values) {
deleteSource(sourceId)
}
sources.clear()
}
}

View File

@ -3,7 +3,7 @@ package ocelot.desktop.audio
import java.nio.ByteBuffer
object BeepGenerator {
def newBeep(pattern: String, frequency: Short, duration: Short): SoundSource = {
def newBeep(pattern: String, frequency: Short, duration: Short): SoundSource.Factory = {
val sampleCounts = pattern.toCharArray
.map(ch => if (ch == '.') duration else 2 * duration)
.map(_ * Audio.sampleRate / 1000)
@ -19,8 +19,8 @@ object BeepGenerator {
val value = (math.signum(math.sin(angle)) * 8192).toShort
offset += step
if (offset > 1) offset -= 1
data.put((value & 0xFF).toByte)
data.put(((value >> 8) & 0xFF).toByte)
data.put((value & 0xff).toByte)
data.put(((value >> 8) & 0xff).toByte)
}
if (data.hasRemaining) {
for (_ <- 0 until pauseSampleCount) {

View File

@ -0,0 +1,6 @@
package ocelot.desktop.audio
trait ClickSoundSourceFactory {
def press: SoundSource.Factory
def release: SoundSource.Factory
}

View File

@ -8,7 +8,8 @@ import java.nio.ByteBuffer
object OggDecoder {
def decode(input: InputStream): SoundSamples = {
val stream = new VorbisStream(new BasicStream(input).getLogicalStreams.iterator().next().asInstanceOf[LogicalOggStream])
val stream =
new VorbisStream(new BasicStream(input).getLogicalStreams.iterator().next().asInstanceOf[LogicalOggStream])
val rate = stream.getIdentificationHeader.getSampleRate
val channels = stream.getIdentificationHeader.getChannels

View File

@ -3,12 +3,10 @@ package ocelot.desktop.audio
import scala.util.control
import scala.util.control.Exception.Catch
case class OpenAlException(func: String, errName: String, code: Int)
extends Exception(s"OpenAL error: $func: $errName")
case class OpenAlException(func: String, errName: String, code: Int) extends Exception(s"OpenAL error: $func: $errName")
object OpenAlException {
def defaulting[T](default: => T): Catch[T] = control.Exception.failAsValue(classOf[OpenAlException])(default)
def ignoring: Catch[Unit] = defaulting(())
}

View File

@ -6,6 +6,7 @@ import scala.collection.mutable.ArrayBuffer
object SoundBuffers extends Resource {
lazy val MachineComputerRunning: SoundBuffer = load("/ocelot/desktop/sounds/machine/computer_running.ogg")
lazy val MachineFloppyAccess: Array[SoundBuffer] = Array(
load("/ocelot/desktop/sounds/machine/floppy_access1.ogg"),
load("/ocelot/desktop/sounds/machine/floppy_access2.ogg"),
@ -14,8 +15,10 @@ object SoundBuffers extends Resource {
load("/ocelot/desktop/sounds/machine/floppy_access5.ogg"),
load("/ocelot/desktop/sounds/machine/floppy_access6.ogg"),
)
lazy val MachineFloppyEject: SoundBuffer = load("/ocelot/desktop/sounds/machine/floppy_eject.ogg")
lazy val MachineFloppyInsert: SoundBuffer = load("/ocelot/desktop/sounds/machine/floppy_insert.ogg")
lazy val MachineHDDAccess: Array[SoundBuffer] = Array(
load("/ocelot/desktop/sounds/machine/hdd_access1.ogg"),
load("/ocelot/desktop/sounds/machine/hdd_access2.ogg"),
@ -25,20 +28,27 @@ object SoundBuffers extends Resource {
load("/ocelot/desktop/sounds/machine/hdd_access6.ogg"),
)
lazy val MachineTapeButton: SoundBuffer = load("/ocelot/desktop/sounds/machine/tape_button.ogg")
lazy val MachineTapeButtonPress: SoundBuffer = load("/ocelot/desktop/sounds/machine/tape_button_press.ogg")
lazy val MachineTapeButtonRelease: SoundBuffer = load("/ocelot/desktop/sounds/machine/tape_button_release.ogg")
lazy val MachineTapeEject: SoundBuffer = load("/ocelot/desktop/sounds/machine/tape_eject.ogg")
lazy val MachineTapeInsert: SoundBuffer = load("/ocelot/desktop/sounds/machine/tape_insert.ogg")
lazy val MachineTapeRewind: SoundBuffer = load("/ocelot/desktop/sounds/machine/tape_rewind.ogg")
lazy val InterfaceClick: SoundBuffer = load("/ocelot/desktop/sounds/interface/click.ogg")
lazy val InterfaceTick: SoundBuffer = load("/ocelot/desktop/sounds/interface/tick.ogg")
lazy val InterfaceClickPress: SoundBuffer = load("/ocelot/desktop/sounds/interface/click_press.ogg")
lazy val InterfaceClickRelease: SoundBuffer = load("/ocelot/desktop/sounds/interface/click_release.ogg")
lazy val InterfaceTickPress: SoundBuffer = load("/ocelot/desktop/sounds/interface/tick_press.ogg")
lazy val InterfaceTickRelease: SoundBuffer = load("/ocelot/desktop/sounds/interface/tick_release.ogg")
lazy val InterfaceShutter: SoundBuffer = load("/ocelot/desktop/sounds/interface/shutter.ogg")
lazy val MinecraftClick: SoundBuffer = load("/ocelot/desktop/sounds/minecraft/click.ogg")
lazy val MinecraftClickPress: SoundBuffer = load("/ocelot/desktop/sounds/minecraft/click_press.ogg")
lazy val MinecraftClickRelease: SoundBuffer = load("/ocelot/desktop/sounds/minecraft/click_release.ogg")
lazy val MinecraftExplosion: SoundBuffer = load("/ocelot/desktop/sounds/minecraft/explosion.ogg")
lazy val SelfDestructingCardCountdownBeep: SoundBuffer = load("/ocelot/desktop/sounds/minecraft/countdown_beep.ogg")
lazy val NoteBlock: Map[String, SoundBuffer] = List(
"banjo", "basedrum", "bass", "bell", "bit", "chime", "cow_bell", "didgeridoo", "flute", "guitar",
"harp", "hat", "iron_xylophone", "pling", "snare", "xylophone"
"harp", "hat", "iron_xylophone", "pling", "snare", "xylophone",
).map(name => {
(name, load(s"/ocelot/desktop/sounds/minecraft/note_block/$name.ogg"))
}).toMap

View File

@ -11,13 +11,13 @@ class SoundSource(
val looping: Boolean,
val pitch: Float,
var volume: Float,
var position: Vector3D = Vector3D(0, 0, 0)
var position: Vector3D = Vector3D(0, 0, 0),
) {
def duration: Option[Duration] = kind match {
case SoundSource.KindSoundBuffer(buffer) =>
case SoundSource.Kind.Buffer(buffer) =>
Some(Duration(buffer.numSamples.toFloat / buffer.sampleRate, TimeUnit.SECONDS))
case SoundSource.KindSoundSamples(SoundSamples(buffer, rate, format)) =>
case SoundSource.Kind.Samples(SoundSamples(buffer, rate, format)) =>
val bps = format match {
case SoundSamples.Format.Stereo16 => 2
case SoundSamples.Format.Mono8 => 1
@ -26,7 +26,7 @@ class SoundSource(
Some(Duration(buffer.limit().toFloat / (rate * bps), TimeUnit.SECONDS))
case SoundSource.KindStream(_) =>
case SoundSource.Kind.Stream(_) =>
None
}
@ -34,15 +34,15 @@ class SoundSource(
Audio.getSourceStatus(this)
}
def isPlaying: Boolean = {
def playing: Boolean = {
status == SoundSource.Status.Playing
}
def isPaused: Boolean = {
def paused: Boolean = {
status == SoundSource.Status.Paused
}
def isStopped: Boolean = {
def stopped: Boolean = {
status == SoundSource.Status.Stopped
}
@ -62,26 +62,52 @@ class SoundSource(
object SoundSource {
sealed trait Kind
case class KindSoundBuffer(buffer: SoundBuffer) extends Kind
case class KindSoundSamples(samples: SoundSamples) extends Kind
case class KindStream(stream: SoundStream) extends Kind
def fromBuffer(buffer: SoundBuffer, soundCategory: SoundCategory.Value,
looping: Boolean = false, pitch: Float = 1f, volume: Float = 1f): SoundSource = {
new SoundSource(SoundSource.KindSoundBuffer(buffer), soundCategory, looping, pitch, volume)
object Kind {
case class Buffer(buffer: SoundBuffer) extends Kind
case class Samples(samples: SoundSamples) extends Kind
case class Stream(stream: SoundStream) extends Kind
}
def fromSamples(samples: SoundSamples, soundCategory: SoundCategory.Value,
looping: Boolean = false, pitch: Float = 1f, volume: Float = 1f): SoundSource = {
new SoundSource(SoundSource.KindSoundSamples(samples), soundCategory, looping, pitch, volume)
class Factory(
val kind: Kind,
val soundCategory: SoundCategory.Value,
val looping: Boolean,
val pitch: Float,
val volume: Float,
) {
def build(): SoundSource = new SoundSource(kind, soundCategory, looping, pitch, volume)
def play(): SoundSource = {
val source = build()
source.play()
source
}
}
def fromStream(stream: SoundStream, soundCategory: SoundCategory.Value,
looping: Boolean = false, pitch: Float = 1f, volume: Float = 1f): SoundSource = {
new SoundSource(SoundSource.KindStream(stream), soundCategory, looping, pitch, volume)
}
def fromBuffer(
buffer: SoundBuffer,
soundCategory: SoundCategory.Value,
looping: Boolean = false,
pitch: Float = 1f,
volume: Float = 1f,
): Factory = new Factory(SoundSource.Kind.Buffer(buffer), soundCategory, looping, pitch, volume)
def fromSamples(
samples: SoundSamples,
soundCategory: SoundCategory.Value,
looping: Boolean = false,
pitch: Float = 1f,
volume: Float = 1f,
): Factory = new Factory(SoundSource.Kind.Samples(samples), soundCategory, looping, pitch, volume)
def fromStream(
stream: SoundStream,
soundCategory: SoundCategory.Value,
looping: Boolean = false,
pitch: Float = 1f,
volume: Float = 1f,
): Factory = new Factory(SoundSource.Kind.Stream(stream), soundCategory, looping, pitch, volume)
object Status extends Enumeration {
val Playing, Paused, Stopped = Value
@ -89,13 +115,40 @@ 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)
object InterfaceClick extends ClickSoundSourceFactory {
override lazy val press: Factory = SoundSource.fromBuffer(SoundBuffers.InterfaceClickPress, 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)
override lazy val release: Factory =
SoundSource.fromBuffer(SoundBuffers.InterfaceClickRelease, SoundCategory.Interface)
}
object InterfaceClickLow extends ClickSoundSourceFactory {
override lazy val press: Factory =
SoundSource.fromBuffer(SoundBuffers.InterfaceClickPress, SoundCategory.Interface, pitch = 0.8f)
override lazy val release: Factory =
SoundSource.fromBuffer(SoundBuffers.InterfaceClickRelease, SoundCategory.Interface, pitch = 0.8f)
}
object InterfaceTick extends ClickSoundSourceFactory {
override lazy val press: Factory = SoundSource.fromBuffer(SoundBuffers.InterfaceTickPress, SoundCategory.Interface)
override lazy val release: Factory =
SoundSource.fromBuffer(SoundBuffers.InterfaceTickRelease, SoundCategory.Interface)
}
lazy val InterfaceShutter: Factory = SoundSource.fromBuffer(SoundBuffers.InterfaceShutter, SoundCategory.Interface)
object MinecraftClick extends ClickSoundSourceFactory {
override lazy val press: Factory = SoundSource.fromBuffer(SoundBuffers.MinecraftClickPress, SoundCategory.Interface)
override lazy val release: Factory =
SoundSource.fromBuffer(SoundBuffers.MinecraftClickRelease, SoundCategory.Interface)
}
lazy val MachineFloppyInsert: Factory =
SoundSource.fromBuffer(SoundBuffers.MachineFloppyInsert, SoundCategory.Environment)
lazy val MachineFloppyEject: Factory =
SoundSource.fromBuffer(SoundBuffers.MachineFloppyEject, SoundCategory.Environment)
}

View File

@ -6,8 +6,8 @@ case class IntColor(color: Int) extends Color {
override def toRGBA: RGBAColor = {
RGBAColor(
(color >> 16).toShort,
((color >> 8) & 0xFF).toShort,
(color & 0xFF).toShort,
((color >> 8) & 0xff).toShort,
(color & 0xff).toShort,
)
}

View File

@ -14,7 +14,7 @@ case class RGBAColor(r: Short, g: Short, b: Short, a: Short = 255) extends Color
r.toFloat / 255f,
g.toFloat / 255f,
b.toFloat / 255f,
a.toFloat / 255f
a.toFloat / 255f,
)
override def toHSVA: HSVAColor = toRGBANorm.toHSVA

View File

@ -1,22 +1,25 @@
package ocelot.desktop.color
import ocelot.desktop.util.NumberUtils.ExtendedFloat
import ocelot.desktop.geometry.Vector3D
import java.nio.ByteBuffer
case class RGBAColorNorm(r: Float, g: Float, b: Float, a: Float = 1f) extends Color {
assert(0 <= r && r <= 1.0f, "Invalid RED channel")
assert(0 <= g && g <= 1.0f, "Invalid GREEN channel")
assert(0 <= b && b <= 1.0f, "Invalid BLUE channel")
assert(0 <= a && a <= 1.0f, "Invalid ALPHA channel")
require(0 <= r && r <= 1.0f, "Invalid RED channel")
require(0 <= g && g <= 1.0f, "Invalid GREEN channel")
require(0 <= b && b <= 1.0f, "Invalid BLUE channel")
require(0 <= a && a <= 1.0f, "Invalid ALPHA channel")
def components: Array[Float] = Array(r, g, b, a)
def rgbVector: Vector3D = Vector3D(r, g, b)
def mapRgb(f: Float => Float): RGBAColorNorm = copy(r = f(r), g = f(g), b = f(b))
def mapA(f: Float => Float): RGBAColorNorm = copy(a = f(a))
final private def componentToLinear(x: Float): Float = {
private final def componentToLinear(x: Float): Float = {
if (x <= 0.0404482362771082)
x / 12.92f
else
@ -58,6 +61,15 @@ case class RGBAColorNorm(r: Float, g: Float, b: Float, a: Float = 1f) extends Co
HSVAColor(hue, saturation, value, a)
}
def lerp(dst: RGBAColorNorm, t: Float): RGBAColorNorm = {
RGBAColorNorm(
r.lerp(dst.r, t),
g.lerp(dst.g, t),
b.lerp(dst.b, t),
a.lerp(dst.a, t),
)
}
def withAlpha(alpha: Float): RGBAColorNorm = RGBAColorNorm(r, g, b, alpha)
// ʕʔ

View File

@ -43,7 +43,7 @@ class Camera extends Entity with GenericCamera with DeviceInfo {
DeviceAttribute.Class -> DeviceClass.Multimedia,
DeviceAttribute.Description -> "Dungeon Scanner 2.5D",
DeviceAttribute.Vendor -> Constants.DeviceInfo.DefaultVendor,
DeviceAttribute.Product -> webcamCapture.map(_.name).getOrElse("Blind Pirate")
DeviceAttribute.Product -> webcamCapture.map(_.name).getOrElse("Blind Pirate"),
)
override def load(nbt: NBTTagCompound, workspace: Workspace): Unit = {

View File

@ -0,0 +1,22 @@
package ocelot.desktop.entity
import ocelot.desktop.entity.traits.OcelotInterface
import totoro.ocelot.brain.Constants
import totoro.ocelot.brain.entity.traits.DeviceInfo
import totoro.ocelot.brain.entity.traits.DeviceInfo.{DeviceAttribute, DeviceClass}
import totoro.ocelot.brain.network.{Network, Node, Visibility}
class OcelotBlock extends OcelotInterface with DeviceInfo {
override val node: Node = Network.newNode(this, Visibility.Network)
.withComponent("ocelot", Visibility.Network)
.create()
private final lazy val deviceInfo = Map(
DeviceAttribute.Class -> DeviceClass.Generic,
DeviceAttribute.Description -> "Cardboard box simulator",
DeviceAttribute.Vendor -> Constants.DeviceInfo.DefaultVendor,
DeviceAttribute.Product -> "Catbus",
)
override def getDeviceInfo: Map[String, String] = deviceInfo
}

View File

@ -1,18 +1,14 @@
package ocelot.desktop.entity
import ocelot.desktop.entity.OcelotCard.{LogDirection, LogEvent}
import ocelot.desktop.entity.traits.OcelotInterface
import totoro.ocelot.brain.Constants
import totoro.ocelot.brain.entity.machine.{Arguments, Callback, Context}
import totoro.ocelot.brain.entity.traits.DeviceInfo.{DeviceAttribute, DeviceClass}
import totoro.ocelot.brain.entity.traits.{DeviceInfo, Entity, Environment, Tiered, result}
import totoro.ocelot.brain.event.{EventBus, NodeEvent}
import totoro.ocelot.brain.entity.traits.{DeviceInfo, Tiered}
import totoro.ocelot.brain.network.{Network, Node, Visibility}
import totoro.ocelot.brain.util.Tier
import totoro.ocelot.brain.util.Tier.Tier
import java.time.Instant
class OcelotCard extends Entity with Environment with DeviceInfo with Tiered {
class OcelotCard extends OcelotInterface with DeviceInfo with Tiered {
override val node: Node = Network.newNode(this, Visibility.Neighbors)
.withComponent("ocelot", Visibility.Neighbors)
.create()
@ -27,47 +23,4 @@ class OcelotCard extends Entity with Environment with DeviceInfo with Tiered {
)
override def getDeviceInfo: Map[String, String] = deviceInfo
@Callback(direct = true, doc = """function(message: string) -- Logs a message to the Ocelot console.""")
def log(context: Context, args: Arguments): Array[AnyRef] = {
val message = args.checkString(0)
EventBus.send(LogEvent(node.address, message, LogDirection.CardToUser))
result()
}
@Callback(direct = true, doc = """function(): integer -- Returns the current Unix timestamp (UTC, in milliseconds).""")
def getTimestamp(context: Context, args: Arguments): Array[AnyRef] = {
result(Instant.now().toEpochMilli)
}
@Callback(direct = true, doc = """function(): integer -- Returns a high-resolution timer value (in nanoseconds).""")
def getInstant(context: Context, args: Arguments): Array[AnyRef] = {
result(System.nanoTime())
}
@Callback(direct = true, doc = """function(): number -- Returns the remaining call budget.""")
def getRemainingCallBudget(context: Context, args: Arguments): Array[AnyRef] = {
result(context.getRemainingCallBudget)
}
@Callback(direct = true, doc = """function(): number -- Returns the maximum call budget.""")
def getMaxCallBudget(context: Context, args: Arguments): Array[AnyRef] = {
result(context.getMaxCallBudget)
}
def pushMessage(message: String): Unit = {
node.sendToReachable("computer.signal", "ocelot_message", message)
EventBus.send(LogEvent(node.address, message, LogDirection.UserToCard))
}
}
object OcelotCard {
case class LogEvent(address: String, message: String, direction: LogDirection) extends NodeEvent
sealed trait LogDirection
object LogDirection {
case object CardToUser extends LogDirection
case object UserToCard extends LogDirection
}
}

View File

@ -19,11 +19,12 @@ import javax.sound.sampled.AudioFormat.Encoding
import javax.sound.sampled.{AudioFormat, AudioSystem}
class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
override val node: Component =
override val node: Component = {
Network
.newNode(this, Visibility.Network)
.withComponent("openfm_radio", Visibility.Network)
.create()
}
// --------------------------- URL ---------------------------
@ -44,14 +45,18 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
private def playSynchronously(): Unit = {
try {
// Trying to parse URL and sending request to host
val connection =
val connection = {
new URI(url.get)
.toURL
.openConnection
.asInstanceOf[HttpURLConnection]
}
connection.setRequestMethod("GET")
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36")
connection.setRequestProperty(
"User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36",
)
connection.setRequestProperty("Content-Language", "en-US")
connection.setDoInput(true)
connection.setDoOutput(true)
@ -65,21 +70,22 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
2,
4,
44100,
false
false,
),
// Obtaining audio input stream from HTTP connection
AudioSystem.getAudioInputStream(new BufferedInputStream(connection.getInputStream))
AudioSystem.getAudioInputStream(new BufferedInputStream(connection.getInputStream)),
)
// Keeping input stream format parameters here to offload the reading loop
val inputStreamFormat = inputStream.getFormat
val inputStreamSampleRate = inputStreamFormat.getSampleRate.toInt
val inputStreamSoundSampleFormat =
val inputStreamSoundSampleFormat = {
if (inputStreamFormat.getChannels > 1)
Format.Stereo16
else
Format.Mono16
}
// Creating Ocelot output sound stream
val (outputStream, outputSource) = Audio.newStream(SoundCategory.Records, volume = volume)
@ -99,7 +105,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
.flip
.asInstanceOf[ByteBuffer],
inputStreamSampleRate,
inputStreamSoundSampleFormat
inputStreamSoundSampleFormat,
))
}
@ -107,8 +113,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
}
logger.info("OpenFM input audio stream has reached EOF, closing thread")
}
catch {
} catch {
case _: InterruptedException =>
case e: Exception => logger.error("OpenFM playback exception", e)
}
@ -147,7 +152,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
}
def isPlaying: Boolean =
playbackSoundSource.isDefined && playbackSoundSource.get.isPlaying || playbackThread.isDefined
playbackSoundSource.isDefined && playbackSoundSource.get.playing || playbackThread.isDefined
@Callback()
def start(context: Context, args: Arguments): Array[AnyRef] =
@ -168,6 +173,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
private var _volume: Float = 1
def volume: Float = _volume
def volume_=(value: Float): Unit = {
_volume = value
@ -211,7 +217,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
// --------------------------- Screen color/text ---------------------------
private val defaultScreenText = "OpenFM"
private val defaultScreenColor = IntColor(0x0AFF0A)
private val defaultScreenColor = IntColor(0x0aff0a)
var screenColor: IntColor = defaultScreenColor
private var _screenText: String = defaultScreenText
@ -272,23 +278,26 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
if (nbt.hasKey("url"))
url = Option(nbt.getString("url"))
screenColor =
screenColor = {
if (nbt.hasKey("screenColor"))
IntColor(nbt.getInteger("screenColor"))
else
defaultScreenColor
}
screenText =
screenText = {
if (nbt.hasKey("screenText"))
nbt.getString("screenText")
else
defaultScreenText
}
volume =
volume = {
if (nbt.hasKey("volume"))
nbt.getDouble("volume").toFloat
else
1
}
isListenRedstone = nbt.hasKey("isListenRedstone") && nbt.getBoolean("isListenRedstone")

View File

@ -0,0 +1,59 @@
package ocelot.desktop.entity.traits
import ocelot.desktop.entity.traits.OcelotInterface.LogEvent
import totoro.ocelot.brain.entity.machine.{Arguments, Callback, Context}
import totoro.ocelot.brain.entity.traits.{result, Entity, Environment}
import totoro.ocelot.brain.event.{EventBus, NodeEvent}
import java.time.Instant
trait OcelotInterface extends Entity with Environment {
@Callback(direct = true, doc = """function(message: string) -- Logs a message to the card's console.""")
def log(context: Context, args: Arguments): Array[AnyRef] = {
val message = args.checkString(0)
EventBus.send(LogEvent.CardToUser(node.address, message))
result()
}
@Callback(direct = true, doc = """function() -- Clears messages logged to the card's console.""")
def clearLog(context: Context, args: Arguments): Array[AnyRef] = {
EventBus.send(LogEvent.Clear(node.address))
result()
}
@Callback(direct = true,
doc = """function(): integer -- Returns the current Unix timestamp (UTC, in milliseconds).""")
def getTimestamp(context: Context, args: Arguments): Array[AnyRef] = {
result(Instant.now().toEpochMilli)
}
@Callback(direct = true, doc = """function(): integer -- Returns a high-resolution timer value (in nanoseconds).""")
def getInstant(context: Context, args: Arguments): Array[AnyRef] = {
result(System.nanoTime())
}
@Callback(direct = true, doc = """function(): number -- Returns the remaining call budget.""")
def getRemainingCallBudget(context: Context, args: Arguments): Array[AnyRef] = {
result(context.getRemainingCallBudget)
}
@Callback(direct = true, doc = """function(): number -- Returns the maximum call budget.""")
def getMaxCallBudget(context: Context, args: Arguments): Array[AnyRef] = {
result(context.getMaxCallBudget)
}
def pushMessage(message: String): Unit = {
node.sendToReachable("computer.signal", "ocelot_message", message)
EventBus.send(LogEvent.UserToCard(node.address, message))
}
}
object OcelotInterface {
sealed trait LogEvent extends NodeEvent
object LogEvent {
case class CardToUser(address: String, message: String) extends LogEvent
case class UserToCard(address: String, message: String) extends LogEvent
case class Clear(address: String) extends LogEvent
}
}

View File

@ -27,25 +27,25 @@ case class Basis3D(x: Vector3D, y: Vector3D, z: Vector3D) {
def *(rhs: Vector3D): Vector3D = Vector3D(
x.x * rhs.x + y.x * rhs.y + z.x * rhs.z,
x.y * rhs.x + y.y * rhs.y + z.y * rhs.z,
x.z * rhs.x + y.z * rhs.y + z.z * rhs.z
x.z * rhs.x + y.z * rhs.y + z.z * rhs.z,
)
def *(rhs: Basis3D): Basis3D = Basis3D(
Vector3D(
x.x * rhs.x.x + y.x * rhs.x.y + z.x * rhs.x.z,
x.y * rhs.x.x + y.y * rhs.x.y + z.y * rhs.x.z,
x.z * rhs.x.x + y.z * rhs.x.y + z.z * rhs.x.z
x.z * rhs.x.x + y.z * rhs.x.y + z.z * rhs.x.z,
),
Vector3D(
x.x * rhs.y.x + y.x * rhs.y.y + z.x * rhs.y.z,
x.y * rhs.y.x + y.y * rhs.y.y + z.y * rhs.y.z,
x.z * rhs.y.x + y.z * rhs.y.y + z.z * rhs.y.z
x.z * rhs.y.x + y.z * rhs.y.y + z.z * rhs.y.z,
),
Vector3D(
x.x * rhs.z.x + y.x * rhs.z.y + z.x * rhs.z.z,
x.y * rhs.z.x + y.y * rhs.z.y + z.y * rhs.z.z,
x.z * rhs.z.x + y.z * rhs.z.y + z.z * rhs.z.z
)
x.z * rhs.z.x + y.z * rhs.z.y + z.z * rhs.z.z,
),
)
def det: Float = x.x * (y.y * z.z - z.y * y.z) -
@ -59,18 +59,18 @@ case class Basis3D(x: Vector3D, y: Vector3D, z: Vector3D) {
Vector3D(
(y.y * z.z - z.y * y.z) * s,
(x.z * z.y - x.y * z.z) * s,
(x.y * y.z - x.z * y.y) * s
(x.y * y.z - x.z * y.y) * s,
),
Vector3D(
(y.z * z.x - y.x * z.z) * s,
(x.x * z.z - x.z * z.x) * s,
(y.x * x.z - x.x * y.z) * s
(y.x * x.z - x.x * y.z) * s,
),
Vector3D(
(y.x * z.y - z.x * y.y) * s,
(z.x * x.y - x.x * z.y) * s,
(x.x * y.y - y.x * x.y) * s
)
(x.x * y.y - y.x * x.y) * s,
),
)
}

View File

@ -1,9 +0,0 @@
package ocelot.desktop.geometry
object FloatUtils {
implicit class ExtendedFloat(val v: Float) extends AnyVal {
def lerp(that: Float, alpha: Float): Float = v * (1 - alpha) + that * alpha
def clamp(min: Float = 0f, max: Float = 1f): Float = v.min(max).max(min)
}
}

View File

@ -3,11 +3,12 @@ package ocelot.desktop.geometry
object ProjectionMatrix3D {
def perspective(aspect: Float, fovY: Float, zNear: Float, zFar: Float): ProjectionMatrix3D = {
val f = (1.0 / math.tan(math.toRadians(fovY) / 2.0)).toFloat
// format: off
ProjectionMatrix3D(
f / aspect, 0, 0, 0,
0, f, 0, 0,
0, 0, (zFar + zNear) / (zNear - zFar), (2 * zFar * zNear) / (zNear - zFar),
0, 0, -1, 0
0, 0, -1, 0,
)
}
}
@ -15,7 +16,6 @@ object ProjectionMatrix3D {
case class ProjectionMatrix3D(m11: Float, m12: Float, m13: Float, m14: Float,
m21: Float, m22: Float, m23: Float, m24: Float,
m31: Float, m32: Float, m33: Float, m34: Float,
m41: Float, m42: Float, m43: Float, m44: Float)
{
m41: Float, m42: Float, m43: Float, m44: Float) {
def array: Array[Float] = Array(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44)
}

View File

@ -55,7 +55,7 @@ case class Quaternion(x: Float, y: Float, z: Float, w: Float) {
y * rhs.z - z * rhs.y + x * rhs.w + w * rhs.x,
z * rhs.x - x * rhs.z + y * rhs.w + w * rhs.y,
x * rhs.y - y * rhs.x + z * rhs.w + w * rhs.z,
w * rhs.w - x * rhs.x - y * rhs.y - z * rhs.z
w * rhs.w - x * rhs.x - y * rhs.y - z * rhs.z,
)
def conj: Quaternion = Quaternion(-x, -y, -z, w)
@ -72,7 +72,7 @@ case class Quaternion(x: Float, y: Float, z: Float, w: Float) {
def basis: Basis3D = Basis3D(
Vector3D(1 - 2 * y * y - 2 * z * z, 2 * x * y + 2 * z * w, 2 * x * z - 2 * y * w),
Vector3D(2 * x * y - 2 * z * w, 1 - 2 * x * x - 2 * z * z, 2 * y * z + 2 * x * w),
Vector3D(2 * x * z + 2 * y * w, 2 * y * z - 2 * x * w, 1 - 2 * x * x - 2 * y * y)
Vector3D(2 * x * z + 2 * y * w, 2 * y * z - 2 * x * w, 1 - 2 * x * x - 2 * y * y),
)
def dot(that: Quaternion): Float = x * that.x + y * that.y + z * that.z + w * that.w

View File

@ -76,7 +76,9 @@ case class Rect2D(x: Float, y: Float, w: Float, h: Float) {
this
}
def inflate(addition: Float): Rect2D = Rect2D(x - addition, y - addition, w + addition * 2, h + addition * 2)
def inflated(dx: Float, dy: Float): Rect2D = Rect2D(x - dx, y - dy, w + dx * 2, h + dy * 2)
def inflated(delta: Float): Rect2D = inflated(delta, delta)
def center: Vector2D = {
min + (size * 0.5f).toVector
@ -86,7 +88,8 @@ case class Rect2D(x: Float, y: Float, w: Float, h: Float) {
Vector2D(x + w / 2f, y),
Vector2D(x + w, y + h / 2f),
Vector2D(x + w / 2f, y + h),
Vector2D(x, y + h / 2f))
Vector2D(x, y + h / 2f),
)
def distanceTo(that: Rect2D): Float = {
((center - that.center).abs - (extent + that.extent)).max(Vector2D(0, 0)).length

View File

@ -32,7 +32,7 @@ case class Size2D(width: Float, height: Float) {
def clamped(min: Size2D, max: Size2D): Size2D = {
Size2D(
math.min(max.width, math.max(min.width, width)),
math.min(max.height, math.max(min.height, height))
math.min(max.height, math.max(min.height, height)),
)
}

View File

@ -5,27 +5,28 @@ import java.nio.ByteBuffer
object Transform2D {
def identity: Transform2D = Transform2D(
1, 0, 0,
0, 1, 0
0, 1, 0,
)
def scale(x: Float, y: Float): Transform2D = Transform2D(
x, 0, 0,
0, y, 0
0, y, 0,
)
def scale(a: Float): Transform2D = Transform2D.scale(a, a)
def translate(x: Float, y: Float): Transform2D = Transform2D(
1, 0, x,
0, 1, y
0, 1, y,
)
def viewport(width: Float, height: Float): Transform2D =
Transform2D.translate(-1f, 1f) >> Transform2D.scale(2f / width, -2f / height)
def rotate(angle: Float): Transform2D = {
val (s, c) = (math.sin(angle).asInstanceOf[Float], math.cos(angle).asInstanceOf[Float])
val (s, c) = (math.sin(angle).toFloat, math.cos(angle).toFloat)
// format: off
Transform2D(
c, -s, 0,
s, c, 0
@ -36,13 +37,13 @@ object Transform2D {
case class Transform2D(m11: Float, m12: Float, m13: Float, m21: Float, m22: Float, m23: Float) {
def array: Array[Float] = Array(m11, m12, m13, m21, m22, m23)
// :|
// format: off
def >>(that: Transform2D): Transform2D = Transform2D(
m11 * that.m11 + m12 * that.m21, m11 * that.m12 + m12 * that.m22, m11 * that.m13 + m12 * that.m23 + m13,
m21 * that.m11 + m22 * that.m21, m21 * that.m12 + m22 * that.m22, m21 * that.m13 + m22 * that.m23 + m23,
)
// (__)
// format: off
def <<(that: Transform2D): Transform2D = Transform2D(
m11 * that.m11 + m21 * that.m12, m12 * that.m11 + m22 * that.m12, m13 * that.m11 + m23 * that.m12 + that.m13,
m11 * that.m21 + m21 * that.m22, m12 * that.m21 + m22 * that.m22, m13 * that.m21 + m23 * that.m22 + that.m23
@ -50,13 +51,14 @@ case class Transform2D(m11: Float, m12: Float, m13: Float, m21: Float, m22: Floa
def *(that: Vector2D): Vector2D = Vector2D(
m11 * that.x + m12 * that.y + m13,
m21 * that.x + m22 * that.y + m23
m21 * that.x + m22 * that.y + m23,
)
override def toString: String =
override def toString: String = {
f"""Transform2D [$m11%6.3f $m12%6.3f $m13%6.3f]
| [$m21%6.3f $m22%6.3f $m23%6.3f]
""".stripMargin
}
// (°°) ┻━┻
def put(buffer: ByteBuffer): Unit = {

View File

@ -26,6 +26,7 @@ object Transform3D {
}
case class Transform3D(basis: Basis3D, origin: Vector3D) {
// format: off
def array: Array[Float] = Array(
basis.x.x, basis.y.x, basis.z.x, origin.x,
basis.x.y, basis.y.y, basis.z.y, origin.y,
@ -44,8 +45,8 @@ case class Transform3D(basis: Basis3D, origin: Vector3D) {
override def toString: String = s"Transform3D [${basis.x}, ${basis.y}, ${basis.z}, $origin]"
def put(buffer: ByteBuffer): Unit = {
buffer.putFloat(basis.x.x); buffer.putFloat(basis.y.x); buffer.putFloat(basis.z.x); buffer.putFloat(origin.x);
buffer.putFloat(basis.x.y); buffer.putFloat(basis.y.y); buffer.putFloat(basis.z.y); buffer.putFloat(origin.y);
buffer.putFloat(basis.x.z); buffer.putFloat(basis.y.z); buffer.putFloat(basis.z.z); buffer.putFloat(origin.z);
buffer.putFloat(basis.x.x); buffer.putFloat(basis.y.x); buffer.putFloat(basis.z.x); buffer.putFloat(origin.x)
buffer.putFloat(basis.x.y); buffer.putFloat(basis.y.y); buffer.putFloat(basis.z.y); buffer.putFloat(origin.y)
buffer.putFloat(basis.x.z); buffer.putFloat(basis.y.z); buffer.putFloat(basis.z.z); buffer.putFloat(origin.z)
}
}

View File

@ -36,9 +36,6 @@ case class Vector2D(x: Float, y: Float) extends Persistable {
def *(scalar: Float): Vector2D = Vector2D(x * scalar, y * scalar)
// TODO: remove
def *(scalar: Double): Vector2D = Vector2D(x * scalar, y * scalar)
def /(scalar: Float): Vector2D = Vector2D(x / scalar, y / scalar)
def snap(v: Float): Vector2D = Vector2D((x / v).floor * v, (y / v).floor * v)

View File

@ -1,6 +1,6 @@
package ocelot.desktop.geometry
import ocelot.desktop.geometry.FloatUtils.ExtendedFloat
import ocelot.desktop.util.NumberUtils.ExtendedFloat
object Vector3D {
val Zero: Vector3D = Vector3D(0, 0, 0)
@ -41,11 +41,11 @@ case class Vector3D(x: Float, y: Float, z: Float) {
def cross(that: Vector3D): Vector3D = Vector3D(
y * that.z - z * that.y,
z * that.x - x * that.z,
x * that.y - y * that.x
x * that.y - y * that.x,
)
def angle(that: Vector3D): Float = {
math.acos((dot(that) / length / that.length).clamp(-1, 1)).toFloat
math.acos((dot(that) / length / that.length).clamped(-1, 1)).toFloat
}
def lerp(that: Vector3D, alpha: Float): Vector3D = {

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