Compare commits
704 Commits
magick
...
ed-09-05-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
592a3f4571 | ||
|
|
27f031f466 | ||
|
|
cda201720b | ||
|
|
50c4fd3a58 | ||
|
|
b74d1b62b5 | ||
|
|
7b2328ba22 | ||
|
|
54d86b8d8f | ||
|
|
27047a3a37 | ||
|
|
b92032fdd7 | ||
|
|
31491775e5 | ||
|
|
9fe1528490 | ||
|
|
6102d3c63d | ||
|
|
29860a0cf7 | ||
|
|
aad5b9e53b | ||
|
|
1952ae3267 | ||
|
|
dd51d0e19e | ||
|
|
c097c98a1e | ||
|
|
83099640e6 | ||
|
|
1a09374b01 | ||
|
|
99212762d6 | ||
|
|
b104125c0e | ||
|
|
15153d95a4 | ||
|
|
caa822b9a0 | ||
|
|
9efe4dc701 | ||
|
|
ab1a2de367 | ||
|
|
d6d1c9ed8a | ||
|
|
bd06aa2365 | ||
|
|
18bd221407 | ||
|
|
8ec52ff69c | ||
|
|
89cbb100fd | ||
|
|
24e227660a | ||
|
|
685188fd10 | ||
|
|
262b9698cf | ||
|
|
6301e94390 | ||
|
|
b90373356e | ||
|
|
cd8e90c58e | ||
|
|
64548a7ead | ||
|
|
0926891f4f | ||
|
|
31a8327106 | ||
|
|
09b5319270 | ||
|
|
28f5d490a4 | ||
|
|
1e30234539 | ||
|
|
b9906eb34c | ||
|
|
35dc85fd47 | ||
|
|
d099b63424 | ||
|
|
0cb50a24c3 | ||
|
|
7a38b22ddb | ||
|
|
61c1aeddf3 | ||
|
|
7d7c71e6a6 | ||
|
|
e4a516514f | ||
|
|
cc9e40820f | ||
|
|
19aeff26ec | ||
|
|
630a7a78ed | ||
|
|
6685146a1e | ||
|
|
547e15a158 | ||
|
|
008f6ef94a | ||
|
|
7794ab8609 | ||
|
|
7d23d01445 | ||
|
|
b58d8a02b6 | ||
|
|
ddb07d5f63 | ||
|
|
aaabd5e914 | ||
|
|
8fa7ea7cf9 | ||
|
|
8f4362df03 | ||
|
|
fbe8374c0f | ||
|
|
aa426c9c3a | ||
|
|
3dcb65feb2 | ||
|
|
026af631f8 | ||
|
|
c1aae2398b | ||
|
|
b8d03b814b | ||
|
|
13dbb95d45 | ||
|
|
3b3cc0e66c | ||
|
|
c7a5587e07 | ||
|
|
1ecc36b049 | ||
|
|
7ffa74abd0 | ||
|
|
104c2afe69 | ||
|
|
eee8e03c15 | ||
|
|
c20df3e39f | ||
|
|
82fe5ab55d | ||
|
|
b8144df7a5 | ||
|
|
d1a5d35623 | ||
|
|
254a9177fc | ||
|
|
93c5e86857 | ||
|
|
b947490d08 | ||
|
|
7d35d54a81 | ||
|
|
c61e683354 | ||
|
|
2335be5e83 | ||
|
|
4a2a63a86b | ||
|
|
70d3cf7ba4 | ||
|
|
b1fca44b63 | ||
|
|
eb2fac40db | ||
|
|
92c2ff0b05 | ||
|
|
ead78b72d2 | ||
|
|
45fc6bed2f | ||
|
|
5183f3ed8b | ||
|
|
37d0cb9c90 | ||
|
|
2f3f917007 | ||
|
|
2b6e12898b | ||
|
|
f411126191 | ||
|
|
c3fe975e8b | ||
|
|
4bb0786018 | ||
|
|
1c125cb14e | ||
|
|
0a15d08550 | ||
|
|
873799095c | ||
|
|
8ee9ca2227 | ||
|
|
83b486b63f | ||
|
|
6b173a4df5 | ||
|
|
445f08ae07 | ||
|
|
72b8fcf99c | ||
|
|
540c45cbe9 | ||
|
|
0e829eb380 | ||
|
|
5ab1cc0c84 | ||
|
|
07d43af4a6 | ||
|
|
e7d24b5648 | ||
|
|
ba009f34fa | ||
|
|
1d233226bb | ||
|
|
ba05b56d51 | ||
|
|
1b3481f094 | ||
|
|
96a3967c3f | ||
|
|
1d5392f86b | ||
|
|
6ecbf0a04c | ||
|
|
960f268d1c | ||
|
|
6fc684812d | ||
|
|
acd8f93b36 | ||
|
|
dd84d48fed | ||
|
|
c045e2488e | ||
|
|
76ecdee946 | ||
|
|
e5246f1e76 | ||
|
|
0450a416e2 | ||
|
|
bedec83cd9 | ||
|
|
0056befd4b | ||
|
|
40a4eeaa39 | ||
|
|
a8bf2689a4 | ||
|
|
a28296433b | ||
|
|
4ede46003a | ||
|
|
f64dd5f45f | ||
|
|
192de3d9cb | ||
|
|
c69bf2f2aa | ||
|
|
f348e6aa30 | ||
|
|
291ecf9643 | ||
|
|
71dbb955ce | ||
|
|
f76a471e5a | ||
|
|
3c3c2daf26 | ||
|
|
f9204832d2 | ||
|
|
7d11ff5562 | ||
|
|
4c692d7d4a | ||
|
|
8a7f709794 | ||
|
|
9b17693396 | ||
|
|
8909dc7d40 | ||
|
|
eab276a12d | ||
|
|
09b6f5c128 | ||
|
|
b728f36f30 | ||
|
|
64a732ad7b | ||
|
|
a4b0a34bc7 | ||
|
|
235091b377 | ||
|
|
131c269e2b | ||
|
|
ce3ba577e0 | ||
|
|
316a3d7f4d | ||
|
|
b90a58d88b | ||
|
|
6680bd4c60 | ||
|
|
70f734edcc | ||
|
|
236da1cd27 | ||
|
|
05f0fddf5d | ||
|
|
17304068b4 | ||
|
|
889b578e56 | ||
|
|
fd9894164f | ||
|
|
a7e6337cbd | ||
|
|
11a4f9d21f | ||
|
|
5053c8afdb | ||
|
|
6596584722 | ||
|
|
05be6d376d | ||
|
|
7ba228732d | ||
|
|
a95b0d000a | ||
|
|
efceaa89e0 | ||
|
|
52ad84c397 | ||
|
|
c00987500f | ||
|
|
7cd1e4e30d | ||
|
|
cf633b4849 | ||
|
|
2c34befbcb | ||
|
|
577106251b | ||
|
|
2b33cb9a47 | ||
|
|
9ca1d01218 | ||
|
|
b9cf9c1d60 | ||
|
|
648472233c | ||
|
|
6e1f0ba390 | ||
|
|
ff762e56a7 | ||
|
|
133d9280ae | ||
|
|
99130ff332 | ||
|
|
b160c42926 | ||
|
|
446f516590 | ||
|
|
5548d90f0d | ||
|
|
81e553401c | ||
|
|
c57164211b | ||
|
|
c0e3c4fac7 | ||
|
|
0d88a1fd7d | ||
|
|
2a0fe88aaf | ||
|
|
6869adfa78 | ||
|
|
b8c0372ac6 | ||
|
|
c3a0ba9dd8 | ||
|
|
0169cb55d8 | ||
|
|
046d062230 | ||
|
|
e57f5fadc5 | ||
|
|
f1c36af0a5 | ||
|
|
bf3e9fe3b4 | ||
|
|
d30b12708d | ||
|
|
d9d36b050a | ||
|
|
a7bf9583dd | ||
|
|
0c8a499a66 | ||
|
|
44e6f3f5db | ||
|
|
4b55f01c55 | ||
|
|
d85d9fbbd1 | ||
|
|
9631fee7f2 | ||
|
|
2e3fd3114b | ||
|
|
dfa365f701 | ||
|
|
4b39c358c8 | ||
|
|
91028a5149 | ||
|
|
2cfc12c134 | ||
|
|
657d9ab740 | ||
|
|
743c732162 | ||
|
|
9520f63aa1 | ||
|
|
098111948e | ||
|
|
5dda487db4 | ||
|
|
bf8002569c | ||
|
|
6b5e99f5eb | ||
|
|
386454706b | ||
|
|
e2087115af | ||
|
|
569253940f | ||
|
|
3c154abb5b | ||
|
|
6d619c9420 | ||
|
|
5aece1679a | ||
|
|
6471f55a49 | ||
|
|
0ab5696391 | ||
|
|
7dae58c9f8 | ||
|
|
f59a4f2a86 | ||
|
|
571cff8cab | ||
|
|
199e05fb34 | ||
|
|
4c66e4f9dc | ||
|
|
7ee18433c8 | ||
|
|
16b3e2233a | ||
|
|
373c368b94 | ||
|
|
eb9ee4541f | ||
|
|
aab103865e | ||
|
|
328134ab18 | ||
|
|
8afae8a1f1 | ||
|
|
16f65f42fc | ||
|
|
eb41f1da5f | ||
|
|
5f216ef1ee | ||
|
|
7c7d8eb645 | ||
|
|
cd90c05ce5 | ||
|
|
9cb3287de4 | ||
|
|
8f1a44fe05 | ||
|
|
2393ce1ceb | ||
|
|
33e1752a49 | ||
|
|
511d8a7267 | ||
|
|
5118865d47 | ||
|
|
1eb81427d3 | ||
|
|
a466b35b27 | ||
|
|
a1b12f95a5 | ||
|
|
cdd900cef6 | ||
|
|
405e020bfa | ||
|
|
757d59cbdd | ||
|
|
4416a21dd5 | ||
|
|
2135cbdb23 | ||
|
|
6cb97502aa | ||
|
|
f89133b3ad | ||
|
|
008935f6ec | ||
|
|
5476daf64e | ||
|
|
f3e9db0141 | ||
|
|
ab66ae9ab0 | ||
|
|
1efdf4a40a | ||
|
|
18fa8bf633 | ||
|
|
0efcd30ac6 | ||
|
|
db200faf11 | ||
|
|
cbb2554c94 | ||
|
|
faa7f3461c | ||
|
|
5c817ddfc1 | ||
|
|
a8c297a374 | ||
|
|
47c5f0dba3 | ||
|
|
fe8ad485f2 | ||
|
|
f544c91298 | ||
|
|
d90cba6eed | ||
|
|
783d3a9aa8 | ||
|
|
ee35fb12a7 | ||
|
|
9f747485e9 | ||
|
|
afbbcd35fa | ||
|
|
3fcbbc0732 | ||
|
|
f5b8b5fbdc | ||
|
|
f88b6f6154 | ||
|
|
f5aa5f1f22 | ||
|
|
72f790844f | ||
|
|
d66ad739ee | ||
|
|
09379a5c77 | ||
|
|
a4504e2fef | ||
|
|
1c78f60bc3 | ||
|
|
a7ad59f6ba | ||
|
|
49d8fe33a3 | ||
|
|
a478446e5a | ||
|
|
f079e77554 | ||
|
|
599a66230b | ||
|
|
be09520aa5 | ||
|
|
02d2611d94 | ||
|
|
33d04ef35b | ||
|
|
353fcd9cfe | ||
|
|
9ccf95e333 | ||
|
|
2cb421bc43 | ||
|
|
1d2335699d | ||
|
|
8869fc3bb2 | ||
|
|
64218bfcab | ||
|
|
34ab1e760d | ||
|
|
7c2ea18088 | ||
|
|
74041cf66f | ||
|
|
8a59e9e1f8 | ||
|
|
2cefd0ee10 | ||
|
|
2621e31343 | ||
|
|
974129689c | ||
|
|
c56f98f99f | ||
|
|
c0ce5fba2a | ||
|
|
37105b3400 | ||
|
|
db555a7c79 | ||
|
|
6d3e788dd6 | ||
|
|
d4556c2657 | ||
|
|
3f508c899d | ||
|
|
1c38d46cd3 | ||
|
|
3a198cbb0d | ||
|
|
6d6e7f0c98 | ||
|
|
4d223f2efd | ||
|
|
cafb41c161 | ||
|
|
dec101465b | ||
|
|
4903bc2e3d | ||
|
|
4d85ca5c2a | ||
|
|
a62e8701cb | ||
|
|
670ad3d962 | ||
|
|
b8a8d6f8fc | ||
|
|
1861ae8e2b | ||
|
|
324cce9bff | ||
|
|
77d7c77f59 | ||
|
|
35885345c2 | ||
|
|
6174713383 | ||
|
|
1840003e1c | ||
|
|
de85e6d518 | ||
|
|
8442a9142c | ||
|
|
5ec5e5ab89 | ||
|
|
6eb681958b | ||
|
|
92f14648ac | ||
|
|
257b04d277 | ||
|
|
2f7d0dedbd | ||
|
|
b4212a08f4 | ||
|
|
36abf1d9ba | ||
|
|
f898e72e57 | ||
|
|
0215292baa | ||
|
|
87cd1bb0ef | ||
|
|
defb8dd941 | ||
|
|
bebcc70c7d | ||
|
|
55177fc388 | ||
|
|
46d6bf18a8 | ||
|
|
7d4d66887b | ||
|
|
cc2fa6f57e | ||
|
|
1dd0e0718c | ||
|
|
18e27342d1 | ||
|
|
ef7d85d285 | ||
|
|
0601075e79 | ||
|
|
012857331b | ||
|
|
c6d935497c | ||
|
|
a04d143c2c | ||
|
|
50fb91bd18 | ||
|
|
36084d9712 | ||
|
|
413d69e71f | ||
|
|
30b74027fb | ||
|
|
394c325246 | ||
|
|
3bed1ddc48 | ||
|
|
053ec6a309 | ||
|
|
8febdc5fc4 | ||
|
|
080b1b1e0c | ||
|
|
e1a5247770 | ||
|
|
11227c754c | ||
|
|
498109a833 | ||
|
|
80bbf19f95 | ||
|
|
adce579370 | ||
|
|
13c6537842 | ||
|
|
35edea1c96 | ||
|
|
37e03e0d29 | ||
|
|
934c5089a9 | ||
|
|
5896e68752 | ||
|
|
1f9f88883c | ||
|
|
32b81de8c5 | ||
|
|
8af23749a1 | ||
|
|
290a984c90 | ||
|
|
c122fdf183 | ||
|
|
d745a788ea | ||
|
|
fe67634397 | ||
|
|
e26662b545 | ||
|
|
abc46f171c | ||
|
|
3847a298c1 | ||
|
|
d578d293df | ||
|
|
9b63bb672f | ||
|
|
1dca65defd | ||
|
|
9378688ffc | ||
|
|
0ae942101a | ||
|
|
7bf0ddbaa8 | ||
|
|
735eeefa03 | ||
|
|
de965416bd | ||
|
|
b292905216 | ||
|
|
0aee198adf | ||
|
|
a0b3a6b679 | ||
|
|
281e5fa448 | ||
|
|
4f056672f9 | ||
|
|
057556b605 | ||
|
|
cf45005471 | ||
|
|
960a50c0ff | ||
|
|
e450fadbaa | ||
|
|
d3b1178428 | ||
|
|
6d16d6b1c9 | ||
|
|
161fd6c83c | ||
|
|
771661f478 | ||
|
|
f6ac494ad3 | ||
|
|
eb648dd0eb | ||
|
|
ef0a4d64c8 | ||
|
|
d797e3753f | ||
|
|
045c81b3c4 | ||
|
|
8ef8cf6098 | ||
|
|
17160fad83 | ||
|
|
8aabb1c2e5 | ||
|
|
7b90c08a2c | ||
|
|
38f490e5eb | ||
|
|
5fa424a43b | ||
|
|
7f6f9c1209 | ||
|
|
4290d5f13e | ||
|
|
480d26aba6 | ||
|
|
b0a11462d6 | ||
|
|
6307c1b1b5 | ||
|
|
91aa16f08a | ||
|
|
49f1c8b197 | ||
|
|
829b453e3c | ||
|
|
14bc595442 | ||
|
|
10ad53df72 | ||
|
|
0c851d256d | ||
|
|
1bfc63c546 | ||
|
|
8117131c91 | ||
|
|
c129bb177c | ||
|
|
716cdcbdf8 | ||
|
|
905102064c | ||
|
|
401911b734 | ||
|
|
eee056eaf0 | ||
|
|
2f0c02ef35 | ||
|
|
88b56efba1 | ||
|
|
31b9f98e6d | ||
|
|
32c1f8f8cd | ||
|
|
c4a6bcca5c | ||
|
|
0e2f5d6bf7 | ||
|
|
06a5e00c99 | ||
|
|
ee0f63d273 | ||
|
|
8eecd9cc2d | ||
|
|
e888747d0c | ||
|
|
8ba56847bb | ||
|
|
39f64dea73 | ||
|
|
43384c1125 | ||
|
|
08960fb197 | ||
|
|
23df20c0fe | ||
|
|
1210e1da86 | ||
|
|
f94997fe5f | ||
|
|
6185ddc64c | ||
|
|
132cc73fb7 | ||
|
|
32bf32d2dc | ||
|
|
894c10c409 | ||
|
|
5b0c3ecf73 | ||
|
|
8bc2ee81dd | ||
|
|
32a90120eb | ||
|
|
f972f3129d | ||
|
|
85c6383cc0 | ||
|
|
289bbc70d7 | ||
|
|
fd0ca42c58 | ||
|
|
af516cff61 | ||
|
|
c5ab839b2c | ||
|
|
52204dfa35 | ||
|
|
e2b260d894 | ||
|
|
35ac720d19 | ||
|
|
b5bcf86b74 | ||
|
|
bffad8053a | ||
|
|
fcf5057b61 | ||
|
|
b0e6ff6779 | ||
|
|
210e6f6444 | ||
|
|
81f2dc7cf9 | ||
|
|
6b1866625c | ||
|
|
c93052c755 | ||
|
|
877ccd6e8f | ||
|
|
f947f97878 | ||
|
|
a899122d1b | ||
|
|
71fae31507 | ||
|
|
4609079680 | ||
|
|
541e87395a | ||
|
|
f059714a75 | ||
|
|
faff0d203a | ||
|
|
0ca5d5110d | ||
|
|
e009a80a8c | ||
|
|
2c0df7fc72 | ||
|
|
50631f430d | ||
|
|
b092f190f6 | ||
|
|
b701804836 | ||
|
|
49b39e9535 | ||
|
|
59dc76b423 | ||
|
|
ba03750394 | ||
|
|
84c3d225ca | ||
|
|
cf96612a81 | ||
|
|
7c07391998 | ||
|
|
58f025ba80 | ||
|
|
c156d09081 | ||
|
|
8f135d9745 | ||
|
|
d342db3d65 | ||
|
|
b2226cbe16 | ||
|
|
ee4ef4b96a | ||
|
|
fb8dcfe6bf | ||
|
|
d29afc0f4c | ||
|
|
caf5ca3dc8 | ||
|
|
fb96392077 | ||
|
|
362033a0c7 | ||
|
|
8b2179d0ae | ||
|
|
9fa08fe8c2 | ||
|
|
29302ae6cd | ||
|
|
0526384d16 | ||
|
|
b6d751eb70 | ||
|
|
b48d0ddefc | ||
|
|
2b3076832f | ||
|
|
7b0b894e44 | ||
|
|
01ce407019 | ||
|
|
d2fb5130c9 | ||
|
|
c4f050259d | ||
|
|
5d80e49d37 | ||
|
|
f253067a69 | ||
|
|
380ace4ef0 | ||
|
|
1fb7393a1b | ||
|
|
3bfd5d2c4e | ||
|
|
3a8249e5fd | ||
|
|
5982007d0e | ||
|
|
bd63a3f9c1 | ||
|
|
663542b82e | ||
|
|
6ad039d0f9 | ||
|
|
ac7842cacd | ||
|
|
f96f92f54f | ||
|
|
c266046cbd | ||
|
|
af939af47c | ||
|
|
c7bbaa48a4 | ||
|
|
b6781946df | ||
|
|
966a54fbc6 | ||
|
|
9482144a19 | ||
|
|
f271c8cff0 | ||
|
|
0b587e9fb9 | ||
|
|
bbbf7172a9 | ||
|
|
6db92d86b5 | ||
|
|
e98a6e17a3 | ||
|
|
4763bffeac | ||
|
|
d74a33d46b | ||
|
|
c70b267bbb | ||
|
|
c3f7ad2fae | ||
|
|
5e498f68a6 | ||
|
|
af734dc5c2 | ||
|
|
bb24ead3b9 | ||
|
|
ca01030da1 | ||
|
|
5eae6335e1 | ||
|
|
ddaaadd204 | ||
|
|
396c613366 | ||
|
|
5c69031d11 | ||
|
|
f395c45dd2 | ||
|
|
2da53d97e5 | ||
|
|
de9c234e20 | ||
|
|
3c6722bd6f | ||
|
|
812abd33a0 | ||
|
|
58e21a8532 | ||
|
|
16ed6ee81d | ||
|
|
95c66b4f96 | ||
|
|
238c87ce17 | ||
|
|
089c9cb967 | ||
|
|
c85bdef7f1 | ||
|
|
8eab1f499f | ||
|
|
3f2e15896b | ||
|
|
12bb476a2d | ||
|
|
4594700d19 | ||
|
|
fc7839eeba | ||
|
|
b22c06230c | ||
|
|
e5a5196b1f | ||
|
|
ef7f0b8322 | ||
|
|
6be204e3f4 | ||
|
|
533453eb81 | ||
|
|
349f78dc80 | ||
|
|
5a5167d6cb | ||
|
|
fa5b93871f | ||
|
|
b2b1926ecc | ||
|
|
7000974319 | ||
|
|
acb112b054 | ||
|
|
ce5934bd67 | ||
|
|
cff8083650 | ||
|
|
fa9abbaff4 | ||
|
|
aa0fc72b1a | ||
|
|
610e73543c | ||
|
|
fdf04c2779 | ||
|
|
c6f8cf080a | ||
|
|
a47c5561a9 | ||
|
|
7aa1818823 | ||
|
|
6373454242 | ||
|
|
299da35c87 | ||
|
|
5b62724de0 | ||
|
|
33abb6bb3f | ||
|
|
1bfcc062f2 | ||
|
|
8703a30647 | ||
|
|
350843d2c0 | ||
|
|
7aa4beede6 | ||
|
|
94f296f9d6 | ||
|
|
5146853484 | ||
|
|
36eaa8c941 | ||
|
|
8245caaf61 | ||
|
|
45df595c15 | ||
|
|
6ec40900ef | ||
|
|
f9f098f64e | ||
|
|
f56373321a | ||
|
|
77090fca62 | ||
|
|
7decf64c39 | ||
|
|
5a89fe0395 | ||
|
|
c8202dd80d | ||
|
|
d5e187dc50 | ||
|
|
4509235604 | ||
|
|
71d5d2a563 | ||
|
|
620a6de0ff | ||
|
|
1eba437555 | ||
|
|
98503cca7c | ||
|
|
81d2a4730e | ||
|
|
0b4c5bb988 | ||
|
|
05ca2baa3d | ||
|
|
02bcfc4593 | ||
|
|
79b4ae3998 | ||
|
|
f801a86351 | ||
|
|
10beb47e0f | ||
|
|
77612e8e51 | ||
|
|
5769bf38f8 | ||
|
|
b14e6e31ea | ||
|
|
e82034c16b | ||
|
|
403852cd01 | ||
|
|
f3a97fc0c5 | ||
|
|
59b8757f14 | ||
|
|
0b0e20ea41 | ||
|
|
73a5a3830d | ||
|
|
a66e233839 | ||
|
|
394390b35b | ||
|
|
ebf87be9f2 | ||
|
|
390249f0cc | ||
|
|
dc1b38054e | ||
|
|
7b94e244b3 | ||
|
|
5b9e0fb08e | ||
|
|
3af744e4a9 | ||
|
|
b1136c98d7 | ||
|
|
126f64a914 | ||
|
|
ee96d8aa66 | ||
|
|
20452ba5bb | ||
|
|
7120e8736a | ||
|
|
24a0298f76 | ||
|
|
94aa61b323 | ||
|
|
31f88ec865 | ||
|
|
460b588de5 | ||
|
|
020f322e27 | ||
|
|
a3750f0e64 | ||
|
|
8d21ef0aa2 | ||
|
|
d9ee1600ee | ||
|
|
9b1a8b07d7 | ||
|
|
3c1ec9ac34 | ||
|
|
b672ea73f0 | ||
|
|
cfa94be4c2 | ||
|
|
8d64d2bc1e | ||
|
|
06ecf2af05 | ||
|
|
b3ee929f1b | ||
|
|
ac8d3d55cc | ||
|
|
4453fe50cf | ||
|
|
66f32d4289 | ||
|
|
d76211514b | ||
|
|
8213c89fdb | ||
|
|
40d90ddcb3 | ||
|
|
45cefc9643 | ||
|
|
fd109d61b8 | ||
|
|
92089da192 | ||
|
|
5659edd207 | ||
|
|
d67d782f99 | ||
|
|
d2dc0734df | ||
|
|
a95fc86f7a | ||
|
|
da136826e7 | ||
|
|
a376f4784a | ||
|
|
395c33024c | ||
|
|
46733616df | ||
|
|
d08600240f | ||
|
|
f2ed508562 | ||
|
|
e0589a1350 | ||
|
|
009d06d978 | ||
|
|
20b16944ad | ||
|
|
c5b4df22a6 | ||
|
|
5a5efa11cf | ||
|
|
e94cd726e1 | ||
|
|
ef72d3cf7f | ||
|
|
24390ef51b | ||
|
|
9f461dec5a | ||
|
|
afcdc8b866 | ||
|
|
0785516eac | ||
|
|
f9f204a6d0 | ||
|
|
03f51ca3aa | ||
|
|
432e6ec45d | ||
|
|
5b8468c9d8 | ||
|
|
e2341c0089 | ||
|
|
ceef55b8d3 | ||
|
|
2db374988c | ||
|
|
177d69cb98 |
@@ -9,9 +9,10 @@ indent_style = space
|
||||
tab_width = 4
|
||||
|
||||
# New line preferences
|
||||
end_of_line = crlf:suggestion
|
||||
#end_of_line = crlf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
max_line_length = 120
|
||||
|
||||
#### .NET Coding Conventions ####
|
||||
|
||||
@@ -104,7 +105,6 @@ csharp_preferred_modifier_order = public, private, protected, internal, new, abs
|
||||
|
||||
# 'using' directive preferences
|
||||
csharp_using_directive_placement = outside_namespace:silent
|
||||
csharp_style_namespace_declarations = file_scoped:suggestion
|
||||
|
||||
#### C# Formatting Rules ####
|
||||
|
||||
@@ -337,7 +337,11 @@ dotnet_naming_symbols.type_parameters_symbols.applicable_kinds = type_parameter
|
||||
|
||||
# ReSharper properties
|
||||
resharper_braces_for_ifelse = required_for_multiline
|
||||
resharper_csharp_wrap_arguments_style = chop_if_long
|
||||
resharper_csharp_wrap_parameters_style = chop_if_long
|
||||
resharper_keep_existing_attribute_arrangement = true
|
||||
resharper_wrap_chained_binary_patterns = chop_if_long
|
||||
resharper_wrap_chained_method_calls = chop_if_long
|
||||
|
||||
[*.{csproj,xml,yml,yaml,dll.config,msbuildproj,targets,props}]
|
||||
indent_size = 2
|
||||
|
||||
16
.github/labeler.yml
vendored
16
.github/labeler.yml
vendored
@@ -1,12 +1,18 @@
|
||||
"Changes: Sprites":
|
||||
- '**/*.rsi/*.png'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: '**/*.rsi/*.png'
|
||||
|
||||
"Changes: Map":
|
||||
- 'Resources/Maps/*.yml'
|
||||
- 'Resources/Prototypes/Maps/*.yml'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'Resources/Maps/*.yml'
|
||||
- 'Resources/Prototypes/Maps/*.yml'
|
||||
|
||||
"Changes: UI":
|
||||
- '**/*.xaml*'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: '**/*.xaml*'
|
||||
|
||||
"No C#":
|
||||
- all: ["!**/*.cs"]
|
||||
- changed-files:
|
||||
# Equiv to any-glob-to-all as long as this has one matcher. If ALL changed files are not C# files, then apply label.
|
||||
- all-globs-to-all-files: "!**/*.cs"
|
||||
|
||||
12
.github/workflows/conflict-labeler.yml
vendored
12
.github/workflows/conflict-labeler.yml
vendored
@@ -1,18 +1,20 @@
|
||||
name: Check Merge Conflicts
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
- ready_for_review
|
||||
|
||||
jobs:
|
||||
Label:
|
||||
if: github.actor != 'PJBot'
|
||||
if: ( github.event.pull_request.draft == false ) && ( github.actor != 'PJBot' )
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check for Merge Conflicts
|
||||
uses: ike709/actions-label-merge-conflict@9eefdd17e10566023c46d2dc6dc04fcb8ec76142
|
||||
uses: eps1lon/actions-label-merge-conflict@v3.0.0
|
||||
with:
|
||||
dirtyLabel: "Merge Conflict"
|
||||
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
7
.github/workflows/labeler-pr.yml
vendored
7
.github/workflows/labeler-pr.yml
vendored
@@ -6,8 +6,9 @@ on:
|
||||
jobs:
|
||||
labeler:
|
||||
if: github.actor != 'PJBot'
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@v3
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
- uses: actions/labeler@v5
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@@ -46,7 +46,7 @@ public class MapLoadBenchmark
|
||||
PoolManager.Shutdown();
|
||||
}
|
||||
|
||||
public static readonly string[] MapsSource = { "Empty", "Box", "Bagel", "Dev", "CentComm", "Atlas", "Core", "TestTeg", "Saltern", "Packed", "Omega", "Cluster", "Reach", "Origin", "Meta", "Marathon", "Europa", "MeteorArena", "Fland", "Barratry" };
|
||||
public static readonly string[] MapsSource = { "Empty", "Box", "Bagel", "Dev", "CentComm", "Atlas", "Core", "TestTeg", "Saltern", "Packed", "Omega", "Cluster", "Reach", "Origin", "Meta", "Marathon", "Europa", "MeteorArena", "Fland", "Barratry", "Oasis" };
|
||||
|
||||
[ParamsSource(nameof(MapsSource))]
|
||||
public string Map;
|
||||
|
||||
@@ -2,6 +2,4 @@
|
||||
|
||||
namespace Content.Client.Access;
|
||||
|
||||
public sealed class IdCardSystem : SharedIdCardSystem
|
||||
{
|
||||
}
|
||||
public sealed class IdCardSystem : SharedIdCardSystem;
|
||||
|
||||
@@ -40,9 +40,9 @@ namespace Content.Client.Access.UI
|
||||
SendMessage(new AgentIDCardJobChangedMessage(newJob));
|
||||
}
|
||||
|
||||
public void OnJobIconChanged(string newJobIcon)
|
||||
public void OnJobIconChanged(string newJobIconId)
|
||||
{
|
||||
SendMessage(new AgentIDCardJobIconChangedMessage(newJobIcon));
|
||||
SendMessage(new AgentIDCardJobIconChangedMessage(newJobIconId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -57,7 +57,7 @@ namespace Content.Client.Access.UI
|
||||
|
||||
_window.SetCurrentName(cast.CurrentName);
|
||||
_window.SetCurrentJob(cast.CurrentJob);
|
||||
_window.SetAllowedIcons(cast.Icons);
|
||||
_window.SetAllowedIcons(cast.Icons, cast.CurrentJobIconId);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Content.Client.Access.UI
|
||||
JobLineEdit.OnFocusExit += e => OnJobChanged?.Invoke(e.Text);
|
||||
}
|
||||
|
||||
public void SetAllowedIcons(HashSet<string> icons)
|
||||
public void SetAllowedIcons(HashSet<string> icons, string currentJobIconId)
|
||||
{
|
||||
IconGrid.DisposeAllChildren();
|
||||
|
||||
@@ -79,6 +79,10 @@ namespace Content.Client.Access.UI
|
||||
jobIconButton.AddChild(jobIconTexture);
|
||||
jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIcon.ID);
|
||||
IconGrid.AddChild(jobIconButton);
|
||||
|
||||
if (jobIconId.Equals(currentJobIconId))
|
||||
jobIconButton.Pressed = true;
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Administration.Components;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[RegisterComponent]
|
||||
public sealed partial class HeadstandComponent : SharedHeadstandComponent
|
||||
{
|
||||
|
||||
|
||||
@@ -3,6 +3,5 @@ using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Administration.Components;
|
||||
|
||||
[NetworkedComponent, RegisterComponent]
|
||||
public sealed partial class KillSignComponent : SharedKillSignComponent
|
||||
{ }
|
||||
[RegisterComponent]
|
||||
public sealed partial class KillSignComponent : SharedKillSignComponent;
|
||||
|
||||
@@ -126,12 +126,15 @@ namespace Content.Client.Administration.Managers
|
||||
|
||||
public AdminData? GetAdminData(EntityUid uid, bool includeDeAdmin = false)
|
||||
{
|
||||
return uid == _player.LocalEntity ? _adminData : null;
|
||||
if (uid == _player.LocalEntity && (_adminData?.Active ?? includeDeAdmin))
|
||||
return _adminData;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public AdminData? GetAdminData(ICommonSession session, bool includeDeAdmin = false)
|
||||
{
|
||||
if (_player.LocalUser == session.UserId)
|
||||
if (_player.LocalUser == session.UserId && (_adminData?.Active ?? includeDeAdmin))
|
||||
return _adminData;
|
||||
|
||||
return null;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Administration.Managers;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -11,10 +14,12 @@ namespace Content.Client.Administration.Systems
|
||||
{
|
||||
[Dependency] private readonly IClientConGroupController _clientConGroupController = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _clientConsoleHost = default!;
|
||||
[Dependency] private readonly ISharedAdminManager _admin = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<GetVerbsEvent<Verb>>(AddAdminVerbs);
|
||||
|
||||
}
|
||||
|
||||
private void AddAdminVerbs(GetVerbsEvent<Verb> args)
|
||||
@@ -33,6 +38,24 @@ namespace Content.Client.Administration.Systems
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
if (!_admin.IsAdmin(args.User))
|
||||
return;
|
||||
|
||||
if (_admin.HasAdminFlag(args.User, AdminFlags.Admin))
|
||||
args.ExtraCategories.Add(VerbCategory.Admin);
|
||||
|
||||
if (_admin.HasAdminFlag(args.User, AdminFlags.Fun) && HasComp<MindContainerComponent>(args.Target))
|
||||
args.ExtraCategories.Add(VerbCategory.Antag);
|
||||
|
||||
if (_admin.HasAdminFlag(args.User, AdminFlags.Debug))
|
||||
args.ExtraCategories.Add(VerbCategory.Debug);
|
||||
|
||||
if (_admin.HasAdminFlag(args.User, AdminFlags.Fun))
|
||||
args.ExtraCategories.Add(VerbCategory.Smite);
|
||||
|
||||
if (_admin.HasAdminFlag(args.User, AdminFlags.Admin))
|
||||
args.ExtraCategories.Add(VerbCategory.Tricks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using Content.Client.Administration.UI.CustomControls;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Client.AutoGenerated;
|
||||
@@ -11,6 +12,7 @@ using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -32,8 +34,11 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
// This is less efficient than just holding a reference to the root control and enumerating children, but you
|
||||
// have to know how the controls are nested, which makes the code more complicated.
|
||||
private readonly List<CheckBox> _roleCheckboxes = new();
|
||||
private readonly ISawmill _banpanelSawmill;
|
||||
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
private enum TabNumbers
|
||||
{
|
||||
@@ -65,6 +70,7 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
_banpanelSawmill = _logManager.GetSawmill("admin.banpanel");
|
||||
PlayerList.OnSelectionChanged += OnPlayerSelectionChanged;
|
||||
PlayerNameLine.OnFocusExit += _ => OnPlayerNameChanged();
|
||||
PlayerCheckbox.OnPressed += _ =>
|
||||
@@ -104,6 +110,11 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
};
|
||||
SubmitButton.OnPressed += SubmitButtonOnOnPressed;
|
||||
|
||||
IpCheckbox.Pressed = _cfg.GetCVar(CCVars.ServerBanIpBanDefault);
|
||||
HwidCheckbox.Pressed = _cfg.GetCVar(CCVars.ServerBanHwidBanDefault);
|
||||
LastConnCheckbox.Pressed = _cfg.GetCVar(CCVars.ServerBanUseLastDetails);
|
||||
EraseCheckbox.Pressed = _cfg.GetCVar(CCVars.ServerBanErasePlayer);
|
||||
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-none"), (int) NoteSeverity.None);
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-low"), (int) NoteSeverity.Minor);
|
||||
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-medium"), (int) NoteSeverity.Medium);
|
||||
@@ -175,6 +186,39 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
c.Pressed = args.Pressed;
|
||||
}
|
||||
}
|
||||
|
||||
if (args.Pressed)
|
||||
{
|
||||
if (!Enum.TryParse(_cfg.GetCVar(CCVars.DepartmentBanDefaultSeverity), true, out NoteSeverity newSeverity))
|
||||
{
|
||||
_banpanelSawmill
|
||||
.Warning("Departmental role ban severity could not be parsed from config!");
|
||||
return;
|
||||
}
|
||||
SeverityOption.SelectId((int) newSeverity);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var childContainer in RolesContainer.Children)
|
||||
{
|
||||
if (childContainer is Container)
|
||||
{
|
||||
foreach (var child in childContainer.Children)
|
||||
{
|
||||
if (child is CheckBox { Pressed: true })
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Enum.TryParse(_cfg.GetCVar(CCVars.RoleBanDefaultSeverity), true, out NoteSeverity newSeverity))
|
||||
{
|
||||
_banpanelSawmill
|
||||
.Warning("Role ban severity could not be parsed from config!");
|
||||
return;
|
||||
}
|
||||
SeverityOption.SelectId((int) newSeverity);
|
||||
}
|
||||
};
|
||||
outerContainer.AddChild(innerContainer);
|
||||
foreach (var role in roleList)
|
||||
@@ -353,6 +397,35 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
{
|
||||
TypeOption.ModulateSelfOverride = null;
|
||||
Tabs.SetTabVisible((int) TabNumbers.Roles, TypeOption.SelectedId == (int) Types.Role);
|
||||
NoteSeverity? newSeverity = null;
|
||||
switch (TypeOption.SelectedId)
|
||||
{
|
||||
case (int)Types.Server:
|
||||
if (Enum.TryParse(_cfg.GetCVar(CCVars.ServerBanDefaultSeverity), true, out NoteSeverity serverSeverity))
|
||||
newSeverity = serverSeverity;
|
||||
else
|
||||
{
|
||||
_banpanelSawmill
|
||||
.Warning("Server ban severity could not be parsed from config!");
|
||||
}
|
||||
|
||||
break;
|
||||
case (int) Types.Role:
|
||||
|
||||
if (Enum.TryParse(_cfg.GetCVar(CCVars.RoleBanDefaultSeverity), true, out NoteSeverity roleSeverity))
|
||||
{
|
||||
newSeverity = roleSeverity;
|
||||
}
|
||||
else
|
||||
{
|
||||
_banpanelSawmill
|
||||
.Warning("Role ban severity could not be parsed from config!");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (newSeverity != null)
|
||||
SeverityOption.SelectId((int) newSeverity.Value);
|
||||
}
|
||||
|
||||
private void UpdateSubmitEnabled()
|
||||
|
||||
17
Content.Client/Animations/TrackUserComponent.cs
Normal file
17
Content.Client/Animations/TrackUserComponent.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace Content.Client.Animations;
|
||||
|
||||
/// <summary>
|
||||
/// Entities with this component tracks the user's world position every frame.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TrackUserComponent : Component
|
||||
{
|
||||
public EntityUid? User;
|
||||
|
||||
/// <summary>
|
||||
/// Offset in the direction of the entity's rotation.
|
||||
/// </summary>
|
||||
public Vector2 Offset = Vector2.Zero;
|
||||
}
|
||||
@@ -163,6 +163,26 @@ namespace Content.Client.Atmos.UI
|
||||
parent.AddChild(panel);
|
||||
panel.AddChild(dataContainer);
|
||||
|
||||
// Volume label
|
||||
var volBox = new BoxContainer { Orientation = BoxContainer.LayoutOrientation.Horizontal };
|
||||
|
||||
volBox.AddChild(new Label
|
||||
{
|
||||
Text = Loc.GetString("gas-analyzer-window-volume-text")
|
||||
});
|
||||
volBox.AddChild(new Control
|
||||
{
|
||||
MinSize = new Vector2(10, 0),
|
||||
HorizontalExpand = true
|
||||
});
|
||||
volBox.AddChild(new Label
|
||||
{
|
||||
Text = Loc.GetString("gas-analyzer-window-volume-val-text", ("volume", $"{gasMix.Volume:0.##}")),
|
||||
Align = Label.AlignMode.Right,
|
||||
HorizontalExpand = true
|
||||
});
|
||||
dataContainer.AddChild(volBox);
|
||||
|
||||
// Pressure label
|
||||
var presBox = new BoxContainer { Orientation = BoxContainer.LayoutOrientation.Horizontal };
|
||||
|
||||
|
||||
119
Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs
Normal file
119
Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using Content.Shared.Audio.Jukebox;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Audio.Components;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Audio.Jukebox;
|
||||
|
||||
public sealed class JukeboxBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
|
||||
[ViewVariables]
|
||||
private JukeboxMenu? _menu;
|
||||
|
||||
public JukeboxBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_menu = new JukeboxMenu();
|
||||
_menu.OnClose += Close;
|
||||
_menu.OpenCentered();
|
||||
|
||||
_menu.OnPlayPressed += args =>
|
||||
{
|
||||
if (args)
|
||||
{
|
||||
SendMessage(new JukeboxPlayingMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
SendMessage(new JukeboxPauseMessage());
|
||||
}
|
||||
};
|
||||
|
||||
_menu.OnStopPressed += () =>
|
||||
{
|
||||
SendMessage(new JukeboxStopMessage());
|
||||
};
|
||||
|
||||
_menu.OnSongSelected += SelectSong;
|
||||
|
||||
_menu.SetTime += SetTime;
|
||||
PopulateMusic();
|
||||
Reload();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reloads the attached menu if it exists.
|
||||
/// </summary>
|
||||
public void Reload()
|
||||
{
|
||||
if (_menu == null || !EntMan.TryGetComponent(Owner, out JukeboxComponent? jukebox))
|
||||
return;
|
||||
|
||||
_menu.SetAudioStream(jukebox.AudioStream);
|
||||
|
||||
if (_protoManager.TryIndex(jukebox.SelectedSongId, out var songProto))
|
||||
{
|
||||
var length = EntMan.System<AudioSystem>().GetAudioLength(songProto.Path.Path.ToString());
|
||||
_menu.SetSelectedSong(songProto.Name, (float) length.TotalSeconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
_menu.SetSelectedSong(string.Empty, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
public void PopulateMusic()
|
||||
{
|
||||
_menu?.Populate(_protoManager.EnumeratePrototypes<JukeboxPrototype>());
|
||||
}
|
||||
|
||||
public void SelectSong(ProtoId<JukeboxPrototype> songid)
|
||||
{
|
||||
SendMessage(new JukeboxSelectedMessage(songid));
|
||||
}
|
||||
|
||||
public void SetTime(float time)
|
||||
{
|
||||
var sentTime = time;
|
||||
|
||||
// You may be wondering, what the fuck is this
|
||||
// Well we want to be able to predict the playback slider change, of which there are many ways to do it
|
||||
// We can't just use SendPredictedMessage because it will reset every tick and audio updates every frame
|
||||
// so it will go BRRRRT
|
||||
// Using ping gets us close enough that it SHOULD, MOST OF THE TIME, fall within the 0.1 second tolerance
|
||||
// that's still on engine so our playback position never gets corrected.
|
||||
if (EntMan.TryGetComponent(Owner, out JukeboxComponent? jukebox) &&
|
||||
EntMan.TryGetComponent(jukebox.AudioStream, out AudioComponent? audioComp))
|
||||
{
|
||||
audioComp.PlaybackPosition = time;
|
||||
}
|
||||
|
||||
SendMessage(new JukeboxSetTimeMessage(sentTime));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
if (_menu == null)
|
||||
return;
|
||||
|
||||
_menu.OnClose -= Close;
|
||||
_menu.Dispose();
|
||||
_menu = null;
|
||||
}
|
||||
}
|
||||
|
||||
18
Content.Client/Audio/Jukebox/JukeboxMenu.xaml
Normal file
18
Content.Client/Audio/Jukebox/JukeboxMenu.xaml
Normal file
@@ -0,0 +1,18 @@
|
||||
<ui:FancyWindow xmlns="https://spacestation14.io" xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
SetSize="400 500" Title="{Loc 'jukebox-menu-title'}">
|
||||
<BoxContainer Margin="4 0" Orientation="Vertical">
|
||||
<ItemList Name="MusicList" SelectMode="Button" Margin="3 3 3 3"
|
||||
HorizontalExpand="True" VerticalExpand="True" SizeFlagsStretchRatio="8"/>
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<Label Name="SongSelected" Text="{Loc 'jukebox-menu-selectedsong'}" />
|
||||
<Label Name="SongName" Text="---" />
|
||||
<Slider Name="PlaybackSlider" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True"
|
||||
VerticalExpand="False" SizeFlagsStretchRatio="1">
|
||||
<Button Name="PlayButton" Text="{Loc 'jukebox-menu-buttonplay'}" />
|
||||
<Button Name="StopButton" Text="{Loc 'jukebox-menu-buttonstop'}" />
|
||||
<Label Name="DurationLabel" Text="00:00 / 00:00" HorizontalAlignment="Right" HorizontalExpand="True"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</ui:FancyWindow>
|
||||
166
Content.Client/Audio/Jukebox/JukeboxMenu.xaml.cs
Normal file
166
Content.Client/Audio/Jukebox/JukeboxMenu.xaml.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
using Content.Shared.Audio.Jukebox;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Audio.Components;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using FancyWindow = Content.Client.UserInterface.Controls.FancyWindow;
|
||||
|
||||
namespace Content.Client.Audio.Jukebox;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class JukeboxMenu : FancyWindow
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
private AudioSystem _audioSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Are we currently 'playing' or paused for the play / pause button.
|
||||
/// </summary>
|
||||
private bool _playState;
|
||||
|
||||
/// <summary>
|
||||
/// True if playing, false if paused.
|
||||
/// </summary>
|
||||
public event Action<bool>? OnPlayPressed;
|
||||
public event Action? OnStopPressed;
|
||||
public event Action<ProtoId<JukeboxPrototype>>? OnSongSelected;
|
||||
public event Action<float>? SetTime;
|
||||
|
||||
private EntityUid? _audio;
|
||||
|
||||
private float _lockTimer;
|
||||
|
||||
public JukeboxMenu()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
_audioSystem = _entManager.System<AudioSystem>();
|
||||
|
||||
MusicList.OnItemSelected += args =>
|
||||
{
|
||||
var entry = MusicList[args.ItemIndex];
|
||||
|
||||
if (entry.Metadata is not string juke)
|
||||
return;
|
||||
|
||||
OnSongSelected?.Invoke(juke);
|
||||
};
|
||||
|
||||
PlayButton.OnPressed += args =>
|
||||
{
|
||||
OnPlayPressed?.Invoke(!_playState);
|
||||
};
|
||||
|
||||
StopButton.OnPressed += args =>
|
||||
{
|
||||
OnStopPressed?.Invoke();
|
||||
};
|
||||
PlaybackSlider.OnReleased += PlaybackSliderKeyUp;
|
||||
|
||||
SetPlayPauseButton(_audioSystem.IsPlaying(_audio), force: true);
|
||||
}
|
||||
|
||||
public JukeboxMenu(AudioSystem audioSystem)
|
||||
{
|
||||
_audioSystem = audioSystem;
|
||||
}
|
||||
|
||||
public void SetAudioStream(EntityUid? audio)
|
||||
{
|
||||
_audio = audio;
|
||||
}
|
||||
|
||||
private void PlaybackSliderKeyUp(Slider args)
|
||||
{
|
||||
SetTime?.Invoke(PlaybackSlider.Value);
|
||||
_lockTimer = 0.5f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-populates the list of jukebox prototypes available.
|
||||
/// </summary>
|
||||
public void Populate(IEnumerable<JukeboxPrototype> jukeboxProtos)
|
||||
{
|
||||
MusicList.Clear();
|
||||
|
||||
foreach (var entry in jukeboxProtos)
|
||||
{
|
||||
MusicList.AddItem(entry.Name, metadata: entry.ID);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPlayPauseButton(bool playing, bool force = false)
|
||||
{
|
||||
if (_playState == playing && !force)
|
||||
return;
|
||||
|
||||
_playState = playing;
|
||||
|
||||
if (playing)
|
||||
{
|
||||
PlayButton.Text = Loc.GetString("jukebox-menu-buttonpause");
|
||||
return;
|
||||
}
|
||||
|
||||
PlayButton.Text = Loc.GetString("jukebox-menu-buttonplay");
|
||||
}
|
||||
|
||||
public void SetSelectedSong(string name, float length)
|
||||
{
|
||||
SetSelectedSongText(name);
|
||||
PlaybackSlider.MaxValue = length;
|
||||
PlaybackSlider.SetValueWithoutEvent(0);
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (_lockTimer > 0f)
|
||||
{
|
||||
_lockTimer -= args.DeltaSeconds;
|
||||
}
|
||||
|
||||
PlaybackSlider.Disabled = _lockTimer > 0f;
|
||||
|
||||
if (_entManager.TryGetComponent(_audio, out AudioComponent? audio))
|
||||
{
|
||||
DurationLabel.Text = $@"{TimeSpan.FromSeconds(audio.PlaybackPosition):mm\:ss} / {_audioSystem.GetAudioLength(audio.FileName):mm\:ss}";
|
||||
}
|
||||
else
|
||||
{
|
||||
DurationLabel.Text = $"00:00 / 00:00";
|
||||
}
|
||||
|
||||
if (PlaybackSlider.Grabbed)
|
||||
return;
|
||||
|
||||
if (audio != null || _entManager.TryGetComponent(_audio, out audio))
|
||||
{
|
||||
PlaybackSlider.SetValueWithoutEvent(audio.PlaybackPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
PlaybackSlider.SetValueWithoutEvent(0f);
|
||||
}
|
||||
|
||||
SetPlayPauseButton(_audioSystem.IsPlaying(_audio, audio));
|
||||
}
|
||||
|
||||
public void SetSelectedSongText(string? text)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
SongName.Text = text;
|
||||
}
|
||||
else
|
||||
{
|
||||
SongName.Text = "---";
|
||||
}
|
||||
}
|
||||
}
|
||||
145
Content.Client/Audio/Jukebox/JukeboxSystem.cs
Normal file
145
Content.Client/Audio/Jukebox/JukeboxSystem.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
using Content.Shared.Audio.Jukebox;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Audio.Jukebox;
|
||||
|
||||
|
||||
public sealed class JukeboxSystem : SharedJukeboxSystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
[Dependency] private readonly AnimationPlayerSystem _animationPlayer = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
|
||||
[Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<JukeboxComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||
SubscribeLocalEvent<JukeboxComponent, AnimationCompletedEvent>(OnAnimationCompleted);
|
||||
SubscribeLocalEvent<JukeboxComponent, AfterAutoHandleStateEvent>(OnJukeboxAfterState);
|
||||
|
||||
_protoManager.PrototypesReloaded += OnProtoReload;
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
_protoManager.PrototypesReloaded -= OnProtoReload;
|
||||
}
|
||||
|
||||
private void OnProtoReload(PrototypesReloadedEventArgs obj)
|
||||
{
|
||||
if (!obj.WasModified<JukeboxPrototype>())
|
||||
return;
|
||||
|
||||
var query = AllEntityQuery<JukeboxComponent, UserInterfaceComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out _, out var ui))
|
||||
{
|
||||
if (!_uiSystem.TryGetOpenUi<JukeboxBoundUserInterface>((uid, ui), JukeboxUiKey.Key, out var bui))
|
||||
continue;
|
||||
|
||||
bui.PopulateMusic();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnJukeboxAfterState(Entity<JukeboxComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
if (!_uiSystem.TryGetOpenUi<JukeboxBoundUserInterface>(ent.Owner, JukeboxUiKey.Key, out var bui))
|
||||
return;
|
||||
|
||||
bui.Reload();
|
||||
}
|
||||
|
||||
private void OnAnimationCompleted(EntityUid uid, JukeboxComponent component, AnimationCompletedEvent args)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||
return;
|
||||
|
||||
if (!TryComp<AppearanceComponent>(uid, out var appearance) ||
|
||||
!_appearanceSystem.TryGetData<JukeboxVisualState>(uid, JukeboxVisuals.VisualState, out var visualState, appearance))
|
||||
{
|
||||
visualState = JukeboxVisualState.On;
|
||||
}
|
||||
|
||||
UpdateAppearance(uid, visualState, component, sprite);
|
||||
}
|
||||
|
||||
private void OnAppearanceChange(EntityUid uid, JukeboxComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
if (!args.AppearanceData.TryGetValue(JukeboxVisuals.VisualState, out var visualStateObject) ||
|
||||
visualStateObject is not JukeboxVisualState visualState)
|
||||
{
|
||||
visualState = JukeboxVisualState.On;
|
||||
}
|
||||
|
||||
UpdateAppearance(uid, visualState, component, args.Sprite);
|
||||
}
|
||||
|
||||
private void UpdateAppearance(EntityUid uid, JukeboxVisualState visualState, JukeboxComponent component, SpriteComponent sprite)
|
||||
{
|
||||
SetLayerState(JukeboxVisualLayers.Base, component.OffState, sprite);
|
||||
|
||||
switch (visualState)
|
||||
{
|
||||
case JukeboxVisualState.On:
|
||||
SetLayerState(JukeboxVisualLayers.Base, component.OnState, sprite);
|
||||
break;
|
||||
|
||||
case JukeboxVisualState.Off:
|
||||
SetLayerState(JukeboxVisualLayers.Base, component.OffState, sprite);
|
||||
break;
|
||||
|
||||
case JukeboxVisualState.Select:
|
||||
PlayAnimation(uid, JukeboxVisualLayers.Base, component.SelectState, 1.0f, sprite);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayAnimation(EntityUid uid, JukeboxVisualLayers layer, string? state, float animationTime, SpriteComponent sprite)
|
||||
{
|
||||
if (string.IsNullOrEmpty(state))
|
||||
return;
|
||||
|
||||
if (!_animationPlayer.HasRunningAnimation(uid, state))
|
||||
{
|
||||
var animation = GetAnimation(layer, state, animationTime);
|
||||
sprite.LayerSetVisible(layer, true);
|
||||
_animationPlayer.Play(uid, animation, state);
|
||||
}
|
||||
}
|
||||
|
||||
private static Animation GetAnimation(JukeboxVisualLayers layer, string state, float animationTime)
|
||||
{
|
||||
return new Animation
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(animationTime),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackSpriteFlick
|
||||
{
|
||||
LayerKey = layer,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackSpriteFlick.KeyFrame(state, 0f)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void SetLayerState(JukeboxVisualLayers layer, string? state, SpriteComponent sprite)
|
||||
{
|
||||
if (string.IsNullOrEmpty(state))
|
||||
return;
|
||||
|
||||
sprite.LayerSetVisible(layer, true);
|
||||
sprite.LayerSetAutoAnimated(layer, true);
|
||||
sprite.LayerSetState(layer, state);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.CardboardBox;
|
||||
using Content.Shared.CardboardBox.Components;
|
||||
using Content.Shared.Examine;
|
||||
@@ -13,9 +14,14 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem
|
||||
[Dependency] private readonly TransformSystem _transform = default!;
|
||||
[Dependency] private readonly ExamineSystemShared _examine = default!;
|
||||
|
||||
private EntityQuery<BodyComponent> _bodyQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_bodyQuery = GetEntityQuery<BodyComponent>();
|
||||
|
||||
SubscribeNetworkEvent<PlayBoxEffectMessage>(OnBoxEffect);
|
||||
}
|
||||
|
||||
@@ -59,6 +65,10 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem
|
||||
if (!_examine.InRangeUnOccluded(sourcePos, mapPos, box.Distance, null))
|
||||
continue;
|
||||
|
||||
// no effect for anything too exotic
|
||||
if (!_bodyQuery.HasComp(mob))
|
||||
continue;
|
||||
|
||||
var ent = Spawn(box.Effect, mapPos);
|
||||
|
||||
if (!xformQuery.TryGetComponent(ent, out var entTransform) || !TryComp<SpriteComponent>(ent, out var sprite))
|
||||
|
||||
@@ -87,14 +87,12 @@ namespace Content.Client.Changelog
|
||||
if (!tab.AdminOnly || isAdmin)
|
||||
{
|
||||
Tabs.SetTabVisible(i, true);
|
||||
tab.Visible = true;
|
||||
visibleTabs++;
|
||||
firstVisible ??= i;
|
||||
}
|
||||
else
|
||||
{
|
||||
Tabs.SetTabVisible(i, false);
|
||||
tab.Visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
31
Content.Client/Chat/UI/EmotesMenu.xaml
Normal file
31
Content.Client/Chat/UI/EmotesMenu.xaml
Normal file
@@ -0,0 +1,31 @@
|
||||
<ui:RadialMenu xmlns="https://spacestation14.io"
|
||||
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
BackButtonStyleClass="RadialMenuBackButton"
|
||||
CloseButtonStyleClass="RadialMenuCloseButton"
|
||||
VerticalExpand="True"
|
||||
HorizontalExpand="True"
|
||||
MinSize="450 450">
|
||||
|
||||
<!-- Main -->
|
||||
<ui:RadialContainer Name="Main" VerticalExpand="True" HorizontalExpand="True" Radius="64" ReserveSpaceForHiddenChildren="False">
|
||||
<ui:RadialMenuTextureButton StyleClasses="RadialMenuButton" SetSize="64 64" ToolTip="{Loc 'emote-menu-category-general'}" TargetLayer="General" Visible="False">
|
||||
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Clothing/Head/Soft/mimesoft.rsi/icon.png"/>
|
||||
</ui:RadialMenuTextureButton>
|
||||
<ui:RadialMenuTextureButton StyleClasses="RadialMenuButton" SetSize="64 64" ToolTip="{Loc 'emote-menu-category-vocal'}" TargetLayer="Vocal" Visible="False">
|
||||
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Emotes/vocal.png"/>
|
||||
</ui:RadialMenuTextureButton>
|
||||
<ui:RadialMenuTextureButton StyleClasses="RadialMenuButton" SetSize="64 64" ToolTip="{Loc 'emote-menu-category-hands'}" TargetLayer="Hands" Visible="False">
|
||||
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Clothing/Hands/Gloves/latex.rsi/icon.png"/>
|
||||
</ui:RadialMenuTextureButton>
|
||||
</ui:RadialContainer>
|
||||
|
||||
<!-- General -->
|
||||
<ui:RadialContainer Name="General" VerticalExpand="True" HorizontalExpand="True" Radius="64"/>
|
||||
|
||||
<!-- Vocal -->
|
||||
<ui:RadialContainer Name="Vocal" VerticalExpand="True" HorizontalExpand="True" Radius="64"/>
|
||||
|
||||
<!-- Hands -->
|
||||
<ui:RadialContainer Name="Hands" VerticalExpand="True" HorizontalExpand="True" Radius="64"/>
|
||||
|
||||
</ui:RadialMenu>
|
||||
112
Content.Client/Chat/UI/EmotesMenu.xaml.cs
Normal file
112
Content.Client/Chat/UI/EmotesMenu.xaml.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Chat.Prototypes;
|
||||
using Content.Shared.Speech;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Chat.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class EmotesMenu : RadialMenu
|
||||
{
|
||||
[Dependency] private readonly EntityManager _entManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
|
||||
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
|
||||
public event Action<ProtoId<EmotePrototype>>? OnPlayEmote;
|
||||
|
||||
public EmotesMenu()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
_spriteSystem = _entManager.System<SpriteSystem>();
|
||||
|
||||
var main = FindControl<RadialContainer>("Main");
|
||||
|
||||
var emotes = _prototypeManager.EnumeratePrototypes<EmotePrototype>();
|
||||
foreach (var emote in emotes)
|
||||
{
|
||||
var player = _playerManager.LocalSession?.AttachedEntity;
|
||||
if (emote.Category == EmoteCategory.Invalid ||
|
||||
emote.ChatTriggers.Count == 0 ||
|
||||
!(player.HasValue && (emote.Whitelist?.IsValid(player.Value, _entManager) ?? true)) ||
|
||||
(emote.Blacklist?.IsValid(player.Value, _entManager) ?? false))
|
||||
continue;
|
||||
|
||||
if (!emote.Available &&
|
||||
_entManager.TryGetComponent<SpeechComponent>(player.Value, out var speech) &&
|
||||
!speech.AllowedEmotes.Contains(emote.ID))
|
||||
continue;
|
||||
|
||||
var parent = FindControl<RadialContainer>(emote.Category.ToString());
|
||||
|
||||
var button = new EmoteMenuButton
|
||||
{
|
||||
StyleClasses = { "RadialMenuButton" },
|
||||
SetSize = new Vector2(64f, 64f),
|
||||
ToolTip = Loc.GetString(emote.Name),
|
||||
ProtoId = emote.ID,
|
||||
};
|
||||
|
||||
var tex = new TextureRect
|
||||
{
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
Texture = _spriteSystem.Frame0(emote.Icon),
|
||||
TextureScale = new Vector2(2f, 2f),
|
||||
};
|
||||
|
||||
button.AddChild(tex);
|
||||
parent.AddChild(button);
|
||||
foreach (var child in main.Children)
|
||||
{
|
||||
if (child is not RadialMenuTextureButton castChild)
|
||||
continue;
|
||||
|
||||
if (castChild.TargetLayer == emote.Category.ToString())
|
||||
{
|
||||
castChild.Visible = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Set up menu actions
|
||||
foreach (var child in Children)
|
||||
{
|
||||
if (child is not RadialContainer container)
|
||||
continue;
|
||||
AddEmoteClickAction(container);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddEmoteClickAction(RadialContainer container)
|
||||
{
|
||||
foreach (var child in container.Children)
|
||||
{
|
||||
if (child is not EmoteMenuButton castChild)
|
||||
continue;
|
||||
|
||||
castChild.OnButtonUp += _ =>
|
||||
{
|
||||
OnPlayEmote?.Invoke(castChild.ProtoId);
|
||||
Close();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public sealed class EmoteMenuButton : RadialMenuTextureButton
|
||||
{
|
||||
public ProtoId<EmotePrototype> ProtoId { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Content.Client.Chemistry.EntitySystems;
|
||||
using Content.Client.Chemistry.UI;
|
||||
|
||||
namespace Content.Client.Chemistry.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Exposes a solution container's contents via a basic item status control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Shows the solution volume, max volume, and transfer amount.
|
||||
/// </remarks>
|
||||
/// <seealso cref="SolutionItemStatusSystem"/>
|
||||
/// <seealso cref="SolutionStatusControl"/>
|
||||
[RegisterComponent]
|
||||
public sealed partial class SolutionItemStatusComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The ID of the solution that will be shown on the item status control.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public string Solution = "default";
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Content.Client.Chemistry.Components;
|
||||
using Content.Client.Chemistry.UI;
|
||||
using Content.Client.Items;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
|
||||
namespace Content.Client.Chemistry.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// Wires up item status logic for <see cref="SolutionItemStatusComponent"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="SolutionStatusControl"/>
|
||||
public sealed class SolutionItemStatusSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
Subs.ItemStatus<SolutionItemStatusComponent>(
|
||||
entity => new SolutionStatusControl(entity, EntityManager, _solutionContainerSystem));
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ public sealed class InjectorStatusControl : Control
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (!_solutionContainers.TryGetSolution(_parent.Owner, InjectorComponent.SolutionName, out _, out var solution))
|
||||
if (!_solutionContainers.TryGetSolution(_parent.Owner, _parent.Comp.SolutionName, out _, out var solution))
|
||||
return;
|
||||
|
||||
// only updates the UI if any of the details are different than they previously were
|
||||
|
||||
59
Content.Client/Chemistry/UI/SolutionStatusControl.cs
Normal file
59
Content.Client/Chemistry/UI/SolutionStatusControl.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using Content.Client.Chemistry.Components;
|
||||
using Content.Client.Chemistry.EntitySystems;
|
||||
using Content.Client.Items.UI;
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.Chemistry.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Displays basic solution information for <see cref="SolutionItemStatusComponent"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="SolutionItemStatusSystem"/>
|
||||
public sealed class SolutionStatusControl : PollingItemStatusControl<SolutionStatusControl.Data>
|
||||
{
|
||||
private readonly Entity<SolutionItemStatusComponent> _parent;
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly SharedSolutionContainerSystem _solutionContainers;
|
||||
private readonly RichTextLabel _label;
|
||||
|
||||
public SolutionStatusControl(
|
||||
Entity<SolutionItemStatusComponent> parent,
|
||||
IEntityManager entityManager,
|
||||
SharedSolutionContainerSystem solutionContainers)
|
||||
{
|
||||
_parent = parent;
|
||||
_entityManager = entityManager;
|
||||
_solutionContainers = solutionContainers;
|
||||
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
|
||||
AddChild(_label);
|
||||
}
|
||||
|
||||
protected override Data PollData()
|
||||
{
|
||||
if (!_solutionContainers.TryGetSolution(_parent.Owner, _parent.Comp.Solution, out _, out var solution))
|
||||
return default;
|
||||
|
||||
FixedPoint2? transferAmount = null;
|
||||
if (_entityManager.TryGetComponent(_parent.Owner, out SolutionTransferComponent? transfer))
|
||||
transferAmount = transfer.TransferAmount;
|
||||
|
||||
return new Data(solution.Volume, solution.MaxVolume, transferAmount);
|
||||
}
|
||||
|
||||
protected override void Update(in Data data)
|
||||
{
|
||||
var markup = Loc.GetString("solution-status-volume",
|
||||
("currentVolume", data.Volume),
|
||||
("maxVolume", data.MaxVolume));
|
||||
if (data.TransferVolume is { } transferVolume)
|
||||
markup += "\n" + Loc.GetString("solution-status-transfer", ("volume", transferVolume));
|
||||
_label.SetMarkup(markup);
|
||||
}
|
||||
|
||||
public readonly record struct Data(FixedPoint2 Volume, FixedPoint2 MaxVolume, FixedPoint2? TransferVolume);
|
||||
}
|
||||
@@ -11,6 +11,7 @@ using Content.Shared.Item;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.Client.GameObjects.SpriteComponent;
|
||||
@@ -53,6 +54,7 @@ public sealed class ClientClothingSystem : ClothingSystem
|
||||
};
|
||||
|
||||
[Dependency] private readonly IResourceCache _cache = default!;
|
||||
[Dependency] private readonly ISerializationManager _serialization = default!;
|
||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
@@ -272,6 +274,7 @@ public sealed class ClientClothingSystem : ClothingSystem
|
||||
// temporary, until layer draw depths get added. Basically: a layer with the key "slot" is being used as a
|
||||
// bookmark to determine where in the list of layers we should insert the clothing layers.
|
||||
bool slotLayerExists = sprite.LayerMapTryGet(slot, out var index);
|
||||
var displacementData = inventory.Displacements.GetValueOrDefault(slot);
|
||||
|
||||
// add the new layers
|
||||
foreach (var (key, layerData) in ev.Layers)
|
||||
@@ -315,6 +318,28 @@ public sealed class ClientClothingSystem : ClothingSystem
|
||||
|
||||
sprite.LayerSetData(index, layerData);
|
||||
layer.Offset += slotDef.Offset;
|
||||
|
||||
if (displacementData != null)
|
||||
{
|
||||
if (displacementData.ShaderOverride != null)
|
||||
sprite.LayerSetShader(index, displacementData.ShaderOverride);
|
||||
|
||||
var displacementKey = $"{key}-displacement";
|
||||
if (!revealedLayers.Add(displacementKey))
|
||||
{
|
||||
Log.Warning($"Duplicate key for clothing visuals DISPLACEMENT: {displacementKey}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var displacementLayer = _serialization.CreateCopy(displacementData.Layer, notNullableOverride: true);
|
||||
displacementLayer.CopyToShaderParameters!.LayerKey = key;
|
||||
|
||||
// Add before main layer for this item.
|
||||
sprite.AddLayer(displacementLayer, index);
|
||||
sprite.LayerMapSet(displacementKey, index);
|
||||
|
||||
revealedLayers.Add(displacementKey);
|
||||
}
|
||||
}
|
||||
|
||||
RaiseLocalEvent(equipment, new EquipmentVisualsUpdatedEvent(equipee, slot, revealedLayers), true);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using System.Threading;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Utility;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
|
||||
@@ -13,6 +15,8 @@ namespace Content.Client.Communications.UI
|
||||
private CommunicationsConsoleBoundUserInterface Owner { get; set; }
|
||||
private readonly CancellationTokenSource _timerCancelTokenSource = new();
|
||||
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
public CommunicationsConsoleMenu(CommunicationsConsoleBoundUserInterface owner)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
@@ -23,6 +27,22 @@ namespace Content.Client.Communications.UI
|
||||
var loc = IoCManager.Resolve<ILocalizationManager>();
|
||||
MessageInput.Placeholder = new Rope.Leaf(loc.GetString("comms-console-menu-announcement-placeholder"));
|
||||
|
||||
var maxAnnounceLength = _cfg.GetCVar(CCVars.ChatMaxAnnouncementLength);
|
||||
MessageInput.OnTextChanged += (args) =>
|
||||
{
|
||||
if (args.Control.TextLength > maxAnnounceLength)
|
||||
{
|
||||
AnnounceButton.Disabled = true;
|
||||
AnnounceButton.ToolTip = Loc.GetString("comms-console-message-too-long");
|
||||
}
|
||||
else
|
||||
{
|
||||
AnnounceButton.Disabled = !owner.CanAnnounce;
|
||||
AnnounceButton.ToolTip = null;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
AnnounceButton.OnPressed += (_) => Owner.AnnounceButtonPressed(Rope.Collapse(MessageInput.TextRope));
|
||||
AnnounceButton.Disabled = !owner.CanAnnounce;
|
||||
|
||||
|
||||
8
Content.Client/DeviceNetwork/JammerSystem.cs
Normal file
8
Content.Client/DeviceNetwork/JammerSystem.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Content.Shared.Radio.EntitySystems;
|
||||
|
||||
namespace Content.Client.DeviceNetwork;
|
||||
|
||||
public sealed class JammerSystem : SharedJammerSystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -21,7 +21,7 @@ public sealed class DoAfterOverlay : Overlay
|
||||
private readonly ProgressColorSystem _progressColor;
|
||||
|
||||
private readonly Texture _barTexture;
|
||||
private readonly ShaderInstance _shader;
|
||||
private readonly ShaderInstance _unshadedShader;
|
||||
|
||||
/// <summary>
|
||||
/// Flash time for cancelled DoAfters
|
||||
@@ -45,7 +45,7 @@ public sealed class DoAfterOverlay : Overlay
|
||||
var sprite = new SpriteSpecifier.Rsi(new("/Textures/Interface/Misc/progress_bar.rsi"), "icon");
|
||||
_barTexture = _entManager.EntitySysManager.GetEntitySystem<SpriteSystem>().Frame0(sprite);
|
||||
|
||||
_shader = protoManager.Index<ShaderPrototype>("unshaded").Instance();
|
||||
_unshadedShader = protoManager.Index<ShaderPrototype>("unshaded").Instance();
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
@@ -58,7 +58,6 @@ public sealed class DoAfterOverlay : Overlay
|
||||
const float scale = 1f;
|
||||
var scaleMatrix = Matrix3.CreateScale(new Vector2(scale, scale));
|
||||
var rotationMatrix = Matrix3.CreateRotation(-rotation);
|
||||
handle.UseShader(_shader);
|
||||
|
||||
var curTime = _timing.CurTime;
|
||||
|
||||
@@ -79,6 +78,13 @@ public sealed class DoAfterOverlay : Overlay
|
||||
if (!bounds.Contains(worldPosition))
|
||||
continue;
|
||||
|
||||
// shades the do-after bar if the do-after bar belongs to other players
|
||||
// does not shade do-afters belonging to the local player
|
||||
if (uid != localEnt)
|
||||
handle.UseShader(null);
|
||||
else
|
||||
handle.UseShader(_unshadedShader);
|
||||
|
||||
// If the entity is paused, we will draw the do-after as it was when the entity got paused.
|
||||
var meta = metaQuery.GetComponent(uid);
|
||||
var time = meta.EntityPaused
|
||||
|
||||
@@ -119,6 +119,7 @@ namespace Content.Client.Entry
|
||||
_prototypeManager.RegisterIgnore("wireLayout");
|
||||
_prototypeManager.RegisterIgnore("alertLevels");
|
||||
_prototypeManager.RegisterIgnore("nukeopsRole");
|
||||
_prototypeManager.RegisterIgnore("ghostRoleRaffleDecider");
|
||||
|
||||
_componentFactory.GenerateNetIds();
|
||||
_adminManager.Initialize();
|
||||
|
||||
@@ -3,7 +3,5 @@ using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Extinguisher;
|
||||
|
||||
[NetworkedComponent, RegisterComponent]
|
||||
public sealed partial class FireExtinguisherComponent : SharedFireExtinguisherComponent
|
||||
{
|
||||
}
|
||||
[RegisterComponent]
|
||||
public sealed partial class FireExtinguisherComponent : SharedFireExtinguisherComponent;
|
||||
|
||||
@@ -86,7 +86,7 @@ public sealed class EyeLerpingSystem : EntitySystem
|
||||
private void HandleMapChange(EntityUid uid, LerpingEyeComponent component, ref EntParentChangedMessage args)
|
||||
{
|
||||
// Is this actually a map change? If yes, stop any lerps
|
||||
if (args.OldMapId != args.Transform.MapID)
|
||||
if (args.OldMapId != args.Transform.MapUid)
|
||||
component.LastRotation = GetRotation(uid, args.Transform);
|
||||
}
|
||||
|
||||
|
||||
48
Content.Client/Fax/System/FaxVisualsSystem.cs
Normal file
48
Content.Client/Fax/System/FaxVisualsSystem.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using Robust.Client.GameObjects;
|
||||
using Content.Shared.Fax.Components;
|
||||
using Content.Shared.Fax;
|
||||
using Robust.Client.Animations;
|
||||
|
||||
namespace Content.Client.Fax.System;
|
||||
|
||||
/// <summary>
|
||||
/// Visualizer for the fax machine which displays the correct sprite based on the inserted entity.
|
||||
/// </summary>
|
||||
public sealed class FaxVisualsSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AnimationPlayerSystem _player = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<FaxMachineComponent, AppearanceChangeEvent>(OnAppearanceChanged);
|
||||
}
|
||||
|
||||
private void OnAppearanceChanged(EntityUid uid, FaxMachineComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
if (_appearance.TryGetData(uid, FaxMachineVisuals.VisualState, out FaxMachineVisualState visuals) && visuals == FaxMachineVisualState.Inserting)
|
||||
{
|
||||
_player.Play(uid, new Animation()
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(2.4),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackSpriteFlick()
|
||||
{
|
||||
LayerKey = FaxMachineVisuals.VisualState,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackSpriteFlick.KeyFrame(component.InsertingState, 0f),
|
||||
new AnimationTrackSpriteFlick.KeyFrame("icon", 2.4f),
|
||||
}
|
||||
}
|
||||
}
|
||||
}, "faxecute");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ public sealed class FaxBoundUi : BoundUserInterface
|
||||
{
|
||||
if (_dialogIsOpen)
|
||||
return;
|
||||
|
||||
|
||||
_dialogIsOpen = true;
|
||||
var filters = new FileDialogFilters(new FileDialogFilters.Group("txt"));
|
||||
await using var file = await _fileDialogManager.OpenFile(filters);
|
||||
@@ -52,8 +52,27 @@ public sealed class FaxBoundUi : BoundUserInterface
|
||||
}
|
||||
|
||||
using var reader = new StreamReader(file);
|
||||
|
||||
var firstLine = await reader.ReadLineAsync();
|
||||
string? label = null;
|
||||
var content = await reader.ReadToEndAsync();
|
||||
SendMessage(new FaxFileMessage(content[..Math.Min(content.Length, FaxFileMessageValidation.MaxContentSize)], _window.OfficePaper));
|
||||
|
||||
if (firstLine is { })
|
||||
{
|
||||
if (firstLine.StartsWith('#'))
|
||||
{
|
||||
label = firstLine[1..].Trim();
|
||||
}
|
||||
else
|
||||
{
|
||||
content = firstLine + "\n" + content;
|
||||
}
|
||||
}
|
||||
|
||||
SendMessage(new FaxFileMessage(
|
||||
label?[..Math.Min(label.Length, FaxFileMessageValidation.MaxLabelSize)],
|
||||
content[..Math.Min(content.Length, FaxFileMessageValidation.MaxContentSize)],
|
||||
_window.OfficePaper));
|
||||
}
|
||||
|
||||
private void OnSendButtonPressed()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Client.Administration.Managers;
|
||||
using Content.Client.Gameplay;
|
||||
using Content.Client.Lobby;
|
||||
using Content.Client.RoundEnd;
|
||||
@@ -6,7 +7,7 @@ using Content.Shared.GameWindow;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.State;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.GameTicking.Managers
|
||||
{
|
||||
@@ -14,17 +15,14 @@ namespace Content.Client.GameTicking.Managers
|
||||
public sealed class ClientGameTicker : SharedGameTicker
|
||||
{
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IClientAdminManager _admin = default!;
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||
|
||||
[ViewVariables] private bool _initialized;
|
||||
private Dictionary<NetEntity, Dictionary<string, uint?>> _jobsAvailable = new();
|
||||
private Dictionary<NetEntity, string> _stationNames = new();
|
||||
|
||||
/// <summary>
|
||||
/// The current round-end window. Could be used to support re-opening the window after closing it.
|
||||
/// </summary>
|
||||
private RoundEndSummaryWindow? _window;
|
||||
|
||||
[ViewVariables] public bool AreWeReady { get; private set; }
|
||||
[ViewVariables] public bool IsGameStarted { get; private set; }
|
||||
[ViewVariables] public string? RestartSound { get; private set; }
|
||||
@@ -44,8 +42,6 @@ namespace Content.Client.GameTicking.Managers
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
DebugTools.Assert(!_initialized);
|
||||
|
||||
SubscribeNetworkEvent<TickerJoinLobbyEvent>(JoinLobby);
|
||||
SubscribeNetworkEvent<TickerJoinGameEvent>(JoinGame);
|
||||
SubscribeNetworkEvent<TickerConnectionStatusEvent>(ConnectionStatus);
|
||||
@@ -53,14 +49,33 @@ namespace Content.Client.GameTicking.Managers
|
||||
SubscribeNetworkEvent<TickerLobbyInfoEvent>(LobbyInfo);
|
||||
SubscribeNetworkEvent<TickerLobbyCountdownEvent>(LobbyCountdown);
|
||||
SubscribeNetworkEvent<RoundEndMessageEvent>(RoundEnd);
|
||||
SubscribeNetworkEvent<RequestWindowAttentionEvent>(msg =>
|
||||
{
|
||||
IoCManager.Resolve<IClyde>().RequestWindowAttention();
|
||||
});
|
||||
SubscribeNetworkEvent<RequestWindowAttentionEvent>(OnAttentionRequest);
|
||||
SubscribeNetworkEvent<TickerLateJoinStatusEvent>(LateJoinStatus);
|
||||
SubscribeNetworkEvent<TickerJobsAvailableEvent>(UpdateJobsAvailable);
|
||||
|
||||
_initialized = true;
|
||||
_admin.AdminStatusUpdated += OnAdminUpdated;
|
||||
OnAdminUpdated();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
_admin.AdminStatusUpdated -= OnAdminUpdated;
|
||||
base.Shutdown();
|
||||
}
|
||||
|
||||
private void OnAdminUpdated()
|
||||
{
|
||||
// Hide some map/grid related logs from clients. This is to try prevent some easy metagaming by just
|
||||
// reading the console. E.g., logs like this one could leak the nuke station/grid:
|
||||
// > Grid NT-Arrivals 1101 (122/n25896) changed parent. Old parent: map 10 (121/n25895). New parent: FTL (123/n26470)
|
||||
#if !DEBUG
|
||||
_map.Log.Level = _admin.IsAdmin() ? LogLevel.Info : LogLevel.Warning;
|
||||
#endif
|
||||
}
|
||||
|
||||
private void OnAttentionRequest(RequestWindowAttentionEvent ev)
|
||||
{
|
||||
_clyde.RequestWindowAttention();
|
||||
}
|
||||
|
||||
private void LateJoinStatus(TickerLateJoinStatusEvent message)
|
||||
@@ -132,12 +147,7 @@ namespace Content.Client.GameTicking.Managers
|
||||
// Force an update in the event of this song being the same as the last.
|
||||
RestartSound = message.RestartSound;
|
||||
|
||||
// Don't open duplicate windows (mainly for replays).
|
||||
if (_window?.RoundId == message.RoundId)
|
||||
return;
|
||||
|
||||
//This is not ideal at all, but I don't see an immediately better fit anywhere else.
|
||||
_window = new RoundEndSummaryWindow(message.GamemodeTitle, message.RoundEndText, message.RoundDuration, message.RoundId, message.AllPlayersEndInfo, _entityManager);
|
||||
_userInterfaceManager.GetUIController<RoundEndSummaryUIController>().OpenRoundEndSummaryWindow(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Client.Ghost.Commands;
|
||||
|
||||
public sealed class ToggleGhostVisibilityCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntitySystemManager _entSysMan = default!;
|
||||
|
||||
public string Command => "toggleghostvisibility";
|
||||
public string Description => "Toggles ghost visibility on the client.";
|
||||
public string Help => "toggleghostvisibility [bool]";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var ghostSystem = _entSysMan.GetEntitySystem<GhostSystem>();
|
||||
|
||||
if (args.Length != 0 && bool.TryParse(args[0], out var visibility))
|
||||
{
|
||||
ghostSystem.ToggleGhostVisibility(visibility);
|
||||
}
|
||||
else
|
||||
{
|
||||
ghostSystem.ToggleGhostVisibility();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ using Content.Shared.Actions;
|
||||
using Content.Shared.Ghost;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
@@ -177,9 +176,9 @@ namespace Content.Client.Ghost
|
||||
_console.RemoteExecuteCommand(null, "ghostroles");
|
||||
}
|
||||
|
||||
public void ToggleGhostVisibility()
|
||||
public void ToggleGhostVisibility(bool? visibility = null)
|
||||
{
|
||||
GhostVisibility = !GhostVisibility;
|
||||
GhostVisibility = visibility ?? !GhostVisibility;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Content.Client.Chemistry.EntitySystems;
|
||||
using Content.Client.Guidebook.Richtext;
|
||||
using Content.Client.Message;
|
||||
using Content.Client.UserInterface.ControlExtensions;
|
||||
using Content.Shared.Body.Prototypes;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using JetBrains.Annotations;
|
||||
@@ -128,7 +129,7 @@ public sealed partial class GuideReagentEmbed : BoxContainer, IDocumentTag, ISea
|
||||
|
||||
var groupLabel = new RichTextLabel();
|
||||
groupLabel.SetMarkup(Loc.GetString("guidebook-reagent-effects-metabolism-group-rate",
|
||||
("group", group), ("rate", effect.MetabolismRate)));
|
||||
("group", _prototype.Index<MetabolismGroupPrototype>(group).LocalizedName), ("rate", effect.MetabolismRate)));
|
||||
var descriptionLabel = new RichTextLabel
|
||||
{
|
||||
Margin = new Thickness(25, 0, 10, 0)
|
||||
|
||||
@@ -136,6 +136,7 @@ public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
|
||||
TreeItem? parent = forcedRoot == null ? null : AddEntry(forcedRoot, null, addedEntries);
|
||||
foreach (var entry in GetSortedEntries(roots))
|
||||
{
|
||||
if (!entry.CrystallPunkAllowed) continue; //CrystallPunk guidebook filter
|
||||
AddEntry(entry.Id, parent, addedEntries);
|
||||
}
|
||||
Tree.SetAllExpanded(true);
|
||||
|
||||
@@ -39,10 +39,13 @@ public class GuideEntry
|
||||
/// If the guide is the child of some other guide, the order simply determined by the order of children in <see cref="Children"/>.
|
||||
/// </summary>
|
||||
[DataField("priority")] public int Priority = 0;
|
||||
|
||||
[DataField]
|
||||
public bool CrystallPunkAllowed = false;
|
||||
}
|
||||
|
||||
[Prototype("guideEntry")]
|
||||
public sealed class GuideEntryPrototype : GuideEntry, IPrototype
|
||||
public sealed partial class GuideEntryPrototype : GuideEntry, IPrototype
|
||||
{
|
||||
public string ID => Id;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -31,6 +32,8 @@ public sealed class GuidebookSystem : EntitySystem
|
||||
[Dependency] private readonly SharedPointLightSystem _pointLightSystem = default!;
|
||||
[Dependency] private readonly TagSystem _tags = default!;
|
||||
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!; //CrystallPunk guidebook filter
|
||||
|
||||
public event Action<List<string>, List<string>?, string?, bool, string?>? OnGuidebookOpen;
|
||||
public const string GuideEmbedTag = "GuideEmbeded";
|
||||
|
||||
@@ -39,6 +42,7 @@ public sealed class GuidebookSystem : EntitySystem
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<GuideHelpComponent, MapInitEvent>(OnCrystallPunkMapInit); //CrystallPunk guidebook filter
|
||||
SubscribeLocalEvent<GuideHelpComponent, GetVerbsEvent<ExamineVerb>>(OnGetVerbs);
|
||||
SubscribeLocalEvent<GuideHelpComponent, ActivateInWorldEvent>(OnInteract);
|
||||
|
||||
@@ -48,6 +52,21 @@ public sealed class GuidebookSystem : EntitySystem
|
||||
OnGuidebookControlsTestGetAlternateVerbs);
|
||||
}
|
||||
|
||||
//CrystallPunk guidebook filter
|
||||
private void OnCrystallPunkMapInit(Entity<GuideHelpComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
foreach (var guide in ent.Comp.Guides)
|
||||
{
|
||||
var guideProto = _proto.Index<GuideEntryPrototype>(guide);
|
||||
if (!guideProto.CrystallPunkAllowed) //REMOVE unnecessary guidebook
|
||||
{
|
||||
RemComp<GuideHelpComponent>(ent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
//CrystallPunk guidebook filter end
|
||||
|
||||
/// <summary>
|
||||
/// Gets a user entity to use for verbs and examinations. If the player has no attached entity, this will use a
|
||||
/// dummy client-side entity so that users can still use the guidebook when not attached to anything (e.g., in the
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace Content.Client.HealthAnalyzer.UI
|
||||
|
||||
var groupTitleText = $"{Loc.GetString(
|
||||
"health-analyzer-window-damage-group-text",
|
||||
("damageGroup", Loc.GetString("health-analyzer-window-damage-group-" + damageGroupId)),
|
||||
("damageGroup", _prototypes.Index<DamageGroupPrototype>(damageGroupId).LocalizedName),
|
||||
("amount", damageAmount)
|
||||
)}";
|
||||
|
||||
@@ -170,7 +170,7 @@ namespace Content.Client.HealthAnalyzer.UI
|
||||
|
||||
var damageString = Loc.GetString(
|
||||
"health-analyzer-window-damage-type-text",
|
||||
("damageType", Loc.GetString("health-analyzer-window-damage-type-" + type)),
|
||||
("damageType", _prototypes.Index<DamageTypePrototype>(type).LocalizedName),
|
||||
("amount", typeAmount)
|
||||
);
|
||||
|
||||
|
||||
@@ -108,8 +108,11 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
|
||||
/// This should not be used if the entity is owned by the server. The server will otherwise
|
||||
/// override this with the appearance data it sends over.
|
||||
/// </remarks>
|
||||
public override void LoadProfile(EntityUid uid, HumanoidCharacterProfile profile, HumanoidAppearanceComponent? humanoid = null)
|
||||
public override void LoadProfile(EntityUid uid, HumanoidCharacterProfile? profile, HumanoidAppearanceComponent? humanoid = null)
|
||||
{
|
||||
if (profile == null)
|
||||
return;
|
||||
|
||||
if (!Resolve(uid, ref humanoid))
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Implants.Components;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
@@ -17,7 +18,7 @@ public sealed class ImplanterStatusControl : Control
|
||||
_parent = parent;
|
||||
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
|
||||
_label.MaxWidth = 350;
|
||||
AddChild(_label);
|
||||
AddChild(new ClipControl { Children = { _label } });
|
||||
|
||||
Update();
|
||||
}
|
||||
@@ -42,17 +43,12 @@ public sealed class ImplanterStatusControl : Control
|
||||
_ => Loc.GetString("injector-invalid-injector-toggle-mode")
|
||||
};
|
||||
|
||||
var (implantName, implantDescription) = _parent.ImplanterSlot.HasItem switch
|
||||
{
|
||||
false => (Loc.GetString("implanter-empty-text"), ""),
|
||||
true => (_parent.ImplantData.Item1, _parent.ImplantData.Item2),
|
||||
};
|
||||
|
||||
var implantName = _parent.ImplanterSlot.HasItem
|
||||
? _parent.ImplantData.Item1
|
||||
: Loc.GetString("implanter-empty-text");
|
||||
|
||||
_label.SetMarkup(Loc.GetString("implanter-label",
|
||||
("implantName", implantName),
|
||||
("implantDescription", implantDescription),
|
||||
("modeString", modeStringLocalized),
|
||||
("lineBreak", "\n")));
|
||||
("modeString", modeStringLocalized)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace Content.Client.Input
|
||||
common.AddFunction(ContentKeyFunctions.ZoomIn);
|
||||
common.AddFunction(ContentKeyFunctions.ResetZoom);
|
||||
common.AddFunction(ContentKeyFunctions.InspectEntity);
|
||||
common.AddFunction(ContentKeyFunctions.ToggleRoundEndSummaryWindow);
|
||||
|
||||
// Not in engine, because engine cannot check for sanbox/admin status before starting placement.
|
||||
common.AddFunction(ContentKeyFunctions.EditorCopyObject);
|
||||
@@ -59,6 +60,7 @@ namespace Content.Client.Input
|
||||
human.AddFunction(ContentKeyFunctions.UseItemInHand);
|
||||
human.AddFunction(ContentKeyFunctions.AltUseItemInHand);
|
||||
human.AddFunction(ContentKeyFunctions.OpenCharacterMenu);
|
||||
human.AddFunction(ContentKeyFunctions.OpenEmotesMenu);
|
||||
human.AddFunction(ContentKeyFunctions.ActivateItemInWorld);
|
||||
human.AddFunction(ContentKeyFunctions.ThrowItemInHand);
|
||||
human.AddFunction(ContentKeyFunctions.AltActivateItemInWorld);
|
||||
|
||||
@@ -199,7 +199,7 @@ namespace Content.Client.Inventory
|
||||
|
||||
public void UIInventoryStorageActivate(string slot)
|
||||
{
|
||||
EntityManager.EntityNetManager?.SendSystemNetworkMessage(new OpenSlotStorageNetworkMessage(slot));
|
||||
EntityManager.RaisePredictiveEvent(new OpenSlotStorageNetworkMessage(slot));
|
||||
}
|
||||
|
||||
public void UIInventoryExamine(string slot, EntityUid uid)
|
||||
@@ -251,6 +251,7 @@ namespace Content.Client.Inventory
|
||||
public string SlotGroup => SlotDef.SlotGroup;
|
||||
public string SlotDisplayName => SlotDef.DisplayName;
|
||||
public string TextureName => "Slots/" + SlotDef.TextureName;
|
||||
public string FullTextureName => SlotDef.FullTextureName;
|
||||
|
||||
public SlotData(SlotDefinition slotDef, ContainerSlot? container = null, bool highlighted = false,
|
||||
bool blocked = false)
|
||||
|
||||
@@ -219,7 +219,7 @@ namespace Content.Client.Inventory
|
||||
|
||||
if (entity == null)
|
||||
{
|
||||
button.SpriteView.SetEntity(null);
|
||||
button.SetEntity(null);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ namespace Content.Client.Inventory
|
||||
else
|
||||
return;
|
||||
|
||||
button.SpriteView.SetEntity(viewEnt);
|
||||
button.SetEntity(viewEnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
28
Content.Client/Items/UI/PollingItemStatusControl.cs
Normal file
28
Content.Client/Items/UI/PollingItemStatusControl.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Items.UI;
|
||||
|
||||
/// <summary>
|
||||
/// A base for item status controls that poll data every frame. Avoids UI updates if data didn't change.
|
||||
/// </summary>
|
||||
/// <typeparam name="TData">The full status control data that is polled every frame.</typeparam>
|
||||
public abstract class PollingItemStatusControl<TData> : Control where TData : struct, IEquatable<TData>
|
||||
{
|
||||
private TData _lastData;
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
var newData = PollData();
|
||||
if (newData.Equals(_lastData))
|
||||
return;
|
||||
|
||||
_lastData = newData;
|
||||
Update(newData);
|
||||
}
|
||||
|
||||
protected abstract TData PollData();
|
||||
protected abstract void Update(in TData data);
|
||||
}
|
||||
18
Content.Client/Labels/EntitySystems/HandLabelerSystem.cs
Normal file
18
Content.Client/Labels/EntitySystems/HandLabelerSystem.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using Content.Client.Labels.UI;
|
||||
using Content.Shared.Labels;
|
||||
using Content.Shared.Labels.Components;
|
||||
using Content.Shared.Labels.EntitySystems;
|
||||
|
||||
namespace Content.Client.Labels.EntitySystems;
|
||||
|
||||
public sealed class HandLabelerSystem : SharedHandLabelerSystem
|
||||
{
|
||||
protected override void UpdateUI(Entity<HandLabelerComponent> ent)
|
||||
{
|
||||
if (UserInterfaceSystem.TryGetOpenUi(ent.Owner, HandLabelerUiKey.Key, out var bui)
|
||||
&& bui is HandLabelerBoundUserInterface cBui)
|
||||
{
|
||||
cBui.Reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Shared.Labels;
|
||||
using Content.Shared.Labels.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Labels.UI
|
||||
@@ -8,11 +9,14 @@ namespace Content.Client.Labels.UI
|
||||
/// </summary>
|
||||
public sealed class HandLabelerBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
|
||||
[ViewVariables]
|
||||
private HandLabelerWindow? _window;
|
||||
|
||||
public HandLabelerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
@@ -27,24 +31,25 @@ namespace Content.Client.Labels.UI
|
||||
|
||||
_window.OnClose += Close;
|
||||
_window.OnLabelChanged += OnLabelChanged;
|
||||
Reload();
|
||||
}
|
||||
|
||||
private void OnLabelChanged(string newLabel)
|
||||
{
|
||||
SendMessage(new HandLabelerLabelChangedMessage(newLabel));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the UI state based on server-sent info
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
if (_window == null || state is not HandLabelerBoundUserInterfaceState cast)
|
||||
// Focus moment
|
||||
if (_entManager.TryGetComponent(Owner, out HandLabelerComponent? labeler) &&
|
||||
labeler.AssignedLabel.Equals(newLabel))
|
||||
return;
|
||||
|
||||
_window.SetCurrentLabel(cast.CurrentLabel);
|
||||
SendPredictedMessage(new HandLabelerLabelChangedMessage(newLabel));
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
if (_window == null || !_entManager.TryGetComponent(Owner, out HandLabelerComponent? component))
|
||||
return;
|
||||
|
||||
_window.SetCurrentLabel(component.AssignedLabel);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
|
||||
@@ -9,17 +9,40 @@ namespace Content.Client.Labels.UI
|
||||
{
|
||||
public event Action<string>? OnLabelChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Is the user currently entering text into the control?
|
||||
/// </summary>
|
||||
private bool _focused;
|
||||
// TODO LineEdit Make this a bool on the LineEdit control
|
||||
|
||||
private string _label = string.Empty;
|
||||
|
||||
public HandLabelerWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
LabelLineEdit.OnTextEntered += e => OnLabelChanged?.Invoke(e.Text);
|
||||
LabelLineEdit.OnFocusExit += e => OnLabelChanged?.Invoke(e.Text);
|
||||
LabelLineEdit.OnTextEntered += e =>
|
||||
{
|
||||
_label = e.Text;
|
||||
OnLabelChanged?.Invoke(_label);
|
||||
};
|
||||
|
||||
LabelLineEdit.OnFocusEnter += _ => _focused = true;
|
||||
LabelLineEdit.OnFocusExit += _ =>
|
||||
{
|
||||
_focused = false;
|
||||
LabelLineEdit.Text = _label;
|
||||
};
|
||||
}
|
||||
|
||||
public void SetCurrentLabel(string label)
|
||||
{
|
||||
LabelLineEdit.Text = label;
|
||||
if (label == _label)
|
||||
return;
|
||||
|
||||
_label = label;
|
||||
if (!_focused)
|
||||
LabelLineEdit.Text = label;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Lathe.UI;
|
||||
@@ -19,6 +21,8 @@ public sealed partial class LatheMenu : DefaultWindow
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IResourceCache _resources = default!;
|
||||
|
||||
private EntityUid _owner;
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
private readonly LatheSystem _lathe;
|
||||
@@ -104,12 +108,21 @@ public sealed partial class LatheMenu : DefaultWindow
|
||||
RecipeList.Children.Clear();
|
||||
foreach (var prototype in sortedRecipesToShow)
|
||||
{
|
||||
var icon = prototype.Icon == null
|
||||
? _spriteSystem.GetPrototypeIcon(prototype.Result).Default
|
||||
: _spriteSystem.Frame0(prototype.Icon);
|
||||
List<Texture> textures;
|
||||
if (_prototypeManager.TryIndex(prototype.Result, out EntityPrototype? entityProto) && entityProto != null)
|
||||
{
|
||||
textures = SpriteComponent.GetPrototypeTextures(entityProto, _resources).Select(o => o.Default).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
textures = prototype.Icon == null
|
||||
? new List<Texture> { _spriteSystem.GetPrototypeIcon(prototype.Result).Default }
|
||||
: new List<Texture> { _spriteSystem.Frame0(prototype.Icon) };
|
||||
}
|
||||
|
||||
var canProduce = _lathe.CanProduce(_owner, prototype, quantity);
|
||||
|
||||
var control = new RecipeControl(prototype, () => GenerateTooltipText(prototype), canProduce, icon);
|
||||
var control = new RecipeControl(prototype, () => GenerateTooltipText(prototype), canProduce, textures);
|
||||
control.OnButtonPressed += s =>
|
||||
{
|
||||
if (!int.TryParse(AmountLineEdit.Text, out var amount) || amount <= 0)
|
||||
|
||||
@@ -5,11 +5,15 @@
|
||||
Margin="0"
|
||||
StyleClasses="ButtonSquare">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<TextureRect
|
||||
Name="RecipeTexture"
|
||||
<LayeredTextureRect
|
||||
Name="RecipeTextures"
|
||||
Margin="0 0 4 0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Stretch="KeepAspectCentered"
|
||||
MinSize="32 32"
|
||||
Stretch="KeepAspectCentered" />
|
||||
CanShrink="true"
|
||||
/>
|
||||
<Label Name="RecipeName" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
</Button>
|
||||
|
||||
@@ -2,8 +2,8 @@ using Content.Shared.Research.Prototypes;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Graphics;
|
||||
|
||||
namespace Content.Client.Lathe.UI;
|
||||
|
||||
@@ -13,12 +13,12 @@ public sealed partial class RecipeControl : Control
|
||||
public Action<string>? OnButtonPressed;
|
||||
public Func<string> TooltipTextSupplier;
|
||||
|
||||
public RecipeControl(LatheRecipePrototype recipe, Func<string> tooltipTextSupplier, bool canProduce, Texture? texture = null)
|
||||
public RecipeControl(LatheRecipePrototype recipe, Func<string> tooltipTextSupplier, bool canProduce, List<Texture> textures)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
RecipeName.Text = recipe.Name;
|
||||
RecipeTexture.Texture = texture;
|
||||
RecipeTextures.Textures = textures;
|
||||
Button.Disabled = !canProduce;
|
||||
TooltipTextSupplier = tooltipTextSupplier;
|
||||
Button.TooltipSupplier = SupplyTooltip;
|
||||
|
||||
@@ -52,7 +52,7 @@ public sealed class ExpendableLightSystem : VisualizerSystem<ExpendableLightComp
|
||||
case ExpendableLightState.Lit:
|
||||
_audioSystem.Stop(comp.PlayingStream);
|
||||
comp.PlayingStream = _audioSystem.PlayPvs(
|
||||
comp.LoopedSound, uid, SharedExpendableLightComponent.LoopedSoundParams)?.Entity;
|
||||
comp.LoopedSound, uid)?.Entity;
|
||||
|
||||
if (args.Sprite.LayerMapTryGet(ExpendableLightVisualLayers.Overlay, out var layerIdx, true))
|
||||
{
|
||||
|
||||
@@ -72,9 +72,6 @@ public sealed class MagicMirrorBoundUserInterface : BoundUserInterface
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
if (_window != null)
|
||||
_window.OnClose -= Close;
|
||||
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
8
Content.Client/MagicMirror/MagicMirrorSystem.cs
Normal file
8
Content.Client/MagicMirror/MagicMirrorSystem.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Content.Shared.MagicMirror;
|
||||
|
||||
namespace Content.Client.MagicMirror;
|
||||
|
||||
public sealed class MagicMirrorSystem : SharedMagicMirrorSystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -49,7 +49,7 @@ public sealed class MouseRotatorSystem : SharedMouseRotatorSystem
|
||||
if (angleDir == curRot.GetCardinalDir())
|
||||
return;
|
||||
|
||||
RaisePredictiveEvent(new RequestMouseRotatorRotationSimpleEvent()
|
||||
RaisePredictiveEvent(new RequestMouseRotatorRotationSimpleEvent()
|
||||
{
|
||||
Direction = angleDir,
|
||||
});
|
||||
|
||||
@@ -88,6 +88,7 @@ public sealed class NetworkConfiguratorBoundUserInterface : BoundUserInterface
|
||||
base.Dispose(disposing);
|
||||
if (!disposing) return;
|
||||
|
||||
_linkMenu?.Dispose();
|
||||
_listMenu?.Dispose();
|
||||
_configurationMenu?.Dispose();
|
||||
}
|
||||
|
||||
7
Content.Client/Nutrition/EntitySystems/DrinkSystem.cs
Normal file
7
Content.Client/Nutrition/EntitySystems/DrinkSystem.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
|
||||
namespace Content.Client.Nutrition.EntitySystems;
|
||||
|
||||
public sealed class DrinkSystem : SharedDrinkSystem
|
||||
{
|
||||
}
|
||||
@@ -36,6 +36,9 @@
|
||||
<CheckBox Name="IntegerScalingCheckBox"
|
||||
Text="{Loc 'ui-options-vp-integer-scaling'}"
|
||||
ToolTip="{Loc 'ui-options-vp-integer-scaling-tooltip'}" />
|
||||
<CheckBox Name="ViewportVerticalFitCheckBox"
|
||||
Text="{Loc 'ui-options-vp-vertical-fit'}"
|
||||
ToolTip="{Loc 'ui-options-vp-vertical-fit-tooltip'}" />
|
||||
<CheckBox Name="ViewportLowResCheckBox" Text="{Loc 'ui-options-vp-low-res'}" />
|
||||
<CheckBox Name="ParallaxLowQualityCheckBox" Text="{Loc 'ui-options-parallax-low-quality'}" />
|
||||
<CheckBox Name="FpsCounterCheckBox" Text="{Loc 'ui-options-fps-counter'}" />
|
||||
|
||||
@@ -67,6 +67,12 @@ namespace Content.Client.Options.UI.Tabs
|
||||
UpdateApplyButton();
|
||||
};
|
||||
|
||||
ViewportVerticalFitCheckBox.OnToggled += _ =>
|
||||
{
|
||||
UpdateViewportScale();
|
||||
UpdateApplyButton();
|
||||
};
|
||||
|
||||
IntegerScalingCheckBox.OnToggled += OnCheckBoxToggled;
|
||||
ViewportLowResCheckBox.OnToggled += OnCheckBoxToggled;
|
||||
ParallaxLowQualityCheckBox.OnToggled += OnCheckBoxToggled;
|
||||
@@ -79,6 +85,7 @@ namespace Content.Client.Options.UI.Tabs
|
||||
ViewportScaleSlider.Value = _cfg.GetCVar(CCVars.ViewportFixedScaleFactor);
|
||||
ViewportStretchCheckBox.Pressed = _cfg.GetCVar(CCVars.ViewportStretch);
|
||||
IntegerScalingCheckBox.Pressed = _cfg.GetCVar(CCVars.ViewportSnapToleranceMargin) != 0;
|
||||
ViewportVerticalFitCheckBox.Pressed = _cfg.GetCVar(CCVars.ViewportVerticalFit);
|
||||
ViewportLowResCheckBox.Pressed = !_cfg.GetCVar(CCVars.ViewportScaleRender);
|
||||
ParallaxLowQualityCheckBox.Pressed = _cfg.GetCVar(CCVars.ParallaxLowQuality);
|
||||
FpsCounterCheckBox.Pressed = _cfg.GetCVar(CCVars.HudFpsCounterVisible);
|
||||
@@ -111,6 +118,7 @@ namespace Content.Client.Options.UI.Tabs
|
||||
_cfg.SetCVar(CCVars.ViewportFixedScaleFactor, (int) ViewportScaleSlider.Value);
|
||||
_cfg.SetCVar(CCVars.ViewportSnapToleranceMargin,
|
||||
IntegerScalingCheckBox.Pressed ? CCVars.ViewportSnapToleranceMargin.DefaultValue : 0);
|
||||
_cfg.SetCVar(CCVars.ViewportVerticalFit, ViewportVerticalFitCheckBox.Pressed);
|
||||
_cfg.SetCVar(CCVars.ViewportScaleRender, !ViewportLowResCheckBox.Pressed);
|
||||
_cfg.SetCVar(CCVars.ParallaxLowQuality, ParallaxLowQualityCheckBox.Pressed);
|
||||
_cfg.SetCVar(CCVars.HudFpsCounterVisible, FpsCounterCheckBox.Pressed);
|
||||
@@ -140,6 +148,7 @@ namespace Content.Client.Options.UI.Tabs
|
||||
var isVPStretchSame = ViewportStretchCheckBox.Pressed == _cfg.GetCVar(CCVars.ViewportStretch);
|
||||
var isVPScaleSame = (int) ViewportScaleSlider.Value == _cfg.GetCVar(CCVars.ViewportFixedScaleFactor);
|
||||
var isIntegerScalingSame = IntegerScalingCheckBox.Pressed == (_cfg.GetCVar(CCVars.ViewportSnapToleranceMargin) != 0);
|
||||
var isVPVerticalFitSame = ViewportVerticalFitCheckBox.Pressed == _cfg.GetCVar(CCVars.ViewportVerticalFit);
|
||||
var isVPResSame = ViewportLowResCheckBox.Pressed == !_cfg.GetCVar(CCVars.ViewportScaleRender);
|
||||
var isPLQSame = ParallaxLowQualityCheckBox.Pressed == _cfg.GetCVar(CCVars.ParallaxLowQuality);
|
||||
var isFpsCounterVisibleSame = FpsCounterCheckBox.Pressed == _cfg.GetCVar(CCVars.HudFpsCounterVisible);
|
||||
@@ -152,6 +161,7 @@ namespace Content.Client.Options.UI.Tabs
|
||||
isVPStretchSame &&
|
||||
isVPScaleSame &&
|
||||
isIntegerScalingSame &&
|
||||
isVPVerticalFitSame &&
|
||||
isVPResSame &&
|
||||
isPLQSame &&
|
||||
isFpsCounterVisibleSame &&
|
||||
@@ -235,6 +245,8 @@ namespace Content.Client.Options.UI.Tabs
|
||||
{
|
||||
ViewportScaleBox.Visible = !ViewportStretchCheckBox.Pressed;
|
||||
IntegerScalingCheckBox.Visible = ViewportStretchCheckBox.Pressed;
|
||||
ViewportVerticalFitCheckBox.Visible = ViewportStretchCheckBox.Pressed;
|
||||
ViewportWidthSlider.Visible = ViewportWidthSliderDisplay.Visible = !ViewportStretchCheckBox.Pressed || ViewportStretchCheckBox.Pressed && !ViewportVerticalFitCheckBox.Pressed;
|
||||
ViewportScaleText.Text = Loc.GetString("ui-options-vp-scale", ("scale", ViewportScaleSlider.Value));
|
||||
}
|
||||
|
||||
|
||||
@@ -215,6 +215,7 @@ namespace Content.Client.Options.UI.Tabs
|
||||
AddButton(ContentKeyFunctions.OpenInventoryMenu);
|
||||
AddButton(ContentKeyFunctions.OpenAHelp);
|
||||
AddButton(ContentKeyFunctions.OpenActionsMenu);
|
||||
AddButton(ContentKeyFunctions.ToggleRoundEndSummaryWindow);
|
||||
AddButton(ContentKeyFunctions.OpenEntitySpawnWindow);
|
||||
AddButton(ContentKeyFunctions.OpenSandboxWindow);
|
||||
AddButton(ContentKeyFunctions.OpenTileSpawnWindow);
|
||||
|
||||
28
Content.Client/Overlays/ShowCriminalRecordIconsSystem.cs
Normal file
28
Content.Client/Overlays/ShowCriminalRecordIconsSystem.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Content.Shared.Overlays;
|
||||
using Content.Shared.Security.Components;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
||||
public sealed class ShowCriminalRecordIconsSystem : EquipmentHudSystem<ShowCriminalRecordIconsComponent>
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<CriminalRecordComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
}
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, CriminalRecordComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
return;
|
||||
|
||||
if (_prototype.TryIndex<StatusIconPrototype>(component.StatusIcon.Id, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,13 @@
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.Overlays;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
||||
public sealed class ShowHungerIconsSystem : EquipmentHudSystem<ShowHungerIconsComponent>
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeMan = default!;
|
||||
[Dependency] private readonly HungerSystem _hunger = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -17,42 +16,12 @@ public sealed class ShowHungerIconsSystem : EquipmentHudSystem<ShowHungerIconsCo
|
||||
SubscribeLocalEvent<HungerComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
}
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, HungerComponent hungerComponent, ref GetStatusIconsEvent args)
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, HungerComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || args.InContainer)
|
||||
if (!IsActive || ev.InContainer)
|
||||
return;
|
||||
|
||||
var hungerIcons = DecideHungerIcon(uid, hungerComponent);
|
||||
|
||||
args.StatusIcons.AddRange(hungerIcons);
|
||||
}
|
||||
|
||||
private IReadOnlyList<StatusIconPrototype> DecideHungerIcon(EntityUid uid, HungerComponent hungerComponent)
|
||||
{
|
||||
var result = new List<StatusIconPrototype>();
|
||||
|
||||
switch (hungerComponent.CurrentThreshold)
|
||||
{
|
||||
case HungerThreshold.Overfed:
|
||||
if (_prototypeMan.TryIndex<StatusIconPrototype>("HungerIconOverfed", out var overfed))
|
||||
{
|
||||
result.Add(overfed);
|
||||
}
|
||||
break;
|
||||
case HungerThreshold.Peckish:
|
||||
if (_prototypeMan.TryIndex<StatusIconPrototype>("HungerIconPeckish", out var peckish))
|
||||
{
|
||||
result.Add(peckish);
|
||||
}
|
||||
break;
|
||||
case HungerThreshold.Starving:
|
||||
if (_prototypeMan.TryIndex<StatusIconPrototype>("HungerIconStarving", out var starving))
|
||||
{
|
||||
result.Add(starving);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
if (_hunger.TryGetStatusIconPrototype(component, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
}
|
||||
|
||||
60
Content.Client/Overlays/ShowJobIconsSystem.cs
Normal file
60
Content.Client/Overlays/ShowJobIconsSystem.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Overlays;
|
||||
using Content.Shared.PDA;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
||||
public sealed class ShowJobIconsSystem : EquipmentHudSystem<ShowJobIconsComponent>
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
|
||||
|
||||
[ValidatePrototypeId<StatusIconPrototype>]
|
||||
private const string JobIconForNoId = "JobIconNoId";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<StatusIconComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
}
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, StatusIconComponent _, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
return;
|
||||
|
||||
var iconId = JobIconForNoId;
|
||||
|
||||
if (_accessReader.FindAccessItemsInventory(uid, out var items))
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
// ID Card
|
||||
if (TryComp<IdCardComponent>(item, out var id))
|
||||
{
|
||||
iconId = id.JobIcon;
|
||||
break;
|
||||
}
|
||||
|
||||
// PDA
|
||||
if (TryComp<PdaComponent>(item, out var pda)
|
||||
&& pda.ContainedId != null
|
||||
&& TryComp(pda.ContainedId, out id))
|
||||
{
|
||||
iconId = id.JobIcon;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_prototype.TryIndex<StatusIconPrototype>(iconId, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
else
|
||||
Log.Error($"Invalid job icon prototype: {iconPrototype}");
|
||||
}
|
||||
}
|
||||
28
Content.Client/Overlays/ShowMindShieldIconsSystem.cs
Normal file
28
Content.Client/Overlays/ShowMindShieldIconsSystem.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Content.Shared.Mindshield.Components;
|
||||
using Content.Shared.Overlays;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
||||
public sealed class ShowMindShieldIconsSystem : EquipmentHudSystem<ShowMindShieldIconsComponent>
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<MindShieldComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
}
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, MindShieldComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
return;
|
||||
|
||||
if (_prototype.TryIndex<StatusIconPrototype>(component.MindShieldStatusIcon.Id, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Mindshield.Components;
|
||||
using Content.Shared.Overlays;
|
||||
using Content.Shared.PDA;
|
||||
using Content.Shared.Security.Components;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
||||
public sealed class ShowSecurityIconsSystem : EquipmentHudSystem<ShowSecurityIconsComponent>
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeMan = default!;
|
||||
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
|
||||
|
||||
[ValidatePrototypeId<StatusIconPrototype>]
|
||||
private const string JobIconForNoId = "JobIconNoId";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<StatusIconComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
}
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, StatusIconComponent _, ref GetStatusIconsEvent @event)
|
||||
{
|
||||
if (!IsActive || @event.InContainer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var securityIcons = DecideSecurityIcon(uid);
|
||||
|
||||
@event.StatusIcons.AddRange(securityIcons);
|
||||
}
|
||||
|
||||
private IReadOnlyList<StatusIconPrototype> DecideSecurityIcon(EntityUid uid)
|
||||
{
|
||||
var result = new List<StatusIconPrototype>();
|
||||
|
||||
var jobIconToGet = JobIconForNoId;
|
||||
if (_accessReader.FindAccessItemsInventory(uid, out var items))
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
// ID Card
|
||||
if (TryComp(item, out IdCardComponent? id))
|
||||
{
|
||||
jobIconToGet = id.JobIcon;
|
||||
break;
|
||||
}
|
||||
|
||||
// PDA
|
||||
if (TryComp(item, out PdaComponent? pda)
|
||||
&& pda.ContainedId != null
|
||||
&& TryComp(pda.ContainedId, out id))
|
||||
{
|
||||
jobIconToGet = id.JobIcon;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_prototypeMan.TryIndex<StatusIconPrototype>(jobIconToGet, out var jobIcon))
|
||||
result.Add(jobIcon);
|
||||
else
|
||||
Log.Error($"Invalid job icon prototype: {jobIcon}");
|
||||
|
||||
if (TryComp<MindShieldComponent>(uid, out var comp))
|
||||
{
|
||||
if (_prototypeMan.TryIndex<StatusIconPrototype>(comp.MindShieldStatusIcon.Id, out var icon))
|
||||
result.Add(icon);
|
||||
}
|
||||
|
||||
if (TryComp<CriminalRecordComponent>(uid, out var record))
|
||||
{
|
||||
if(_prototypeMan.TryIndex<StatusIconPrototype>(record.StatusIcon.Id, out var criminalIcon))
|
||||
result.Add(criminalIcon);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
using Content.Shared.Overlays;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Content.Shared.NukeOps;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
||||
public sealed class ShowSyndicateIconsSystem : EquipmentHudSystem<ShowSyndicateIconsComponent>
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
@@ -16,28 +17,13 @@ public sealed class ShowSyndicateIconsSystem : EquipmentHudSystem<ShowSyndicateI
|
||||
SubscribeLocalEvent<NukeOperativeComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
}
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, NukeOperativeComponent nukeOperativeComponent, ref GetStatusIconsEvent args)
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, NukeOperativeComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || args.InContainer)
|
||||
{
|
||||
if (!IsActive || ev.InContainer)
|
||||
return;
|
||||
}
|
||||
|
||||
var syndicateIcons = SyndicateIcon(uid, nukeOperativeComponent);
|
||||
|
||||
args.StatusIcons.AddRange(syndicateIcons);
|
||||
}
|
||||
|
||||
private IReadOnlyList<StatusIconPrototype> SyndicateIcon(EntityUid uid, NukeOperativeComponent nukeOperativeComponent)
|
||||
{
|
||||
var result = new List<StatusIconPrototype>();
|
||||
|
||||
if (_prototype.TryIndex<StatusIconPrototype>(nukeOperativeComponent.SyndStatusIcon, out var syndicateicon))
|
||||
{
|
||||
result.Add(syndicateicon);
|
||||
}
|
||||
|
||||
return result;
|
||||
if (_prototype.TryIndex<StatusIconPrototype>(component.SyndStatusIcon, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.Overlays;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Overlays;
|
||||
|
||||
public sealed class ShowThirstIconsSystem : EquipmentHudSystem<ShowThirstIconsComponent>
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeMan = default!;
|
||||
[Dependency] private readonly ThirstSystem _thirst = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -17,42 +16,12 @@ public sealed class ShowThirstIconsSystem : EquipmentHudSystem<ShowThirstIconsCo
|
||||
SubscribeLocalEvent<ThirstComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
}
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, ThirstComponent thirstComponent, ref GetStatusIconsEvent args)
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, ThirstComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive || args.InContainer)
|
||||
if (!IsActive || ev.InContainer)
|
||||
return;
|
||||
|
||||
var thirstIcons = DecideThirstIcon(uid, thirstComponent);
|
||||
|
||||
args.StatusIcons.AddRange(thirstIcons);
|
||||
}
|
||||
|
||||
private IReadOnlyList<StatusIconPrototype> DecideThirstIcon(EntityUid uid, ThirstComponent thirstComponent)
|
||||
{
|
||||
var result = new List<StatusIconPrototype>();
|
||||
|
||||
switch (thirstComponent.CurrentThirstThreshold)
|
||||
{
|
||||
case ThirstThreshold.OverHydrated:
|
||||
if (_prototypeMan.TryIndex<StatusIconPrototype>("ThirstIconOverhydrated", out var overhydrated))
|
||||
{
|
||||
result.Add(overhydrated);
|
||||
}
|
||||
break;
|
||||
case ThirstThreshold.Thirsty:
|
||||
if (_prototypeMan.TryIndex<StatusIconPrototype>("ThirstIconThirsty", out var thirsty))
|
||||
{
|
||||
result.Add(thirsty);
|
||||
}
|
||||
break;
|
||||
case ThirstThreshold.Parched:
|
||||
if (_prototypeMan.TryIndex<StatusIconPrototype>("ThirstIconParched", out var parched))
|
||||
{
|
||||
result.Add(parched);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
if (_thirst.TryGetStatusIconPrototype(component, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype!);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ namespace Content.Client.PDA
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
SendMessage(new PdaRequestUpdateInterfaceMessage());
|
||||
_menu = new PdaMenu();
|
||||
_menu.OpenCenteredLeft();
|
||||
_menu.OnClose += Close;
|
||||
@@ -32,17 +31,17 @@ namespace Content.Client.PDA
|
||||
|
||||
_menu.EjectIdButton.OnPressed += _ =>
|
||||
{
|
||||
SendMessage(new ItemSlotButtonPressedEvent(PdaComponent.PdaIdSlotId));
|
||||
SendPredictedMessage(new ItemSlotButtonPressedEvent(PdaComponent.PdaIdSlotId));
|
||||
};
|
||||
|
||||
_menu.EjectPenButton.OnPressed += _ =>
|
||||
{
|
||||
SendMessage(new ItemSlotButtonPressedEvent(PdaComponent.PdaPenSlotId));
|
||||
SendPredictedMessage(new ItemSlotButtonPressedEvent(PdaComponent.PdaPenSlotId));
|
||||
};
|
||||
|
||||
_menu.EjectPaiButton.OnPressed += _ =>
|
||||
{
|
||||
SendMessage(new ItemSlotButtonPressedEvent(PdaComponent.PdaPaiSlotId));
|
||||
SendPredictedMessage(new ItemSlotButtonPressedEvent(PdaComponent.PdaPaiSlotId));
|
||||
};
|
||||
|
||||
_menu.ActivateMusicButton.OnPressed += _ =>
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
using Content.Shared.Paper;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Paper;
|
||||
|
||||
[NetworkedComponent, RegisterComponent]
|
||||
public sealed partial class PaperComponent : SharedPaperComponent
|
||||
{
|
||||
}
|
||||
[RegisterComponent]
|
||||
public sealed partial class PaperComponent : SharedPaperComponent;
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Pinpointer;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
|
||||
namespace Content.Client.Pinpointer;
|
||||
|
||||
public sealed class NavMapSystem : SharedNavMapSystem
|
||||
public sealed partial class NavMapSystem : SharedNavMapSystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<NavMapComponent, ComponentHandleState>(OnHandleState);
|
||||
}
|
||||
|
||||
@@ -21,89 +17,34 @@ public sealed class NavMapSystem : SharedNavMapSystem
|
||||
if (args.Current is not NavMapComponentState state)
|
||||
return;
|
||||
|
||||
component.Chunks.Clear();
|
||||
|
||||
foreach (var (origin, data) in state.TileData)
|
||||
if (!state.FullState)
|
||||
{
|
||||
component.Chunks.Add(origin, new NavMapChunk(origin)
|
||||
foreach (var index in component.Chunks.Keys)
|
||||
{
|
||||
TileData = data,
|
||||
});
|
||||
if (!state.AllChunks!.Contains(index))
|
||||
component.Chunks.Remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
component.Beacons.Clear();
|
||||
component.Beacons.AddRange(state.Beacons);
|
||||
|
||||
component.Airlocks.Clear();
|
||||
component.Airlocks.AddRange(state.Airlocks);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NavMapOverlay : Overlay
|
||||
{
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IMapManager _mapManager;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
private List<Entity<MapGridComponent>> _grids = new();
|
||||
|
||||
public NavMapOverlay(IEntityManager entManager, IMapManager mapManager)
|
||||
{
|
||||
_entManager = entManager;
|
||||
_mapManager = mapManager;
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
var query = _entManager.GetEntityQuery<NavMapComponent>();
|
||||
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
|
||||
var scale = Matrix3.CreateScale(new Vector2(1f, 1f));
|
||||
|
||||
_grids.Clear();
|
||||
_mapManager.FindGridsIntersecting(args.MapId, args.WorldBounds, ref _grids);
|
||||
|
||||
foreach (var grid in _grids)
|
||||
else
|
||||
{
|
||||
if (!query.TryGetComponent(grid, out var navMap) || !xformQuery.TryGetComponent(grid.Owner, out var xform))
|
||||
continue;
|
||||
|
||||
// TODO: Faster helper method
|
||||
var (_, _, matrix, invMatrix) = xform.GetWorldPositionRotationMatrixWithInv();
|
||||
|
||||
var localAABB = invMatrix.TransformBox(args.WorldBounds);
|
||||
Matrix3.Multiply(in scale, in matrix, out var matty);
|
||||
|
||||
args.WorldHandle.SetTransform(matty);
|
||||
|
||||
for (var x = Math.Floor(localAABB.Left); x <= Math.Ceiling(localAABB.Right); x += SharedNavMapSystem.ChunkSize * grid.Comp.TileSize)
|
||||
foreach (var index in component.Chunks.Keys)
|
||||
{
|
||||
for (var y = Math.Floor(localAABB.Bottom); y <= Math.Ceiling(localAABB.Top); y += SharedNavMapSystem.ChunkSize * grid.Comp.TileSize)
|
||||
{
|
||||
var floored = new Vector2i((int) x, (int) y);
|
||||
|
||||
var chunkOrigin = SharedMapSystem.GetChunkIndices(floored, SharedNavMapSystem.ChunkSize);
|
||||
|
||||
if (!navMap.Chunks.TryGetValue(chunkOrigin, out var chunk))
|
||||
continue;
|
||||
|
||||
// TODO: Okay maybe I should just use ushorts lmao...
|
||||
for (var i = 0; i < SharedNavMapSystem.ChunkSize * SharedNavMapSystem.ChunkSize; i++)
|
||||
{
|
||||
var value = (int) Math.Pow(2, i);
|
||||
|
||||
var mask = chunk.TileData & value;
|
||||
|
||||
if (mask == 0x0)
|
||||
continue;
|
||||
|
||||
var tile = chunk.Origin * SharedNavMapSystem.ChunkSize + SharedNavMapSystem.GetTile(mask);
|
||||
args.WorldHandle.DrawRect(new Box2(tile * grid.Comp.TileSize, (tile + 1) * grid.Comp.TileSize), Color.Aqua, false);
|
||||
}
|
||||
}
|
||||
if (!state.Chunks.ContainsKey(index))
|
||||
component.Chunks.Remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
args.WorldHandle.SetTransform(Matrix3.Identity);
|
||||
foreach (var (origin, chunk) in state.Chunks)
|
||||
{
|
||||
var newChunk = new NavMapChunk(origin);
|
||||
Array.Copy(chunk, newChunk.TileData, chunk.Length);
|
||||
component.Chunks[origin] = newChunk;
|
||||
}
|
||||
|
||||
component.Beacons.Clear();
|
||||
foreach (var (nuid, beacon) in state.Beacons)
|
||||
{
|
||||
component.Beacons[nuid] = beacon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Timing;
|
||||
using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
using Content.Shared.Atmos;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Pinpointer.UI;
|
||||
|
||||
@@ -27,6 +30,7 @@ public partial class NavMapControl : MapGridControl
|
||||
{
|
||||
[Dependency] private IResourceCache _cache = default!;
|
||||
private readonly SharedTransformSystem _transformSystem;
|
||||
private readonly SharedNavMapSystem _navMapSystem;
|
||||
|
||||
public EntityUid? Owner;
|
||||
public EntityUid? MapUid;
|
||||
@@ -40,7 +44,10 @@ public partial class NavMapControl : MapGridControl
|
||||
// Tracked data
|
||||
public Dictionary<EntityCoordinates, (bool Visible, Color Color)> TrackedCoordinates = new();
|
||||
public Dictionary<NetEntity, NavMapBlip> TrackedEntities = new();
|
||||
public Dictionary<Vector2i, List<NavMapLine>>? TileGrid = default!;
|
||||
|
||||
public List<(Vector2, Vector2)> TileLines = new();
|
||||
public List<(Vector2, Vector2)> TileRects = new();
|
||||
public List<(Vector2[], Color)> TilePolygons = new();
|
||||
|
||||
// Default colors
|
||||
public Color WallColor = new(102, 217, 102);
|
||||
@@ -53,14 +60,23 @@ public partial class NavMapControl : MapGridControl
|
||||
protected static float MinDisplayedRange = 8f;
|
||||
protected static float MaxDisplayedRange = 128f;
|
||||
protected static float DefaultDisplayedRange = 48f;
|
||||
protected float MinmapScaleModifier = 0.075f;
|
||||
protected float FullWallInstep = 0.165f;
|
||||
protected float ThinWallThickness = 0.165f;
|
||||
protected float ThinDoorThickness = 0.30f;
|
||||
|
||||
// Local variables
|
||||
private float _updateTimer = 0.25f;
|
||||
private float _updateTimer = 1.0f;
|
||||
private Dictionary<Color, Color> _sRGBLookUp = new();
|
||||
protected Color BackgroundColor;
|
||||
protected float BackgroundOpacity = 0.9f;
|
||||
private int _targetFontsize = 8;
|
||||
|
||||
private Dictionary<Vector2i, Vector2i> _horizLines = new();
|
||||
private Dictionary<Vector2i, Vector2i> _horizLinesReversed = new();
|
||||
private Dictionary<Vector2i, Vector2i> _vertLines = new();
|
||||
private Dictionary<Vector2i, Vector2i> _vertLinesReversed = new();
|
||||
|
||||
// Components
|
||||
private NavMapComponent? _navMap;
|
||||
private MapGridComponent? _grid;
|
||||
@@ -72,6 +88,7 @@ public partial class NavMapControl : MapGridControl
|
||||
private readonly Label _zoom = new()
|
||||
{
|
||||
VerticalAlignment = VAlignment.Top,
|
||||
HorizontalExpand = true,
|
||||
Margin = new Thickness(8f, 8f),
|
||||
};
|
||||
|
||||
@@ -80,6 +97,7 @@ public partial class NavMapControl : MapGridControl
|
||||
Text = Loc.GetString("navmap-recenter"),
|
||||
VerticalAlignment = VAlignment.Top,
|
||||
HorizontalAlignment = HAlignment.Right,
|
||||
HorizontalExpand = true,
|
||||
Margin = new Thickness(8f, 4f),
|
||||
Disabled = true,
|
||||
};
|
||||
@@ -87,9 +105,10 @@ public partial class NavMapControl : MapGridControl
|
||||
private readonly CheckBox _beacons = new()
|
||||
{
|
||||
Text = Loc.GetString("navmap-toggle-beacons"),
|
||||
Margin = new Thickness(4f, 0f),
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
HorizontalExpand = true,
|
||||
Margin = new Thickness(4f, 0f),
|
||||
Pressed = true,
|
||||
};
|
||||
|
||||
@@ -98,6 +117,8 @@ public partial class NavMapControl : MapGridControl
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_transformSystem = EntManager.System<SharedTransformSystem>();
|
||||
_navMapSystem = EntManager.System<SharedNavMapSystem>();
|
||||
|
||||
BackgroundColor = Color.FromSrgb(TileColor.WithAlpha(BackgroundOpacity));
|
||||
|
||||
RectClipContent = true;
|
||||
@@ -112,6 +133,8 @@ public partial class NavMapControl : MapGridControl
|
||||
BorderColor = StyleNano.PanelDark
|
||||
},
|
||||
VerticalExpand = false,
|
||||
HorizontalExpand = true,
|
||||
SetWidth = 650f,
|
||||
Children =
|
||||
{
|
||||
new BoxContainer()
|
||||
@@ -130,6 +153,7 @@ public partial class NavMapControl : MapGridControl
|
||||
var topContainer = new BoxContainer()
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
topPanel,
|
||||
@@ -157,6 +181,9 @@ public partial class NavMapControl : MapGridControl
|
||||
{
|
||||
EntManager.TryGetComponent(MapUid, out _navMap);
|
||||
EntManager.TryGetComponent(MapUid, out _grid);
|
||||
EntManager.TryGetComponent(MapUid, out _xform);
|
||||
EntManager.TryGetComponent(MapUid, out _physics);
|
||||
EntManager.TryGetComponent(MapUid, out _fixtures);
|
||||
|
||||
UpdateNavMap();
|
||||
}
|
||||
@@ -251,119 +278,93 @@ public partial class NavMapControl : MapGridControl
|
||||
EntManager.TryGetComponent(MapUid, out _physics);
|
||||
EntManager.TryGetComponent(MapUid, out _fixtures);
|
||||
|
||||
if (_navMap == null || _grid == null || _xform == null)
|
||||
return;
|
||||
|
||||
// Map re-centering
|
||||
_recenter.Disabled = DrawRecenter();
|
||||
|
||||
_zoom.Text = Loc.GetString("navmap-zoom", ("value", $"{(DefaultDisplayedRange / WorldRange ):0.0}"));
|
||||
|
||||
if (_navMap == null || _xform == null)
|
||||
return;
|
||||
// Update zoom text
|
||||
_zoom.Text = Loc.GetString("navmap-zoom", ("value", $"{(DefaultDisplayedRange / WorldRange):0.0}"));
|
||||
|
||||
// Update offset with physics local center
|
||||
var offset = Offset;
|
||||
|
||||
if (_physics != null)
|
||||
offset += _physics.LocalCenter;
|
||||
|
||||
// Draw tiles
|
||||
if (_fixtures != null)
|
||||
var offsetVec = new Vector2(offset.X, -offset.Y);
|
||||
|
||||
// Wall sRGB
|
||||
if (!_sRGBLookUp.TryGetValue(WallColor, out var wallsRGB))
|
||||
{
|
||||
wallsRGB = Color.ToSrgb(WallColor);
|
||||
_sRGBLookUp[WallColor] = wallsRGB;
|
||||
}
|
||||
|
||||
// Draw floor tiles
|
||||
if (TilePolygons.Any())
|
||||
{
|
||||
Span<Vector2> verts = new Vector2[8];
|
||||
|
||||
foreach (var fixture in _fixtures.Fixtures.Values)
|
||||
foreach (var (polygonVerts, polygonColor) in TilePolygons)
|
||||
{
|
||||
if (fixture.Shape is not PolygonShape poly)
|
||||
continue;
|
||||
|
||||
for (var i = 0; i < poly.VertexCount; i++)
|
||||
for (var i = 0; i < polygonVerts.Length; i++)
|
||||
{
|
||||
var vert = poly.Vertices[i] - offset;
|
||||
|
||||
var vert = polygonVerts[i] - offset;
|
||||
verts[i] = ScalePosition(new Vector2(vert.X, -vert.Y));
|
||||
}
|
||||
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts[..poly.VertexCount], TileColor);
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts[..polygonVerts.Length], polygonColor);
|
||||
}
|
||||
}
|
||||
|
||||
var area = new Box2(-WorldRange, -WorldRange, WorldRange + 1f, WorldRange + 1f).Translated(offset);
|
||||
|
||||
// Drawing lines can be rather expensive due to the number of neighbors that need to be checked in order
|
||||
// to figure out where they should be drawn. However, we don't *need* to do check these every frame.
|
||||
// Instead, lets periodically update where to draw each line and then store these points in a list.
|
||||
// Then we can just run through the list each frame and draw the lines without any extra computation.
|
||||
|
||||
// Draw walls
|
||||
if (TileGrid != null && TileGrid.Count > 0)
|
||||
// Draw map lines
|
||||
if (TileLines.Any())
|
||||
{
|
||||
var walls = new ValueList<Vector2>();
|
||||
var lines = new ValueList<Vector2>(TileLines.Count * 2);
|
||||
|
||||
foreach ((var chunk, var chunkedLines) in TileGrid)
|
||||
foreach (var (o, t) in TileLines)
|
||||
{
|
||||
var offsetChunk = new Vector2(chunk.X, chunk.Y) * SharedNavMapSystem.ChunkSize;
|
||||
var origin = ScalePosition(o - offsetVec);
|
||||
var terminus = ScalePosition(t - offsetVec);
|
||||
|
||||
if (offsetChunk.X < area.Left - SharedNavMapSystem.ChunkSize || offsetChunk.X > area.Right)
|
||||
continue;
|
||||
|
||||
if (offsetChunk.Y < area.Bottom - SharedNavMapSystem.ChunkSize || offsetChunk.Y > area.Top)
|
||||
continue;
|
||||
|
||||
foreach (var chunkedLine in chunkedLines)
|
||||
{
|
||||
var start = ScalePosition(chunkedLine.Origin - new Vector2(offset.X, -offset.Y));
|
||||
var end = ScalePosition(chunkedLine.Terminus - new Vector2(offset.X, -offset.Y));
|
||||
|
||||
walls.Add(start);
|
||||
walls.Add(end);
|
||||
}
|
||||
lines.Add(origin);
|
||||
lines.Add(terminus);
|
||||
}
|
||||
|
||||
if (walls.Count > 0)
|
||||
{
|
||||
if (!_sRGBLookUp.TryGetValue(WallColor, out var sRGB))
|
||||
{
|
||||
sRGB = Color.ToSrgb(WallColor);
|
||||
_sRGBLookUp[WallColor] = sRGB;
|
||||
}
|
||||
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.LineList, walls.Span, sRGB);
|
||||
}
|
||||
if (lines.Count > 0)
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.LineList, lines.Span, wallsRGB);
|
||||
}
|
||||
|
||||
var airlockBuffer = Vector2.One * (MinimapScale / 2.25f) * 0.75f;
|
||||
var airlockLines = new ValueList<Vector2>();
|
||||
var foobarVec = new Vector2(1, -1);
|
||||
|
||||
foreach (var airlock in _navMap.Airlocks)
|
||||
// Draw map rects
|
||||
if (TileRects.Any())
|
||||
{
|
||||
var position = airlock.Position - offset;
|
||||
position = ScalePosition(position with { Y = -position.Y });
|
||||
airlockLines.Add(position + airlockBuffer);
|
||||
airlockLines.Add(position - airlockBuffer * foobarVec);
|
||||
var rects = new ValueList<Vector2>(TileRects.Count * 8);
|
||||
|
||||
airlockLines.Add(position + airlockBuffer);
|
||||
airlockLines.Add(position + airlockBuffer * foobarVec);
|
||||
|
||||
airlockLines.Add(position - airlockBuffer);
|
||||
airlockLines.Add(position + airlockBuffer * foobarVec);
|
||||
|
||||
airlockLines.Add(position - airlockBuffer);
|
||||
airlockLines.Add(position - airlockBuffer * foobarVec);
|
||||
|
||||
airlockLines.Add(position + airlockBuffer * -Vector2.UnitY);
|
||||
airlockLines.Add(position - airlockBuffer * -Vector2.UnitY);
|
||||
}
|
||||
|
||||
if (airlockLines.Count > 0)
|
||||
{
|
||||
if (!_sRGBLookUp.TryGetValue(WallColor, out var sRGB))
|
||||
foreach (var (lt, rb) in TileRects)
|
||||
{
|
||||
sRGB = Color.ToSrgb(WallColor);
|
||||
_sRGBLookUp[WallColor] = sRGB;
|
||||
var leftTop = ScalePosition(lt - offsetVec);
|
||||
var rightBottom = ScalePosition(rb - offsetVec);
|
||||
|
||||
var rightTop = new Vector2(rightBottom.X, leftTop.Y);
|
||||
var leftBottom = new Vector2(leftTop.X, rightBottom.Y);
|
||||
|
||||
rects.Add(leftTop);
|
||||
rects.Add(rightTop);
|
||||
rects.Add(rightTop);
|
||||
rects.Add(rightBottom);
|
||||
rects.Add(rightBottom);
|
||||
rects.Add(leftBottom);
|
||||
rects.Add(leftBottom);
|
||||
rects.Add(leftTop);
|
||||
}
|
||||
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.LineList, airlockLines.Span, sRGB);
|
||||
if (rects.Count > 0)
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.LineList, rects.Span, wallsRGB);
|
||||
}
|
||||
|
||||
// Invoke post wall drawing action
|
||||
if (PostWallDrawingAction != null)
|
||||
PostWallDrawingAction.Invoke(handle);
|
||||
|
||||
@@ -373,10 +374,10 @@ public partial class NavMapControl : MapGridControl
|
||||
var rectBuffer = new Vector2(5f, 3f);
|
||||
|
||||
// Calculate font size for current zoom level
|
||||
var fontSize = (int) Math.Round(1 / WorldRange * DefaultDisplayedRange * UIScale * _targetFontsize , 0);
|
||||
var fontSize = (int) Math.Round(1 / WorldRange * DefaultDisplayedRange * UIScale * _targetFontsize, 0);
|
||||
var font = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Bold.ttf"), fontSize);
|
||||
|
||||
foreach (var beacon in _navMap.Beacons)
|
||||
foreach (var beacon in _navMap.Beacons.Values)
|
||||
{
|
||||
var position = beacon.Position - offset;
|
||||
position = ScalePosition(position with { Y = -position.Y });
|
||||
@@ -409,8 +410,6 @@ public partial class NavMapControl : MapGridControl
|
||||
}
|
||||
|
||||
// Tracked entities (can use a supplied sprite as a marker instead; should probably just replace TrackedCoordinates with this eventually)
|
||||
var iconVertexUVs = new Dictionary<(Texture, Color), ValueList<DrawVertexUV2D>>();
|
||||
|
||||
foreach (var blip in TrackedEntities.Values)
|
||||
{
|
||||
if (blip.Blinks && !lit)
|
||||
@@ -419,9 +418,6 @@ public partial class NavMapControl : MapGridControl
|
||||
if (blip.Texture == null)
|
||||
continue;
|
||||
|
||||
if (!iconVertexUVs.TryGetValue((blip.Texture, blip.Color), out var vertexUVs))
|
||||
vertexUVs = new();
|
||||
|
||||
var mapPos = blip.Coordinates.ToMap(EntManager, _transformSystem);
|
||||
|
||||
if (mapPos.MapId != MapId.Nullspace)
|
||||
@@ -429,29 +425,11 @@ public partial class NavMapControl : MapGridControl
|
||||
var position = _transformSystem.GetInvWorldMatrix(_xform).Transform(mapPos.Position) - offset;
|
||||
position = ScalePosition(new Vector2(position.X, -position.Y));
|
||||
|
||||
var scalingCoefficient = 2.5f;
|
||||
var positionOffset = scalingCoefficient * float.Sqrt(MinimapScale);
|
||||
var scalingCoefficient = MinmapScaleModifier * float.Sqrt(MinimapScale);
|
||||
var positionOffset = new Vector2(scalingCoefficient * blip.Texture.Width, scalingCoefficient * blip.Texture.Height);
|
||||
|
||||
vertexUVs.Add(new DrawVertexUV2D(new Vector2(position.X - positionOffset, position.Y - positionOffset), new Vector2(1f, 1f)));
|
||||
vertexUVs.Add(new DrawVertexUV2D(new Vector2(position.X - positionOffset, position.Y + positionOffset), new Vector2(1f, 0f)));
|
||||
vertexUVs.Add(new DrawVertexUV2D(new Vector2(position.X + positionOffset, position.Y - positionOffset), new Vector2(0f, 1f)));
|
||||
vertexUVs.Add(new DrawVertexUV2D(new Vector2(position.X - positionOffset, position.Y + positionOffset), new Vector2(1f, 0f)));
|
||||
vertexUVs.Add(new DrawVertexUV2D(new Vector2(position.X + positionOffset, position.Y - positionOffset), new Vector2(0f, 1f)));
|
||||
vertexUVs.Add(new DrawVertexUV2D(new Vector2(position.X + positionOffset, position.Y + positionOffset), new Vector2(0f, 0f)));
|
||||
handle.DrawTextureRect(blip.Texture, new UIBox2(position - positionOffset, position + positionOffset), blip.Color);
|
||||
}
|
||||
|
||||
iconVertexUVs[(blip.Texture, blip.Color)] = vertexUVs;
|
||||
}
|
||||
|
||||
foreach ((var (texture, color), var vertexUVs) in iconVertexUVs)
|
||||
{
|
||||
if (!_sRGBLookUp.TryGetValue(color, out var sRGB))
|
||||
{
|
||||
sRGB = Color.ToSrgb(color);
|
||||
_sRGBLookUp[color] = sRGB;
|
||||
}
|
||||
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, texture, vertexUVs.Span, sRGB);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -469,124 +447,266 @@ public partial class NavMapControl : MapGridControl
|
||||
}
|
||||
|
||||
protected virtual void UpdateNavMap()
|
||||
{
|
||||
// Clear stale values
|
||||
TilePolygons.Clear();
|
||||
TileLines.Clear();
|
||||
TileRects.Clear();
|
||||
|
||||
UpdateNavMapFloorTiles();
|
||||
UpdateNavMapWallLines();
|
||||
UpdateNavMapAirlocks();
|
||||
}
|
||||
|
||||
private void UpdateNavMapFloorTiles()
|
||||
{
|
||||
if (_fixtures == null)
|
||||
return;
|
||||
|
||||
var verts = new Vector2[8];
|
||||
|
||||
foreach (var fixture in _fixtures.Fixtures.Values)
|
||||
{
|
||||
if (fixture.Shape is not PolygonShape poly)
|
||||
continue;
|
||||
|
||||
for (var i = 0; i < poly.VertexCount; i++)
|
||||
{
|
||||
var vert = poly.Vertices[i];
|
||||
verts[i] = new Vector2(MathF.Round(vert.X), MathF.Round(vert.Y));
|
||||
}
|
||||
|
||||
TilePolygons.Add((verts[..poly.VertexCount], TileColor));
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateNavMapWallLines()
|
||||
{
|
||||
if (_navMap == null || _grid == null)
|
||||
return;
|
||||
|
||||
TileGrid = GetDecodedWallChunks(_navMap.Chunks, _grid);
|
||||
}
|
||||
// We'll use the following dictionaries to combine collinear wall lines
|
||||
_horizLines.Clear();
|
||||
_horizLinesReversed.Clear();
|
||||
_vertLines.Clear();
|
||||
_vertLinesReversed.Clear();
|
||||
|
||||
public Dictionary<Vector2i, List<NavMapLine>> GetDecodedWallChunks
|
||||
(Dictionary<Vector2i, NavMapChunk> chunks,
|
||||
MapGridComponent grid)
|
||||
{
|
||||
var decodedOutput = new Dictionary<Vector2i, List<NavMapLine>>();
|
||||
const int southMask = (int) AtmosDirection.South << (int) NavMapChunkType.Wall;
|
||||
const int eastMask = (int) AtmosDirection.East << (int) NavMapChunkType.Wall;
|
||||
const int westMask = (int) AtmosDirection.West << (int) NavMapChunkType.Wall;
|
||||
const int northMask = (int) AtmosDirection.North << (int) NavMapChunkType.Wall;
|
||||
|
||||
foreach ((var chunkOrigin, var chunk) in chunks)
|
||||
foreach (var (chunkOrigin, chunk) in _navMap.Chunks)
|
||||
{
|
||||
var list = new List<NavMapLine>();
|
||||
|
||||
// TODO: Okay maybe I should just use ushorts lmao...
|
||||
for (var i = 0; i < SharedNavMapSystem.ChunkSize * SharedNavMapSystem.ChunkSize; i++)
|
||||
for (var i = 0; i < SharedNavMapSystem.ArraySize; i++)
|
||||
{
|
||||
var value = (int) Math.Pow(2, i);
|
||||
|
||||
var mask = chunk.TileData & value;
|
||||
|
||||
if (mask == 0x0)
|
||||
var tileData = chunk.TileData[i] & SharedNavMapSystem.WallMask;
|
||||
if (tileData == 0)
|
||||
continue;
|
||||
|
||||
// Alright now we'll work out our edges
|
||||
var relativeTile = SharedNavMapSystem.GetTile(mask);
|
||||
var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relativeTile) * grid.TileSize;
|
||||
var position = new Vector2(tile.X, -tile.Y);
|
||||
tileData >>= (int) NavMapChunkType.Wall;
|
||||
|
||||
var relativeTile = SharedNavMapSystem.GetTileFromIndex(i);
|
||||
var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relativeTile) * _grid.TileSize;
|
||||
|
||||
if (tileData != SharedNavMapSystem.AllDirMask)
|
||||
{
|
||||
AddRectForThinWall(tileData, tile);
|
||||
continue;
|
||||
}
|
||||
|
||||
tile = tile with { Y = -tile.Y };
|
||||
NavMapChunk? neighborChunk;
|
||||
bool neighbor;
|
||||
|
||||
// North edge
|
||||
if (relativeTile.Y == SharedNavMapSystem.ChunkSize - 1)
|
||||
{
|
||||
neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(0, 1), out neighborChunk) &&
|
||||
(neighborChunk.TileData &
|
||||
SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, 0))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(0, 1));
|
||||
neighbor = (chunk.TileData & flag) != 0x0;
|
||||
}
|
||||
var neighborData = 0;
|
||||
if (relativeTile.Y != SharedNavMapSystem.ChunkSize - 1)
|
||||
neighborData = chunk.TileData[i+1];
|
||||
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Up, out neighborChunk))
|
||||
neighborData = neighborChunk.TileData[i + 1 - SharedNavMapSystem.ChunkSize];
|
||||
|
||||
if (!neighbor)
|
||||
if ((neighborData & southMask) == 0)
|
||||
{
|
||||
// Add points
|
||||
list.Add(new NavMapLine(position + new Vector2(0f, -grid.TileSize), position + new Vector2(grid.TileSize, -grid.TileSize)));
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize),
|
||||
tile + new Vector2i(_grid.TileSize, -_grid.TileSize), _horizLines,
|
||||
_horizLinesReversed);
|
||||
}
|
||||
|
||||
// East edge
|
||||
if (relativeTile.X == SharedNavMapSystem.ChunkSize - 1)
|
||||
{
|
||||
neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(1, 0), out neighborChunk) &&
|
||||
(neighborChunk.TileData &
|
||||
SharedNavMapSystem.GetFlag(new Vector2i(0, relativeTile.Y))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(1, 0));
|
||||
neighbor = (chunk.TileData & flag) != 0x0;
|
||||
}
|
||||
neighborData = 0;
|
||||
if (relativeTile.X != SharedNavMapSystem.ChunkSize - 1)
|
||||
neighborData = chunk.TileData[i+SharedNavMapSystem.ChunkSize];
|
||||
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Right, out neighborChunk))
|
||||
neighborData = neighborChunk.TileData[i + SharedNavMapSystem.ChunkSize - SharedNavMapSystem.ArraySize];
|
||||
|
||||
if (!neighbor)
|
||||
if ((neighborData & westMask) == 0)
|
||||
{
|
||||
// Add points
|
||||
list.Add(new NavMapLine(position + new Vector2(grid.TileSize, -grid.TileSize), position + new Vector2(grid.TileSize, 0f)));
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(_grid.TileSize, -_grid.TileSize),
|
||||
tile + new Vector2i(_grid.TileSize, 0), _vertLines, _vertLinesReversed);
|
||||
}
|
||||
|
||||
// South edge
|
||||
if (relativeTile.Y == 0)
|
||||
{
|
||||
neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(0, -1), out neighborChunk) &&
|
||||
(neighborChunk.TileData &
|
||||
SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, SharedNavMapSystem.ChunkSize - 1))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(0, -1));
|
||||
neighbor = (chunk.TileData & flag) != 0x0;
|
||||
}
|
||||
neighborData = 0;
|
||||
if (relativeTile.Y != 0)
|
||||
neighborData = chunk.TileData[i-1];
|
||||
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Down, out neighborChunk))
|
||||
neighborData = neighborChunk.TileData[i - 1 + SharedNavMapSystem.ChunkSize];
|
||||
|
||||
if (!neighbor)
|
||||
if ((neighborData & northMask) == 0)
|
||||
{
|
||||
// Add points
|
||||
list.Add(new NavMapLine(position + new Vector2(grid.TileSize, 0f), position));
|
||||
AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), _horizLines,
|
||||
_horizLinesReversed);
|
||||
}
|
||||
|
||||
// West edge
|
||||
if (relativeTile.X == 0)
|
||||
neighborData = 0;
|
||||
if (relativeTile.X != 0)
|
||||
neighborData = chunk.TileData[i-SharedNavMapSystem.ChunkSize];
|
||||
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Left, out neighborChunk))
|
||||
neighborData = neighborChunk.TileData[i - SharedNavMapSystem.ChunkSize + SharedNavMapSystem.ArraySize];
|
||||
|
||||
if ((neighborData & eastMask) == 0)
|
||||
{
|
||||
neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(-1, 0), out neighborChunk) &&
|
||||
(neighborChunk.TileData &
|
||||
SharedNavMapSystem.GetFlag(new Vector2i(SharedNavMapSystem.ChunkSize - 1, relativeTile.Y))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(-1, 0));
|
||||
neighbor = (chunk.TileData & flag) != 0x0;
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, _vertLines,
|
||||
_vertLinesReversed);
|
||||
}
|
||||
|
||||
if (!neighbor)
|
||||
{
|
||||
// Add point
|
||||
list.Add(new NavMapLine(position, position + new Vector2(0f, -grid.TileSize)));
|
||||
}
|
||||
|
||||
// Draw a diagonal line for interiors.
|
||||
list.Add(new NavMapLine(position + new Vector2(0f, -grid.TileSize), position + new Vector2(grid.TileSize, 0f)));
|
||||
// Add a diagonal line for interiors. Unless there are a lot of double walls, there is no point combining these
|
||||
TileLines.Add((tile + new Vector2(0, -_grid.TileSize), tile + new Vector2(_grid.TileSize, 0)));
|
||||
}
|
||||
|
||||
decodedOutput.Add(chunkOrigin, list);
|
||||
}
|
||||
|
||||
return decodedOutput;
|
||||
// Record the combined lines
|
||||
foreach (var (origin, terminal) in _horizLines)
|
||||
{
|
||||
TileLines.Add((origin, terminal));
|
||||
}
|
||||
|
||||
foreach (var (origin, terminal) in _vertLines)
|
||||
{
|
||||
TileLines.Add((origin, terminal));
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateNavMapAirlocks()
|
||||
{
|
||||
if (_navMap == null || _grid == null)
|
||||
return;
|
||||
|
||||
foreach (var chunk in _navMap.Chunks.Values)
|
||||
{
|
||||
for (var i = 0; i < SharedNavMapSystem.ArraySize; i++)
|
||||
{
|
||||
var tileData = chunk.TileData[i] & SharedNavMapSystem.AirlockMask;
|
||||
if (tileData == 0)
|
||||
continue;
|
||||
|
||||
tileData >>= (int) NavMapChunkType.Airlock;
|
||||
|
||||
var relative = SharedNavMapSystem.GetTileFromIndex(i);
|
||||
var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relative) * _grid.TileSize;
|
||||
|
||||
// If the edges of an airlock tile are not all occupied, draw a thin airlock for each edge
|
||||
if (tileData != SharedNavMapSystem.AllDirMask)
|
||||
{
|
||||
AddRectForThinAirlock(tileData, tile);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise add a single full tile airlock
|
||||
TileRects.Add((new Vector2(tile.X + FullWallInstep, -tile.Y - FullWallInstep),
|
||||
new Vector2(tile.X - FullWallInstep + 1f, -tile.Y + FullWallInstep - 1)));
|
||||
|
||||
TileLines.Add((new Vector2(tile.X + 0.5f, -tile.Y - FullWallInstep),
|
||||
new Vector2(tile.X + 0.5f, -tile.Y + FullWallInstep - 1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddRectForThinWall(int tileData, Vector2i tile)
|
||||
{
|
||||
var leftTop = new Vector2(-0.5f, 0.5f - ThinWallThickness);
|
||||
var rightBottom = new Vector2(0.5f, 0.5f);
|
||||
|
||||
for (var i = 0; i < SharedNavMapSystem.Directions; i++)
|
||||
{
|
||||
var dirMask = 1 << i;
|
||||
if ((tileData & dirMask) == 0)
|
||||
continue;
|
||||
|
||||
var tilePosition = new Vector2(tile.X + 0.5f, -tile.Y - 0.5f);
|
||||
|
||||
// TODO NAVMAP
|
||||
// Consider using faster rotation operations, given that these are always 90 degree increments
|
||||
var angle = -((AtmosDirection) dirMask).ToAngle();
|
||||
TileRects.Add((angle.RotateVec(leftTop) + tilePosition, angle.RotateVec(rightBottom) + tilePosition));
|
||||
}
|
||||
}
|
||||
|
||||
private void AddRectForThinAirlock(int tileData, Vector2i tile)
|
||||
{
|
||||
var leftTop = new Vector2(-0.5f + FullWallInstep, 0.5f - FullWallInstep - ThinDoorThickness);
|
||||
var rightBottom = new Vector2(0.5f - FullWallInstep, 0.5f - FullWallInstep);
|
||||
var centreTop = new Vector2(0f, 0.5f - FullWallInstep - ThinDoorThickness);
|
||||
var centreBottom = new Vector2(0f, 0.5f - FullWallInstep);
|
||||
|
||||
for (var i = 0; i < SharedNavMapSystem.Directions; i++)
|
||||
{
|
||||
var dirMask = 1 << i;
|
||||
if ((tileData & dirMask) == 0)
|
||||
continue;
|
||||
|
||||
var tilePosition = new Vector2(tile.X + 0.5f, -tile.Y - 0.5f);
|
||||
var angle = -((AtmosDirection) dirMask).ToAngle();
|
||||
TileRects.Add((angle.RotateVec(leftTop) + tilePosition, angle.RotateVec(rightBottom) + tilePosition));
|
||||
TileLines.Add((angle.RotateVec(centreTop) + tilePosition, angle.RotateVec(centreBottom) + tilePosition));
|
||||
}
|
||||
}
|
||||
|
||||
protected void AddOrUpdateNavMapLine(
|
||||
Vector2i origin,
|
||||
Vector2i terminus,
|
||||
Dictionary<Vector2i, Vector2i> lookup,
|
||||
Dictionary<Vector2i, Vector2i> lookupReversed)
|
||||
{
|
||||
Vector2i foundTermius;
|
||||
Vector2i foundOrigin;
|
||||
|
||||
// Does our new line end at the beginning of an existing line?
|
||||
if (lookup.Remove(terminus, out foundTermius))
|
||||
{
|
||||
DebugTools.Assert(lookupReversed[foundTermius] == terminus);
|
||||
|
||||
// Does our new line start at the end of an existing line?
|
||||
if (lookupReversed.Remove(origin, out foundOrigin))
|
||||
{
|
||||
// Our new line just connects two existing lines
|
||||
DebugTools.Assert(lookup[foundOrigin] == origin);
|
||||
lookup[foundOrigin] = foundTermius;
|
||||
lookupReversed[foundTermius] = foundOrigin;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Our new line precedes an existing line, extending it further to the left
|
||||
lookup[origin] = foundTermius;
|
||||
lookupReversed[foundTermius] = origin;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Does our new line start at the end of an existing line?
|
||||
if (lookupReversed.Remove(origin, out foundOrigin))
|
||||
{
|
||||
// Our new line just extends an existing line further to the right
|
||||
DebugTools.Assert(lookup[foundOrigin] == origin);
|
||||
lookup[foundOrigin] = terminus;
|
||||
lookupReversed[terminus] = foundOrigin;
|
||||
return;
|
||||
}
|
||||
|
||||
// Completely disconnected line segment.
|
||||
lookup.Add(origin, terminus);
|
||||
lookupReversed.Add(terminus, origin);
|
||||
}
|
||||
|
||||
protected Vector2 GetOffset()
|
||||
@@ -612,15 +732,3 @@ public struct NavMapBlip
|
||||
Selectable = selectable;
|
||||
}
|
||||
}
|
||||
|
||||
public struct NavMapLine
|
||||
{
|
||||
public readonly Vector2 Origin;
|
||||
public readonly Vector2 Terminus;
|
||||
|
||||
public NavMapLine(Vector2 origin, Vector2 terminus)
|
||||
{
|
||||
Origin = origin;
|
||||
Terminus = terminus;
|
||||
}
|
||||
}
|
||||
|
||||
33
Content.Client/Polymorph/Systems/ChameleonProjectorSystem.cs
Normal file
33
Content.Client/Polymorph/Systems/ChameleonProjectorSystem.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Polymorph.Components;
|
||||
using Content.Shared.Polymorph.Systems;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Polymorph.Systems;
|
||||
|
||||
public sealed class ChameleonProjectorSystem : SharedChameleonProjectorSystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
|
||||
private EntityQuery<AppearanceComponent> _appearanceQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_appearanceQuery = GetEntityQuery<AppearanceComponent>();
|
||||
|
||||
SubscribeLocalEvent<ChameleonDisguiseComponent, AfterAutoHandleStateEvent>(OnHandleState);
|
||||
}
|
||||
|
||||
private void OnHandleState(Entity<ChameleonDisguiseComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
CopyComp<SpriteComponent>(ent);
|
||||
CopyComp<GenericVisualizerComponent>(ent);
|
||||
CopyComp<SolutionContainerVisualsComponent>(ent);
|
||||
|
||||
// reload appearance to hopefully prevent any invisible layers
|
||||
if (_appearanceQuery.TryComp(ent, out var appearance))
|
||||
_appearance.QueueUpdate(ent, appearance);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ using Content.Shared.Popups;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Map;
|
||||
@@ -163,6 +162,15 @@ namespace Content.Client.Popups
|
||||
PopupEntity(message, uid, type);
|
||||
}
|
||||
|
||||
public override void PopupClient(string? message, EntityUid? recipient, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (recipient == null)
|
||||
return;
|
||||
|
||||
if (_timing.IsFirstTimePredicted)
|
||||
PopupCursor(message, recipient.Value, type);
|
||||
}
|
||||
|
||||
public override void PopupClient(string? message, EntityUid uid, EntityUid? recipient, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (recipient == null)
|
||||
@@ -172,6 +180,15 @@ namespace Content.Client.Popups
|
||||
PopupEntity(message, uid, recipient.Value, type);
|
||||
}
|
||||
|
||||
public override void PopupClient(string? message, EntityCoordinates coordinates, EntityUid? recipient, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (recipient == null)
|
||||
return;
|
||||
|
||||
if (_timing.IsFirstTimePredicted)
|
||||
PopupCoordinates(message, coordinates, recipient.Value, type);
|
||||
}
|
||||
|
||||
public override void PopupEntity(string? message, EntityUid uid, PopupType type = PopupType.Small)
|
||||
{
|
||||
if (TryComp(uid, out TransformComponent? transform))
|
||||
|
||||
21
Content.Client/Power/ActivatableUIRequiresPowerSystem.cs
Normal file
21
Content.Client/Power/ActivatableUIRequiresPowerSystem.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Content.Shared.Power.Components;
|
||||
using Content.Shared.UserInterface;
|
||||
using Content.Shared.Wires;
|
||||
|
||||
namespace Content.Client.Power;
|
||||
|
||||
public sealed class ActivatableUIRequiresPowerSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ActivatableUIRequiresPowerComponent, ActivatableUIOpenAttemptEvent>(OnActivate);
|
||||
}
|
||||
|
||||
private void OnActivate(EntityUid uid, ActivatableUIRequiresPowerComponent component, ActivatableUIOpenAttemptEvent args)
|
||||
{
|
||||
// Client can't predict the power properly at the moment so rely upon the server to do it.
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using Robust.Client.Graphics;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Map.Components;
|
||||
using System.Numerics;
|
||||
using static Content.Shared.Power.SharedPowerMonitoringConsoleSystem;
|
||||
|
||||
namespace Content.Client.Power;
|
||||
|
||||
@@ -23,8 +24,13 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
|
||||
|
||||
public PowerMonitoringCableNetworksComponent? PowerMonitoringCableNetworks;
|
||||
public List<PowerMonitoringConsoleLineGroup> HiddenLineGroups = new();
|
||||
public Dictionary<Vector2i, List<PowerMonitoringConsoleLine>>? PowerCableNetwork;
|
||||
public Dictionary<Vector2i, List<PowerMonitoringConsoleLine>>? FocusCableNetwork;
|
||||
public List<PowerMonitoringConsoleLine> PowerCableNetwork = new();
|
||||
public List<PowerMonitoringConsoleLine> FocusCableNetwork = new();
|
||||
|
||||
private Dictionary<Vector2i, Vector2i>[] _horizLines = [new(), new(), new()];
|
||||
private Dictionary<Vector2i, Vector2i>[] _horizLinesReversed = [new(), new(), new()];
|
||||
private Dictionary<Vector2i, Vector2i>[] _vertLines = [new(), new(), new()];
|
||||
private Dictionary<Vector2i, Vector2i>[] _vertLinesReversed = [new(), new(), new()];
|
||||
|
||||
private MapGridComponent? _grid;
|
||||
|
||||
@@ -48,15 +54,15 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
|
||||
if (!_entManager.TryGetComponent<PowerMonitoringCableNetworksComponent>(Owner, out var cableNetworks))
|
||||
return;
|
||||
|
||||
if (!_entManager.TryGetComponent(MapUid, out _grid))
|
||||
return;
|
||||
|
||||
PowerCableNetwork = GetDecodedPowerCableChunks(cableNetworks.AllChunks, _grid);
|
||||
FocusCableNetwork = GetDecodedPowerCableChunks(cableNetworks.FocusChunks, _grid);
|
||||
PowerCableNetwork = GetDecodedPowerCableChunks(cableNetworks.AllChunks);
|
||||
FocusCableNetwork = GetDecodedPowerCableChunks(cableNetworks.FocusChunks);
|
||||
}
|
||||
|
||||
public void DrawAllCableNetworks(DrawingHandleScreen handle)
|
||||
{
|
||||
if (!_entManager.TryGetComponent(MapUid, out _grid))
|
||||
return;
|
||||
|
||||
// Draw full cable network
|
||||
if (PowerCableNetwork != null && PowerCableNetwork.Count > 0)
|
||||
{
|
||||
@@ -69,36 +75,29 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
|
||||
DrawCableNetwork(handle, FocusCableNetwork, Color.White);
|
||||
}
|
||||
|
||||
public void DrawCableNetwork(DrawingHandleScreen handle, Dictionary<Vector2i, List<PowerMonitoringConsoleLine>> fullCableNetwork, Color modulator)
|
||||
public void DrawCableNetwork(DrawingHandleScreen handle, List<PowerMonitoringConsoleLine> fullCableNetwork, Color modulator)
|
||||
{
|
||||
if (!_entManager.TryGetComponent(MapUid, out _grid))
|
||||
return;
|
||||
|
||||
var offset = GetOffset();
|
||||
var area = new Box2(-WorldRange, -WorldRange, WorldRange + 1f, WorldRange + 1f).Translated(offset);
|
||||
offset = offset with { Y = -offset.Y };
|
||||
|
||||
if (WorldRange / WorldMaxRange > 0.5f)
|
||||
{
|
||||
var cableNetworks = new ValueList<Vector2>[3];
|
||||
|
||||
foreach ((var chunk, var chunkedLines) in fullCableNetwork)
|
||||
foreach (var line in fullCableNetwork)
|
||||
{
|
||||
var offsetChunk = new Vector2(chunk.X, chunk.Y) * SharedNavMapSystem.ChunkSize;
|
||||
|
||||
if (offsetChunk.X < area.Left - SharedNavMapSystem.ChunkSize || offsetChunk.X > area.Right)
|
||||
if (HiddenLineGroups.Contains(line.Group))
|
||||
continue;
|
||||
|
||||
if (offsetChunk.Y < area.Bottom - SharedNavMapSystem.ChunkSize || offsetChunk.Y > area.Top)
|
||||
continue;
|
||||
var cableOffset = _powerCableOffsets[(int) line.Group];
|
||||
var start = ScalePosition(line.Origin + cableOffset - offset);
|
||||
var end = ScalePosition(line.Terminus + cableOffset - offset);
|
||||
|
||||
foreach (var chunkedLine in chunkedLines)
|
||||
{
|
||||
if (HiddenLineGroups.Contains(chunkedLine.Group))
|
||||
continue;
|
||||
|
||||
var start = ScalePosition(chunkedLine.Origin - new Vector2(offset.X, -offset.Y));
|
||||
var end = ScalePosition(chunkedLine.Terminus - new Vector2(offset.X, -offset.Y));
|
||||
|
||||
cableNetworks[(int) chunkedLine.Group].Add(start);
|
||||
cableNetworks[(int) chunkedLine.Group].Add(end);
|
||||
}
|
||||
cableNetworks[(int) line.Group].Add(start);
|
||||
cableNetworks[(int) line.Group].Add(end);
|
||||
}
|
||||
|
||||
for (int cableNetworkIdx = 0; cableNetworkIdx < cableNetworks.Length; cableNetworkIdx++)
|
||||
@@ -124,48 +123,39 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
|
||||
{
|
||||
var cableVertexUVs = new ValueList<Vector2>[3];
|
||||
|
||||
foreach ((var chunk, var chunkedLines) in fullCableNetwork)
|
||||
foreach (var line in fullCableNetwork)
|
||||
{
|
||||
var offsetChunk = new Vector2(chunk.X, chunk.Y) * SharedNavMapSystem.ChunkSize;
|
||||
|
||||
if (offsetChunk.X < area.Left - SharedNavMapSystem.ChunkSize || offsetChunk.X > area.Right)
|
||||
if (HiddenLineGroups.Contains(line.Group))
|
||||
continue;
|
||||
|
||||
if (offsetChunk.Y < area.Bottom - SharedNavMapSystem.ChunkSize || offsetChunk.Y > area.Top)
|
||||
continue;
|
||||
var cableOffset = _powerCableOffsets[(int) line.Group];
|
||||
|
||||
foreach (var chunkedLine in chunkedLines)
|
||||
{
|
||||
if (HiddenLineGroups.Contains(chunkedLine.Group))
|
||||
continue;
|
||||
var leftTop = ScalePosition(new Vector2
|
||||
(Math.Min(line.Origin.X, line.Terminus.X) - 0.1f,
|
||||
Math.Min(line.Origin.Y, line.Terminus.Y) - 0.1f)
|
||||
+ cableOffset - offset);
|
||||
|
||||
var leftTop = ScalePosition(new Vector2
|
||||
(Math.Min(chunkedLine.Origin.X, chunkedLine.Terminus.X) - 0.1f,
|
||||
Math.Min(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) - 0.1f)
|
||||
- new Vector2(offset.X, -offset.Y));
|
||||
var rightTop = ScalePosition(new Vector2
|
||||
(Math.Max(line.Origin.X, line.Terminus.X) + 0.1f,
|
||||
Math.Min(line.Origin.Y, line.Terminus.Y) - 0.1f)
|
||||
+ cableOffset - offset);
|
||||
|
||||
var rightTop = ScalePosition(new Vector2
|
||||
(Math.Max(chunkedLine.Origin.X, chunkedLine.Terminus.X) + 0.1f,
|
||||
Math.Min(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) - 0.1f)
|
||||
- new Vector2(offset.X, -offset.Y));
|
||||
var leftBottom = ScalePosition(new Vector2
|
||||
(Math.Min(line.Origin.X, line.Terminus.X) - 0.1f,
|
||||
Math.Max(line.Origin.Y, line.Terminus.Y) + 0.1f)
|
||||
+ cableOffset - offset);
|
||||
|
||||
var leftBottom = ScalePosition(new Vector2
|
||||
(Math.Min(chunkedLine.Origin.X, chunkedLine.Terminus.X) - 0.1f,
|
||||
Math.Max(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) + 0.1f)
|
||||
- new Vector2(offset.X, -offset.Y));
|
||||
var rightBottom = ScalePosition(new Vector2
|
||||
(Math.Max(line.Origin.X, line.Terminus.X) + 0.1f,
|
||||
Math.Max(line.Origin.Y, line.Terminus.Y) + 0.1f)
|
||||
+ cableOffset - offset);
|
||||
|
||||
var rightBottom = ScalePosition(new Vector2
|
||||
(Math.Max(chunkedLine.Origin.X, chunkedLine.Terminus.X) + 0.1f,
|
||||
Math.Max(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) + 0.1f)
|
||||
- new Vector2(offset.X, -offset.Y));
|
||||
|
||||
cableVertexUVs[(int) chunkedLine.Group].Add(leftBottom);
|
||||
cableVertexUVs[(int) chunkedLine.Group].Add(leftTop);
|
||||
cableVertexUVs[(int) chunkedLine.Group].Add(rightBottom);
|
||||
cableVertexUVs[(int) chunkedLine.Group].Add(leftTop);
|
||||
cableVertexUVs[(int) chunkedLine.Group].Add(rightBottom);
|
||||
cableVertexUVs[(int) chunkedLine.Group].Add(rightTop);
|
||||
}
|
||||
cableVertexUVs[(int) line.Group].Add(leftBottom);
|
||||
cableVertexUVs[(int) line.Group].Add(leftTop);
|
||||
cableVertexUVs[(int) line.Group].Add(rightBottom);
|
||||
cableVertexUVs[(int) line.Group].Add(leftTop);
|
||||
cableVertexUVs[(int) line.Group].Add(rightBottom);
|
||||
cableVertexUVs[(int) line.Group].Add(rightTop);
|
||||
}
|
||||
|
||||
for (int cableNetworkIdx = 0; cableNetworkIdx < cableVertexUVs.Length; cableNetworkIdx++)
|
||||
@@ -188,34 +178,43 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<Vector2i, List<PowerMonitoringConsoleLine>>? GetDecodedPowerCableChunks(Dictionary<Vector2i, PowerCableChunk>? chunks, MapGridComponent? grid)
|
||||
public List<PowerMonitoringConsoleLine> GetDecodedPowerCableChunks(Dictionary<Vector2i, PowerCableChunk>? chunks)
|
||||
{
|
||||
if (chunks == null || grid == null)
|
||||
return null;
|
||||
var decodedOutput = new List<PowerMonitoringConsoleLine>();
|
||||
|
||||
var decodedOutput = new Dictionary<Vector2i, List<PowerMonitoringConsoleLine>>();
|
||||
if (!_entManager.TryGetComponent(MapUid, out _grid))
|
||||
return decodedOutput;
|
||||
|
||||
foreach ((var chunkOrigin, var chunk) in chunks)
|
||||
if (chunks == null)
|
||||
return decodedOutput;
|
||||
|
||||
Array.ForEach(_horizLines, x=> x.Clear());
|
||||
Array.ForEach(_horizLinesReversed, x=> x.Clear());
|
||||
Array.ForEach(_vertLines, x=> x.Clear());
|
||||
Array.ForEach(_vertLinesReversed, x=> x.Clear());
|
||||
|
||||
foreach (var (chunkOrigin, chunk) in chunks)
|
||||
{
|
||||
var list = new List<PowerMonitoringConsoleLine>();
|
||||
|
||||
for (int cableIdx = 0; cableIdx < chunk.PowerCableData.Length; cableIdx++)
|
||||
for (var cableIdx = 0; cableIdx < 3; cableIdx++)
|
||||
{
|
||||
var horizLines = _horizLines[cableIdx];
|
||||
var horizLinesReversed = _horizLinesReversed[cableIdx];
|
||||
var vertLines = _vertLines[cableIdx];
|
||||
var vertLinesReversed = _vertLinesReversed[cableIdx];
|
||||
|
||||
var chunkMask = chunk.PowerCableData[cableIdx];
|
||||
|
||||
Vector2 offset = _powerCableOffsets[cableIdx];
|
||||
|
||||
for (var chunkIdx = 0; chunkIdx < SharedNavMapSystem.ChunkSize * SharedNavMapSystem.ChunkSize; chunkIdx++)
|
||||
for (var chunkIdx = 0; chunkIdx < ChunkSize * ChunkSize; chunkIdx++)
|
||||
{
|
||||
var value = (int) Math.Pow(2, chunkIdx);
|
||||
var value = 1 << chunkIdx;
|
||||
var mask = chunkMask & value;
|
||||
|
||||
if (mask == 0x0)
|
||||
continue;
|
||||
|
||||
var relativeTile = SharedNavMapSystem.GetTile(mask);
|
||||
var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relativeTile) * grid.TileSize;
|
||||
var position = new Vector2(tile.X, -tile.Y);
|
||||
var relativeTile = GetTileFromIndex(chunkIdx);
|
||||
var tile = (chunk.Origin * ChunkSize + relativeTile) * _grid.TileSize;
|
||||
tile = tile with { Y = -tile.Y };
|
||||
|
||||
PowerCableChunk neighborChunk;
|
||||
bool neighbor;
|
||||
@@ -223,56 +222,65 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
|
||||
// Note: we only check the north and east neighbors
|
||||
|
||||
// East
|
||||
if (relativeTile.X == SharedNavMapSystem.ChunkSize - 1)
|
||||
if (relativeTile.X == ChunkSize - 1)
|
||||
{
|
||||
neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(1, 0), out neighborChunk) &&
|
||||
(neighborChunk.PowerCableData[cableIdx] & SharedNavMapSystem.GetFlag(new Vector2i(0, relativeTile.Y))) != 0x0;
|
||||
(neighborChunk.PowerCableData[cableIdx] & GetFlag(new Vector2i(0, relativeTile.Y))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(1, 0));
|
||||
var flag = GetFlag(relativeTile + new Vector2i(1, 0));
|
||||
neighbor = (chunkMask & flag) != 0x0;
|
||||
}
|
||||
|
||||
if (neighbor)
|
||||
{
|
||||
// Add points
|
||||
var line = new PowerMonitoringConsoleLine
|
||||
(position + offset + new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f),
|
||||
position + new Vector2(1f, 0f) + offset + new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f),
|
||||
(PowerMonitoringConsoleLineGroup) cableIdx);
|
||||
|
||||
list.Add(line);
|
||||
AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), horizLines, horizLinesReversed);
|
||||
}
|
||||
|
||||
// North
|
||||
if (relativeTile.Y == SharedNavMapSystem.ChunkSize - 1)
|
||||
if (relativeTile.Y == ChunkSize - 1)
|
||||
{
|
||||
neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(0, 1), out neighborChunk) &&
|
||||
(neighborChunk.PowerCableData[cableIdx] & SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, 0))) != 0x0;
|
||||
(neighborChunk.PowerCableData[cableIdx] & GetFlag(new Vector2i(relativeTile.X, 0))) != 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(0, 1));
|
||||
var flag = GetFlag(relativeTile + new Vector2i(0, 1));
|
||||
neighbor = (chunkMask & flag) != 0x0;
|
||||
}
|
||||
|
||||
if (neighbor)
|
||||
{
|
||||
// Add points
|
||||
var line = new PowerMonitoringConsoleLine
|
||||
(position + offset + new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f),
|
||||
position + new Vector2(0f, -1f) + offset + new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f),
|
||||
(PowerMonitoringConsoleLineGroup) cableIdx);
|
||||
|
||||
list.Add(line);
|
||||
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, vertLines, vertLinesReversed);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (list.Count > 0)
|
||||
decodedOutput.Add(chunkOrigin, list);
|
||||
var gridOffset = new Vector2(_grid.TileSize * 0.5f, -_grid.TileSize * 0.5f);
|
||||
|
||||
for (var index = 0; index < _horizLines.Length; index++)
|
||||
{
|
||||
var horizLines = _horizLines[index];
|
||||
foreach (var (origin, terminal) in horizLines)
|
||||
{
|
||||
decodedOutput.Add(new PowerMonitoringConsoleLine(origin + gridOffset, terminal + gridOffset,
|
||||
(PowerMonitoringConsoleLineGroup) index));
|
||||
}
|
||||
}
|
||||
|
||||
for (var index = 0; index < _vertLines.Length; index++)
|
||||
{
|
||||
var vertLines = _vertLines[index];
|
||||
foreach (var (origin, terminal) in vertLines)
|
||||
{
|
||||
decodedOutput.Add(new PowerMonitoringConsoleLine(origin + gridOffset, terminal + gridOffset,
|
||||
(PowerMonitoringConsoleLineGroup) index));
|
||||
}
|
||||
}
|
||||
|
||||
return decodedOutput;
|
||||
|
||||
@@ -170,9 +170,6 @@ public sealed partial class PowerMonitoringWindow : FancyWindow
|
||||
NavMap.TrackedEntities[mon.Value] = blip;
|
||||
}
|
||||
|
||||
// Update nav map
|
||||
NavMap.ForceNavMapUpdate();
|
||||
|
||||
// If the entry group doesn't match the current tab, the data is out dated, do not use it
|
||||
if (allEntries.Length > 0 && allEntries[0].Group != GetCurrentPowerMonitoringConsoleGroup())
|
||||
return;
|
||||
|
||||
@@ -135,6 +135,9 @@ namespace Content.Client.Preferences.UI
|
||||
_humanoidProfileEditor.CharacterSlot = characterIndexCopy;
|
||||
_humanoidProfileEditor.UpdateControls();
|
||||
_preferencesManager.SelectCharacter(character);
|
||||
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
||||
controller.UpdateProfile(_humanoidProfileEditor.Profile);
|
||||
controller.ReloadCharacterUI();
|
||||
UpdateUI();
|
||||
args.Event.Handle();
|
||||
};
|
||||
|
||||
@@ -697,6 +697,20 @@ namespace Content.Client.Preferences.UI
|
||||
|
||||
var color = SkinColor.TintedHues(_rgbSkinColorSelector.Color);
|
||||
|
||||
CMarkings.CurrentSkinColor = color;
|
||||
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color));
|
||||
break;
|
||||
}
|
||||
case HumanoidSkinColor.VoxFeathers:
|
||||
{
|
||||
if (!_rgbSkinColorContainer.Visible)
|
||||
{
|
||||
_skinColor.Visible = false;
|
||||
_rgbSkinColorContainer.Visible = true;
|
||||
}
|
||||
|
||||
var color = SkinColor.ClosestVoxColor(_rgbSkinColorSelector.Color);
|
||||
|
||||
CMarkings.CurrentSkinColor = color;
|
||||
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color));
|
||||
break;
|
||||
@@ -728,7 +742,6 @@ namespace Content.Client.Preferences.UI
|
||||
CharacterSlot = _preferencesManager.Preferences.SelectedCharacterIndex;
|
||||
|
||||
UpdateAntagRequirements();
|
||||
UpdateRoleRequirements();
|
||||
UpdateControls();
|
||||
ShowClothes.Pressed = true;
|
||||
}
|
||||
@@ -908,6 +921,18 @@ namespace Content.Client.Preferences.UI
|
||||
_rgbSkinColorSelector.Color = Profile.Appearance.SkinColor;
|
||||
break;
|
||||
}
|
||||
case HumanoidSkinColor.VoxFeathers:
|
||||
{
|
||||
if (!_rgbSkinColorContainer.Visible)
|
||||
{
|
||||
_skinColor.Visible = false;
|
||||
_rgbSkinColorContainer.Visible = true;
|
||||
}
|
||||
|
||||
_rgbSkinColorSelector.Color = SkinColor.ClosestVoxColor(Profile.Appearance.SkinColor);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1115,6 +1140,7 @@ namespace Content.Client.Preferences.UI
|
||||
UpdateEyePickers();
|
||||
UpdateSaveButton();
|
||||
UpdateLoadouts();
|
||||
UpdateRoleRequirements();
|
||||
UpdateJobPriorities();
|
||||
UpdateAntagPreferences();
|
||||
UpdateTraitPreferences();
|
||||
|
||||
@@ -195,9 +195,16 @@ public sealed partial class ReplaySpectatorSystem
|
||||
if (uid != _player.LocalEntity)
|
||||
return;
|
||||
|
||||
if (args.Transform.MapUid != null || args.OldMapId == MapId.Nullspace)
|
||||
if (args.Transform.MapUid != null || args.OldMapId == null)
|
||||
return;
|
||||
|
||||
if (_spectatorData != null)
|
||||
{
|
||||
// Currently scrubbing/setting the replay tick
|
||||
// the observer will get respawned once the state was applied
|
||||
return;
|
||||
}
|
||||
|
||||
// The entity being spectated from was moved to null-space.
|
||||
// This was probably because they were spectating some entity in a client-side replay that left PVS range.
|
||||
// Simple respawn the ghost.
|
||||
|
||||
51
Content.Client/RoundEnd/RoundEndSummaryUIController.cs
Normal file
51
Content.Client/RoundEnd/RoundEndSummaryUIController.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using Content.Client.GameTicking.Managers;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Input;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface.Controllers;
|
||||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Client.RoundEnd;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class RoundEndSummaryUIController : UIController,
|
||||
IOnSystemLoaded<ClientGameTicker>
|
||||
{
|
||||
[Dependency] private readonly IInputManager _input = default!;
|
||||
|
||||
private RoundEndSummaryWindow? _window;
|
||||
|
||||
private void ToggleScoreboardWindow(ICommonSession? session = null)
|
||||
{
|
||||
if (_window == null)
|
||||
return;
|
||||
|
||||
if (_window.IsOpen)
|
||||
{
|
||||
_window.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
_window.OpenCenteredRight();
|
||||
_window.MoveToFront();
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenRoundEndSummaryWindow(RoundEndMessageEvent message)
|
||||
{
|
||||
// Don't open duplicate windows (mainly for replays).
|
||||
if (_window?.RoundId == message.RoundId)
|
||||
return;
|
||||
|
||||
_window = new RoundEndSummaryWindow(message.GamemodeTitle, message.RoundEndText,
|
||||
message.RoundDuration, message.RoundId, message.AllPlayersEndInfo, EntityManager);
|
||||
}
|
||||
|
||||
public void OnSystemLoaded(ClientGameTicker system)
|
||||
{
|
||||
_input.SetInputCommand(ContentKeyFunctions.ToggleRoundEndSummaryWindow,
|
||||
InputCmdHandler.FromDelegate(ToggleScoreboardWindow));
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client.Message;
|
||||
using Content.Shared.GameTicking;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -100,7 +100,7 @@ public partial class BaseShuttleControl : MapGridControl
|
||||
var textDimensions = handle.GetDimensions(Font, text, UIScale);
|
||||
|
||||
handle.DrawCircle(origin, scaledRadius, color, false);
|
||||
handle.DrawString(Font, ScalePosition(new Vector2(0f, -radius)) - new Vector2(0f, textDimensions.Y), text, color);
|
||||
handle.DrawString(Font, ScalePosition(new Vector2(0f, -radius)) - new Vector2(0f, textDimensions.Y), text, UIScale, color);
|
||||
}
|
||||
|
||||
const int gridLinesRadial = 8;
|
||||
|
||||
@@ -2,7 +2,4 @@ using Content.Shared.Station;
|
||||
|
||||
namespace Content.Client.Station;
|
||||
|
||||
public sealed class StationSpawningSystem : SharedStationSpawningSystem
|
||||
{
|
||||
|
||||
}
|
||||
public sealed class StationSpawningSystem : SharedStationSpawningSystem;
|
||||
|
||||
@@ -17,6 +17,14 @@ public sealed class StorageBoundUserInterface : BoundUserInterface
|
||||
_storage = _entManager.System<StorageSystem>();
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
if (_entManager.TryGetComponent<StorageComponent>(Owner, out var comp))
|
||||
_storage.OpenStorageWindow((Owner, comp));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
@@ -25,16 +33,5 @@ public sealed class StorageBoundUserInterface : BoundUserInterface
|
||||
|
||||
_storage.CloseStorageWindow(Owner);
|
||||
}
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
{
|
||||
base.ReceiveMessage(message);
|
||||
|
||||
if (message is StorageModifyWindowMessage)
|
||||
{
|
||||
if (_entManager.TryGetComponent<StorageComponent>(Owner, out var comp))
|
||||
_storage.OpenStorageWindow((Owner, comp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ public sealed class StorageSystem : SharedStorageSystem
|
||||
|
||||
SubscribeLocalEvent<StorageComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeNetworkEvent<PickupAnimationEvent>(HandlePickupAnimation);
|
||||
SubscribeNetworkEvent<AnimateInsertingEntitiesEvent>(HandleAnimatingInsertingEntities);
|
||||
SubscribeAllEvent<AnimateInsertingEntitiesEvent>(HandleAnimatingInsertingEntities);
|
||||
}
|
||||
|
||||
public override void UpdateUI(Entity<StorageComponent?> entity)
|
||||
@@ -111,7 +111,7 @@ public sealed class StorageSystem : SharedStorageSystem
|
||||
if (!Resolve(entity, ref entity.Comp, false))
|
||||
return;
|
||||
|
||||
if (entity.Comp.OpenInterfaces.GetValueOrDefault(StorageComponent.StorageUiKey.Key) is not { } bui)
|
||||
if (entity.Comp.ClientOpenInterfaces.GetValueOrDefault(StorageComponent.StorageUiKey.Key) is not { } bui)
|
||||
return;
|
||||
|
||||
bui.Close();
|
||||
|
||||
@@ -35,7 +35,7 @@ public sealed class StrippableSystem : SharedStrippableSystem
|
||||
if (!TryComp(uid, out UserInterfaceComponent? uiComp))
|
||||
return;
|
||||
|
||||
foreach (var ui in uiComp.OpenInterfaces.Values)
|
||||
foreach (var ui in uiComp.ClientOpenInterfaces.Values)
|
||||
{
|
||||
if (ui is StrippableBoundUserInterface stripUi)
|
||||
stripUi.DirtyMenu();
|
||||
|
||||
@@ -136,6 +136,8 @@ namespace Content.Client.Stylesheets
|
||||
public const string StyleClassPowerStateGood = "PowerStateGood";
|
||||
|
||||
public const string StyleClassItemStatus = "ItemStatus";
|
||||
public const string StyleClassItemStatusNotHeld = "ItemStatusNotHeld";
|
||||
public static readonly Color ItemStatusNotHeldColor = Color.Gray;
|
||||
|
||||
//Background
|
||||
public const string StyleClassBackgroundBaseDark = "PanelBackgroundBaseDark";
|
||||
@@ -1234,6 +1236,16 @@ namespace Content.Client.Stylesheets
|
||||
new StyleProperty("font", notoSans10),
|
||||
}),
|
||||
|
||||
Element()
|
||||
.Class(StyleClassItemStatusNotHeld)
|
||||
.Prop("font", notoSansItalic10)
|
||||
.Prop("font-color", ItemStatusNotHeldColor),
|
||||
|
||||
Element<RichTextLabel>()
|
||||
.Class(StyleClassItemStatus)
|
||||
.Prop(nameof(RichTextLabel.LineHeightScale), 0.7f)
|
||||
.Prop(nameof(Control.Margin), new Thickness(0, 0, 0, -6)),
|
||||
|
||||
// Slider
|
||||
new StyleRule(SelectorElement.Type(typeof(Slider)), new []
|
||||
{
|
||||
|
||||
11
Content.Client/Tips/TippyUI.xaml
Normal file
11
Content.Client/Tips/TippyUI.xaml
Normal file
@@ -0,0 +1,11 @@
|
||||
<tips:TippyUI xmlns="https://spacestation14.io"
|
||||
xmlns:tips="clr-namespace:Content.Client.Tips"
|
||||
MinSize="64 64"
|
||||
Visible="False">
|
||||
<PanelContainer Name="LabelPanel" Access="Public" Visible="False" MaxWidth="300" MaxHeight="200">
|
||||
<ScrollContainer Name="ScrollingContents" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalExpand="True" VerticalExpand="True" HScrollEnabled="False" ReturnMeasure="True">
|
||||
<RichTextLabel Name="Label" Access="Public"/>
|
||||
</ScrollContainer>
|
||||
</PanelContainer>
|
||||
<SpriteView Name="Entity" Access="Public" MinSize="128 128"/>
|
||||
</tips:TippyUI>
|
||||
54
Content.Client/Tips/TippyUI.xaml.cs
Normal file
54
Content.Client/Tips/TippyUI.xaml.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using Content.Client.Paper;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Tips;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class TippyUI : UIWidget
|
||||
{
|
||||
public TippyState State = TippyState.Hidden;
|
||||
public bool ModifyLayers = true;
|
||||
|
||||
public TippyUI()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public void InitLabel(PaperVisualsComponent? visuals, IResourceCache resCache)
|
||||
{
|
||||
if (visuals == null)
|
||||
return;
|
||||
|
||||
Label.ModulateSelfOverride = visuals.FontAccentColor;
|
||||
|
||||
if (visuals.BackgroundImagePath == null)
|
||||
return;
|
||||
|
||||
LabelPanel.ModulateSelfOverride = visuals.BackgroundModulate;
|
||||
var backgroundImage = resCache.GetResource<TextureResource>(visuals.BackgroundImagePath);
|
||||
var backgroundImageMode = visuals.BackgroundImageTile ? StyleBoxTexture.StretchMode.Tile : StyleBoxTexture.StretchMode.Stretch;
|
||||
var backgroundPatchMargin = visuals.BackgroundPatchMargin;
|
||||
LabelPanel.PanelOverride = new StyleBoxTexture
|
||||
{
|
||||
Texture = backgroundImage,
|
||||
TextureScale = visuals.BackgroundScale,
|
||||
Mode = backgroundImageMode,
|
||||
PatchMarginLeft = backgroundPatchMargin.Left,
|
||||
PatchMarginBottom = backgroundPatchMargin.Bottom,
|
||||
PatchMarginRight = backgroundPatchMargin.Right,
|
||||
PatchMarginTop = backgroundPatchMargin.Top
|
||||
};
|
||||
}
|
||||
|
||||
public enum TippyState : byte
|
||||
{
|
||||
Hidden,
|
||||
Revealing,
|
||||
Speaking,
|
||||
Hiding,
|
||||
}
|
||||
}
|
||||
241
Content.Client/Tips/TippyUIController.cs
Normal file
241
Content.Client/Tips/TippyUIController.cs
Normal file
@@ -0,0 +1,241 @@
|
||||
using Content.Client.Gameplay;
|
||||
using System.Numerics;
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Paper;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Tips;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controllers;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using static Content.Client.Tips.TippyUI;
|
||||
|
||||
namespace Content.Client.Tips;
|
||||
|
||||
public sealed class TippyUIController : UIController
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IResourceCache _resCache = default!;
|
||||
[UISystemDependency] private readonly AudioSystem _audio = default!;
|
||||
|
||||
public const float Padding = 50;
|
||||
public static Angle WaddleRotation = Angle.FromDegrees(10);
|
||||
|
||||
private EntityUid _entity;
|
||||
private float _secondsUntilNextState;
|
||||
private int _previousStep = 0;
|
||||
private TippyEvent? _currentMessage;
|
||||
private readonly Queue<TippyEvent> _queuedMessages = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
UIManager.OnScreenChanged += OnScreenChanged;
|
||||
SubscribeNetworkEvent<TippyEvent>(OnTippyEvent);
|
||||
}
|
||||
|
||||
private void OnTippyEvent(TippyEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
_queuedMessages.Enqueue(msg);
|
||||
}
|
||||
|
||||
public override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
var screen = UIManager.ActiveScreen;
|
||||
if (screen == null)
|
||||
{
|
||||
_queuedMessages.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
var tippy = screen.GetOrAddWidget<TippyUI>();
|
||||
_secondsUntilNextState -= args.DeltaSeconds;
|
||||
|
||||
if (_secondsUntilNextState <= 0)
|
||||
NextState(tippy);
|
||||
else
|
||||
{
|
||||
var pos = UpdatePosition(tippy, screen.Size, args); ;
|
||||
LayoutContainer.SetPosition(tippy, pos);
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 UpdatePosition(TippyUI tippy, Vector2 screenSize, FrameEventArgs args)
|
||||
{
|
||||
if (_currentMessage == null)
|
||||
return default;
|
||||
|
||||
var slideTime = _currentMessage.SlideTime;
|
||||
|
||||
var offset = tippy.State switch
|
||||
{
|
||||
TippyState.Hidden => 0,
|
||||
TippyState.Revealing => Math.Clamp(1 - _secondsUntilNextState / slideTime, 0, 1),
|
||||
TippyState.Hiding => Math.Clamp(_secondsUntilNextState / slideTime, 0, 1),
|
||||
_ => 1,
|
||||
};
|
||||
|
||||
var waddle = _currentMessage.WaddleInterval;
|
||||
|
||||
if (_currentMessage == null
|
||||
|| waddle <= 0
|
||||
|| tippy.State == TippyState.Hidden
|
||||
|| tippy.State == TippyState.Speaking
|
||||
|| !EntityManager.TryGetComponent(_entity, out SpriteComponent? sprite))
|
||||
{
|
||||
return new Vector2(screenSize.X - offset * (tippy.DesiredSize.X + Padding), (screenSize.Y - tippy.DesiredSize.Y) / 2);
|
||||
}
|
||||
|
||||
var numSteps = (int) Math.Ceiling(slideTime / waddle);
|
||||
var curStep = (int) Math.Floor(numSteps * offset);
|
||||
var stepSize = (tippy.DesiredSize.X + Padding) / numSteps;
|
||||
|
||||
if (curStep != _previousStep)
|
||||
{
|
||||
_previousStep = curStep;
|
||||
sprite.Rotation = sprite.Rotation > 0
|
||||
? -WaddleRotation
|
||||
: WaddleRotation;
|
||||
|
||||
if (EntityManager.TryGetComponent(_entity, out FootstepModifierComponent? step))
|
||||
{
|
||||
var audioParams = step.FootstepSoundCollection.Params
|
||||
.AddVolume(-7f)
|
||||
.WithVariation(0.1f);
|
||||
_audio.PlayGlobal(step.FootstepSoundCollection, EntityUid.Invalid, audioParams);
|
||||
}
|
||||
}
|
||||
|
||||
return new Vector2(screenSize.X - stepSize * curStep, (screenSize.Y - tippy.DesiredSize.Y) / 2);
|
||||
}
|
||||
|
||||
private void NextState(TippyUI tippy)
|
||||
{
|
||||
SpriteComponent? sprite;
|
||||
switch (tippy.State)
|
||||
{
|
||||
case TippyState.Hidden:
|
||||
if (!_queuedMessages.TryDequeue(out var next))
|
||||
return;
|
||||
|
||||
if (next.Proto != null)
|
||||
{
|
||||
_entity = EntityManager.SpawnEntity(next.Proto, MapCoordinates.Nullspace);
|
||||
tippy.ModifyLayers = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_entity = EntityManager.SpawnEntity(_cfg.GetCVar(CCVars.TippyEntity), MapCoordinates.Nullspace);
|
||||
tippy.ModifyLayers = true;
|
||||
}
|
||||
if (!EntityManager.TryGetComponent(_entity, out sprite))
|
||||
return;
|
||||
if (!EntityManager.HasComponent<PaperVisualsComponent>(_entity))
|
||||
{
|
||||
var paper = EntityManager.AddComponent<PaperVisualsComponent>(_entity);
|
||||
paper.BackgroundImagePath = "/Textures/Interface/Paper/paper_background_default.svg.96dpi.png";
|
||||
paper.BackgroundPatchMargin = new(16f, 16f, 16f, 16f);
|
||||
paper.BackgroundModulate = new(255, 255, 204);
|
||||
paper.FontAccentColor = new(0, 0, 0);
|
||||
}
|
||||
tippy.InitLabel(EntityManager.GetComponentOrNull<PaperVisualsComponent>(_entity), _resCache);
|
||||
|
||||
var scale = sprite.Scale;
|
||||
if (tippy.ModifyLayers)
|
||||
{
|
||||
sprite.Scale = Vector2.One;
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.Scale = new Vector2(3, 3);
|
||||
}
|
||||
tippy.Entity.SetEntity(_entity);
|
||||
tippy.Entity.Scale = scale;
|
||||
|
||||
_currentMessage = next;
|
||||
_secondsUntilNextState = next.SlideTime;
|
||||
tippy.State = TippyState.Revealing;
|
||||
_previousStep = 0;
|
||||
if (tippy.ModifyLayers)
|
||||
{
|
||||
sprite.LayerSetAnimationTime("revealing", 0);
|
||||
sprite.LayerSetVisible("revealing", true);
|
||||
sprite.LayerSetVisible("speaking", false);
|
||||
sprite.LayerSetVisible("hiding", false);
|
||||
}
|
||||
sprite.Rotation = 0;
|
||||
tippy.Label.SetMarkup(_currentMessage.Msg);
|
||||
tippy.Label.Visible = false;
|
||||
tippy.LabelPanel.Visible = false;
|
||||
tippy.Visible = true;
|
||||
sprite.Visible = true;
|
||||
break;
|
||||
|
||||
case TippyState.Revealing:
|
||||
tippy.State = TippyState.Speaking;
|
||||
if (!EntityManager.TryGetComponent(_entity, out sprite))
|
||||
return;
|
||||
sprite.Rotation = 0;
|
||||
_previousStep = 0;
|
||||
if (tippy.ModifyLayers)
|
||||
{
|
||||
sprite.LayerSetAnimationTime("speaking", 0);
|
||||
sprite.LayerSetVisible("revealing", false);
|
||||
sprite.LayerSetVisible("speaking", true);
|
||||
sprite.LayerSetVisible("hiding", false);
|
||||
}
|
||||
tippy.Label.Visible = true;
|
||||
tippy.LabelPanel.Visible = true;
|
||||
tippy.InvalidateArrange();
|
||||
tippy.InvalidateMeasure();
|
||||
if (_currentMessage != null)
|
||||
_secondsUntilNextState = _currentMessage.SpeakTime;
|
||||
|
||||
break;
|
||||
|
||||
case TippyState.Speaking:
|
||||
tippy.State = TippyState.Hiding;
|
||||
if (!EntityManager.TryGetComponent(_entity, out sprite))
|
||||
return;
|
||||
if (tippy.ModifyLayers)
|
||||
{
|
||||
sprite.LayerSetAnimationTime("hiding", 0);
|
||||
sprite.LayerSetVisible("revealing", false);
|
||||
sprite.LayerSetVisible("speaking", false);
|
||||
sprite.LayerSetVisible("hiding", true);
|
||||
}
|
||||
tippy.LabelPanel.Visible = false;
|
||||
if (_currentMessage != null)
|
||||
_secondsUntilNextState = _currentMessage.SlideTime;
|
||||
break;
|
||||
|
||||
default: // finished hiding
|
||||
|
||||
EntityManager.DeleteEntity(_entity);
|
||||
_entity = default;
|
||||
tippy.Visible = false;
|
||||
_currentMessage = null;
|
||||
_secondsUntilNextState = 0;
|
||||
tippy.State = TippyState.Hidden;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnScreenChanged((UIScreen? Old, UIScreen? New) ev)
|
||||
{
|
||||
ev.Old?.RemoveWidget<TippyUI>();
|
||||
_currentMessage = null;
|
||||
EntityManager.DeleteEntity(_entity);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using Content.Client.Tools.UI;
|
||||
using Content.Shared.Tools.Components;
|
||||
|
||||
namespace Content.Client.Tools.Components
|
||||
{
|
||||
[RegisterComponent, Access(typeof(ToolSystem), typeof(WelderStatusControl))]
|
||||
public sealed partial class WelderComponent : SharedWelderComponent
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool UiUpdateNeeded { get; set; }
|
||||
|
||||
[ViewVariables]
|
||||
public float FuelCapacity { get; set; }
|
||||
|
||||
[ViewVariables]
|
||||
public float Fuel { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
using Content.Client.Items;
|
||||
using Content.Client.Tools.Components;
|
||||
using Content.Client.Tools.UI;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Tools.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using SharedToolSystem = Content.Shared.Tools.Systems.SharedToolSystem;
|
||||
|
||||
namespace Content.Client.Tools
|
||||
@@ -15,8 +13,7 @@ namespace Content.Client.Tools
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<WelderComponent, ComponentHandleState>(OnWelderHandleState);
|
||||
Subs.ItemStatus<WelderComponent>(ent => new WelderStatusControl(ent));
|
||||
Subs.ItemStatus<WelderComponent>(ent => new WelderStatusControl(ent, EntityManager, this));
|
||||
Subs.ItemStatus<MultipleToolComponent>(ent => new MultipleToolStatusControl(ent));
|
||||
}
|
||||
|
||||
@@ -42,20 +39,5 @@ namespace Content.Client.Tools
|
||||
sprite.LayerSetSprite(0, current.Sprite);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWelderHandleState(EntityUid uid, WelderComponent welder, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not WelderComponentState state)
|
||||
return;
|
||||
|
||||
welder.FuelCapacity = state.FuelCapacity;
|
||||
welder.Fuel = state.Fuel;
|
||||
welder.UiUpdateNeeded = true;
|
||||
}
|
||||
|
||||
protected override bool IsWelder(EntityUid uid)
|
||||
{
|
||||
return HasComp<WelderComponent>(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +1,45 @@
|
||||
using Content.Client.Items.UI;
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.Tools.Components;
|
||||
using Content.Shared.Item;
|
||||
using Robust.Client.UserInterface;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Tools.Components;
|
||||
using Content.Shared.Tools.Systems;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Timing;
|
||||
using ItemToggleComponent = Content.Shared.Item.ItemToggle.Components.ItemToggleComponent;
|
||||
|
||||
namespace Content.Client.Tools.UI;
|
||||
|
||||
public sealed class WelderStatusControl : Control
|
||||
public sealed class WelderStatusControl : PollingItemStatusControl<WelderStatusControl.Data>
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
|
||||
private readonly WelderComponent _parent;
|
||||
private readonly ItemToggleComponent? _toggleComponent;
|
||||
private readonly Entity<WelderComponent> _parent;
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly SharedToolSystem _toolSystem;
|
||||
private readonly RichTextLabel _label;
|
||||
|
||||
public WelderStatusControl(Entity<WelderComponent> parent)
|
||||
public WelderStatusControl(Entity<WelderComponent> parent, IEntityManager entityManager, SharedToolSystem toolSystem)
|
||||
{
|
||||
_parent = parent;
|
||||
_entMan = IoCManager.Resolve<IEntityManager>();
|
||||
if (_entMan.TryGetComponent<ItemToggleComponent>(parent, out var itemToggle))
|
||||
_toggleComponent = itemToggle;
|
||||
_entityManager = entityManager;
|
||||
_toolSystem = toolSystem;
|
||||
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
|
||||
AddChild(_label);
|
||||
|
||||
UpdateDraw();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
protected override Data PollData()
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (!_parent.UiUpdateNeeded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Update();
|
||||
var (fuel, capacity) = _toolSystem.GetWelderFuelAndCapacity(_parent, _parent.Comp);
|
||||
return new Data(fuel, capacity, _parent.Comp.Enabled);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
protected override void Update(in Data data)
|
||||
{
|
||||
_parent.UiUpdateNeeded = false;
|
||||
|
||||
var fuelCap = _parent.FuelCapacity;
|
||||
var fuel = _parent.Fuel;
|
||||
var lit = false;
|
||||
if (_toggleComponent != null)
|
||||
{
|
||||
lit = _toggleComponent.Activated;
|
||||
}
|
||||
|
||||
_label.SetMarkup(Loc.GetString("welder-component-on-examine-detailed-message",
|
||||
("colorName", fuel < fuelCap / 4f ? "darkorange" : "orange"),
|
||||
("fuelLeft", Math.Round(fuel, 1)),
|
||||
("fuelCapacity", fuelCap),
|
||||
("status", Loc.GetString(lit ? "welder-component-on-examine-welder-lit-message" : "welder-component-on-examine-welder-not-lit-message"))));
|
||||
("colorName", data.Fuel < data.FuelCapacity / 4f ? "darkorange" : "orange"),
|
||||
("fuelLeft", data.Fuel),
|
||||
("fuelCapacity", data.FuelCapacity),
|
||||
("status", Loc.GetString(data.Lit ? "welder-component-on-examine-welder-lit-message" : "welder-component-on-examine-welder-not-lit-message"))));
|
||||
}
|
||||
|
||||
public record struct Data(FixedPoint2 Fuel, FixedPoint2 FuelCapacity, bool Lit);
|
||||
}
|
||||
|
||||
55
Content.Client/UserInterface/Controls/ClipControl.cs
Normal file
55
Content.Client/UserInterface/Controls/ClipControl.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.UserInterface.Controls;
|
||||
|
||||
/// <summary>
|
||||
/// Pretends to child controls that there's infinite space.
|
||||
/// This can be used to make something like a <see cref="RichTextLabel"/> clip instead of wrapping.
|
||||
/// </summary>
|
||||
public sealed class ClipControl : Control
|
||||
{
|
||||
private bool _clipHorizontal = true;
|
||||
private bool _clipVertical = true;
|
||||
|
||||
public bool ClipHorizontal
|
||||
{
|
||||
get => _clipHorizontal;
|
||||
set
|
||||
{
|
||||
_clipHorizontal = value;
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
|
||||
public bool ClipVertical
|
||||
{
|
||||
get => _clipVertical;
|
||||
set
|
||||
{
|
||||
_clipVertical = value;
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
|
||||
protected override Vector2 MeasureOverride(Vector2 availableSize)
|
||||
{
|
||||
if (ClipHorizontal)
|
||||
availableSize = availableSize with { X = float.PositiveInfinity };
|
||||
if (ClipVertical)
|
||||
availableSize = availableSize with { Y = float.PositiveInfinity };
|
||||
|
||||
return base.MeasureOverride(availableSize);
|
||||
}
|
||||
|
||||
protected override Vector2 ArrangeOverride(Vector2 finalSize)
|
||||
{
|
||||
foreach (var child in Children)
|
||||
{
|
||||
child.Arrange(UIBox2.FromDimensions(Vector2.Zero, child.DesiredSize));
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,7 @@ namespace Content.Client.UserInterface.Controls
|
||||
var stretch = _cfg.GetCVar(CCVars.ViewportStretch);
|
||||
var renderScaleUp = _cfg.GetCVar(CCVars.ViewportScaleRender);
|
||||
var fixedFactor = _cfg.GetCVar(CCVars.ViewportFixedScaleFactor);
|
||||
var verticalFit = _cfg.GetCVar(CCVars.ViewportVerticalFit);
|
||||
|
||||
if (stretch)
|
||||
{
|
||||
@@ -60,6 +61,7 @@ namespace Content.Client.UserInterface.Controls
|
||||
// Did not find a snap, enable stretching.
|
||||
Viewport.FixedStretchSize = null;
|
||||
Viewport.StretchMode = ScalingViewportStretchMode.Bilinear;
|
||||
Viewport.IgnoreDimension = verticalFit ? ScalingViewportIgnoreDimension.Horizontal : ScalingViewportIgnoreDimension.None;
|
||||
|
||||
if (renderScaleUp)
|
||||
{
|
||||
@@ -104,6 +106,8 @@ namespace Content.Client.UserInterface.Controls
|
||||
// where we are clipping the viewport to make it fit.
|
||||
var cfgToleranceClip = _cfg.GetCVar(CCVars.ViewportSnapToleranceClip);
|
||||
|
||||
var cfgVerticalFit = _cfg.GetCVar(CCVars.ViewportVerticalFit);
|
||||
|
||||
// Calculate if the viewport, when rendered at an integer scale,
|
||||
// is close enough to the control size to enable "snapping" to NN,
|
||||
// potentially cutting a tiny bit off/leaving a margin.
|
||||
@@ -123,7 +127,8 @@ namespace Content.Client.UserInterface.Controls
|
||||
// The rule for which snap fits is that at LEAST one axis needs to be in the tolerance size wise.
|
||||
// One axis MAY be larger but not smaller than tolerance.
|
||||
// Obviously if it's too small it's bad, and if it's too big on both axis we should stretch up.
|
||||
if (Fits(dx) && Fits(dy) || Fits(dx) && Larger(dy) || Larger(dx) && Fits(dy))
|
||||
// Additionally, if the viewport's supposed to be vertically fit, then the horizontal scale should just be ignored where appropriate.
|
||||
if ((Fits(dx) || cfgVerticalFit) && Fits(dy) || !cfgVerticalFit && Fits(dx) && Larger(dy) || Larger(dx) && Fits(dy))
|
||||
{
|
||||
// Found snap that fits.
|
||||
return i;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user