Compare commits
799 Commits
ed-12-11-2
...
ed-13-01-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8890a72257 | ||
|
|
03ba5a2c3b | ||
|
|
78d90c30b8 | ||
|
|
f90b090ed8 | ||
|
|
255dad8b55 | ||
|
|
b63b0f7e67 | ||
|
|
c624194486 | ||
|
|
2b4af1892d | ||
|
|
46cfaad625 | ||
|
|
f5edf03bf6 | ||
|
|
b5f70616c3 | ||
|
|
e11d8a1c1a | ||
|
|
3cce07be5a | ||
|
|
93c4e6068f | ||
|
|
89b9a5c1fe | ||
|
|
a6847c08e8 | ||
|
|
485ddaa739 | ||
|
|
f2ef46d90e | ||
|
|
61897eae4f | ||
|
|
4a2c3c9d59 | ||
|
|
3d0f303930 | ||
|
|
0af7a7ec64 | ||
|
|
c41927cdf2 | ||
|
|
c3976dd08d | ||
|
|
1af112aeb2 | ||
|
|
9ab34417f4 | ||
|
|
005a6012be | ||
|
|
f1fc16c6f0 | ||
|
|
2b0fa46857 | ||
|
|
4366e940cb | ||
|
|
76d828e9fd | ||
|
|
00ecf1f4b6 | ||
|
|
815c031a16 | ||
|
|
8248fcb07e | ||
|
|
228a7226ff | ||
|
|
5c21ec2774 | ||
|
|
35eb0c48c1 | ||
|
|
676b85b22b | ||
|
|
5f2583fab0 | ||
|
|
8892d3913d | ||
|
|
71e6bd4f24 | ||
|
|
b647583b7b | ||
|
|
ed75beb0fb | ||
|
|
fe43e51930 | ||
|
|
9ea8111d9e | ||
|
|
639db84a0b | ||
|
|
9f798b4f6c | ||
|
|
be3064b99c | ||
|
|
9a8f66727f | ||
|
|
945e74b690 | ||
|
|
fc1e9d2638 | ||
|
|
36d02fdf6e | ||
|
|
a6a4a8da3f | ||
|
|
be6c2754f9 | ||
|
|
86e74a0fc2 | ||
|
|
a75ac73c70 | ||
|
|
4011de828f | ||
|
|
5c9ea3f428 | ||
|
|
de6ddb52be | ||
|
|
3b53c1abbb | ||
|
|
4acfec8e36 | ||
|
|
608b433da4 | ||
|
|
c2ffc25970 | ||
|
|
072b973f3b | ||
|
|
da8bb996b1 | ||
|
|
d6a9fe1c84 | ||
|
|
a7ca552dfc | ||
|
|
e07609b3b3 | ||
|
|
2c41ed0939 | ||
|
|
4bb68c4bac | ||
|
|
bfb256f0ba | ||
|
|
9f9553b526 | ||
|
|
5d0d37161d | ||
|
|
3f84b04e4c | ||
|
|
4155440a1b | ||
|
|
6c465153ab | ||
|
|
01980cb0bf | ||
|
|
1b478d46f5 | ||
|
|
616d34102b | ||
|
|
b24ab38992 | ||
|
|
a21d8099a9 | ||
|
|
d92ed75703 | ||
|
|
97dd5513f5 | ||
|
|
9acce42f92 | ||
|
|
860052c383 | ||
|
|
ca0596e6f1 | ||
|
|
34e9979dc5 | ||
|
|
fab5dd178d | ||
|
|
bf727b0ab6 | ||
|
|
52d39aac46 | ||
|
|
4b34dd672e | ||
|
|
392046fc70 | ||
|
|
7803254b6f | ||
|
|
4064d554d6 | ||
|
|
94186c4a6b | ||
|
|
c83532ec3f | ||
|
|
1cbc8c1dcc | ||
|
|
31aec7385d | ||
|
|
4e9862d01f | ||
|
|
8fcfca689f | ||
|
|
1c33073af4 | ||
|
|
39b2ce8cef | ||
|
|
285e9349b6 | ||
|
|
2c4be6be00 | ||
|
|
843a5de809 | ||
|
|
2dd99b5669 | ||
|
|
5d8e916cb2 | ||
|
|
030f97b2be | ||
|
|
b82605b185 | ||
|
|
f98192daff | ||
|
|
661e96831f | ||
|
|
dfc0e0199a | ||
|
|
38bd2e5c1c | ||
|
|
4ddd7d3c5f | ||
|
|
c873f90f6b | ||
|
|
f28c8127e2 | ||
|
|
1450c4cade | ||
|
|
2ff8238030 | ||
|
|
4173d96d83 | ||
|
|
6331379213 | ||
|
|
3358801b42 | ||
|
|
58181c3412 | ||
|
|
1abe9db99c | ||
|
|
7baa1ae070 | ||
|
|
b3551fb000 | ||
|
|
06e2bba8ab | ||
|
|
b011dbb61e | ||
|
|
9f4aa1ebe0 | ||
|
|
1f859167fd | ||
|
|
12c5c7e91b | ||
|
|
00f12e092a | ||
|
|
03a54e90f5 | ||
|
|
9a4f9561e6 | ||
|
|
3a917bcb56 | ||
|
|
50cc90fb41 | ||
|
|
919fdc0798 | ||
|
|
bbceffac0e | ||
|
|
e4974cba25 | ||
|
|
a89d254425 | ||
|
|
a0a405768a | ||
|
|
0e850ab370 | ||
|
|
5a07413ef8 | ||
|
|
1419b8868e | ||
|
|
f19377d6cb | ||
|
|
4c0a1348bd | ||
|
|
f4fd64756f | ||
|
|
34b906640f | ||
|
|
a2912e3922 | ||
|
|
864c2257b0 | ||
|
|
39600f9516 | ||
|
|
7f2907a93a | ||
|
|
0462993153 | ||
|
|
ee72cdacdc | ||
|
|
fe69ef63f0 | ||
|
|
ae5c1f5995 | ||
|
|
7f6d55a1b6 | ||
|
|
209c1769ee | ||
|
|
25980d8888 | ||
|
|
efa76be390 | ||
|
|
128721ee4e | ||
|
|
ef45c16667 | ||
|
|
2581c24bfe | ||
|
|
043510fb51 | ||
|
|
66ea113eda | ||
|
|
c1b229aea5 | ||
|
|
28fa7cedc7 | ||
|
|
943be3e75c | ||
|
|
e7d5ab67ec | ||
|
|
e70a3b8a21 | ||
|
|
9de569d973 | ||
|
|
f7bf694707 | ||
|
|
383ecdd70f | ||
|
|
c33d3350bd | ||
|
|
8f36d7d1c8 | ||
|
|
fa6de41ae9 | ||
|
|
6f42ebf206 | ||
|
|
18de5ea986 | ||
|
|
52117fdc13 | ||
|
|
8a0edad886 | ||
|
|
4fe69f2383 | ||
|
|
345adcd5d0 | ||
|
|
18fe8b9df0 | ||
|
|
b649517a17 | ||
|
|
c1558f689f | ||
|
|
97a2e0deef | ||
|
|
659bc8f2de | ||
|
|
868699a219 | ||
|
|
d01839a990 | ||
|
|
8359541706 | ||
|
|
337ef6c5aa | ||
|
|
e7294bdf4f | ||
|
|
6b99493e80 | ||
|
|
87f39af1cf | ||
|
|
94594debf3 | ||
|
|
6c925d2c82 | ||
|
|
1c0db473bc | ||
|
|
9f0a8aac7d | ||
|
|
50fddd0804 | ||
|
|
42f7279f52 | ||
|
|
8b0b822207 | ||
|
|
91b85be1a4 | ||
|
|
122e307694 | ||
|
|
ecc2edea6d | ||
|
|
908e476643 | ||
|
|
aaec1e684e | ||
|
|
5863cfc78a | ||
|
|
e7330ece9c | ||
|
|
27cb17f865 | ||
|
|
a9cf54aca8 | ||
|
|
984b29082d | ||
|
|
a55d26eb8b | ||
|
|
8fbaffe2e7 | ||
|
|
77a4cda8bc | ||
|
|
59e955a559 | ||
|
|
3fe576b4c9 | ||
|
|
fb6e85e87e | ||
|
|
616ddc1776 | ||
|
|
7780b867ac | ||
|
|
bbf5369f14 | ||
|
|
332f870304 | ||
|
|
0fc2cca10c | ||
|
|
ab8447956c | ||
|
|
32164c2ab8 | ||
|
|
0d31b8c37a | ||
|
|
8f2d16aabf | ||
|
|
67e5cc2104 | ||
|
|
a77fcdec76 | ||
|
|
d90f6ce086 | ||
|
|
038ba39b97 | ||
|
|
27e59d35fb | ||
|
|
f4765260cb | ||
|
|
36c676741f | ||
|
|
af7e552d3c | ||
|
|
9eda08cd12 | ||
|
|
74e9576e66 | ||
|
|
001b0edc7e | ||
|
|
db69ae67fe | ||
|
|
2ccd471388 | ||
|
|
74f873a9f7 | ||
|
|
17e3ef32b7 | ||
|
|
c35505c6b4 | ||
|
|
95328dab68 | ||
|
|
046dde7717 | ||
|
|
1e80dd2891 | ||
|
|
782a2978f0 | ||
|
|
d8afcdcdf0 | ||
|
|
1368092d43 | ||
|
|
881e4d3432 | ||
|
|
2a6800f354 | ||
|
|
01c1d8f824 | ||
|
|
852b3e9bb6 | ||
|
|
1f42413416 | ||
|
|
d5323332a6 | ||
|
|
cf738857fe | ||
|
|
196782774b | ||
|
|
386e431ea7 | ||
|
|
2635888b6a | ||
|
|
cd23805750 | ||
|
|
612f8bedab | ||
|
|
a4d6f09a4f | ||
|
|
1266b05b02 | ||
|
|
51a45ae640 | ||
|
|
e0a1db6e45 | ||
|
|
5e8db16660 | ||
|
|
46d288cc39 | ||
|
|
a248c176ae | ||
|
|
862df73ff2 | ||
|
|
5958801182 | ||
|
|
57a795261f | ||
|
|
e102b5011e | ||
|
|
35c45e70f6 | ||
|
|
f655f74b03 | ||
|
|
102d4fbc95 | ||
|
|
df7b9d1e04 | ||
|
|
7d82d2d5da | ||
|
|
a3fc690236 | ||
|
|
58269bb65e | ||
|
|
744b105fc6 | ||
|
|
06071a5d8a | ||
|
|
d7d0977eb6 | ||
|
|
4cea96f6eb | ||
|
|
e30c5848c5 | ||
|
|
c9d2b8fb2b | ||
|
|
2c00abbcae | ||
|
|
7eb714d38f | ||
|
|
e1f67be2a8 | ||
|
|
e704f4e9a8 | ||
|
|
b46f487503 | ||
|
|
3f8757dec9 | ||
|
|
9329fcf32e | ||
|
|
115b3e0519 | ||
|
|
eb76a9ef6a | ||
|
|
93a49ca398 | ||
|
|
9d7846ed72 | ||
|
|
18322ec76f | ||
|
|
b959a64f2c | ||
|
|
48f62da2dc | ||
|
|
2b3cc9e868 | ||
|
|
842bbeb85f | ||
|
|
c42b77d0c9 | ||
|
|
6c8b0a07f3 | ||
|
|
67d444feb0 | ||
|
|
ce9fc82382 | ||
|
|
5ba868af79 | ||
|
|
1a8e469c58 | ||
|
|
976cae9c78 | ||
|
|
fc738b6690 | ||
|
|
c28f4fdc7f | ||
|
|
89522e7144 | ||
|
|
af918c1253 | ||
|
|
be15a4097c | ||
|
|
cd736e4fd4 | ||
|
|
576bbe91d0 | ||
|
|
e4b60b31c0 | ||
|
|
7b681b66ae | ||
|
|
2b487baea0 | ||
|
|
7ed32f2c7d | ||
|
|
ee10fc246f | ||
|
|
0e4728b6d4 | ||
|
|
99786342fb | ||
|
|
b1fca73f91 | ||
|
|
22b0f46890 | ||
|
|
bec8d7a7d4 | ||
|
|
dd96f77faa | ||
|
|
9cb46d9343 | ||
|
|
35b1c04494 | ||
|
|
60bf48ccce | ||
|
|
3844352fee | ||
|
|
8bb4c82890 | ||
|
|
a7957ddf65 | ||
|
|
5ab8e467a5 | ||
|
|
38c35d8315 | ||
|
|
7fd89806e2 | ||
|
|
1510e892f0 | ||
|
|
8f2b19e103 | ||
|
|
a7e1adf395 | ||
|
|
d57e731635 | ||
|
|
670f1a8e28 | ||
|
|
ede8a1d377 | ||
|
|
c4556fcbfe | ||
|
|
34bfc59b18 | ||
|
|
98ad7a872f | ||
|
|
f9da25881d | ||
|
|
5384adb2b3 | ||
|
|
0c4dd9d1b9 | ||
|
|
8472603b76 | ||
|
|
c2b7c5146d | ||
|
|
61454e351c | ||
|
|
73d5129644 | ||
|
|
4661e9ef86 | ||
|
|
299b13f21b | ||
|
|
3d53e748fa | ||
|
|
337952dcb4 | ||
|
|
dc5333ee18 | ||
|
|
0662299120 | ||
|
|
c85642d920 | ||
|
|
20be8298b7 | ||
|
|
80be633aa1 | ||
|
|
264ef809bd | ||
|
|
ccc97f0e70 | ||
|
|
15017b7c4f | ||
|
|
2d54561a31 | ||
|
|
7f5c3cf751 | ||
|
|
cf73cd5ddc | ||
|
|
1a96a2bcc8 | ||
|
|
0d45dfada9 | ||
|
|
44daf85566 | ||
|
|
9365e3a99b | ||
|
|
417668b88b | ||
|
|
4beb1016cc | ||
|
|
52a0f44aa1 | ||
|
|
3300ff2a06 | ||
|
|
b66fd98514 | ||
|
|
e244472546 | ||
|
|
de19418126 | ||
|
|
6add781c4a | ||
|
|
02cbeee6dc | ||
|
|
d6d95f1a7a | ||
|
|
7d410d6094 | ||
|
|
2d6e52b21c | ||
|
|
623a35b3fe | ||
|
|
94090f7403 | ||
|
|
274c3ab0b9 | ||
|
|
38c8ffb9b5 | ||
|
|
52a886947f | ||
|
|
d6ba7598bd | ||
|
|
207c849e2d | ||
|
|
cd1177d9ba | ||
|
|
3e0b93d071 | ||
|
|
64df860b50 | ||
|
|
0e6ec2e1af | ||
|
|
8f3973b2eb | ||
|
|
97ece026d5 | ||
|
|
dc8b859916 | ||
|
|
ce34c6f2a0 | ||
|
|
8252418fdc | ||
|
|
ce672acf91 | ||
|
|
094fbc445f | ||
|
|
319cd18ee8 | ||
|
|
6f51aa4b15 | ||
|
|
59bce41390 | ||
|
|
bcf2b9afeb | ||
|
|
90c8776a60 | ||
|
|
da68ae94c1 | ||
|
|
304e1a268a | ||
|
|
fbfcccbe16 | ||
|
|
82528dce37 | ||
|
|
e6a6900719 | ||
|
|
152cf3388b | ||
|
|
2e26ca786d | ||
|
|
fc8f7fb0ba | ||
|
|
c10a72be39 | ||
|
|
7ffd8012f6 | ||
|
|
cf202e805d | ||
|
|
87182635b6 | ||
|
|
0e88b6dc6f | ||
|
|
f6630e1ec9 | ||
|
|
2c2a435dbd | ||
|
|
bfd005a37f | ||
|
|
e50c98c618 | ||
|
|
029b0374e9 | ||
|
|
c1491e91b8 | ||
|
|
2e31eb32cb | ||
|
|
f0829a6652 | ||
|
|
e4e84aea74 | ||
|
|
b800d98260 | ||
|
|
cc804bf316 | ||
|
|
638a1dc489 | ||
|
|
7f966ab9b1 | ||
|
|
560fd8dd77 | ||
|
|
b3841e8414 | ||
|
|
7e6e4709c4 | ||
|
|
d4067bcaab | ||
|
|
c6ac192dfb | ||
|
|
237324e268 | ||
|
|
bf312f2306 | ||
|
|
5fdf03c216 | ||
|
|
2d405c9652 | ||
|
|
3984f0aa0a | ||
|
|
a46323d9f9 | ||
|
|
061b1b8adb | ||
|
|
44ef60a642 | ||
|
|
efb35d7953 | ||
|
|
720fccf3f3 | ||
|
|
ab36b15080 | ||
|
|
766389a66b | ||
|
|
fb76cd952e | ||
|
|
3d984603f1 | ||
|
|
450bf813b8 | ||
|
|
973f42b3b2 | ||
|
|
021c9832ef | ||
|
|
bb43b37fd8 | ||
|
|
86791583f9 | ||
|
|
08db47cb28 | ||
|
|
b45c9d5129 | ||
|
|
e9ef00f475 | ||
|
|
54a9dce68d | ||
|
|
17a224c86a | ||
|
|
9236e9e5b9 | ||
|
|
6c86d7b32b | ||
|
|
373d3a892b | ||
|
|
f99331aa00 | ||
|
|
87d6b0b79b | ||
|
|
bbdadd7144 | ||
|
|
3ad227a904 | ||
|
|
422d1a9d32 | ||
|
|
2e340578ff | ||
|
|
360f6982f4 | ||
|
|
93bdd813c4 | ||
|
|
109edeb4b5 | ||
|
|
b53c8ea60e | ||
|
|
4a21ed87db | ||
|
|
6463e7e08e | ||
|
|
a790955b76 | ||
|
|
f6813e39c7 | ||
|
|
a206acc220 | ||
|
|
de613e45f8 | ||
|
|
ac65c5a55d | ||
|
|
9704ed7a00 | ||
|
|
56fd7cbb6f | ||
|
|
1bc65624c8 | ||
|
|
1b6c9e866b | ||
|
|
8ea388b309 | ||
|
|
6187675c27 | ||
|
|
c861c56a69 | ||
|
|
eb9540364e | ||
|
|
ab7221efad | ||
|
|
dfc3562bfc | ||
|
|
41d2cf166d | ||
|
|
470c869ce2 | ||
|
|
d5225d1f46 | ||
|
|
a69fc39fc0 | ||
|
|
f27fa1ed30 | ||
|
|
e9eca826d8 | ||
|
|
f0e9de8489 | ||
|
|
b8c8f7d0f8 | ||
|
|
b8466d8321 | ||
|
|
49724a9b9d | ||
|
|
ae576abe1f | ||
|
|
45cf4ec340 | ||
|
|
ea7f5433ac | ||
|
|
32f48d974f | ||
|
|
14103e7a86 | ||
|
|
da9b2e6a10 | ||
|
|
f53e3ec3c1 | ||
|
|
9a898bb98e | ||
|
|
86a3d1636f | ||
|
|
ed2cd23309 | ||
|
|
3c6c5ab6c9 | ||
|
|
d642ee7707 | ||
|
|
8057fad4d3 | ||
|
|
cae49ae0d2 | ||
|
|
191112a8a8 | ||
|
|
406f22de3f | ||
|
|
b72c2bfb7b | ||
|
|
dbc2fd689a | ||
|
|
897a9cf683 | ||
|
|
b37113f7f2 | ||
|
|
42127c4682 | ||
|
|
91b9d4a7f0 | ||
|
|
e436a50c36 | ||
|
|
84df2b857e | ||
|
|
aa80a88cc4 | ||
|
|
2229a6a04b | ||
|
|
f706170ee1 | ||
|
|
ef89d5cc21 | ||
|
|
e958c0c9b0 | ||
|
|
11dbf50ed6 | ||
|
|
8522ffe8ce | ||
|
|
fab9993a3b | ||
|
|
4cecf99e65 | ||
|
|
855547a2d4 | ||
|
|
45af6a13fc | ||
|
|
11dae2ff93 | ||
|
|
ae01ebda0e | ||
|
|
bde85858a3 | ||
|
|
c3786a56dc | ||
|
|
bdf4a46edf | ||
|
|
a42bacd3a9 | ||
|
|
1e93e12330 | ||
|
|
0a587c9ccc | ||
|
|
7feafcbe95 | ||
|
|
5409815be9 | ||
|
|
d8ecf12fca | ||
|
|
616907009d | ||
|
|
fff97fa819 | ||
|
|
6bc205484f | ||
|
|
a28adf4ae4 | ||
|
|
677e5194d6 | ||
|
|
de516905f0 | ||
|
|
306277afe0 | ||
|
|
08bfb43feb | ||
|
|
5a9a2d463b | ||
|
|
09ca45a621 | ||
|
|
b4ec946bd9 | ||
|
|
646d41d3a7 | ||
|
|
a6c7bc5993 | ||
|
|
66fe15f8c7 | ||
|
|
94ac0b1399 | ||
|
|
a3edf04dd3 | ||
|
|
403528cbf3 | ||
|
|
38c70d6c9b | ||
|
|
5a751a820a | ||
|
|
3758715bdc | ||
|
|
8ad951183c | ||
|
|
548433baca | ||
|
|
96fc06a443 | ||
|
|
4f703ae9ce | ||
|
|
11ee2f9a37 | ||
|
|
f5930bb566 | ||
|
|
0f0b141f21 | ||
|
|
75a096b6bd | ||
|
|
beeffdb5e1 | ||
|
|
5c0a32b8b8 | ||
|
|
c02a027cf1 | ||
|
|
e96e80bc95 | ||
|
|
4fcfab972d | ||
|
|
4abd793ef9 | ||
|
|
f23b6522b2 | ||
|
|
693e5f1fad | ||
|
|
cb246f5d7d | ||
|
|
f9533a637a | ||
|
|
98caf50626 | ||
|
|
a9be561ea7 | ||
|
|
4f3ac3ea68 | ||
|
|
59b09383ca | ||
|
|
a7003acd77 | ||
|
|
a818c2a134 | ||
|
|
8acbf87d8f | ||
|
|
f5d0e955e3 | ||
|
|
b8b33b97af | ||
|
|
44db676b24 | ||
|
|
1b3672e095 | ||
|
|
75acce0d62 | ||
|
|
a13a4f7a99 | ||
|
|
b177a1d019 | ||
|
|
0ec23362fe | ||
|
|
7e8e2c7212 | ||
|
|
e98383d572 | ||
|
|
1fa1975e60 | ||
|
|
9c666457c2 | ||
|
|
6e53cd98a4 | ||
|
|
35e2c641c1 | ||
|
|
fdf3df9fbd | ||
|
|
eebf06d9d6 | ||
|
|
ed1ae96fa2 | ||
|
|
89392e2424 | ||
|
|
efa28fc650 | ||
|
|
2002de9bb0 | ||
|
|
7f5bae99bb | ||
|
|
c4e2eb9d02 | ||
|
|
ed638c94a8 | ||
|
|
e79b046c4a | ||
|
|
ec278dc98f | ||
|
|
42ee90e53e | ||
|
|
a949cf33e9 | ||
|
|
895648aa2c | ||
|
|
0e2e6a001f | ||
|
|
437a586906 | ||
|
|
10ee37a47c | ||
|
|
909235cdbe | ||
|
|
dffece473a | ||
|
|
68eaf6ff25 | ||
|
|
96d2fe477d | ||
|
|
79ff990ddf | ||
|
|
d67f7619c4 | ||
|
|
647db6aa87 | ||
|
|
b0fd9d5a55 | ||
|
|
824efd4b25 | ||
|
|
97be261631 | ||
|
|
31d5a66866 | ||
|
|
e290588624 | ||
|
|
22987fc77f | ||
|
|
c7f83523ef | ||
|
|
f484118e2d | ||
|
|
96b9d1a714 | ||
|
|
d9c677e91b | ||
|
|
0991b6bbe8 | ||
|
|
5cd92431b9 | ||
|
|
476f90df09 | ||
|
|
01d6df3d0a | ||
|
|
a68c6cb29e | ||
|
|
ef6f23e4b8 | ||
|
|
06308961ed | ||
|
|
bdab41248d | ||
|
|
f071bf65e0 | ||
|
|
f5b63b8393 | ||
|
|
d9a5ffbef4 | ||
|
|
47f94d1139 | ||
|
|
2c82a2dfc0 | ||
|
|
7077b930f2 | ||
|
|
4f659b9d6d | ||
|
|
11963e50b1 | ||
|
|
6bcfe6fb3d | ||
|
|
1f5eb6a08b | ||
|
|
c4e8751ee6 | ||
|
|
862c2ac858 | ||
|
|
4426bbe784 | ||
|
|
e7e1d96051 | ||
|
|
3173a3461e | ||
|
|
7d82a7bf5c | ||
|
|
abdefbd622 | ||
|
|
da4fa9bea9 | ||
|
|
09d0565413 | ||
|
|
6683dc9037 | ||
|
|
089f190266 | ||
|
|
7a0fb9fc15 | ||
|
|
3a6ae97566 | ||
|
|
4fc7a4c56e | ||
|
|
cf96679d0b | ||
|
|
97ce69fef6 | ||
|
|
e3b611085b | ||
|
|
465170f1e1 | ||
|
|
b4e0362ed4 | ||
|
|
f75be07a05 | ||
|
|
606d44bcb0 | ||
|
|
dfda557d4b | ||
|
|
530a741b7b | ||
|
|
d205d17ba3 | ||
|
|
2c9f2279d6 | ||
|
|
3b9365160c | ||
|
|
9a5c49b961 | ||
|
|
53ce812356 | ||
|
|
0437ec6d56 | ||
|
|
1bebb3390c | ||
|
|
669bc148f9 | ||
|
|
815e37e512 | ||
|
|
ace158df0e | ||
|
|
0f30639cf2 | ||
|
|
5dbea42751 | ||
|
|
c86201308a | ||
|
|
75ec546550 | ||
|
|
e9c66cfe98 | ||
|
|
4f754b814b | ||
|
|
755f322e29 | ||
|
|
5e54536141 | ||
|
|
f2b7743bdd | ||
|
|
b91c977f7a | ||
|
|
fa3a04a527 | ||
|
|
9643598c70 | ||
|
|
51d2b51ad0 | ||
|
|
6f7066eda8 | ||
|
|
c978eefedb | ||
|
|
8cf279e200 | ||
|
|
99b4604d50 | ||
|
|
52c1708117 | ||
|
|
bd2d0ee5e5 | ||
|
|
21074bd98d | ||
|
|
e9f6a02f18 | ||
|
|
af3593a3b7 | ||
|
|
cc3712be0c | ||
|
|
ef51700094 | ||
|
|
8b154899b5 | ||
|
|
8776c71e59 | ||
|
|
70e3650246 | ||
|
|
a1966d8671 | ||
|
|
806e1e46cb | ||
|
|
4f3db43696 | ||
|
|
36aceb178c | ||
|
|
37958378cb | ||
|
|
1136200dc8 | ||
|
|
bbdbad5691 | ||
|
|
991bbc2d4f | ||
|
|
b9c2b0c41b | ||
|
|
a138fede2b | ||
|
|
197d9e68dc | ||
|
|
21979a7b5f | ||
|
|
63f2c8491c | ||
|
|
9b7200607b | ||
|
|
1c8992ffbe | ||
|
|
2801ebea8d | ||
|
|
9396ce302a | ||
|
|
33b780fd1f | ||
|
|
d939e991bb | ||
|
|
d1c66d71e7 | ||
|
|
675e42df24 | ||
|
|
287a9a07de | ||
|
|
40044203e6 | ||
|
|
1e368ae300 | ||
|
|
b9685850fa | ||
|
|
41b84fc29d | ||
|
|
6ed2ab9e85 | ||
|
|
fea5769cc5 | ||
|
|
84338686a3 | ||
|
|
80e148c265 | ||
|
|
667daa168f | ||
|
|
012855475e | ||
|
|
261c18f764 | ||
|
|
6a1d631b14 | ||
|
|
9ee47b927b | ||
|
|
03723afee6 | ||
|
|
d13765fa5c | ||
|
|
8257dc87de | ||
|
|
fc0d85b487 | ||
|
|
379fb4cb6a | ||
|
|
6c7336b0a8 | ||
|
|
18971f2705 | ||
|
|
5b0761dab2 | ||
|
|
b15d5a7f27 | ||
|
|
e72d63e8a9 | ||
|
|
d588409909 | ||
|
|
ed865ae973 | ||
|
|
3972a25258 | ||
|
|
69c0f8773f | ||
|
|
190d965c52 | ||
|
|
a399c1ec7c | ||
|
|
33516b77ed | ||
|
|
03843734e4 | ||
|
|
b0c5023fda | ||
|
|
b35d2902d4 | ||
|
|
d7ed5b4386 | ||
|
|
581a4d14fc | ||
|
|
d863e3c5ca | ||
|
|
00aaffbc00 | ||
|
|
3aff20173c | ||
|
|
9dc90a258e | ||
|
|
4b633fde9c | ||
|
|
963009a440 | ||
|
|
08de5aeae1 | ||
|
|
d0114d9738 | ||
|
|
7169788e16 | ||
|
|
36390b23d1 | ||
|
|
c6fe5682c2 | ||
|
|
1abc60b995 | ||
|
|
834b6ebaaa | ||
|
|
eaa6017ada | ||
|
|
e75a71d7d3 | ||
|
|
0cc1f32b3b | ||
|
|
a4e7ad008c | ||
|
|
eb1168a831 | ||
|
|
4c8a235e61 | ||
|
|
f226f28e52 | ||
|
|
bad25e3397 | ||
|
|
964ef33fc7 | ||
|
|
c3fa1b45d0 | ||
|
|
602541b548 | ||
|
|
30018a3ab1 | ||
|
|
daf674e37b | ||
|
|
1f22dfda7b |
37
.github/CODEOWNERS
vendored
37
.github/CODEOWNERS
vendored
@@ -2,49 +2,30 @@
|
||||
|
||||
# Sorting by path instead of by who added it one day :(
|
||||
# this isn't how codeowners rules work pls read the first comment instead of trying to force a sorting order
|
||||
/Resources/ConfigPresets/WizardsDen/ @Chief-Engineer
|
||||
|
||||
# Moony's Gargantuan List Of Things She Cares About, or MGLOTSCA for short.
|
||||
# You need to add your name to these entries, not make a new one, if you care about them.
|
||||
/Content.*/Toolshed/ @moonheart08
|
||||
**/Toolshed/** @moonheart08
|
||||
*Command.cs @moonheart08
|
||||
/Content.*/Administration/ @moonheart08 @DrSmugleaf @Chief-Engineer
|
||||
/Content.*/Station/ @moonheart08
|
||||
/Content.*/Maps/ @moonheart08
|
||||
/Content.*/GameTicking/ @moonheart08 @EmoGarbage404
|
||||
/Resources/ServerInfo/ @moonheart08 @Chief-Engineer
|
||||
/Resources/ServerInfo/Guidebook/ @moonheart08 @EmoGarbage404
|
||||
/Resources/ServerInfo/Guidebook/ServerRules/ @Chief-Engineer
|
||||
/Resources/engineCommandPerms.yml @moonheart08 @Chief-Engineer
|
||||
/Resources/clientCommandPerms.yml @moonheart08 @Chief-Engineer
|
||||
/Resources/ConfigPresets/WizardsDen/ @nikthechampiongr @crazybrain23
|
||||
/Content.*/Administration/ @DrSmugleaf @nikthechampiongr @crazybrain23
|
||||
/Resources/ServerInfo/ @nikthechampiongr @crazybrain23
|
||||
/Resources/ServerInfo/Guidebook/ServerRules/ @nikthechampiongr @crazybrain23
|
||||
|
||||
/Resources/Prototypes/Maps/** @Emisse
|
||||
|
||||
/Resources/Prototypes/Body/ @DrSmugleaf # suffering
|
||||
/Resources/Prototypes/Entities/Mobs/Player/ @DrSmugleaf
|
||||
/Resources/Prototypes/Entities/Mobs/Species/ @DrSmugleaf
|
||||
/Resources/Prototypes/Guidebook/rules.yml @Chief-Engineer
|
||||
/Resources/Prototypes/Guidebook/rules.yml @nikthechampiongr @crazybrain23
|
||||
/Content.*/Body/ @DrSmugleaf
|
||||
/Content.YAMLLinter @DrSmugleaf
|
||||
/Content.Shared/Damage/ @DrSmugleaf
|
||||
|
||||
/Content.*/Anomaly/ @EmoGarbage404 @TheShuEd
|
||||
/Content.*/Lathe/ @EmoGarbage404
|
||||
/Content.*/Materials/ @EmoGarbage404
|
||||
/Content.*/Mech/ @EmoGarbage404
|
||||
/Content.*/Research/ @EmoGarbage404
|
||||
/Content.*/Stack/ @EmoGarbage404
|
||||
/Content.*/Xenoarchaeology/ @EmoGarbage404
|
||||
/Content.*/Zombies/ @EmoGarbage404
|
||||
/Resources/Prototypes/Entities/Structures/Specific/anomalies.yml @EmoGarbage404 @TheShuEd
|
||||
/Resources/Prototypes/Research/ @EmoGarbage404
|
||||
/Content.*/Anomaly/ @TheShuEd
|
||||
/Resources/Prototypes/Entities/Structures/Specific/anomalies.yml @TheShuEd
|
||||
|
||||
/Content.*/Forensics/ @ficcialfaint
|
||||
|
||||
# SKREEEE
|
||||
/Content.*.Database/ @PJB3005 @DrSmugleaf
|
||||
/Content.Shared.Database/Log*.cs @PJB3005 @DrSmugleaf @Chief-Engineer
|
||||
/Content.Shared.Database/Log*.cs @PJB3005 @DrSmugleaf @nikthechampiongr @crazybrain23
|
||||
/Pow3r/ @PJB3005
|
||||
/Content.Server/Power/Pow3r/ @PJB3005
|
||||
|
||||
@@ -52,7 +33,7 @@
|
||||
/Content.*/Atmos/ @Partmedia
|
||||
/Content.*/Botany/ @Partmedia
|
||||
|
||||
#Jezi
|
||||
# Jezi
|
||||
/Content.*/Medical @Jezithyr
|
||||
/Content.*/Body @Jezithyr
|
||||
|
||||
|
||||
6
.github/labeler.yml
vendored
6
.github/labeler.yml
vendored
@@ -16,7 +16,11 @@
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: '**/*.swsl'
|
||||
|
||||
"No C#":
|
||||
"Changes: Audio":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: '**/*.ogg'
|
||||
|
||||
"Changes: No C#":
|
||||
- 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"
|
||||
|
||||
4
.github/workflows/build-map-renderer.yml
vendored
4
.github/workflows/build-map-renderer.yml
vendored
@@ -2,11 +2,11 @@
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, staging, trying ]
|
||||
branches: [ master, staging, stable ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
branches: [ master ]
|
||||
branches: [ master, staging, stable ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
4
.github/workflows/build-test-debug.yml
vendored
4
.github/workflows/build-test-debug.yml
vendored
@@ -2,11 +2,11 @@ name: Build & Test Debug
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, staging, trying ]
|
||||
branches: [ master, staging, stable ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
branches: [ master ]
|
||||
branches: [ master, staging, stable ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@@ -16,6 +16,6 @@ jobs:
|
||||
- name: Check for Merge Conflicts
|
||||
uses: eps1lon/actions-label-merge-conflict@v3.0.0
|
||||
with:
|
||||
dirtyLabel: "Merge Conflict"
|
||||
dirtyLabel: "S: Merge Conflict"
|
||||
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
||||
commentOnDirty: "This pull request has conflicts, please resolve those before we can evaluate the pull request."
|
||||
4
.github/workflows/labeler-needsreview.yml
vendored
4
.github/workflows/labeler-needsreview.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: "Status: Needs Review"
|
||||
labels: "S: Needs Review"
|
||||
- uses: actions-ecosystem/action-remove-labels@v1
|
||||
with:
|
||||
labels: "Status: Awaiting Changes"
|
||||
labels: "S: Awaiting Changes"
|
||||
|
||||
23
.github/workflows/labeler-review.yml
vendored
Normal file
23
.github/workflows/labeler-review.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: "Labels: Approved"
|
||||
on:
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
jobs:
|
||||
add_label:
|
||||
# Change the repository name after you've made sure the team name is correct for your fork!
|
||||
if: ${{ (github.repository == 'space-wizards/space-station-14') && (github.event.review.state == 'APPROVED') }}
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: tspascoal/get-user-teams-membership@v3
|
||||
id: checkUserMember
|
||||
with:
|
||||
username: ${{ github.actor }}
|
||||
team: "content-maintainers,junior-maintainers"
|
||||
GITHUB_TOKEN: ${{ secrets.LABELER_PAT }}
|
||||
- if: ${{ steps.checkUserMember.outputs.isTeamMember == 'true' }}
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: "S: Approved"
|
||||
20
.github/workflows/labeler-size.yml
vendored
Normal file
20
.github/workflows/labeler-size.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: "Labels: Size"
|
||||
on: pull_request_target
|
||||
jobs:
|
||||
size-label:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: size-label
|
||||
uses: "pascalgn/size-label-action@v0.5.5"
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
with:
|
||||
# Custom size configuration
|
||||
sizes: >
|
||||
{
|
||||
"0": "XS",
|
||||
"10": "S",
|
||||
"100": "M",
|
||||
"1000": "L",
|
||||
"5000": "XL"
|
||||
}
|
||||
16
.github/workflows/labeler-stable.yml
vendored
Normal file
16
.github/workflows/labeler-stable.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
name: "Labels: Branch stable"
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
branches:
|
||||
- 'stable'
|
||||
|
||||
jobs:
|
||||
add_label:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: "Branch: Stable"
|
||||
16
.github/workflows/labeler-staging.yml
vendored
Normal file
16
.github/workflows/labeler-staging.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
name: "Labels: Branch staging"
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
branches:
|
||||
- 'staging'
|
||||
|
||||
jobs:
|
||||
add_label:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: "Branch: Staging"
|
||||
4
.github/workflows/labeler-untriaged.yml
vendored
4
.github/workflows/labeler-untriaged.yml
vendored
@@ -3,6 +3,8 @@
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
add_label:
|
||||
@@ -11,4 +13,4 @@ jobs:
|
||||
- uses: actions-ecosystem/action-add-labels@v1
|
||||
if: join(github.event.issue.labels) == ''
|
||||
with:
|
||||
labels: "Status: Untriaged"
|
||||
labels: "S: Untriaged"
|
||||
|
||||
4
.github/workflows/test-packaging.yml
vendored
4
.github/workflows/test-packaging.yml
vendored
@@ -2,7 +2,7 @@
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, staging, trying ]
|
||||
branches: [ master, staging, stable ]
|
||||
paths:
|
||||
- '**.cs'
|
||||
- '**.csproj'
|
||||
@@ -16,7 +16,7 @@ on:
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
branches: [ master ]
|
||||
branches: [ master, staging, stable ]
|
||||
paths:
|
||||
- '**.cs'
|
||||
- '**.csproj'
|
||||
|
||||
2
.github/workflows/validate-rgas.yml
vendored
2
.github/workflows/validate-rgas.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: RGA schema validator
|
||||
on:
|
||||
push:
|
||||
branches: [ master, staging, trying ]
|
||||
branches: [ master, staging, stable ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
|
||||
2
.github/workflows/validate-rsis.yml
vendored
2
.github/workflows/validate-rsis.yml
vendored
@@ -2,7 +2,7 @@ name: RSI Validator
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ staging, trying ]
|
||||
branches: [ master, staging, stable ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
paths:
|
||||
|
||||
2
.github/workflows/validate_mapfiles.yml
vendored
2
.github/workflows/validate_mapfiles.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: Map file schema validator
|
||||
on:
|
||||
push:
|
||||
branches: [ master, staging, trying ]
|
||||
branches: [ master, staging, stable ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
|
||||
2
.github/workflows/yaml-linter.yml
vendored
2
.github/workflows/yaml-linter.yml
vendored
@@ -2,7 +2,7 @@ name: YAML Linter
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, staging, trying ]
|
||||
branches: [ master, staging, stable ]
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize, ready_for_review ]
|
||||
|
||||
@@ -258,13 +258,13 @@ namespace Content.Client.Actions
|
||||
|
||||
public void LinkAllActions(ActionsComponent? actions = null)
|
||||
{
|
||||
if (_playerManager.LocalEntity is not { } user ||
|
||||
!Resolve(user, ref actions, false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_playerManager.LocalEntity is not { } user ||
|
||||
!Resolve(user, ref actions, false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LinkActions?.Invoke(actions);
|
||||
LinkActions?.Invoke(actions);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
|
||||
@@ -22,11 +22,11 @@ namespace Content.Client.Administration.UI.BanPanel;
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class BanPanel : DefaultWindow
|
||||
{
|
||||
public event Action<string?, (IPAddress, int)?, bool, byte[]?, bool, uint, string, NoteSeverity, string[]?, bool>? BanSubmitted;
|
||||
public event Action<string?, (IPAddress, int)?, bool, ImmutableTypedHwid?, bool, uint, string, NoteSeverity, string[]?, bool>? BanSubmitted;
|
||||
public event Action<string>? PlayerChanged;
|
||||
private string? PlayerUsername { get; set; }
|
||||
private (IPAddress, int)? IpAddress { get; set; }
|
||||
private byte[]? Hwid { get; set; }
|
||||
private ImmutableTypedHwid? Hwid { get; set; }
|
||||
private double TimeEntered { get; set; }
|
||||
private uint Multiplier { get; set; }
|
||||
private bool HasBanFlag { get; set; }
|
||||
@@ -371,9 +371,8 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
private void OnHwidChanged()
|
||||
{
|
||||
var hwidString = HwidLine.Text;
|
||||
var length = 3 * (hwidString.Length / 4) - hwidString.TakeLast(2).Count(c => c == '=');
|
||||
Hwid = new byte[length];
|
||||
if (HwidCheckbox.Pressed && !(string.IsNullOrEmpty(hwidString) && LastConnCheckbox.Pressed) && !Convert.TryFromBase64String(hwidString, Hwid, out _))
|
||||
ImmutableTypedHwid? hwid = null;
|
||||
if (HwidCheckbox.Pressed && !(string.IsNullOrEmpty(hwidString) && LastConnCheckbox.Pressed) && !ImmutableTypedHwid.TryParse(hwidString, out hwid))
|
||||
{
|
||||
ErrorLevel |= ErrorLevelEnum.Hwid;
|
||||
HwidLine.ModulateSelfOverride = Color.Red;
|
||||
@@ -390,7 +389,7 @@ public sealed partial class BanPanel : DefaultWindow
|
||||
Hwid = null;
|
||||
return;
|
||||
}
|
||||
Hwid = Convert.FromHexString(hwidString);
|
||||
Hwid = hwid;
|
||||
}
|
||||
|
||||
private void OnTypeChanged()
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<Label Name="ExpiryLabel" Text="{Loc admin-note-editor-expiry-label}" Visible="False" />
|
||||
<HistoryLineEdit Name="ExpiryLineEdit" PlaceHolder="{Loc admin-note-editor-expiry-placeholder}"
|
||||
Visible="False" HorizontalExpand="True" />
|
||||
<OptionButton Name="ExpiryLengthDropdown" Visible="False" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<OptionButton Name="TypeOption" HorizontalAlignment="Center" />
|
||||
|
||||
@@ -17,6 +17,17 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _console = default!;
|
||||
|
||||
private enum Multipliers
|
||||
{
|
||||
Minutes,
|
||||
Hours,
|
||||
Days,
|
||||
Weeks,
|
||||
Months,
|
||||
Years,
|
||||
Centuries
|
||||
}
|
||||
|
||||
public event Action<int, NoteType, string, NoteSeverity?, bool, DateTime?>? SubmitPressed;
|
||||
|
||||
public NoteEdit(SharedAdminNote? note, string playerName, bool canCreate, bool canEdit)
|
||||
@@ -31,6 +42,20 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
|
||||
ResetSubmitButton();
|
||||
|
||||
// It's weird to use minutes as the IDs, but it works and makes sense kind of :)
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-minutes"), (int) Multipliers.Minutes);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-hours"), (int) Multipliers.Hours);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-days"), (int) Multipliers.Days);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-weeks"), (int) Multipliers.Weeks);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-months"), (int) Multipliers.Months);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-years"), (int) Multipliers.Years);
|
||||
ExpiryLengthDropdown.AddItem(Loc.GetString("admin-note-button-centuries"), (int) Multipliers.Centuries);
|
||||
ExpiryLengthDropdown.OnItemSelected += OnLengthChanged;
|
||||
|
||||
ExpiryLengthDropdown.SelectId((int) Multipliers.Weeks);
|
||||
|
||||
ExpiryLineEdit.OnTextChanged += OnTextChanged;
|
||||
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-note"), (int) NoteType.Note);
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-message"), (int) NoteType.Message);
|
||||
TypeOption.AddItem(Loc.GetString("admin-note-editor-type-watchlist"), (int) NoteType.Watchlist);
|
||||
@@ -134,6 +159,7 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
SecretCheckBox.Pressed = false;
|
||||
SeverityOption.Disabled = false;
|
||||
PermanentCheckBox.Pressed = true;
|
||||
SubmitButton.Disabled = true;
|
||||
UpdatePermanentCheckboxFields();
|
||||
break;
|
||||
case (int) NoteType.Message: // Message: these are shown to the player when they log on
|
||||
@@ -172,8 +198,9 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
{
|
||||
ExpiryLabel.Visible = !PermanentCheckBox.Pressed;
|
||||
ExpiryLineEdit.Visible = !PermanentCheckBox.Pressed;
|
||||
ExpiryLengthDropdown.Visible = !PermanentCheckBox.Pressed;
|
||||
|
||||
ExpiryLineEdit.Text = !PermanentCheckBox.Pressed ? DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") : string.Empty;
|
||||
ExpiryLineEdit.Text = !PermanentCheckBox.Pressed ? 1.ToString() : string.Empty;
|
||||
}
|
||||
|
||||
private void OnSecretPressed(BaseButton.ButtonEventArgs _)
|
||||
@@ -187,6 +214,16 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
SeverityOption.SelectId(args.Id);
|
||||
}
|
||||
|
||||
private void OnLengthChanged(OptionButton.ItemSelectedEventArgs args)
|
||||
{
|
||||
ExpiryLengthDropdown.SelectId(args.Id);
|
||||
}
|
||||
|
||||
private void OnTextChanged(HistoryLineEdit.LineEditEventArgs args)
|
||||
{
|
||||
ParseExpiryTime();
|
||||
}
|
||||
|
||||
private void OnSubmitButtonPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
if (!ParseExpiryTime())
|
||||
@@ -263,13 +300,24 @@ public sealed partial class NoteEdit : FancyWindow
|
||||
return true;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ExpiryLineEdit.Text) || !DateTime.TryParse(ExpiryLineEdit.Text, out var result) || DateTime.UtcNow > result)
|
||||
if (string.IsNullOrWhiteSpace(ExpiryLineEdit.Text) || !uint.TryParse(ExpiryLineEdit.Text, out var inputInt))
|
||||
{
|
||||
ExpiryLineEdit.ModulateSelfOverride = Color.Red;
|
||||
return false;
|
||||
}
|
||||
|
||||
ExpiryTime = result.ToUniversalTime();
|
||||
var mult = ExpiryLengthDropdown.SelectedId switch
|
||||
{
|
||||
(int) Multipliers.Minutes => TimeSpan.FromMinutes(1).TotalMinutes,
|
||||
(int) Multipliers.Hours => TimeSpan.FromHours(1).TotalMinutes,
|
||||
(int) Multipliers.Days => TimeSpan.FromDays(1).TotalMinutes,
|
||||
(int) Multipliers.Weeks => TimeSpan.FromDays(7).TotalMinutes,
|
||||
(int) Multipliers.Months => TimeSpan.FromDays(30).TotalMinutes,
|
||||
(int) Multipliers.Years => TimeSpan.FromDays(365).TotalMinutes,
|
||||
(int) Multipliers.Centuries => TimeSpan.FromDays(36525).TotalMinutes,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(ExpiryLengthDropdown.SelectedId), "Multiplier out of range :(")
|
||||
};
|
||||
ExpiryTime = DateTime.UtcNow.AddMinutes(inputInt * mult);
|
||||
ExpiryLineEdit.ModulateSelfOverride = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Linq;
|
||||
using Content.Shared.Alert;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
@@ -24,8 +25,7 @@ public sealed class ClientAlertsSystem : AlertsSystem
|
||||
|
||||
SubscribeLocalEvent<AlertsComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
|
||||
SubscribeLocalEvent<AlertsComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
|
||||
|
||||
SubscribeLocalEvent<AlertsComponent, AfterAutoHandleStateEvent>(ClientAlertsHandleState);
|
||||
SubscribeLocalEvent<AlertsComponent, ComponentHandleState>(OnHandleState);
|
||||
}
|
||||
protected override void LoadPrototypes()
|
||||
{
|
||||
@@ -47,6 +47,16 @@ public sealed class ClientAlertsSystem : AlertsSystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnHandleState(Entity<AlertsComponent> alerts, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not AlertComponentState cast)
|
||||
return;
|
||||
|
||||
alerts.Comp.Alerts = cast.Alerts;
|
||||
|
||||
UpdateHud(alerts);
|
||||
}
|
||||
|
||||
protected override void AfterShowAlert(Entity<AlertsComponent> alerts)
|
||||
{
|
||||
UpdateHud(alerts);
|
||||
@@ -57,11 +67,6 @@ public sealed class ClientAlertsSystem : AlertsSystem
|
||||
UpdateHud(alerts);
|
||||
}
|
||||
|
||||
private void ClientAlertsHandleState(Entity<AlertsComponent> alerts, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
UpdateHud(alerts);
|
||||
}
|
||||
|
||||
private void UpdateHud(Entity<AlertsComponent> entity)
|
||||
{
|
||||
if (_playerManager.LocalEntity == entity.Owner)
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.Atmos.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class PipeColorVisualsComponent : Component
|
||||
{
|
||||
}
|
||||
public sealed partial class PipeColorVisualsComponent : Component;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:s="clr-namespace:Content.Client.Stylesheets"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Orientation="Vertical" HorizontalExpand ="True" Margin="0 0 0 3">
|
||||
@@ -62,7 +61,7 @@
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<!-- If the alarm is inactive, this is label is diplayed instead -->
|
||||
<!-- If the alarm is inactive, this is label is displayed instead -->
|
||||
<Label Name="NoDataLabel" Text="{Loc 'atmos-alerts-window-no-data-available'}" HorizontalAlignment="Center" Margin="0 15" FontColorOverride="#a9a9a9" ReservesSpace="False" Visible="False"></Label>
|
||||
|
||||
<!-- Silencing progress bar -->
|
||||
|
||||
@@ -31,19 +31,6 @@ public sealed partial class AtmosAlarmEntryContainer : BoxContainer
|
||||
[AtmosAlarmType.Danger] = "atmos-alerts-window-danger-state",
|
||||
};
|
||||
|
||||
private Dictionary<Gas, string> _gasShorthands = new Dictionary<Gas, string>()
|
||||
{
|
||||
[Gas.Ammonia] = "NH₃",
|
||||
[Gas.CarbonDioxide] = "CO₂",
|
||||
[Gas.Frezon] = "F",
|
||||
[Gas.Nitrogen] = "N₂",
|
||||
[Gas.NitrousOxide] = "N₂O",
|
||||
[Gas.Oxygen] = "O₂",
|
||||
[Gas.Plasma] = "P",
|
||||
[Gas.Tritium] = "T",
|
||||
[Gas.WaterVapor] = "H₂O",
|
||||
};
|
||||
|
||||
public AtmosAlarmEntryContainer(NetEntity uid, EntityCoordinates? coordinates)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
@@ -136,8 +123,9 @@ public sealed partial class AtmosAlarmEntryContainer : BoxContainer
|
||||
GasGridContainer.RemoveAllChildren();
|
||||
|
||||
var gasData = focusData.Value.GasData.Where(g => g.Key != Gas.Oxygen);
|
||||
var keyValuePairs = gasData.ToList();
|
||||
|
||||
if (gasData.Count() == 0)
|
||||
if (keyValuePairs.Count == 0)
|
||||
{
|
||||
// No other gases
|
||||
var gasLabel = new Label()
|
||||
@@ -158,17 +146,14 @@ public sealed partial class AtmosAlarmEntryContainer : BoxContainer
|
||||
else
|
||||
{
|
||||
// Add an entry for each gas
|
||||
foreach ((var gas, (var mol, var percent, var alert)) in gasData)
|
||||
foreach ((var gas, (var mol, var percent, var alert)) in keyValuePairs)
|
||||
{
|
||||
var gasPercent = (FixedPoint2)0f;
|
||||
gasPercent = percent * 100f;
|
||||
|
||||
if (!_gasShorthands.TryGetValue(gas, out var gasShorthand))
|
||||
gasShorthand = "X";
|
||||
FixedPoint2 gasPercent = percent * 100f;
|
||||
var gasAbbreviation = Atmospherics.GasAbbreviations.GetValueOrDefault(gas, Loc.GetString("gas-unknown-abbreviation"));
|
||||
|
||||
var gasLabel = new Label()
|
||||
{
|
||||
Text = Loc.GetString("atmos-alerts-window-other-gases-value", ("shorthand", gasShorthand), ("value", gasPercent)),
|
||||
Text = Loc.GetString("atmos-alerts-window-other-gases-value", ("shorthand", gasAbbreviation), ("value", gasPercent)),
|
||||
FontOverride = normalFont,
|
||||
FontColorOverride = GetAlarmStateColor(alert),
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
|
||||
@@ -14,8 +14,6 @@ public sealed class AtmosAlertsComputerBoundUserInterface : BoundUserInterface
|
||||
_menu = new AtmosAlertsComputerWindow(this, Owner);
|
||||
_menu.OpenCentered();
|
||||
_menu.OnClose += Close;
|
||||
|
||||
EntMan.TryGetComponent<TransformComponent>(Owner, out var xform);
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
@@ -24,9 +22,6 @@ public sealed class AtmosAlertsComputerBoundUserInterface : BoundUserInterface
|
||||
|
||||
var castState = (AtmosAlertsComputerBoundInterfaceState) state;
|
||||
|
||||
if (castState == null)
|
||||
return;
|
||||
|
||||
EntMan.TryGetComponent<TransformComponent>(Owner, out var xform);
|
||||
_menu?.UpdateUI(xform?.Coordinates, castState.AirAlarms, castState.FireAlarms, castState.FocusData);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:ui="clr-namespace:Content.Client.Pinpointer.UI"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
Title="{Loc 'atmos-alerts-window-title'}"
|
||||
Resizable="False"
|
||||
SetSize="1120 750"
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
using Content.Shared.Atmos.Components;
|
||||
|
||||
namespace Content.Client.Atmos.Consoles;
|
||||
|
||||
public sealed class AtmosMonitoringConsoleBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[ViewVariables]
|
||||
private AtmosMonitoringConsoleWindow? _menu;
|
||||
|
||||
public AtmosMonitoringConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { }
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_menu = new AtmosMonitoringConsoleWindow(this, Owner);
|
||||
_menu.OpenCentered();
|
||||
_menu.OnClose += Close;
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not AtmosMonitoringConsoleBoundInterfaceState castState)
|
||||
return;
|
||||
|
||||
EntMan.TryGetComponent<TransformComponent>(Owner, out var xform);
|
||||
_menu?.UpdateUI(xform?.Coordinates, castState.AtmosNetworks);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
_menu?.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
using Content.Client.Pinpointer.UI;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Pinpointer;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Map.Components;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Content.Client.Atmos.Consoles;
|
||||
|
||||
public sealed partial class AtmosMonitoringConsoleNavMapControl : NavMapControl
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
|
||||
public bool ShowPipeNetwork = true;
|
||||
public int? FocusNetId = null;
|
||||
|
||||
private const int ChunkSize = 4;
|
||||
|
||||
private readonly Color _basePipeNetColor = Color.LightGray;
|
||||
private readonly Color _unfocusedPipeNetColor = Color.DimGray;
|
||||
|
||||
private List<AtmosMonitoringConsoleLine> _atmosPipeNetwork = new();
|
||||
private Dictionary<Color, Color> _sRGBLookUp = new Dictionary<Color, Color>();
|
||||
|
||||
// Look up tables for merging continuous lines. Indexed by line color
|
||||
private Dictionary<Color, Dictionary<Vector2i, Vector2i>> _horizLines = new();
|
||||
private Dictionary<Color, Dictionary<Vector2i, Vector2i>> _horizLinesReversed = new();
|
||||
private Dictionary<Color, Dictionary<Vector2i, Vector2i>> _vertLines = new();
|
||||
private Dictionary<Color, Dictionary<Vector2i, Vector2i>> _vertLinesReversed = new();
|
||||
|
||||
public AtmosMonitoringConsoleNavMapControl() : base()
|
||||
{
|
||||
PostWallDrawingAction += DrawAllPipeNetworks;
|
||||
}
|
||||
|
||||
protected override void UpdateNavMap()
|
||||
{
|
||||
base.UpdateNavMap();
|
||||
|
||||
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(Owner, out var console))
|
||||
return;
|
||||
|
||||
if (!_entManager.TryGetComponent<MapGridComponent>(MapUid, out var grid))
|
||||
return;
|
||||
|
||||
_atmosPipeNetwork = GetDecodedAtmosPipeChunks(console.AtmosPipeChunks, grid);
|
||||
}
|
||||
|
||||
private void DrawAllPipeNetworks(DrawingHandleScreen handle)
|
||||
{
|
||||
if (!ShowPipeNetwork)
|
||||
return;
|
||||
|
||||
// Draw networks
|
||||
if (_atmosPipeNetwork != null && _atmosPipeNetwork.Any())
|
||||
DrawPipeNetwork(handle, _atmosPipeNetwork);
|
||||
}
|
||||
|
||||
private void DrawPipeNetwork(DrawingHandleScreen handle, List<AtmosMonitoringConsoleLine> atmosPipeNetwork)
|
||||
{
|
||||
var offset = GetOffset();
|
||||
offset = offset with { Y = -offset.Y };
|
||||
|
||||
if (WorldRange / WorldMaxRange > 0.5f)
|
||||
{
|
||||
var pipeNetworks = new Dictionary<Color, ValueList<Vector2>>();
|
||||
|
||||
foreach (var chunkedLine in atmosPipeNetwork)
|
||||
{
|
||||
var start = ScalePosition(chunkedLine.Origin - offset);
|
||||
var end = ScalePosition(chunkedLine.Terminus - offset);
|
||||
|
||||
if (!pipeNetworks.TryGetValue(chunkedLine.Color, out var subNetwork))
|
||||
subNetwork = new ValueList<Vector2>();
|
||||
|
||||
subNetwork.Add(start);
|
||||
subNetwork.Add(end);
|
||||
|
||||
pipeNetworks[chunkedLine.Color] = subNetwork;
|
||||
}
|
||||
|
||||
foreach ((var color, var subNetwork) in pipeNetworks)
|
||||
{
|
||||
if (subNetwork.Count > 0)
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.LineList, subNetwork.Span, color);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
var pipeVertexUVs = new Dictionary<Color, ValueList<Vector2>>();
|
||||
|
||||
foreach (var chunkedLine in atmosPipeNetwork)
|
||||
{
|
||||
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)
|
||||
- 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)
|
||||
- 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)
|
||||
- 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)
|
||||
- offset);
|
||||
|
||||
if (!pipeVertexUVs.TryGetValue(chunkedLine.Color, out var pipeVertexUV))
|
||||
pipeVertexUV = new ValueList<Vector2>();
|
||||
|
||||
pipeVertexUV.Add(leftBottom);
|
||||
pipeVertexUV.Add(leftTop);
|
||||
pipeVertexUV.Add(rightBottom);
|
||||
pipeVertexUV.Add(leftTop);
|
||||
pipeVertexUV.Add(rightBottom);
|
||||
pipeVertexUV.Add(rightTop);
|
||||
|
||||
pipeVertexUVs[chunkedLine.Color] = pipeVertexUV;
|
||||
}
|
||||
|
||||
foreach ((var color, var pipeVertexUV) in pipeVertexUVs)
|
||||
{
|
||||
if (pipeVertexUV.Count > 0)
|
||||
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, pipeVertexUV.Span, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<AtmosMonitoringConsoleLine> GetDecodedAtmosPipeChunks(Dictionary<Vector2i, AtmosPipeChunk>? chunks, MapGridComponent? grid)
|
||||
{
|
||||
var decodedOutput = new List<AtmosMonitoringConsoleLine>();
|
||||
|
||||
if (chunks == null || grid == null)
|
||||
return decodedOutput;
|
||||
|
||||
// Clear stale look up table values
|
||||
_horizLines.Clear();
|
||||
_horizLinesReversed.Clear();
|
||||
_vertLines.Clear();
|
||||
_vertLinesReversed.Clear();
|
||||
|
||||
// Generate masks
|
||||
var northMask = (ulong)1 << 0;
|
||||
var southMask = (ulong)1 << 1;
|
||||
var westMask = (ulong)1 << 2;
|
||||
var eastMask = (ulong)1 << 3;
|
||||
|
||||
foreach ((var chunkOrigin, var chunk) in chunks)
|
||||
{
|
||||
var list = new List<AtmosMonitoringConsoleLine>();
|
||||
|
||||
foreach (var ((netId, hexColor), atmosPipeData) in chunk.AtmosPipeData)
|
||||
{
|
||||
// Determine the correct coloration for the pipe
|
||||
var color = Color.FromHex(hexColor) * _basePipeNetColor;
|
||||
|
||||
if (FocusNetId != null && FocusNetId != netId)
|
||||
color *= _unfocusedPipeNetColor;
|
||||
|
||||
// Get the associated line look up tables
|
||||
if (!_horizLines.TryGetValue(color, out var horizLines))
|
||||
{
|
||||
horizLines = new();
|
||||
_horizLines[color] = horizLines;
|
||||
}
|
||||
|
||||
if (!_horizLinesReversed.TryGetValue(color, out var horizLinesReversed))
|
||||
{
|
||||
horizLinesReversed = new();
|
||||
_horizLinesReversed[color] = horizLinesReversed;
|
||||
}
|
||||
|
||||
if (!_vertLines.TryGetValue(color, out var vertLines))
|
||||
{
|
||||
vertLines = new();
|
||||
_vertLines[color] = vertLines;
|
||||
}
|
||||
|
||||
if (!_vertLinesReversed.TryGetValue(color, out var vertLinesReversed))
|
||||
{
|
||||
vertLinesReversed = new();
|
||||
_vertLinesReversed[color] = vertLinesReversed;
|
||||
}
|
||||
|
||||
// Loop over the chunk
|
||||
for (var tileIdx = 0; tileIdx < ChunkSize * ChunkSize; tileIdx++)
|
||||
{
|
||||
if (atmosPipeData == 0)
|
||||
continue;
|
||||
|
||||
var mask = (ulong)SharedNavMapSystem.AllDirMask << tileIdx * SharedNavMapSystem.Directions;
|
||||
|
||||
if ((atmosPipeData & mask) == 0)
|
||||
continue;
|
||||
|
||||
var relativeTile = GetTileFromIndex(tileIdx);
|
||||
var tile = (chunk.Origin * ChunkSize + relativeTile) * grid.TileSize;
|
||||
tile = tile with { Y = -tile.Y };
|
||||
|
||||
// Calculate the draw point offsets
|
||||
var vertLineOrigin = (atmosPipeData & northMask << tileIdx * SharedNavMapSystem.Directions) > 0 ?
|
||||
new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 1f) : new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f);
|
||||
|
||||
var vertLineTerminus = (atmosPipeData & southMask << tileIdx * SharedNavMapSystem.Directions) > 0 ?
|
||||
new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0f) : new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f);
|
||||
|
||||
var horizLineOrigin = (atmosPipeData & eastMask << tileIdx * SharedNavMapSystem.Directions) > 0 ?
|
||||
new Vector2(grid.TileSize * 1f, -grid.TileSize * 0.5f) : new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f);
|
||||
|
||||
var horizLineTerminus = (atmosPipeData & westMask << tileIdx * SharedNavMapSystem.Directions) > 0 ?
|
||||
new Vector2(grid.TileSize * 0f, -grid.TileSize * 0.5f) : new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f);
|
||||
|
||||
// Since we can have pipe lines that have a length of a half tile,
|
||||
// double the vectors and convert to vector2i so we can merge them
|
||||
AddOrUpdateNavMapLine(ConvertVector2ToVector2i(tile + horizLineOrigin, 2), ConvertVector2ToVector2i(tile + horizLineTerminus, 2), horizLines, horizLinesReversed);
|
||||
AddOrUpdateNavMapLine(ConvertVector2ToVector2i(tile + vertLineOrigin, 2), ConvertVector2ToVector2i(tile + vertLineTerminus, 2), vertLines, vertLinesReversed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scale the vector2is back down and convert to vector2
|
||||
foreach (var (color, horizLines) in _horizLines)
|
||||
{
|
||||
// Get the corresponding sRBG color
|
||||
var sRGB = GetsRGBColor(color);
|
||||
|
||||
foreach (var (origin, terminal) in horizLines)
|
||||
decodedOutput.Add(new AtmosMonitoringConsoleLine
|
||||
(ConvertVector2iToVector2(origin, 0.5f), ConvertVector2iToVector2(terminal, 0.5f), sRGB));
|
||||
}
|
||||
|
||||
foreach (var (color, vertLines) in _vertLines)
|
||||
{
|
||||
// Get the corresponding sRBG color
|
||||
var sRGB = GetsRGBColor(color);
|
||||
|
||||
foreach (var (origin, terminal) in vertLines)
|
||||
decodedOutput.Add(new AtmosMonitoringConsoleLine
|
||||
(ConvertVector2iToVector2(origin, 0.5f), ConvertVector2iToVector2(terminal, 0.5f), sRGB));
|
||||
}
|
||||
|
||||
return decodedOutput;
|
||||
}
|
||||
|
||||
private Vector2 ConvertVector2iToVector2(Vector2i vector, float scale = 1f)
|
||||
{
|
||||
return new Vector2(vector.X * scale, vector.Y * scale);
|
||||
}
|
||||
|
||||
private Vector2i ConvertVector2ToVector2i(Vector2 vector, float scale = 1f)
|
||||
{
|
||||
return new Vector2i((int)MathF.Round(vector.X * scale), (int)MathF.Round(vector.Y * scale));
|
||||
}
|
||||
|
||||
private Vector2i GetTileFromIndex(int index)
|
||||
{
|
||||
var x = index / ChunkSize;
|
||||
var y = index % ChunkSize;
|
||||
return new Vector2i(x, y);
|
||||
}
|
||||
|
||||
private Color GetsRGBColor(Color color)
|
||||
{
|
||||
if (!_sRGBLookUp.TryGetValue(color, out var sRGB))
|
||||
{
|
||||
sRGB = Color.ToSrgb(color);
|
||||
_sRGBLookUp[color] = sRGB;
|
||||
}
|
||||
|
||||
return sRGB;
|
||||
}
|
||||
}
|
||||
|
||||
public struct AtmosMonitoringConsoleLine
|
||||
{
|
||||
public readonly Vector2 Origin;
|
||||
public readonly Vector2 Terminus;
|
||||
public readonly Color Color;
|
||||
|
||||
public AtmosMonitoringConsoleLine(Vector2 origin, Vector2 terminus, Color color)
|
||||
{
|
||||
Origin = origin;
|
||||
Terminus = terminus;
|
||||
Color = color;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Atmos.Consoles;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Atmos.Consoles;
|
||||
|
||||
public sealed class AtmosMonitoringConsoleSystem : SharedAtmosMonitoringConsoleSystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<AtmosMonitoringConsoleComponent, ComponentHandleState>(OnHandleState);
|
||||
}
|
||||
|
||||
private void OnHandleState(EntityUid uid, AtmosMonitoringConsoleComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
Dictionary<Vector2i, Dictionary<(int, string), ulong>> modifiedChunks;
|
||||
Dictionary<NetEntity, AtmosDeviceNavMapData> atmosDevices;
|
||||
|
||||
switch (args.Current)
|
||||
{
|
||||
case AtmosMonitoringConsoleDeltaState delta:
|
||||
{
|
||||
modifiedChunks = delta.ModifiedChunks;
|
||||
atmosDevices = delta.AtmosDevices;
|
||||
|
||||
foreach (var index in component.AtmosPipeChunks.Keys)
|
||||
{
|
||||
if (!delta.AllChunks!.Contains(index))
|
||||
component.AtmosPipeChunks.Remove(index);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case AtmosMonitoringConsoleState state:
|
||||
{
|
||||
modifiedChunks = state.Chunks;
|
||||
atmosDevices = state.AtmosDevices;
|
||||
|
||||
foreach (var index in component.AtmosPipeChunks.Keys)
|
||||
{
|
||||
if (!state.Chunks.ContainsKey(index))
|
||||
component.AtmosPipeChunks.Remove(index);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (origin, chunk) in modifiedChunks)
|
||||
{
|
||||
var newChunk = new AtmosPipeChunk(origin);
|
||||
newChunk.AtmosPipeData = new Dictionary<(int, string), ulong>(chunk);
|
||||
|
||||
component.AtmosPipeChunks[origin] = newChunk;
|
||||
}
|
||||
|
||||
component.AtmosDevices.Clear();
|
||||
|
||||
foreach (var (nuid, atmosDevice) in atmosDevices)
|
||||
{
|
||||
component.AtmosDevices[nuid] = atmosDevice;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:ui="clr-namespace:Content.Client.Atmos.Consoles"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
Title="{Loc 'atmos-monitoring-window-title'}"
|
||||
Resizable="False"
|
||||
SetSize="1120 750"
|
||||
MinSize="1120 750">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<!-- Main display -->
|
||||
<BoxContainer Orientation="Horizontal" VerticalExpand="True" HorizontalExpand="True">
|
||||
<!-- Nav map -->
|
||||
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
|
||||
<ui:AtmosMonitoringConsoleNavMapControl Name="NavMap" Margin="5 5" VerticalExpand="True" HorizontalExpand="True">
|
||||
|
||||
<!-- System warning -->
|
||||
<PanelContainer Name="SystemWarningPanel"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Top"
|
||||
HorizontalExpand="True"
|
||||
Margin="0 48 0 0"
|
||||
Visible="False">
|
||||
<RichTextLabel Name="SystemWarningLabel" Margin="12 8 12 8"/>
|
||||
</PanelContainer>
|
||||
|
||||
</ui:AtmosMonitoringConsoleNavMapControl>
|
||||
|
||||
<!-- Nav map legend -->
|
||||
<BoxContainer Orientation="Horizontal" Margin="0 10 0 10">
|
||||
<TextureRect Stretch="KeepAspectCentered"
|
||||
TexturePath="/Textures/Interface/NavMap/beveled_square.png"
|
||||
Modulate="#a9a9a9"
|
||||
SetSize="16 16"
|
||||
Margin="20 0 5 0"/>
|
||||
<Label Text="{Loc 'atmos-monitoring-window-label-gas-opening'}"/>
|
||||
<TextureRect Stretch="KeepAspectCentered"
|
||||
TexturePath="/Textures/Interface/NavMap/beveled_circle.png"
|
||||
SetSize="16 16"
|
||||
Modulate="#a9a9a9"
|
||||
Margin="20 0 5 0"/>
|
||||
<Label Text="{Loc 'atmos-monitoring-window-label-gas-scrubber'}"/>
|
||||
<TextureRect Stretch="KeepAspectCentered"
|
||||
TexturePath="/Textures/Interface/NavMap/beveled_arrow_east.png"
|
||||
SetSize="16 16"
|
||||
Modulate="#a9a9a9"
|
||||
Margin="20 0 5 0"/>
|
||||
<Label Text="{Loc 'atmos-monitoring-window-label-gas-flow-regulator'}"/>
|
||||
<TextureRect Stretch="KeepAspectCentered"
|
||||
TexturePath="/Textures/Interface/NavMap/beveled_hexagon.png"
|
||||
SetSize="16 16"
|
||||
Modulate="#a9a9a9"
|
||||
Margin="20 0 5 0"/>
|
||||
<Label Text="{Loc 'atmos-monitoring-window-label-thermoregulator'}"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Atmosphere status -->
|
||||
<BoxContainer Orientation="Vertical" VerticalExpand="True" SetWidth="440" Margin="0 0 10 10">
|
||||
|
||||
<!-- Station name -->
|
||||
<controls:StripeBack>
|
||||
<PanelContainer>
|
||||
<RichTextLabel Name="StationName" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0 5 0 3"/>
|
||||
</PanelContainer>
|
||||
</controls:StripeBack>
|
||||
|
||||
<!-- Alarm status (entries added by C# code) -->
|
||||
<TabContainer Name="MasterTabContainer" VerticalExpand="True" HorizontalExpand="True" Margin="0 10 0 0">
|
||||
<ScrollContainer HorizontalExpand="True" Margin="8, 8, 8, 8">
|
||||
<BoxContainer Name="AtmosNetworksTable" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" Margin="0 0 0 10"/>
|
||||
</ScrollContainer>
|
||||
</TabContainer>
|
||||
|
||||
<!-- Overlay toggles -->
|
||||
<BoxContainer Orientation="Vertical" Margin="0 10 0 0">
|
||||
<Label Text="{Loc 'atmos-monitoring-window-toggle-overlays'}" Margin="0 0 0 5"/>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<CheckBox Name="ShowPipeNetwork" Text="{Loc 'atmos-monitoring-window-show-pipe-network'}" Pressed="True" HorizontalExpand="True"/>
|
||||
<CheckBox Name="ShowGasPipeSensors" Text="{Loc 'atmos-monitoring-window-show-gas-pipe-sensors'}" Pressed="False" HorizontalExpand="True"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
</BoxContainer>
|
||||
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Footer -->
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<PanelContainer StyleClasses="LowDivider" />
|
||||
<BoxContainer Orientation="Horizontal" Margin="10 2 5 0" VerticalAlignment="Bottom">
|
||||
<Label Text="{Loc 'atmos-monitoring-window-flavor-left'}" StyleClasses="WindowFooterText" />
|
||||
<Label Text="{Loc 'atmos-monitoring-window-flavor-right'}" StyleClasses="WindowFooterText"
|
||||
HorizontalAlignment="Right" HorizontalExpand="True" Margin="0 0 5 0" />
|
||||
<TextureRect StyleClasses="NTLogoDark" Stretch="KeepAspectCentered"
|
||||
VerticalAlignment="Center" HorizontalAlignment="Right" SetSize="19 19"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
@@ -0,0 +1,455 @@
|
||||
using Content.Client.Pinpointer.UI;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Prototypes;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Client.Atmos.Consoles;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AtmosMonitoringConsoleWindow : FancyWindow
|
||||
{
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IPrototypeManager _protoManager;
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
|
||||
private EntityUid? _owner;
|
||||
private NetEntity? _focusEntity;
|
||||
private int? _focusNetId;
|
||||
|
||||
private bool _autoScrollActive = false;
|
||||
|
||||
private readonly Color _unfocusedDeviceColor = Color.DimGray;
|
||||
private ProtoId<NavMapBlipPrototype> _navMapConsoleProtoId = "NavMapConsole";
|
||||
private ProtoId<NavMapBlipPrototype> _gasPipeSensorProtoId = "GasPipeSensor";
|
||||
|
||||
public AtmosMonitoringConsoleWindow(AtmosMonitoringConsoleBoundUserInterface userInterface, EntityUid? owner)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_entManager = IoCManager.Resolve<IEntityManager>();
|
||||
_protoManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
_spriteSystem = _entManager.System<SpriteSystem>();
|
||||
|
||||
// Pass the owner to nav map
|
||||
_owner = owner;
|
||||
NavMap.Owner = _owner;
|
||||
|
||||
// Set nav map grid uid
|
||||
var stationName = Loc.GetString("atmos-monitoring-window-unknown-location");
|
||||
EntityCoordinates? consoleCoords = null;
|
||||
|
||||
if (_entManager.TryGetComponent<TransformComponent>(owner, out var xform))
|
||||
{
|
||||
consoleCoords = xform.Coordinates;
|
||||
NavMap.MapUid = xform.GridUid;
|
||||
|
||||
// Assign station name
|
||||
if (_entManager.TryGetComponent<MetaDataComponent>(xform.GridUid, out var stationMetaData))
|
||||
stationName = stationMetaData.EntityName;
|
||||
|
||||
var msg = new FormattedMessage();
|
||||
msg.TryAddMarkup(Loc.GetString("atmos-monitoring-window-station-name", ("stationName", stationName)), out _);
|
||||
|
||||
StationName.SetMessage(msg);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
StationName.SetMessage(stationName);
|
||||
NavMap.Visible = false;
|
||||
}
|
||||
|
||||
// Set trackable entity selected action
|
||||
NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap;
|
||||
|
||||
// Update nav map
|
||||
NavMap.ForceNavMapUpdate();
|
||||
|
||||
// Set tab container headers
|
||||
MasterTabContainer.SetTabTitle(0, Loc.GetString("atmos-monitoring-window-tab-networks"));
|
||||
|
||||
// Set UI toggles
|
||||
ShowPipeNetwork.OnToggled += _ => OnShowPipeNetworkToggled();
|
||||
ShowGasPipeSensors.OnToggled += _ => OnShowGasPipeSensors();
|
||||
|
||||
// Set nav map colors
|
||||
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner, out var console))
|
||||
return;
|
||||
|
||||
NavMap.TileColor = console.NavMapTileColor;
|
||||
NavMap.WallColor = console.NavMapWallColor;
|
||||
|
||||
// Initalize
|
||||
UpdateUI(consoleCoords, Array.Empty<AtmosMonitoringConsoleEntry>());
|
||||
}
|
||||
|
||||
#region Toggle handling
|
||||
|
||||
private void OnShowPipeNetworkToggled()
|
||||
{
|
||||
if (_owner == null)
|
||||
return;
|
||||
|
||||
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner.Value, out var console))
|
||||
return;
|
||||
|
||||
NavMap.ShowPipeNetwork = ShowPipeNetwork.Pressed;
|
||||
|
||||
foreach (var (netEnt, device) in console.AtmosDevices)
|
||||
{
|
||||
if (device.NavMapBlip == _gasPipeSensorProtoId)
|
||||
continue;
|
||||
|
||||
if (ShowPipeNetwork.Pressed)
|
||||
AddTrackedEntityToNavMap(device);
|
||||
|
||||
else
|
||||
NavMap.TrackedEntities.Remove(netEnt);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnShowGasPipeSensors()
|
||||
{
|
||||
if (_owner == null)
|
||||
return;
|
||||
|
||||
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner.Value, out var console))
|
||||
return;
|
||||
|
||||
foreach (var (netEnt, device) in console.AtmosDevices)
|
||||
{
|
||||
if (device.NavMapBlip != _gasPipeSensorProtoId)
|
||||
continue;
|
||||
|
||||
if (ShowGasPipeSensors.Pressed)
|
||||
AddTrackedEntityToNavMap(device, true);
|
||||
|
||||
else
|
||||
NavMap.TrackedEntities.Remove(netEnt);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void UpdateUI
|
||||
(EntityCoordinates? consoleCoords,
|
||||
AtmosMonitoringConsoleEntry[] atmosNetworks)
|
||||
{
|
||||
if (_owner == null)
|
||||
return;
|
||||
|
||||
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner.Value, out var console))
|
||||
return;
|
||||
|
||||
// Reset nav map values
|
||||
NavMap.TrackedCoordinates.Clear();
|
||||
NavMap.TrackedEntities.Clear();
|
||||
|
||||
if (_focusEntity != null && !console.AtmosDevices.Any(x => x.Key == _focusEntity))
|
||||
ClearFocus();
|
||||
|
||||
// Add tracked entities to the nav map
|
||||
UpdateNavMapBlips();
|
||||
|
||||
// Show the monitor location
|
||||
var consoleNetEnt = _entManager.GetNetEntity(_owner);
|
||||
|
||||
if (consoleCoords != null && consoleNetEnt != null)
|
||||
{
|
||||
var proto = _protoManager.Index(_navMapConsoleProtoId);
|
||||
|
||||
if (proto.TexturePaths != null && proto.TexturePaths.Length != 0)
|
||||
{
|
||||
var texture = _spriteSystem.Frame0(new SpriteSpecifier.Texture(proto.TexturePaths[0]));
|
||||
var blip = new NavMapBlip(consoleCoords.Value, texture, proto.Color, proto.Blinks, proto.Selectable);
|
||||
NavMap.TrackedEntities[consoleNetEnt.Value] = blip;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the nav map
|
||||
NavMap.ForceNavMapUpdate();
|
||||
|
||||
// Clear excess children from the tables
|
||||
while (AtmosNetworksTable.ChildCount > atmosNetworks.Length)
|
||||
AtmosNetworksTable.RemoveChild(AtmosNetworksTable.GetChild(AtmosNetworksTable.ChildCount - 1));
|
||||
|
||||
// Update all entries in each table
|
||||
for (int index = 0; index < atmosNetworks.Length; index++)
|
||||
{
|
||||
var entry = atmosNetworks.ElementAt(index);
|
||||
UpdateUIEntry(entry, index, AtmosNetworksTable, console);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateNavMapBlips()
|
||||
{
|
||||
if (_owner == null || !_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner.Value, out var console))
|
||||
return;
|
||||
|
||||
if (NavMap.Visible)
|
||||
{
|
||||
foreach (var (netEnt, device) in console.AtmosDevices)
|
||||
{
|
||||
// Update the focus network ID, incase it has changed
|
||||
if (_focusEntity == netEnt)
|
||||
{
|
||||
_focusNetId = device.NetId;
|
||||
NavMap.FocusNetId = _focusNetId;
|
||||
}
|
||||
|
||||
var isSensor = device.NavMapBlip == _gasPipeSensorProtoId;
|
||||
|
||||
// Skip network devices if the toggled is off
|
||||
if (!ShowPipeNetwork.Pressed && !isSensor)
|
||||
continue;
|
||||
|
||||
// Skip gas pipe sensors if the toggle is off
|
||||
if (!ShowGasPipeSensors.Pressed && isSensor)
|
||||
continue;
|
||||
|
||||
AddTrackedEntityToNavMap(device, isSensor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddTrackedEntityToNavMap(AtmosDeviceNavMapData metaData, bool isSensor = false)
|
||||
{
|
||||
var proto = _protoManager.Index(metaData.NavMapBlip);
|
||||
|
||||
if (proto.TexturePaths == null || proto.TexturePaths.Length == 0)
|
||||
return;
|
||||
|
||||
var idx = Math.Clamp((int)metaData.Direction / 2, 0, proto.TexturePaths.Length - 1);
|
||||
var texture = proto.TexturePaths.Length > 0 ? proto.TexturePaths[idx] : proto.TexturePaths[0];
|
||||
var color = isSensor ? proto.Color : proto.Color * metaData.PipeColor;
|
||||
|
||||
if (_focusNetId != null && metaData.NetId != _focusNetId)
|
||||
color *= _unfocusedDeviceColor;
|
||||
|
||||
var blinks = proto.Blinks || _focusEntity == metaData.NetEntity;
|
||||
var coords = _entManager.GetCoordinates(metaData.NetCoordinates);
|
||||
var blip = new NavMapBlip(coords, _spriteSystem.Frame0(new SpriteSpecifier.Texture(texture)), color, blinks, proto.Selectable, proto.Scale);
|
||||
NavMap.TrackedEntities[metaData.NetEntity] = blip;
|
||||
}
|
||||
|
||||
private void UpdateUIEntry(AtmosMonitoringConsoleEntry data, int index, Control table, AtmosMonitoringConsoleComponent console)
|
||||
{
|
||||
// Make new UI entry if required
|
||||
if (index >= table.ChildCount)
|
||||
{
|
||||
var newEntryContainer = new AtmosMonitoringEntryContainer(data);
|
||||
|
||||
// On click
|
||||
newEntryContainer.FocusButton.OnButtonUp += args =>
|
||||
{
|
||||
if (_focusEntity == newEntryContainer.Data.NetEntity)
|
||||
{
|
||||
ClearFocus();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
SetFocus(newEntryContainer.Data.NetEntity, newEntryContainer.Data.NetId);
|
||||
|
||||
var coords = _entManager.GetCoordinates(newEntryContainer.Data.Coordinates);
|
||||
NavMap.CenterToCoordinates(coords);
|
||||
}
|
||||
|
||||
// Update affected UI elements across all tables
|
||||
UpdateConsoleTable(console, AtmosNetworksTable, _focusEntity);
|
||||
};
|
||||
|
||||
// Add the entry to the current table
|
||||
table.AddChild(newEntryContainer);
|
||||
}
|
||||
|
||||
// Update values and UI elements
|
||||
var tableChild = table.GetChild(index);
|
||||
|
||||
if (tableChild is not AtmosMonitoringEntryContainer)
|
||||
{
|
||||
table.RemoveChild(tableChild);
|
||||
UpdateUIEntry(data, index, table, console);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var entryContainer = (AtmosMonitoringEntryContainer)tableChild;
|
||||
entryContainer.UpdateEntry(data, data.NetEntity == _focusEntity);
|
||||
}
|
||||
|
||||
private void UpdateConsoleTable(AtmosMonitoringConsoleComponent console, Control table, NetEntity? currTrackedEntity)
|
||||
{
|
||||
foreach (var tableChild in table.Children)
|
||||
{
|
||||
if (tableChild is not AtmosAlarmEntryContainer)
|
||||
continue;
|
||||
|
||||
var entryContainer = (AtmosAlarmEntryContainer)tableChild;
|
||||
|
||||
if (entryContainer.NetEntity != currTrackedEntity)
|
||||
entryContainer.RemoveAsFocus();
|
||||
|
||||
else if (entryContainer.NetEntity == currTrackedEntity)
|
||||
entryContainer.SetAsFocus();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetTrackedEntityFromNavMap(NetEntity? focusEntity)
|
||||
{
|
||||
if (focusEntity == null)
|
||||
return;
|
||||
|
||||
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner, out var console))
|
||||
return;
|
||||
|
||||
foreach (var (netEnt, device) in console.AtmosDevices)
|
||||
{
|
||||
if (netEnt != focusEntity)
|
||||
continue;
|
||||
|
||||
if (device.NavMapBlip != _gasPipeSensorProtoId)
|
||||
return;
|
||||
|
||||
// Set new focus
|
||||
SetFocus(focusEntity.Value, device.NetId);
|
||||
|
||||
// Get the scroll position of the selected entity on the selected button the UI
|
||||
ActivateAutoScrollToFocus();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
AutoScrollToFocus();
|
||||
}
|
||||
|
||||
private void ActivateAutoScrollToFocus()
|
||||
{
|
||||
_autoScrollActive = true;
|
||||
}
|
||||
|
||||
private void AutoScrollToFocus()
|
||||
{
|
||||
if (!_autoScrollActive)
|
||||
return;
|
||||
|
||||
var scroll = AtmosNetworksTable.Parent as ScrollContainer;
|
||||
if (scroll == null)
|
||||
return;
|
||||
|
||||
if (!TryGetVerticalScrollbar(scroll, out var vScrollbar))
|
||||
return;
|
||||
|
||||
if (!TryGetNextScrollPosition(out float? nextScrollPosition))
|
||||
return;
|
||||
|
||||
vScrollbar.ValueTarget = nextScrollPosition.Value;
|
||||
|
||||
if (MathHelper.CloseToPercent(vScrollbar.Value, vScrollbar.ValueTarget))
|
||||
_autoScrollActive = false;
|
||||
}
|
||||
|
||||
private bool TryGetVerticalScrollbar(ScrollContainer scroll, [NotNullWhen(true)] out VScrollBar? vScrollBar)
|
||||
{
|
||||
vScrollBar = null;
|
||||
|
||||
foreach (var control in scroll.Children)
|
||||
{
|
||||
if (control is not VScrollBar)
|
||||
continue;
|
||||
|
||||
vScrollBar = (VScrollBar)control;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryGetNextScrollPosition([NotNullWhen(true)] out float? nextScrollPosition)
|
||||
{
|
||||
nextScrollPosition = null;
|
||||
|
||||
var scroll = AtmosNetworksTable.Parent as ScrollContainer;
|
||||
if (scroll == null)
|
||||
return false;
|
||||
|
||||
var container = scroll.Children.ElementAt(0) as BoxContainer;
|
||||
if (container == null || container.Children.Count() == 0)
|
||||
return false;
|
||||
|
||||
// Exit if the heights of the children haven't been initialized yet
|
||||
if (!container.Children.Any(x => x.Height > 0))
|
||||
return false;
|
||||
|
||||
nextScrollPosition = 0;
|
||||
|
||||
foreach (var control in container.Children)
|
||||
{
|
||||
if (control is not AtmosMonitoringEntryContainer)
|
||||
continue;
|
||||
|
||||
var entry = (AtmosMonitoringEntryContainer)control;
|
||||
|
||||
if (entry.Data.NetEntity == _focusEntity)
|
||||
return true;
|
||||
|
||||
nextScrollPosition += control.Height;
|
||||
}
|
||||
|
||||
// Failed to find control
|
||||
nextScrollPosition = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SetFocus(NetEntity focusEntity, int focusNetId)
|
||||
{
|
||||
_focusEntity = focusEntity;
|
||||
_focusNetId = focusNetId;
|
||||
NavMap.FocusNetId = focusNetId;
|
||||
|
||||
OnFocusChanged();
|
||||
}
|
||||
|
||||
private void ClearFocus()
|
||||
{
|
||||
_focusEntity = null;
|
||||
_focusNetId = null;
|
||||
NavMap.FocusNetId = null;
|
||||
|
||||
OnFocusChanged();
|
||||
}
|
||||
|
||||
private void OnFocusChanged()
|
||||
{
|
||||
UpdateNavMapBlips();
|
||||
NavMap.ForceNavMapUpdate();
|
||||
|
||||
if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner, out var console))
|
||||
return;
|
||||
|
||||
for (int index = 0; index < AtmosNetworksTable.ChildCount; index++)
|
||||
{
|
||||
var entry = (AtmosMonitoringEntryContainer)AtmosNetworksTable.GetChild(index);
|
||||
|
||||
if (entry == null)
|
||||
continue;
|
||||
|
||||
UpdateUIEntry(entry.Data, index, AtmosNetworksTable, console);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:s="clr-namespace:Content.Client.Stylesheets"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Orientation="Vertical" HorizontalExpand ="True" Margin="0 0 0 3">
|
||||
|
||||
<!-- Network selection button -->
|
||||
<Button Name="FocusButton" HorizontalExpand="True" VerticalExpand="True" Margin="0 0 6 8" StyleClasses="OpenLeft" Access="Public">
|
||||
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical">
|
||||
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Horizontal" SetHeight="32">
|
||||
<PanelContainer Name="NetworkColorStripe" HorizontalAlignment="Left" SetWidth="8" VerticalExpand="True" Margin="-8 -2 0 0">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#d7d7d7"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
</PanelContainer>
|
||||
<Label Name="NetworkNameLabel" Text="???" HorizontalExpand="True" HorizontalAlignment="Center"/>
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Panel that appears on selecting the device -->
|
||||
|
||||
<PanelContainer HorizontalExpand="True" Margin="-8 0 -14 -4" Access="Public">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#25252a"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer Name="MainDataContainer" HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical">
|
||||
<Control>
|
||||
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical">
|
||||
<BoxContainer HorizontalExpand="True" Orientation="Horizontal">
|
||||
<Label Name="TemperatureHeaderLabel" Text="{Loc 'atmos-alerts-window-temperature-label'}" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||
<Label Name="PressureHeaderLabel" Text="{Loc 'atmos-alerts-window-pressure-label'}" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||
<Label Name="TotalMolHeaderLabel" Text="{Loc 'atmos-alerts-window-total-mol-label'}" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||
</BoxContainer>
|
||||
<PanelContainer HorizontalExpand="True">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#202023"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
<BoxContainer HorizontalExpand="True" Orientation="Horizontal">
|
||||
<Label Name="TemperatureLabel" Text="???" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||
<Label Name="PressureLabel" Text="???" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||
<Label Name="TotalMolLabel" Text="???" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
<BoxContainer HorizontalExpand="True" Orientation="Horizontal" Margin="8 0">
|
||||
<TextureRect Name="ArrowTexture" VerticalAlignment="Center" SetSize="12 12" Stretch="KeepAspectCentered" Margin="3 0" TexturePath="/Textures/Interface/Nano/triangle_right.png"></TextureRect>
|
||||
<Label Name="GasesHeaderLabel" Text="{Loc 'atmos-monitoring-window-label-gases'}" HorizontalAlignment="Left" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="4 0 0 0" SetHeight="24"></Label>
|
||||
</BoxContainer>
|
||||
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
|
||||
<!-- Atmosphere status -->
|
||||
<Control Name="FocusContainer" ReservesSpace="False" Visible="False">
|
||||
<!-- Main container for displaying atmospheric data -->
|
||||
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical">
|
||||
<PanelContainer HorizontalExpand="True">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#202023"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
|
||||
<!-- Gas entries added via C# code -->
|
||||
<GridContainer Name="GasGridContainer" HorizontalExpand="True" Columns = "4"></GridContainer>
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
</BoxContainer>
|
||||
|
||||
<!-- If the alarm is inactive, this is label is displayed instead -->
|
||||
<Label Name="NoDataLabel" Text="{Loc 'atmos-alerts-window-no-data-available'}" HorizontalAlignment="Center" Margin="0 15" FontColorOverride="#a9a9a9" ReservesSpace="False" Visible="False"></Label>
|
||||
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
</Button>
|
||||
</BoxContainer>
|
||||
@@ -0,0 +1,166 @@
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Temperature;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Client.Atmos.Consoles;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AtmosMonitoringEntryContainer : BoxContainer
|
||||
{
|
||||
public AtmosMonitoringConsoleEntry Data;
|
||||
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IResourceCache _cache;
|
||||
|
||||
public AtmosMonitoringEntryContainer(AtmosMonitoringConsoleEntry data)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_entManager = IoCManager.Resolve<IEntityManager>();
|
||||
_cache = IoCManager.Resolve<IResourceCache>();
|
||||
|
||||
Data = data;
|
||||
|
||||
// Modulate colored stripe
|
||||
NetworkColorStripe.Modulate = data.Color;
|
||||
|
||||
// Load fonts
|
||||
var headerFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Bold.ttf"), 11);
|
||||
var normalFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSansDisplay/NotoSansDisplay-Regular.ttf"), 11);
|
||||
|
||||
// Set fonts
|
||||
TemperatureHeaderLabel.FontOverride = headerFont;
|
||||
PressureHeaderLabel.FontOverride = headerFont;
|
||||
TotalMolHeaderLabel.FontOverride = headerFont;
|
||||
GasesHeaderLabel.FontOverride = headerFont;
|
||||
|
||||
TemperatureLabel.FontOverride = normalFont;
|
||||
PressureLabel.FontOverride = normalFont;
|
||||
TotalMolLabel.FontOverride = normalFont;
|
||||
|
||||
NoDataLabel.FontOverride = headerFont;
|
||||
}
|
||||
|
||||
public void UpdateEntry(AtmosMonitoringConsoleEntry updatedData, bool isFocus)
|
||||
{
|
||||
// Load fonts
|
||||
var normalFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSansDisplay/NotoSansDisplay-Regular.ttf"), 11);
|
||||
|
||||
// Update name and values
|
||||
if (!string.IsNullOrEmpty(updatedData.Address))
|
||||
NetworkNameLabel.Text = Loc.GetString("atmos-alerts-window-alarm-label", ("name", updatedData.EntityName), ("address", updatedData.Address));
|
||||
|
||||
else
|
||||
NetworkNameLabel.Text = Loc.GetString(updatedData.EntityName);
|
||||
|
||||
Data = updatedData;
|
||||
|
||||
// Modulate colored stripe
|
||||
NetworkColorStripe.Modulate = Data.Color;
|
||||
|
||||
// Focus updates
|
||||
if (isFocus)
|
||||
SetAsFocus();
|
||||
else
|
||||
RemoveAsFocus();
|
||||
|
||||
// Check if powered
|
||||
if (!updatedData.IsPowered)
|
||||
{
|
||||
MainDataContainer.Visible = false;
|
||||
NoDataLabel.Visible = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Set container visibility
|
||||
MainDataContainer.Visible = true;
|
||||
NoDataLabel.Visible = false;
|
||||
|
||||
// Update temperature
|
||||
var isNotVacuum = updatedData.TotalMolData > 1e-6f;
|
||||
var tempK = (FixedPoint2)updatedData.TemperatureData;
|
||||
var tempC = (FixedPoint2)TemperatureHelpers.KelvinToCelsius(tempK.Float());
|
||||
|
||||
TemperatureLabel.Text = isNotVacuum ?
|
||||
Loc.GetString("atmos-alerts-window-temperature-value", ("valueInC", tempC), ("valueInK", tempK)) :
|
||||
Loc.GetString("atmos-alerts-window-invalid-value");
|
||||
|
||||
TemperatureLabel.FontColorOverride = isNotVacuum ? Color.DarkGray : StyleNano.DisabledFore;
|
||||
|
||||
// Update pressure
|
||||
PressureLabel.Text = Loc.GetString("atmos-alerts-window-pressure-value", ("value", (FixedPoint2)updatedData.PressureData));
|
||||
PressureLabel.FontColorOverride = isNotVacuum ? Color.DarkGray : StyleNano.DisabledFore;
|
||||
|
||||
// Update total mol
|
||||
TotalMolLabel.Text = Loc.GetString("atmos-alerts-window-total-mol-value", ("value", (FixedPoint2)updatedData.TotalMolData));
|
||||
TotalMolLabel.FontColorOverride = isNotVacuum ? Color.DarkGray : StyleNano.DisabledFore;
|
||||
|
||||
// Update other present gases
|
||||
GasGridContainer.RemoveAllChildren();
|
||||
|
||||
if (updatedData.GasData.Count() == 0)
|
||||
{
|
||||
// No gases
|
||||
var gasLabel = new Label()
|
||||
{
|
||||
Text = Loc.GetString("atmos-alerts-window-other-gases-value-nil"),
|
||||
FontOverride = normalFont,
|
||||
FontColorOverride = StyleNano.DisabledFore,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalExpand = true,
|
||||
Margin = new Thickness(0, 2, 0, 0),
|
||||
SetHeight = 24f,
|
||||
};
|
||||
|
||||
GasGridContainer.AddChild(gasLabel);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Add an entry for each gas
|
||||
foreach (var (gas, percent) in updatedData.GasData)
|
||||
{
|
||||
var gasPercent = (FixedPoint2)0f;
|
||||
gasPercent = percent * 100f;
|
||||
|
||||
var gasAbbreviation = Atmospherics.GasAbbreviations.GetValueOrDefault(gas, Loc.GetString("gas-unknown-abbreviation"));
|
||||
|
||||
var gasLabel = new Label()
|
||||
{
|
||||
Text = Loc.GetString("atmos-alerts-window-other-gases-value", ("shorthand", gasAbbreviation), ("value", gasPercent)),
|
||||
FontOverride = normalFont,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalExpand = true,
|
||||
Margin = new Thickness(0, 2, 0, 0),
|
||||
SetHeight = 24f,
|
||||
};
|
||||
|
||||
GasGridContainer.AddChild(gasLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetAsFocus()
|
||||
{
|
||||
FocusButton.AddStyleClass(StyleNano.StyleClassButtonColorGreen);
|
||||
ArrowTexture.TexturePath = "/Textures/Interface/Nano/inverted_triangle.svg.png";
|
||||
FocusContainer.Visible = true;
|
||||
}
|
||||
|
||||
public void RemoveAsFocus()
|
||||
{
|
||||
FocusButton.RemoveStyleClass(StyleNano.StyleClassButtonColorGreen);
|
||||
ArrowTexture.TexturePath = "/Textures/Interface/Nano/triangle_right.png";
|
||||
FocusContainer.Visible = false;
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,6 @@ using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Atmos.Piping;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
||||
|
||||
namespace Content.Client.Atmos.EntitySystems;
|
||||
|
||||
@@ -19,7 +17,7 @@ public sealed class AtmosPipeAppearanceSystem : EntitySystem
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PipeAppearanceComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<PipeAppearanceComponent, AppearanceChangeEvent>(OnAppearanceChanged, after: new[] { typeof(SubFloorHideSystem) });
|
||||
SubscribeLocalEvent<PipeAppearanceComponent, AppearanceChangeEvent>(OnAppearanceChanged, after: [typeof(SubFloorHideSystem)]);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, PipeAppearanceComponent component, ComponentInit args)
|
||||
@@ -84,7 +82,8 @@ public sealed class AtmosPipeAppearanceSystem : EntitySystem
|
||||
|
||||
layer.Visible &= visible;
|
||||
|
||||
if (!visible) continue;
|
||||
if (!visible)
|
||||
continue;
|
||||
|
||||
layer.Color = color;
|
||||
}
|
||||
|
||||
23
Content.Client/Atmos/EntitySystems/GasPressurePumpSystem.cs
Normal file
23
Content.Client/Atmos/EntitySystems/GasPressurePumpSystem.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Content.Client.Atmos.UI;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Atmos.EntitySystems;
|
||||
using Content.Shared.Atmos.Piping.Binary.Components;
|
||||
|
||||
namespace Content.Client.Atmos.EntitySystems;
|
||||
|
||||
public sealed class GasPressurePumpSystem : SharedGasPressurePumpSystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<GasPressurePumpComponent, AfterAutoHandleStateEvent>(OnPumpUpdate);
|
||||
}
|
||||
|
||||
private void OnPumpUpdate(Entity<GasPressurePumpComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
if (UserInterfaceSystem.TryGetOpenUi<GasPressurePumpBoundUserInterface>(ent.Owner, GasPressurePumpUiKey.Key, out var bui))
|
||||
{
|
||||
bui.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Atmos.Monitor;
|
||||
using Content.Shared.Power;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Client.Atmos.Monitor;
|
||||
|
||||
@@ -27,7 +22,7 @@ public sealed class AtmosAlarmableVisualsSystem : VisualizerSystem<AtmosAlarmabl
|
||||
{
|
||||
foreach (var visLayer in component.HideOnDepowered)
|
||||
{
|
||||
if (args.Sprite.LayerMapTryGet(visLayer, out int powerVisibilityLayer))
|
||||
if (args.Sprite.LayerMapTryGet(visLayer, out var powerVisibilityLayer))
|
||||
args.Sprite.LayerSetVisible(powerVisibilityLayer, powered);
|
||||
}
|
||||
}
|
||||
@@ -36,7 +31,7 @@ public sealed class AtmosAlarmableVisualsSystem : VisualizerSystem<AtmosAlarmabl
|
||||
{
|
||||
foreach (var (setLayer, powerState) in component.SetOnDepowered)
|
||||
{
|
||||
if (args.Sprite.LayerMapTryGet(setLayer, out int setStateLayer))
|
||||
if (args.Sprite.LayerMapTryGet(setLayer, out var setStateLayer))
|
||||
args.Sprite.LayerSetState(setStateLayer, new RSI.StateId(powerState));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Monitor;
|
||||
using Content.Shared.Atmos.Monitor.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
|
||||
namespace Content.Client.Atmos.Monitor.UI;
|
||||
|
||||
@@ -78,6 +74,7 @@ public sealed class AirAlarmBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing) _window?.Dispose();
|
||||
if (disposing)
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ using Content.Shared.Atmos.Monitor.Components;
|
||||
using Content.Shared.Atmos.Piping.Unary.Components;
|
||||
using Content.Shared.Temperature;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
@@ -59,7 +58,7 @@ public sealed partial class AirAlarmWindow : FancyWindow
|
||||
AirAlarmMode.Fill => "air-alarm-ui-mode-fill",
|
||||
AirAlarmMode.Panic => "air-alarm-ui-mode-panic",
|
||||
AirAlarmMode.None => "air-alarm-ui-mode-none",
|
||||
_ => "error"
|
||||
_ => "error",
|
||||
};
|
||||
_modes.AddItem(Loc.GetString(text));
|
||||
}
|
||||
@@ -70,7 +69,7 @@ public sealed partial class AirAlarmWindow : FancyWindow
|
||||
AirAlarmModeChanged!.Invoke((AirAlarmMode) args.Id);
|
||||
};
|
||||
|
||||
_autoMode.OnToggled += args =>
|
||||
_autoMode.OnToggled += _ =>
|
||||
{
|
||||
AutoModeChanged!.Invoke(_autoMode.Pressed);
|
||||
};
|
||||
@@ -176,22 +175,18 @@ public sealed partial class AirAlarmWindow : FancyWindow
|
||||
|
||||
public static Color ColorForThreshold(float amount, AtmosAlarmThreshold threshold)
|
||||
{
|
||||
threshold.CheckThreshold(amount, out AtmosAlarmType curAlarm);
|
||||
threshold.CheckThreshold(amount, out var curAlarm);
|
||||
return ColorForAlarm(curAlarm);
|
||||
}
|
||||
|
||||
public static Color ColorForAlarm(AtmosAlarmType curAlarm)
|
||||
{
|
||||
if(curAlarm == AtmosAlarmType.Danger)
|
||||
return curAlarm switch
|
||||
{
|
||||
return StyleNano.DangerousRedFore;
|
||||
}
|
||||
else if(curAlarm == AtmosAlarmType.Warning)
|
||||
{
|
||||
return StyleNano.ConcerningOrangeFore;
|
||||
}
|
||||
|
||||
return StyleNano.GoodGreenFore;
|
||||
AtmosAlarmType.Danger => StyleNano.DangerousRedFore,
|
||||
AtmosAlarmType.Warning => StyleNano.ConcerningOrangeFore,
|
||||
_ => StyleNano.GoodGreenFore,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
using System;
|
||||
using Content.Shared.Atmos.Monitor;
|
||||
using Content.Shared.Atmos.Monitor.Components;
|
||||
using Content.Shared.Atmos.Piping.Unary.Components;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Client.Atmos.Monitor.UI.Widgets;
|
||||
|
||||
@@ -25,7 +21,7 @@ public sealed partial class PumpControl : BoxContainer
|
||||
private OptionButton _pressureCheck => CPressureCheck;
|
||||
private FloatSpinBox _externalBound => CExternalBound;
|
||||
private FloatSpinBox _internalBound => CInternalBound;
|
||||
private Button _copySettings => CCopySettings;
|
||||
private Button _copySettings => CCopySettings;
|
||||
|
||||
public PumpControl(GasVentPumpData data, string address)
|
||||
{
|
||||
@@ -86,7 +82,7 @@ public sealed partial class PumpControl : BoxContainer
|
||||
_data.PressureChecks = (VentPressureBound) args.Id;
|
||||
PumpDataChanged?.Invoke(_address, _data);
|
||||
};
|
||||
|
||||
|
||||
_copySettings.OnPressed += _ =>
|
||||
{
|
||||
PumpDataCopied?.Invoke(_data);
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Monitor;
|
||||
using Content.Shared.Atmos.Monitor.Components;
|
||||
using Content.Shared.Atmos.Piping.Unary.Components;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Client.Atmos.Monitor.UI.Widgets;
|
||||
|
||||
@@ -27,7 +21,7 @@ public sealed partial class ScrubberControl : BoxContainer
|
||||
private OptionButton _pumpDirection => CPumpDirection;
|
||||
private FloatSpinBox _volumeRate => CVolumeRate;
|
||||
private CheckBox _wideNet => CWideNet;
|
||||
private Button _copySettings => CCopySettings;
|
||||
private Button _copySettings => CCopySettings;
|
||||
|
||||
private GridContainer _gases => CGasContainer;
|
||||
private Dictionary<Gas, Button> _gasControls = new();
|
||||
@@ -77,7 +71,7 @@ public sealed partial class ScrubberControl : BoxContainer
|
||||
_data.PumpDirection = (ScrubberPumpDirection) args.Id;
|
||||
ScrubberDataChanged?.Invoke(_address, _data);
|
||||
};
|
||||
|
||||
|
||||
_copySettings.OnPressed += _ =>
|
||||
{
|
||||
ScrubberDataCopied?.Invoke(_data);
|
||||
|
||||
@@ -43,7 +43,8 @@ public sealed partial class SensorInfo : BoxContainer
|
||||
var label = new RichTextLabel();
|
||||
|
||||
var fractionGas = amount / data.TotalMoles;
|
||||
label.SetMarkup(Loc.GetString("air-alarm-ui-gases-indicator", ("gas", $"{gas}"),
|
||||
label.SetMarkup(Loc.GetString("air-alarm-ui-gases-indicator",
|
||||
("gas", $"{gas}"),
|
||||
("color", AirAlarmWindow.ColorForThreshold(fractionGas, data.GasThresholds[gas])),
|
||||
("amount", $"{amount:0.####}"),
|
||||
("percentage", $"{(100 * fractionGas):0.##}")));
|
||||
@@ -53,9 +54,9 @@ public sealed partial class SensorInfo : BoxContainer
|
||||
var threshold = data.GasThresholds[gas];
|
||||
var gasThresholdControl = new ThresholdControl(Loc.GetString($"air-alarm-ui-thresholds-gas-title", ("gas", $"{gas}")), threshold, AtmosMonitorThresholdType.Gas, gas, 100);
|
||||
gasThresholdControl.Margin = new Thickness(20, 2, 2, 2);
|
||||
gasThresholdControl.ThresholdDataChanged += (type, threshold, arg3) =>
|
||||
gasThresholdControl.ThresholdDataChanged += (type, alarmThreshold, arg3) =>
|
||||
{
|
||||
OnThresholdUpdate!(_address, type, threshold, arg3);
|
||||
OnThresholdUpdate!(_address, type, alarmThreshold, arg3);
|
||||
};
|
||||
|
||||
_gasThresholds.Add(gas, gasThresholdControl);
|
||||
@@ -64,7 +65,8 @@ public sealed partial class SensorInfo : BoxContainer
|
||||
|
||||
_pressureThreshold = new ThresholdControl(Loc.GetString("air-alarm-ui-thresholds-pressure-title"), data.PressureThreshold, AtmosMonitorThresholdType.Pressure);
|
||||
PressureThresholdContainer.AddChild(_pressureThreshold);
|
||||
_temperatureThreshold = new ThresholdControl(Loc.GetString("air-alarm-ui-thresholds-temperature-title"), data.TemperatureThreshold,
|
||||
_temperatureThreshold = new ThresholdControl(Loc.GetString("air-alarm-ui-thresholds-temperature-title"),
|
||||
data.TemperatureThreshold,
|
||||
AtmosMonitorThresholdType.Temperature);
|
||||
TemperatureThresholdContainer.AddChild(_temperatureThreshold);
|
||||
|
||||
@@ -103,7 +105,8 @@ public sealed partial class SensorInfo : BoxContainer
|
||||
}
|
||||
|
||||
var fractionGas = amount / data.TotalMoles;
|
||||
label.SetMarkup(Loc.GetString("air-alarm-ui-gases-indicator", ("gas", $"{gas}"),
|
||||
label.SetMarkup(Loc.GetString("air-alarm-ui-gases-indicator",
|
||||
("gas", $"{gas}"),
|
||||
("color", AirAlarmWindow.ColorForThreshold(fractionGas, data.GasThresholds[gas])),
|
||||
("amount", $"{amount:0.####}"),
|
||||
("percentage", $"{(100 * fractionGas):0.##}")));
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using Content.Client.Message;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Monitor;
|
||||
using Content.Shared.Temperature;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
using System;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Monitor;
|
||||
using Content.Shared.Atmos.Monitor.Components;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
// holy FUCK
|
||||
// this technically works because some of this you can *not* do in XAML but holy FUCK
|
||||
@@ -115,29 +111,38 @@ public sealed partial class ThresholdControl : BoxContainer
|
||||
_enabled.Pressed = !_threshold.Ignore;
|
||||
}
|
||||
|
||||
private String LabelForBound(string boundType) //<todo.eoin Replace this with enums
|
||||
private string LabelForBound(string boundType) //<todo.eoin Replace this with enums
|
||||
{
|
||||
return Loc.GetString($"air-alarm-ui-thresholds-{boundType}");
|
||||
}
|
||||
|
||||
public void UpdateThresholdData(AtmosAlarmThreshold threshold, float currentAmount)
|
||||
{
|
||||
threshold.CheckThreshold(currentAmount, out AtmosAlarmType alarm, out AtmosMonitorThresholdBound which);
|
||||
threshold.CheckThreshold(currentAmount, out var alarm, out var bound);
|
||||
|
||||
var upperDangerState = AtmosAlarmType.Normal;
|
||||
var lowerDangerState = AtmosAlarmType.Normal;
|
||||
var upperWarningState = AtmosAlarmType.Normal;
|
||||
var lowerWarningState = AtmosAlarmType.Normal;
|
||||
|
||||
if(alarm == AtmosAlarmType.Danger)
|
||||
switch (alarm)
|
||||
{
|
||||
if(which == AtmosMonitorThresholdBound.Upper) upperDangerState = alarm;
|
||||
else lowerDangerState = alarm;
|
||||
}
|
||||
else if(alarm == AtmosAlarmType.Warning)
|
||||
{
|
||||
if(which == AtmosMonitorThresholdBound.Upper) upperWarningState = alarm;
|
||||
else lowerWarningState = alarm;
|
||||
case AtmosAlarmType.Danger:
|
||||
{
|
||||
if (bound == AtmosMonitorThresholdBound.Upper)
|
||||
upperDangerState = alarm;
|
||||
else
|
||||
lowerDangerState = alarm;
|
||||
break;
|
||||
}
|
||||
case AtmosAlarmType.Warning:
|
||||
{
|
||||
if (bound == AtmosMonitorThresholdBound.Upper)
|
||||
upperWarningState = alarm;
|
||||
else
|
||||
lowerWarningState = alarm;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_upperBoundControl.SetValue(threshold.UpperBound.Value);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client.Atmos.EntitySystems;
|
||||
@@ -101,14 +102,9 @@ public sealed class AtmosDebugOverlay : Overlay
|
||||
else
|
||||
{
|
||||
// Red-Green-Blue interpolation
|
||||
if (interp < 0.5f)
|
||||
{
|
||||
res = Color.InterpolateBetween(Color.Red, Color.LimeGreen, interp * 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
res = Color.InterpolateBetween(Color.LimeGreen, Color.Blue, (interp - 0.5f) * 2);
|
||||
}
|
||||
res = interp < 0.5f
|
||||
? Color.InterpolateBetween(Color.Red, Color.LimeGreen, interp * 2)
|
||||
: Color.InterpolateBetween(Color.LimeGreen, Color.Blue, (interp - 0.5f) * 2);
|
||||
}
|
||||
|
||||
res = res.WithAlpha(0.75f);
|
||||
@@ -181,7 +177,10 @@ public sealed class AtmosDebugOverlay : Overlay
|
||||
handle.DrawCircle(tileCentre, 0.05f, Color.Black);
|
||||
}
|
||||
|
||||
private void CheckAndShowBlockDir(AtmosDebugOverlayData data, DrawingHandleWorld handle, AtmosDirection dir,
|
||||
private void CheckAndShowBlockDir(
|
||||
AtmosDebugOverlayData data,
|
||||
DrawingHandleWorld handle,
|
||||
AtmosDirection dir,
|
||||
Vector2 tileCentre)
|
||||
{
|
||||
if (!data.BlockDirection.HasFlag(dir))
|
||||
@@ -243,7 +242,7 @@ public sealed class AtmosDebugOverlay : Overlay
|
||||
|
||||
var moles = data.Moles == null
|
||||
? "No Air"
|
||||
: data.Moles.Sum().ToString();
|
||||
: data.Moles.Sum().ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
handle.DrawString(_font, pos, $"Moles: {moles}");
|
||||
pos += offset;
|
||||
@@ -263,7 +262,12 @@ public sealed class AtmosDebugOverlay : Overlay
|
||||
private void GetGrids(MapId mapId, Box2Rotated box)
|
||||
{
|
||||
_grids.Clear();
|
||||
_mapManager.FindGridsIntersecting(mapId, box, ref _grids, (EntityUid uid, MapGridComponent grid,
|
||||
_mapManager.FindGridsIntersecting(
|
||||
mapId,
|
||||
box,
|
||||
ref _grids,
|
||||
(EntityUid uid,
|
||||
MapGridComponent grid,
|
||||
ref List<(Entity<MapGridComponent>, DebugMessage)> state) =>
|
||||
{
|
||||
if (_system.TileData.TryGetValue(uid, out var data))
|
||||
|
||||
@@ -1,65 +1,63 @@
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Atmos.Piping.Binary.Components;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Localizations;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.Atmos.UI
|
||||
namespace Content.Client.Atmos.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="GasPressurePumpWindow"/> and updates it when new server messages are received.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public sealed class GasPressurePumpBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="GasPressurePumpWindow"/> and updates it when new server messages are received.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public sealed class GasPressurePumpBoundUserInterface : BoundUserInterface
|
||||
[ViewVariables]
|
||||
private const float MaxPressure = Atmospherics.MaxOutputPressure;
|
||||
|
||||
[ViewVariables]
|
||||
private GasPressurePumpWindow? _window;
|
||||
|
||||
public GasPressurePumpBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
[ViewVariables]
|
||||
private const float MaxPressure = Atmospherics.MaxOutputPressure;
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
private GasPressurePumpWindow? _window;
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
public GasPressurePumpBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
_window = this.CreateWindow<GasPressurePumpWindow>();
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
_window.ToggleStatusButtonPressed += OnToggleStatusButtonPressed;
|
||||
_window.PumpOutputPressureChanged += OnPumpOutputPressurePressed;
|
||||
Update();
|
||||
}
|
||||
|
||||
_window = this.CreateWindow<GasPressurePumpWindow>();
|
||||
public void Update()
|
||||
{
|
||||
if (_window == null)
|
||||
return;
|
||||
|
||||
_window.ToggleStatusButtonPressed += OnToggleStatusButtonPressed;
|
||||
_window.PumpOutputPressureChanged += OnPumpOutputPressurePressed;
|
||||
}
|
||||
_window.Title = Identity.Name(Owner, EntMan);
|
||||
|
||||
private void OnToggleStatusButtonPressed()
|
||||
{
|
||||
if (_window is null) return;
|
||||
SendMessage(new GasPressurePumpToggleStatusMessage(_window.PumpStatus));
|
||||
}
|
||||
if (!EntMan.TryGetComponent(Owner, out GasPressurePumpComponent? pump))
|
||||
return;
|
||||
|
||||
private void OnPumpOutputPressurePressed(string value)
|
||||
{
|
||||
var pressure = UserInputParser.TryFloat(value, out var parsed) ? parsed : 0f;
|
||||
if (pressure > MaxPressure) pressure = MaxPressure;
|
||||
_window.SetPumpStatus(pump.Enabled);
|
||||
_window.MaxPressure = pump.MaxTargetPressure;
|
||||
_window.SetOutputPressure(pump.TargetPressure);
|
||||
}
|
||||
|
||||
SendMessage(new GasPressurePumpChangeOutputPressureMessage(pressure));
|
||||
}
|
||||
private void OnToggleStatusButtonPressed()
|
||||
{
|
||||
if (_window is null) return;
|
||||
SendPredictedMessage(new GasPressurePumpToggleStatusMessage(_window.PumpStatus));
|
||||
}
|
||||
|
||||
/// <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 GasPressurePumpBoundUserInterfaceState cast)
|
||||
return;
|
||||
|
||||
_window.Title = (cast.PumpLabel);
|
||||
_window.SetPumpStatus(cast.Enabled);
|
||||
_window.SetOutputPressure(cast.OutputPressure);
|
||||
}
|
||||
private void OnPumpOutputPressurePressed(float value)
|
||||
{
|
||||
SendPredictedMessage(new GasPressurePumpChangeOutputPressureMessage(value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
MinSize="200 120" Title="Pressure Pump">
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
SetSize="340 110" MinSize="340 110" Title="Pressure Pump">
|
||||
<BoxContainer Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10">
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<Label Text="{Loc comp-gas-pump-ui-pump-status}"/>
|
||||
<Control MinSize="5 0" />
|
||||
<Label Text="{Loc comp-gas-pump-ui-pump-status}" Margin="0 0 5 0"/>
|
||||
<Button Name="ToggleStatusButton"/>
|
||||
<Control HorizontalExpand="True"/>
|
||||
<Button HorizontalAlignment="Right" Name="SetOutputPressureButton" Text="{Loc comp-gas-pump-ui-pump-set-rate}" Disabled="True" Margin="0 0 5 0"/>
|
||||
<Button Name="SetMaxPressureButton" Text="{Loc comp-gas-pump-ui-pump-set-max}" />
|
||||
</BoxContainer>
|
||||
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<Label Text="{Loc comp-gas-pump-ui-pump-output-pressure}"/>
|
||||
<Control MinSize="5 0" />
|
||||
<LineEdit Name="PumpPressureOutputInput" MinSize="70 0" />
|
||||
<Control MinSize="5 0" />
|
||||
<Button Name="SetMaxPressureButton" Text="{Loc comp-gas-pump-ui-pump-set-max}" />
|
||||
<Control MinSize="5 0" />
|
||||
<Control HorizontalExpand="True" />
|
||||
<Button Name="SetOutputPressureButton" Text="{Loc comp-gas-pump-ui-pump-set-rate}" HorizontalAlignment="Right" Disabled="True"/>
|
||||
<FloatSpinBox HorizontalExpand="True" Name="PumpPressureOutputInput" MinSize="70 0" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
</controls:FancyWindow>
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Content.Client.Atmos.EntitySystems;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Prototypes;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Client.Atmos.UI
|
||||
{
|
||||
@@ -16,12 +10,25 @@ namespace Content.Client.Atmos.UI
|
||||
/// Client-side UI used to control a gas pressure pump.
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class GasPressurePumpWindow : DefaultWindow
|
||||
public sealed partial class GasPressurePumpWindow : FancyWindow
|
||||
{
|
||||
public bool PumpStatus = true;
|
||||
|
||||
public event Action? ToggleStatusButtonPressed;
|
||||
public event Action<string>? PumpOutputPressureChanged;
|
||||
public event Action<float>? PumpOutputPressureChanged;
|
||||
|
||||
public float MaxPressure
|
||||
{
|
||||
get => _maxPressure;
|
||||
set
|
||||
{
|
||||
_maxPressure = value;
|
||||
|
||||
PumpPressureOutputInput.Value = MathF.Min(value, PumpPressureOutputInput.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private float _maxPressure = Atmospherics.MaxOutputPressure;
|
||||
|
||||
public GasPressurePumpWindow()
|
||||
{
|
||||
@@ -30,23 +37,25 @@ namespace Content.Client.Atmos.UI
|
||||
ToggleStatusButton.OnPressed += _ => SetPumpStatus(!PumpStatus);
|
||||
ToggleStatusButton.OnPressed += _ => ToggleStatusButtonPressed?.Invoke();
|
||||
|
||||
PumpPressureOutputInput.OnTextChanged += _ => SetOutputPressureButton.Disabled = false;
|
||||
PumpPressureOutputInput.OnValueChanged += _ => SetOutputPressureButton.Disabled = false;
|
||||
|
||||
SetOutputPressureButton.OnPressed += _ =>
|
||||
{
|
||||
PumpOutputPressureChanged?.Invoke(PumpPressureOutputInput.Text ??= "");
|
||||
PumpPressureOutputInput.Value = Math.Clamp(PumpPressureOutputInput.Value, 0f, _maxPressure);
|
||||
PumpOutputPressureChanged?.Invoke(PumpPressureOutputInput.Value);
|
||||
SetOutputPressureButton.Disabled = true;
|
||||
};
|
||||
|
||||
SetMaxPressureButton.OnPressed += _ =>
|
||||
{
|
||||
PumpPressureOutputInput.Text = Atmospherics.MaxOutputPressure.ToString(CultureInfo.CurrentCulture);
|
||||
PumpPressureOutputInput.Value = _maxPressure;
|
||||
SetOutputPressureButton.Disabled = false;
|
||||
};
|
||||
}
|
||||
|
||||
public void SetOutputPressure(float pressure)
|
||||
{
|
||||
PumpPressureOutputInput.Text = pressure.ToString(CultureInfo.CurrentCulture);
|
||||
PumpPressureOutputInput.Value = pressure;
|
||||
}
|
||||
|
||||
public void SetPumpStatus(bool enabled)
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Numerics;
|
||||
using Content.Client.Chat.Managers;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Speech;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
@@ -141,7 +142,12 @@ namespace Content.Client.Chat.UI
|
||||
Modulate = Color.White;
|
||||
}
|
||||
|
||||
var offset = (-_eyeManager.CurrentEye.Rotation).ToWorldVec() * -EntityVerticalOffset;
|
||||
var baseOffset = 0f;
|
||||
|
||||
if (_entityManager.TryGetComponent<SpeechComponent>(_senderEntity, out var speech))
|
||||
baseOffset = speech.SpeechBubbleOffset;
|
||||
|
||||
var offset = (-_eyeManager.CurrentEye.Rotation).ToWorldVec() * -(EntityVerticalOffset + baseOffset);
|
||||
var worldPos = _transformSystem.GetWorldPosition(xform) + offset;
|
||||
|
||||
var lowerCenter = _eyeManager.WorldToScreen(worldPos) / UIScale;
|
||||
|
||||
@@ -12,6 +12,7 @@ using Robust.Shared.Utility;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Client.Graphics;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Content.Client.Chemistry.UI
|
||||
@@ -90,10 +91,40 @@ namespace Content.Client.Chemistry.UI
|
||||
|
||||
private ReagentButton MakeReagentButton(string text, ChemMasterReagentAmount amount, ReagentId id, bool isBuffer, string styleClass)
|
||||
{
|
||||
var button = new ReagentButton(text, amount, id, isBuffer, styleClass);
|
||||
button.OnPressed += args
|
||||
=> OnReagentButtonPressed?.Invoke(args, button);
|
||||
return button;
|
||||
var reagentTransferButton = new ReagentButton(text, amount, id, isBuffer, styleClass);
|
||||
reagentTransferButton.OnPressed += args
|
||||
=> OnReagentButtonPressed?.Invoke(args, reagentTransferButton);
|
||||
return reagentTransferButton;
|
||||
}
|
||||
/// <summary>
|
||||
/// Conditionally generates a set of reagent buttons based on the supplied boolean argument.
|
||||
/// This was moved outside of BuildReagentRow to facilitate conditional logic, stops indentation depth getting out of hand as well.
|
||||
/// </summary>
|
||||
private List<ReagentButton> CreateReagentTransferButtons(ReagentId reagent, bool isBuffer, bool addReagentButtons)
|
||||
{
|
||||
if (!addReagentButtons)
|
||||
return new List<ReagentButton>(); // Return an empty list if reagentTransferButton creation is disabled.
|
||||
|
||||
var buttonConfigs = new (string text, ChemMasterReagentAmount amount, string styleClass)[]
|
||||
{
|
||||
("1", ChemMasterReagentAmount.U1, StyleBase.ButtonOpenBoth),
|
||||
("5", ChemMasterReagentAmount.U5, StyleBase.ButtonOpenBoth),
|
||||
("10", ChemMasterReagentAmount.U10, StyleBase.ButtonOpenBoth),
|
||||
("25", ChemMasterReagentAmount.U25, StyleBase.ButtonOpenBoth),
|
||||
("50", ChemMasterReagentAmount.U50, StyleBase.ButtonOpenBoth),
|
||||
("100", ChemMasterReagentAmount.U100, StyleBase.ButtonOpenBoth),
|
||||
(Loc.GetString("chem-master-window-buffer-all-amount"), ChemMasterReagentAmount.All, StyleBase.ButtonOpenLeft),
|
||||
};
|
||||
|
||||
var buttons = new List<ReagentButton>();
|
||||
|
||||
foreach (var (text, amount, styleClass) in buttonConfigs)
|
||||
{
|
||||
var reagentTransferButton = MakeReagentButton(text, amount, reagent, isBuffer, styleClass);
|
||||
buttons.Add(reagentTransferButton);
|
||||
}
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -102,25 +133,36 @@ namespace Content.Client.Chemistry.UI
|
||||
/// <param name="state">State data sent by the server.</param>
|
||||
public void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
var castState = (ChemMasterBoundUserInterfaceState) state;
|
||||
var castState = (ChemMasterBoundUserInterfaceState)state;
|
||||
|
||||
if (castState.UpdateLabel)
|
||||
LabelLine = GenerateLabel(castState);
|
||||
|
||||
// Ensure the Panel Info is updated, including UI elements for Buffer Volume, Output Container and so on
|
||||
UpdatePanelInfo(castState);
|
||||
|
||||
var output = castState.OutputContainerInfo;
|
||||
|
||||
|
||||
BufferCurrentVolume.Text = $" {castState.BufferCurrentVolume?.Int() ?? 0}u";
|
||||
|
||||
|
||||
InputEjectButton.Disabled = castState.InputContainerInfo is null;
|
||||
OutputEjectButton.Disabled = output is null;
|
||||
CreateBottleButton.Disabled = output?.Reagents == null;
|
||||
CreatePillButton.Disabled = output?.Entities == null;
|
||||
|
||||
OutputEjectButton.Disabled = castState.OutputContainerInfo is null;
|
||||
CreateBottleButton.Disabled = castState.OutputContainerInfo?.Reagents == null;
|
||||
CreatePillButton.Disabled = castState.OutputContainerInfo?.Entities == null;
|
||||
|
||||
UpdateDosageFields(castState);
|
||||
}
|
||||
|
||||
//assign default values for pill and bottle fields.
|
||||
private void UpdateDosageFields(ChemMasterBoundUserInterfaceState castState)
|
||||
{
|
||||
var output = castState.OutputContainerInfo;
|
||||
var remainingCapacity = output is null ? 0 : (output.MaxVolume - output.CurrentVolume).Int();
|
||||
var holdsReagents = output?.Reagents != null;
|
||||
var pillNumberMax = holdsReagents ? 0 : remainingCapacity;
|
||||
var bottleAmountMax = holdsReagents ? remainingCapacity : 0;
|
||||
var bufferVolume = castState.BufferCurrentVolume?.Int() ?? 0;
|
||||
|
||||
PillDosage.Value = (int)Math.Min(bufferVolume, castState.PillDosageLimit);
|
||||
|
||||
PillTypeButtons[castState.SelectedPillType].Pressed = true;
|
||||
PillNumber.IsValid = x => x >= 0 && x <= pillNumberMax;
|
||||
PillDosage.IsValid = x => x > 0 && x <= castState.PillDosageLimit;
|
||||
@@ -130,8 +172,19 @@ namespace Content.Client.Chemistry.UI
|
||||
PillNumber.Value = pillNumberMax;
|
||||
if (BottleDosage.Value > bottleAmountMax)
|
||||
BottleDosage.Value = bottleAmountMax;
|
||||
}
|
||||
|
||||
// Avoid division by zero
|
||||
if (PillDosage.Value > 0)
|
||||
{
|
||||
PillNumber.Value = Math.Min(bufferVolume / PillDosage.Value, pillNumberMax);
|
||||
}
|
||||
else
|
||||
{
|
||||
PillNumber.Value = 0;
|
||||
}
|
||||
|
||||
BottleDosage.Value = Math.Min(bottleAmountMax, bufferVolume);
|
||||
}
|
||||
/// <summary>
|
||||
/// Generate a product label based on reagents in the buffer.
|
||||
/// </summary>
|
||||
@@ -178,46 +231,23 @@ namespace Content.Client.Chemistry.UI
|
||||
var bufferVol = new Label
|
||||
{
|
||||
Text = $"{state.BufferCurrentVolume}u",
|
||||
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
|
||||
StyleClasses = { StyleNano.StyleClassLabelSecondaryColor }
|
||||
};
|
||||
bufferHBox.AddChild(bufferVol);
|
||||
|
||||
// initialises rowCount to allow for striped rows
|
||||
|
||||
var rowCount = 0;
|
||||
foreach (var (reagent, quantity) in state.BufferReagents)
|
||||
{
|
||||
// Try to get the prototype for the given reagent. This gives us its name.
|
||||
_prototypeManager.TryIndex(reagent.Prototype, out ReagentPrototype? proto);
|
||||
var reagentId = reagent;
|
||||
_prototypeManager.TryIndex(reagentId.Prototype, out ReagentPrototype? proto);
|
||||
var name = proto?.LocalizedName ?? Loc.GetString("chem-master-window-unknown-reagent-text");
|
||||
|
||||
if (proto != null)
|
||||
{
|
||||
BufferInfo.Children.Add(new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
new Label {Text = $"{name}: "},
|
||||
new Label
|
||||
{
|
||||
Text = $"{quantity}u",
|
||||
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
|
||||
},
|
||||
|
||||
// Padding
|
||||
new Control {HorizontalExpand = true},
|
||||
|
||||
MakeReagentButton("1", ChemMasterReagentAmount.U1, reagent, true, StyleBase.ButtonOpenRight),
|
||||
MakeReagentButton("5", ChemMasterReagentAmount.U5, reagent, true, StyleBase.ButtonOpenBoth),
|
||||
MakeReagentButton("10", ChemMasterReagentAmount.U10, reagent, true, StyleBase.ButtonOpenBoth),
|
||||
MakeReagentButton("25", ChemMasterReagentAmount.U25, reagent, true, StyleBase.ButtonOpenBoth),
|
||||
MakeReagentButton("50", ChemMasterReagentAmount.U50, reagent, true, StyleBase.ButtonOpenBoth),
|
||||
MakeReagentButton("100", ChemMasterReagentAmount.U100, reagent, true, StyleBase.ButtonOpenBoth),
|
||||
MakeReagentButton(Loc.GetString("chem-master-window-buffer-all-amount"), ChemMasterReagentAmount.All, reagent, true, StyleBase.ButtonOpenLeft),
|
||||
}
|
||||
});
|
||||
}
|
||||
var reagentColor = proto?.SubstanceColor ?? default(Color);
|
||||
BufferInfo.Children.Add(BuildReagentRow(reagentColor, rowCount++, name, reagentId, quantity, true, true));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void BuildContainerUI(Control control, ContainerInfo? info, bool addReagentButtons)
|
||||
{
|
||||
control.Children.Clear();
|
||||
@@ -228,104 +258,111 @@ namespace Content.Client.Chemistry.UI
|
||||
{
|
||||
Text = Loc.GetString("chem-master-window-no-container-loaded-text")
|
||||
});
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
// Name of the container and its fill status (Ex: 44/100u)
|
||||
control.Children.Add(new BoxContainer
|
||||
{
|
||||
// Name of the container and its fill status (Ex: 44/100u)
|
||||
control.Children.Add(new BoxContainer
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
new Label { Text = $"{info.DisplayName}: " },
|
||||
new Label
|
||||
{
|
||||
new Label {Text = $"{info.DisplayName}: "},
|
||||
new Label
|
||||
{
|
||||
Text = $"{info.CurrentVolume}/{info.MaxVolume}",
|
||||
StyleClasses = {StyleNano.StyleClassLabelSecondaryColor}
|
||||
}
|
||||
Text = $"{info.CurrentVolume}/{info.MaxVolume}",
|
||||
StyleClasses = { StyleNano.StyleClassLabelSecondaryColor }
|
||||
}
|
||||
});
|
||||
|
||||
IEnumerable<(string Name, ReagentId Id, FixedPoint2 Quantity)> contents;
|
||||
|
||||
if (info.Entities != null)
|
||||
{
|
||||
contents = info.Entities.Select(x => (x.Id, default(ReagentId), x.Quantity));
|
||||
}
|
||||
else if (info.Reagents != null)
|
||||
});
|
||||
// Initialises rowCount to allow for striped rows
|
||||
var rowCount = 0;
|
||||
|
||||
// Handle entities if they are not null
|
||||
if (info.Entities != null)
|
||||
{
|
||||
foreach (var (id, quantity) in info.Entities.Select(x => (x.Id, x.Quantity)))
|
||||
{
|
||||
contents = info.Reagents.Select(x =>
|
||||
{
|
||||
_prototypeManager.TryIndex(x.Reagent.Prototype, out ReagentPrototype? proto);
|
||||
var name = proto?.LocalizedName
|
||||
?? Loc.GetString("chem-master-window-unknown-reagent-text");
|
||||
|
||||
return (name, Id: x.Reagent, x.Quantity);
|
||||
})
|
||||
.OrderBy(r => r.Item1);
|
||||
control.Children.Add(BuildReagentRow(default(Color), rowCount++, id, default(ReagentId), quantity, false, addReagentButtons));
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
// Handle reagents if they are not null
|
||||
if (info.Reagents != null)
|
||||
{
|
||||
foreach (var reagent in info.Reagents)
|
||||
{
|
||||
return;
|
||||
_prototypeManager.TryIndex(reagent.Reagent.Prototype, out ReagentPrototype? proto);
|
||||
var name = proto?.LocalizedName ?? Loc.GetString("chem-master-window-unknown-reagent-text");
|
||||
var reagentColor = proto?.SubstanceColor ?? default(Color);
|
||||
|
||||
control.Children.Add(BuildReagentRow(reagentColor, rowCount++, name, reagent.Reagent, reagent.Quantity, false, addReagentButtons));
|
||||
}
|
||||
|
||||
|
||||
foreach (var (name, id, quantity) in contents)
|
||||
{
|
||||
var inner = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
new Label { Text = $"{name}: " },
|
||||
new Label
|
||||
{
|
||||
Text = $"{quantity}u",
|
||||
StyleClasses = { StyleNano.StyleClassLabelSecondaryColor },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (addReagentButtons)
|
||||
{
|
||||
var cs = inner.Children;
|
||||
|
||||
// Padding
|
||||
cs.Add(new Control { HorizontalExpand = true });
|
||||
|
||||
cs.Add(MakeReagentButton(
|
||||
"1", ChemMasterReagentAmount.U1, id, false, StyleBase.ButtonOpenRight));
|
||||
cs.Add(MakeReagentButton(
|
||||
"5", ChemMasterReagentAmount.U5, id, false, StyleBase.ButtonOpenBoth));
|
||||
cs.Add(MakeReagentButton(
|
||||
"10", ChemMasterReagentAmount.U10, id, false, StyleBase.ButtonOpenBoth));
|
||||
cs.Add(MakeReagentButton(
|
||||
"25", ChemMasterReagentAmount.U25, id, false, StyleBase.ButtonOpenBoth));
|
||||
cs.Add(MakeReagentButton(
|
||||
"50", ChemMasterReagentAmount.U50, id, false, StyleBase.ButtonOpenBoth));
|
||||
cs.Add(MakeReagentButton(
|
||||
"100", ChemMasterReagentAmount.U100, id, false, StyleBase.ButtonOpenBoth));
|
||||
cs.Add(MakeReagentButton(
|
||||
Loc.GetString("chem-master-window-buffer-all-amount"),
|
||||
ChemMasterReagentAmount.All, id, false, StyleBase.ButtonOpenLeft));
|
||||
}
|
||||
|
||||
control.Children.Add(inner);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public String LabelLine
|
||||
/// <summary>
|
||||
/// Take reagent/entity data and present rows, labels, and buttons appropriately. todo sprites?
|
||||
/// </summary>
|
||||
private Control BuildReagentRow(Color reagentColor, int rowCount, string name, ReagentId reagent, FixedPoint2 quantity, bool isBuffer, bool addReagentButtons)
|
||||
{
|
||||
get
|
||||
//Colors rows and sets fallback for reagentcolor to the same as background, this will hide colorPanel for entities hopefully
|
||||
var rowColor1 = Color.FromHex("#1B1B1E");
|
||||
var rowColor2 = Color.FromHex("#202025");
|
||||
var currentRowColor = (rowCount % 2 == 1) ? rowColor1 : rowColor2;
|
||||
if ((reagentColor == default(Color))|(!addReagentButtons))
|
||||
{
|
||||
return LabelLineEdit.Text;
|
||||
reagentColor = currentRowColor;
|
||||
}
|
||||
set
|
||||
//this calls the separated button builder, and stores the return to render after labels
|
||||
var reagentButtonConstructors = CreateReagentTransferButtons(reagent, isBuffer, addReagentButtons);
|
||||
|
||||
// Create the row layout with the color panel
|
||||
var rowContainer = new BoxContainer
|
||||
{
|
||||
LabelLineEdit.Text = value;
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
new Label { Text = $"{name}: " },
|
||||
new Label
|
||||
{
|
||||
Text = $"{quantity}u",
|
||||
StyleClasses = { StyleNano.StyleClassLabelSecondaryColor }
|
||||
},
|
||||
|
||||
// Padding
|
||||
new Control { HorizontalExpand = true },
|
||||
// Colored panels for reagents
|
||||
new PanelContainer
|
||||
{
|
||||
Name = "colorPanel",
|
||||
VerticalExpand = true,
|
||||
MinWidth = 4,
|
||||
PanelOverride = new StyleBoxFlat
|
||||
{
|
||||
BackgroundColor = reagentColor
|
||||
},
|
||||
Margin = new Thickness(0, 1)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Add the reagent buttons after the color panel
|
||||
foreach (var reagentTransferButton in reagentButtonConstructors)
|
||||
{
|
||||
rowContainer.AddChild(reagentTransferButton);
|
||||
}
|
||||
//Apply panencontainer to allow for striped rows
|
||||
return new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat(currentRowColor),
|
||||
Children = { rowContainer }
|
||||
};
|
||||
}
|
||||
|
||||
public string LabelLine
|
||||
{
|
||||
get => LabelLineEdit.Text;
|
||||
set => LabelLineEdit.Text = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ public sealed class ClientClothingSystem : ClothingSystem
|
||||
/// For some context, im currently refactoring inventory. Part of that is slots not being indexed by a massive enum anymore, but by strings.
|
||||
/// Problem here: Every rsi-state is using the old enum-names in their state. I already used the new inventoryslots ALOT. tldr: its this or another week of renaming files.
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, string> TemporarySlotMap = new()
|
||||
public static readonly Dictionary<string, string> TemporarySlotMap = new() //CP14 Public
|
||||
{
|
||||
{"head", "HELMET"},
|
||||
{"eyes", "EYES"},
|
||||
@@ -65,6 +65,7 @@ public sealed class ClientClothingSystem : ClothingSystem
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ClothingComponent, GetEquipmentVisualsEvent>(OnGetVisuals);
|
||||
SubscribeLocalEvent<ClothingComponent, InventoryTemplateUpdated>(OnInventoryTemplateUpdated);
|
||||
|
||||
SubscribeLocalEvent<InventoryComponent, VisualsChangedEvent>(OnVisualsChanged);
|
||||
SubscribeLocalEvent<SpriteComponent, DidUnequipEvent>(OnDidUnequip);
|
||||
@@ -77,11 +78,7 @@ public sealed class ClientClothingSystem : ClothingSystem
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
var enumerator = _inventorySystem.GetSlotEnumerator((uid, component));
|
||||
while (enumerator.NextItem(out var item, out var slot))
|
||||
{
|
||||
RenderEquipment(uid, item, slot.Name, component);
|
||||
}
|
||||
UpdateAllSlots(uid, component);
|
||||
|
||||
// No clothing equipped -> make sure the layer is hidden, though this should already be handled by on-unequip.
|
||||
if (args.Sprite.LayerMapTryGet(HumanoidVisualLayers.StencilMask, out var layer))
|
||||
@@ -91,6 +88,23 @@ public sealed class ClientClothingSystem : ClothingSystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnInventoryTemplateUpdated(Entity<ClothingComponent> ent, ref InventoryTemplateUpdated args)
|
||||
{
|
||||
UpdateAllSlots(ent.Owner, clothing: ent.Comp);
|
||||
}
|
||||
|
||||
private void UpdateAllSlots(
|
||||
EntityUid uid,
|
||||
InventoryComponent? inventoryComponent = null,
|
||||
ClothingComponent? clothing = null)
|
||||
{
|
||||
var enumerator = _inventorySystem.GetSlotEnumerator((uid, inventoryComponent));
|
||||
while (enumerator.NextItem(out var item, out var slot))
|
||||
{
|
||||
RenderEquipment(uid, item, slot.Name, inventoryComponent, clothingComponent: clothing);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGetVisuals(EntityUid uid, ClothingComponent item, GetEquipmentVisualsEvent args)
|
||||
{
|
||||
if (!TryComp(args.Equipee, out InventoryComponent? inventory))
|
||||
|
||||
@@ -7,7 +7,7 @@ using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Clothing;
|
||||
|
||||
public sealed class FlippableClothingVisualizerSystem : VisualizerSystem<FlippableClothingVisualsComponent>
|
||||
public sealed class FlippableClothingVisualizerSystem : VisualizerSystem<FlippableClothingVisualsComponent>
|
||||
{
|
||||
[Dependency] private readonly SharedItemSystem _itemSys = default!;
|
||||
|
||||
|
||||
@@ -1,17 +1,62 @@
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="{Loc 'comms-console-menu-title'}"
|
||||
MinSize="400 225">
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" Margin="5">
|
||||
<TextEdit Name="MessageInput" HorizontalExpand="True" VerticalExpand="True" Margin="0 0 0 5" MinHeight="100" />
|
||||
<Button Name="AnnounceButton" Text="{Loc 'comms-console-menu-announcement-button'}" ToolTip="{Loc 'comms-console-menu-announcement-button-tooltip'}" StyleClasses="OpenLeft" Access="Public" />
|
||||
<Button Name="BroadcastButton" Text="{Loc 'comms-console-menu-broadcast-button'}" ToolTip="{Loc 'comms-console-menu-broadcast-button-tooltip'}" StyleClasses="OpenLeft" Access="Public" />
|
||||
MinSize="400 300">
|
||||
|
||||
<OptionButton Name="AlertLevelButton" ToolTip="{Loc 'comms-console-menu-alert-level-button-tooltip'}" StyleClasses="OpenRight" Access="Public" />
|
||||
<!-- Main Container -->
|
||||
<BoxContainer Orientation="Vertical"
|
||||
HorizontalExpand="False"
|
||||
VerticalExpand="True"
|
||||
Margin="6 6 6 5">
|
||||
|
||||
<Control MinSize="10 10" />
|
||||
<TextEdit Name="MessageInput"
|
||||
VerticalExpand="True"
|
||||
HorizontalExpand="True"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalAlignment="Stretch"
|
||||
MinHeight="100"/>
|
||||
|
||||
<RichTextLabel Name="CountdownLabel" VerticalExpand="True" />
|
||||
<Button Name="EmergencyShuttleButton" Text="Placeholder Text" ToolTip="{Loc 'comms-console-menu-emergency-shuttle-button-tooltip'}" Access="Public" />
|
||||
<!-- ButtonsPart -->
|
||||
<BoxContainer Orientation="Vertical"
|
||||
VerticalAlignment="Bottom"
|
||||
SeparationOverride="4">
|
||||
|
||||
<!-- AnnouncePart -->
|
||||
<BoxContainer Orientation="Vertical"
|
||||
Margin="0 2">
|
||||
|
||||
<Button Name="AnnounceButton"
|
||||
Access="Public"
|
||||
Text="{Loc 'comms-console-menu-announcement-button'}"
|
||||
ToolTip="{Loc 'comms-console-menu-announcement-button-tooltip'}"
|
||||
StyleClasses="OpenLeft"
|
||||
Margin="0 0 1 0"
|
||||
Disabled="True"/>
|
||||
|
||||
<Button Name="BroadcastButton"
|
||||
Access="Public"
|
||||
Text="{Loc 'comms-console-menu-broadcast-button'}"
|
||||
ToolTip="{Loc 'comms-console-menu-broadcast-button-tooltip'}"
|
||||
StyleClasses="OpenBoth"/>
|
||||
|
||||
<OptionButton Name="AlertLevelButton"
|
||||
Access="Public"
|
||||
ToolTip="{Loc 'comms-console-menu-alert-level-button-tooltip'}"
|
||||
StyleClasses="OpenRight"/>
|
||||
|
||||
</BoxContainer>
|
||||
|
||||
<!-- EmergencyPart -->
|
||||
<BoxContainer Orientation="Vertical"
|
||||
SeparationOverride="6">
|
||||
|
||||
<RichTextLabel Name="CountdownLabel"/>
|
||||
|
||||
<Button Name="EmergencyShuttleButton"
|
||||
Access="Public"
|
||||
Text="Placeholder Text"
|
||||
ToolTip="{Loc 'comms-console-menu-emergency-shuttle-button-tooltip'}"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io">
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" SizeFlagsStretchRatio="0.4" Margin="0 0 5 0">
|
||||
<BoxContainer Orientation="Vertical" MinWidth="243" Margin="0 0 5 0">
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="0 0 0 5">
|
||||
<LineEdit Name="SearchBar" PlaceHolder="Search" HorizontalExpand="True"/>
|
||||
<OptionButton Name="OptionCategories" Access="Public" MinSize="130 0"/>
|
||||
</BoxContainer>
|
||||
<ItemList Name="Recipes" Access="Public" SelectMode="Single" VerticalExpand="True"/>
|
||||
<ScrollContainer Name="RecipesGridScrollContainer" VerticalExpand="True" Access="Public" Visible="False">
|
||||
<GridContainer Name="RecipesGrid" Columns="5" Access="Public"/>
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" SizeFlagsStretchRatio="0.6">
|
||||
<Button Name="FavoriteButton" Visible="false" HorizontalExpand="False"
|
||||
HorizontalAlignment="Right" Margin="0 0 0 15"/>
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Button Name="MenuGridViewButton" ToggleMode="True" Text="{Loc construction-menu-grid-view}"/>
|
||||
<Button Name="FavoriteButton" Visible="false"/>
|
||||
</BoxContainer>
|
||||
<Control>
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" Margin="0 0 0 5">
|
||||
<BoxContainer Orientation="Horizontal" Align="Center">
|
||||
|
||||
@@ -25,11 +25,16 @@ namespace Content.Client.Construction.UI
|
||||
OptionButton OptionCategories { get; }
|
||||
|
||||
bool EraseButtonPressed { get; set; }
|
||||
bool GridViewButtonPressed { get; set; }
|
||||
bool BuildButtonPressed { get; set; }
|
||||
|
||||
ItemList Recipes { get; }
|
||||
ItemList RecipeStepList { get; }
|
||||
|
||||
|
||||
ScrollContainer RecipesGridScrollContainer { get; }
|
||||
GridContainer RecipesGrid { get; }
|
||||
|
||||
event EventHandler<(string search, string catagory)> PopulateRecipes;
|
||||
event EventHandler<ItemList.Item?> RecipeSelected;
|
||||
event EventHandler RecipeFavorited;
|
||||
@@ -72,9 +77,16 @@ namespace Content.Client.Construction.UI
|
||||
set => EraseButton.Pressed = value;
|
||||
}
|
||||
|
||||
public bool GridViewButtonPressed
|
||||
{
|
||||
get => MenuGridViewButton.Pressed;
|
||||
set => MenuGridViewButton.Pressed = value;
|
||||
}
|
||||
|
||||
public ConstructionMenu()
|
||||
{
|
||||
SetSize = MinSize = new Vector2(720, 320);
|
||||
SetSize = new Vector2(560, 450);
|
||||
MinSize = new Vector2(560, 320);
|
||||
|
||||
IoCManager.InjectDependencies(this);
|
||||
RobustXamlLoader.Load(this);
|
||||
@@ -102,6 +114,9 @@ namespace Content.Client.Construction.UI
|
||||
EraseButton.OnToggled += args => EraseButtonToggled?.Invoke(this, args.Pressed);
|
||||
|
||||
FavoriteButton.OnPressed += args => RecipeFavorited?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
MenuGridViewButton.OnPressed += _ =>
|
||||
PopulateRecipes?.Invoke(this, (SearchBar.Text, Categories[OptionCategories.SelectedId]));
|
||||
}
|
||||
|
||||
public event EventHandler? ClearAllGhosts;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.UserInterface.Systems.MenuBar.Widgets;
|
||||
using Content.Shared.Construction.Prototypes;
|
||||
using Content.Shared.Tag;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
@@ -11,7 +12,6 @@ using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Robust.Client.UserInterface.Controls.BaseButton;
|
||||
|
||||
@@ -33,10 +33,12 @@ namespace Content.Client.Construction.UI
|
||||
|
||||
private readonly IConstructionMenuView _constructionView;
|
||||
private readonly EntityWhitelistSystem _whitelistSystem;
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
|
||||
private ConstructionSystem? _constructionSystem;
|
||||
private ConstructionPrototype? _selected;
|
||||
private List<ConstructionPrototype> _favoritedRecipes = [];
|
||||
private Dictionary<string, TextureButton> _recipeButtons = new();
|
||||
private string _selectedCategory = string.Empty;
|
||||
private string _favoriteCatName = "construction-category-favorites";
|
||||
private string _forAllCategoryName = "construction-category-all";
|
||||
@@ -85,6 +87,7 @@ namespace Content.Client.Construction.UI
|
||||
IoCManager.InjectDependencies(this);
|
||||
_constructionView = new ConstructionMenu();
|
||||
_whitelistSystem = _entManager.System<EntityWhitelistSystem>();
|
||||
_spriteSystem = _entManager.System<SpriteSystem>();
|
||||
|
||||
// This is required so that if we load after the system is initialized, we can bind to it immediately
|
||||
if (_systemManager.TryGetEntitySystem<ConstructionSystem>(out var constructionSystem))
|
||||
@@ -150,12 +153,24 @@ namespace Content.Client.Construction.UI
|
||||
PopulateInfo(_selected);
|
||||
}
|
||||
|
||||
private void OnGridViewRecipeSelected(object? sender, ConstructionPrototype? recipe)
|
||||
{
|
||||
if (recipe is null)
|
||||
{
|
||||
_selected = null;
|
||||
_constructionView.ClearRecipeInfo();
|
||||
return;
|
||||
}
|
||||
|
||||
_selected = recipe;
|
||||
if (_placementManager.IsActive && !_placementManager.Eraser) UpdateGhostPlacement();
|
||||
PopulateInfo(_selected);
|
||||
}
|
||||
|
||||
private void OnViewPopulateRecipes(object? sender, (string search, string catagory) args)
|
||||
{
|
||||
var (search, category) = args;
|
||||
var recipesList = _constructionView.Recipes;
|
||||
|
||||
recipesList.Clear();
|
||||
var recipes = new List<ConstructionPrototype>();
|
||||
|
||||
var isEmptyCategory = string.IsNullOrEmpty(category) || category == _forAllCategoryName;
|
||||
@@ -170,7 +185,7 @@ namespace Content.Client.Construction.UI
|
||||
if (recipe.Hide)
|
||||
continue;
|
||||
|
||||
if (!recipe.CrystallPunkAllowed) //CrystallPunk clearing recipes
|
||||
if (!recipe.CrystallPunkAllowed) //CrystallEdge clearing recipes
|
||||
continue;
|
||||
|
||||
if (_playerManager.LocalSession == null
|
||||
@@ -204,12 +219,73 @@ namespace Content.Client.Construction.UI
|
||||
|
||||
recipes.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.InvariantCulture));
|
||||
|
||||
foreach (var recipe in recipes)
|
||||
{
|
||||
recipesList.Add(GetItem(recipe, recipesList));
|
||||
}
|
||||
var recipesList = _constructionView.Recipes;
|
||||
recipesList.Clear();
|
||||
|
||||
// There is apparently no way to set which
|
||||
var recipesGrid = _constructionView.RecipesGrid;
|
||||
recipesGrid.RemoveAllChildren();
|
||||
|
||||
_constructionView.RecipesGridScrollContainer.Visible = _constructionView.GridViewButtonPressed;
|
||||
_constructionView.Recipes.Visible = !_constructionView.GridViewButtonPressed;
|
||||
|
||||
if (_constructionView.GridViewButtonPressed)
|
||||
{
|
||||
foreach (var recipe in recipes)
|
||||
{
|
||||
var itemButton = new TextureButton
|
||||
{
|
||||
TextureNormal = _spriteSystem.Frame0(recipe.Icon),
|
||||
VerticalAlignment = Control.VAlignment.Center,
|
||||
Name = recipe.Name,
|
||||
ToolTip = recipe.Name,
|
||||
Scale = new Vector2(1.35f),
|
||||
ToggleMode = true,
|
||||
};
|
||||
var itemButtonPanelContainer = new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat { BackgroundColor = StyleNano.ButtonColorDefault },
|
||||
Children = { itemButton },
|
||||
};
|
||||
|
||||
itemButton.OnToggled += buttonToggledEventArgs =>
|
||||
{
|
||||
SelectGridButton(itemButton, buttonToggledEventArgs.Pressed);
|
||||
|
||||
if (buttonToggledEventArgs.Pressed &&
|
||||
_selected != null &&
|
||||
_recipeButtons.TryGetValue(_selected.Name, out var oldButton))
|
||||
{
|
||||
oldButton.Pressed = false;
|
||||
SelectGridButton(oldButton, false);
|
||||
}
|
||||
|
||||
OnGridViewRecipeSelected(this, buttonToggledEventArgs.Pressed ? recipe : null);
|
||||
};
|
||||
|
||||
recipesGrid.AddChild(itemButtonPanelContainer);
|
||||
_recipeButtons[recipe.Name] = itemButton;
|
||||
var isCurrentButtonSelected = _selected == recipe;
|
||||
itemButton.Pressed = isCurrentButtonSelected;
|
||||
SelectGridButton(itemButton, isCurrentButtonSelected);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var recipe in recipes)
|
||||
{
|
||||
recipesList.Add(GetItem(recipe, recipesList));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectGridButton(TextureButton button, bool select)
|
||||
{
|
||||
if (button.Parent is not PanelContainer buttonPanel)
|
||||
return;
|
||||
|
||||
button.Modulate = select ? Color.Green : Color.White;
|
||||
var buttonColor = select ? StyleNano.ButtonColorDefault : Color.Transparent;
|
||||
buttonPanel.PanelOverride = new StyleBoxFlat { BackgroundColor = buttonColor };
|
||||
}
|
||||
|
||||
private void PopulateCategories(string? selectCategory = null)
|
||||
@@ -260,11 +336,10 @@ namespace Content.Client.Construction.UI
|
||||
|
||||
private void PopulateInfo(ConstructionPrototype prototype)
|
||||
{
|
||||
var spriteSys = _systemManager.GetEntitySystem<SpriteSystem>();
|
||||
_constructionView.ClearRecipeInfo();
|
||||
|
||||
_constructionView.SetRecipeInfo(
|
||||
prototype.Name, prototype.Description, spriteSys.Frame0(prototype.Icon),
|
||||
prototype.Name, prototype.Description, _spriteSystem.Frame0(prototype.Icon),
|
||||
prototype.Type != ConstructionType.Item,
|
||||
!_favoritedRecipes.Contains(prototype));
|
||||
|
||||
@@ -277,7 +352,6 @@ namespace Content.Client.Construction.UI
|
||||
if (_constructionSystem?.GetGuide(prototype) is not { } guide)
|
||||
return;
|
||||
|
||||
var spriteSys = _systemManager.GetEntitySystem<SpriteSystem>();
|
||||
|
||||
foreach (var entry in guide.Entries)
|
||||
{
|
||||
@@ -293,20 +367,20 @@ namespace Content.Client.Construction.UI
|
||||
// The padding needs to be applied regardless of text length... (See PadLeft documentation)
|
||||
text = text.PadLeft(text.Length + entry.Padding);
|
||||
|
||||
var icon = entry.Icon != null ? spriteSys.Frame0(entry.Icon) : Texture.Transparent;
|
||||
var icon = entry.Icon != null ? _spriteSystem.Frame0(entry.Icon) : Texture.Transparent;
|
||||
stepList.AddItem(text, icon, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static ItemList.Item GetItem(ConstructionPrototype recipe, ItemList itemList)
|
||||
private ItemList.Item GetItem(ConstructionPrototype recipe, ItemList itemList)
|
||||
{
|
||||
return new(itemList)
|
||||
{
|
||||
Metadata = recipe,
|
||||
Text = recipe.Name,
|
||||
Icon = recipe.Icon.Frame0(),
|
||||
Icon = _spriteSystem.Frame0(recipe.Icon),
|
||||
TooltipEnabled = true,
|
||||
TooltipText = recipe.Description
|
||||
TooltipText = recipe.Description,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Content.Client.Crayon.UI
|
||||
private void PopulateCrayons()
|
||||
{
|
||||
var crayonDecals = _protoManager.EnumeratePrototypes<DecalPrototype>().Where(x => x.Tags.Contains("crayon"));
|
||||
_menu?.Populate(crayonDecals);
|
||||
_menu?.Populate(crayonDecals.ToList());
|
||||
}
|
||||
|
||||
public override void OnProtoReload(PrototypesReloadedEventArgs args)
|
||||
@@ -44,6 +44,16 @@ namespace Content.Client.Crayon.UI
|
||||
PopulateCrayons();
|
||||
}
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
{
|
||||
base.ReceiveMessage(message);
|
||||
|
||||
if (_menu is null || message is not CrayonUsedMessage crayonMessage)
|
||||
return;
|
||||
|
||||
_menu.AdvanceState(crayonMessage.DrawnDecal);
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
Title="{Loc 'crayon-window-title'}"
|
||||
MinSize="250 300"
|
||||
SetSize="250 300">
|
||||
MinSize="450 500"
|
||||
SetSize="450 500">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<ColorSelectorSliders Name="ColorSelector" Visible="False" />
|
||||
<LineEdit Name="Search" />
|
||||
<LineEdit Name="Search" Margin="0 0 0 8" PlaceHolder="{Loc 'crayon-window-placeholder'}" />
|
||||
<ScrollContainer VerticalExpand="True">
|
||||
<GridContainer Name="Grid" Columns="6">
|
||||
<!-- Crayon decals get added here by code -->
|
||||
</GridContainer>
|
||||
<BoxContainer Name="Grids" Orientation="Vertical">
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Crayon;
|
||||
using Content.Shared.Decals;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
@@ -18,7 +20,12 @@ namespace Content.Client.Crayon.UI
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class CrayonWindow : DefaultWindow
|
||||
{
|
||||
private Dictionary<string, Texture>? _decals;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
|
||||
private readonly SpriteSystem _spriteSystem = default!;
|
||||
|
||||
private Dictionary<string, List<(string Name, Texture Texture)>>? _decals;
|
||||
private List<string>? _allDecals;
|
||||
private string? _autoSelected;
|
||||
private string? _selected;
|
||||
private Color _color;
|
||||
|
||||
@@ -28,8 +35,10 @@ namespace Content.Client.Crayon.UI
|
||||
public CrayonWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
_spriteSystem = _entitySystem.GetEntitySystem<SpriteSystem>();
|
||||
|
||||
Search.OnTextChanged += _ => RefreshList();
|
||||
Search.OnTextChanged += SearchChanged;
|
||||
ColorSelector.OnColorChanged += SelectColor;
|
||||
}
|
||||
|
||||
@@ -44,51 +53,94 @@ namespace Content.Client.Crayon.UI
|
||||
private void RefreshList()
|
||||
{
|
||||
// Clear
|
||||
Grid.DisposeAllChildren();
|
||||
if (_decals == null)
|
||||
Grids.DisposeAllChildren();
|
||||
|
||||
if (_decals == null || _allDecals == null)
|
||||
return;
|
||||
|
||||
var filter = Search.Text;
|
||||
foreach (var (decal, tex) in _decals)
|
||||
var comma = filter.IndexOf(',');
|
||||
var first = (comma == -1 ? filter : filter[..comma]).Trim();
|
||||
|
||||
var names = _decals.Keys.ToList();
|
||||
names.Sort((a, b) => a == "random" ? 1 : b == "random" ? -1 : a.CompareTo(b));
|
||||
|
||||
if (_autoSelected != null && first != _autoSelected && _allDecals.Contains(first))
|
||||
{
|
||||
if (!decal.Contains(filter))
|
||||
_selected = first;
|
||||
_autoSelected = _selected;
|
||||
OnSelected?.Invoke(_selected);
|
||||
}
|
||||
|
||||
foreach (var categoryName in names)
|
||||
{
|
||||
var locName = Loc.GetString("crayon-category-" + categoryName);
|
||||
var category = _decals[categoryName].Where(d => locName.Contains(first) || d.Name.Contains(first)).ToList();
|
||||
|
||||
if (category.Count == 0)
|
||||
continue;
|
||||
|
||||
var button = new TextureButton()
|
||||
var label = new Label
|
||||
{
|
||||
TextureNormal = tex,
|
||||
Name = decal,
|
||||
ToolTip = decal,
|
||||
Modulate = _color,
|
||||
Text = locName
|
||||
};
|
||||
button.OnPressed += ButtonOnPressed;
|
||||
if (_selected == decal)
|
||||
|
||||
var grid = new GridContainer
|
||||
{
|
||||
var panelContainer = new PanelContainer()
|
||||
Columns = 6,
|
||||
Margin = new Thickness(0, 0, 0, 16)
|
||||
};
|
||||
|
||||
Grids.AddChild(label);
|
||||
Grids.AddChild(grid);
|
||||
|
||||
foreach (var (name, texture) in category)
|
||||
{
|
||||
var button = new TextureButton()
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat()
|
||||
{
|
||||
BackgroundColor = StyleNano.ButtonColorDefault,
|
||||
},
|
||||
Children =
|
||||
{
|
||||
button,
|
||||
},
|
||||
TextureNormal = texture,
|
||||
Name = name,
|
||||
ToolTip = name,
|
||||
Modulate = _color,
|
||||
Scale = new System.Numerics.Vector2(2, 2)
|
||||
};
|
||||
Grid.AddChild(panelContainer);
|
||||
}
|
||||
else
|
||||
{
|
||||
Grid.AddChild(button);
|
||||
button.OnPressed += ButtonOnPressed;
|
||||
|
||||
if (_selected == name)
|
||||
{
|
||||
var panelContainer = new PanelContainer()
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat()
|
||||
{
|
||||
BackgroundColor = StyleNano.ButtonColorDefault,
|
||||
},
|
||||
Children =
|
||||
{
|
||||
button,
|
||||
},
|
||||
};
|
||||
grid.AddChild(panelContainer);
|
||||
}
|
||||
else
|
||||
{
|
||||
grid.AddChild(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SearchChanged(LineEdit.LineEditEventArgs obj)
|
||||
{
|
||||
_autoSelected = ""; // Placeholder to kick off the auto-select in refreshlist()
|
||||
RefreshList();
|
||||
}
|
||||
|
||||
private void ButtonOnPressed(ButtonEventArgs obj)
|
||||
{
|
||||
if (obj.Button.Name == null) return;
|
||||
|
||||
_selected = obj.Button.Name;
|
||||
_autoSelected = null;
|
||||
OnSelected?.Invoke(_selected);
|
||||
RefreshList();
|
||||
}
|
||||
@@ -107,12 +159,38 @@ namespace Content.Client.Crayon.UI
|
||||
RefreshList();
|
||||
}
|
||||
|
||||
public void Populate(IEnumerable<DecalPrototype> prototypes)
|
||||
public void AdvanceState(string drawnDecal)
|
||||
{
|
||||
_decals = new Dictionary<string, Texture>();
|
||||
var filter = Search.Text;
|
||||
if (!filter.Contains(',') || !filter.Contains(drawnDecal))
|
||||
return;
|
||||
|
||||
var first = filter[..filter.IndexOf(',')].Trim();
|
||||
|
||||
if (first.Equals(drawnDecal, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
Search.Text = filter[(filter.IndexOf(',') + 1)..].Trim();
|
||||
_autoSelected = first;
|
||||
}
|
||||
|
||||
RefreshList();
|
||||
}
|
||||
|
||||
public void Populate(List<DecalPrototype> prototypes)
|
||||
{
|
||||
_decals = [];
|
||||
_allDecals = [];
|
||||
|
||||
prototypes.Sort((a, b) => a.ID.CompareTo(b.ID));
|
||||
|
||||
foreach (var decalPrototype in prototypes)
|
||||
{
|
||||
_decals.Add(decalPrototype.ID, decalPrototype.Sprite.Frame0());
|
||||
var category = "random";
|
||||
if (decalPrototype.Tags.Count > 1 && decalPrototype.Tags[1].StartsWith("crayon-"))
|
||||
category = decalPrototype.Tags[1].Replace("crayon-", "");
|
||||
var list = _decals.GetOrNew(category);
|
||||
list.Add((decalPrototype.ID, _spriteSystem.Frame0(decalPrototype.Sprite)));
|
||||
_allDecals.Add(decalPrototype.ID);
|
||||
}
|
||||
|
||||
RefreshList();
|
||||
|
||||
@@ -124,6 +124,10 @@ public sealed class ColorFlashEffectSystem : SharedColorFlashEffectSystem
|
||||
continue;
|
||||
}
|
||||
|
||||
var targetEv = new GetFlashEffectTargetEvent(ent);
|
||||
RaiseLocalEvent(ent, ref targetEv);
|
||||
ent = targetEv.Target;
|
||||
|
||||
EnsureComp<ColorFlashEffectComponent>(ent, out comp);
|
||||
comp.NetSyncEnabled = false;
|
||||
comp.Color = sprite.Color;
|
||||
@@ -132,3 +136,9 @@ public sealed class ColorFlashEffectSystem : SharedColorFlashEffectSystem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised on an entity to change the target for a color flash effect.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct GetFlashEffectTargetEvent(EntityUid Target);
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
using Content.Shared.Electrocution;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Client.Electrocution;
|
||||
|
||||
/// <summary>
|
||||
/// Shows the Electrocution HUD to entities with the ShowElectrocutionHUDComponent.
|
||||
/// </summary>
|
||||
public sealed class ElectrocutionHUDVisualizerSystem : VisualizerSystem<ElectrocutionHUDVisualsComponent>
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerMan = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ShowElectrocutionHUDComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<ShowElectrocutionHUDComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<ShowElectrocutionHUDComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
|
||||
SubscribeLocalEvent<ShowElectrocutionHUDComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
|
||||
}
|
||||
|
||||
private void OnPlayerAttached(Entity<ShowElectrocutionHUDComponent> ent, ref LocalPlayerAttachedEvent args)
|
||||
{
|
||||
ShowHUD();
|
||||
}
|
||||
|
||||
private void OnPlayerDetached(Entity<ShowElectrocutionHUDComponent> ent, ref LocalPlayerDetachedEvent args)
|
||||
{
|
||||
RemoveHUD();
|
||||
}
|
||||
|
||||
private void OnInit(Entity<ShowElectrocutionHUDComponent> ent, ref ComponentInit args)
|
||||
{
|
||||
if (_playerMan.LocalEntity == ent)
|
||||
{
|
||||
ShowHUD();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnShutdown(Entity<ShowElectrocutionHUDComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
if (_playerMan.LocalEntity == ent)
|
||||
{
|
||||
RemoveHUD();
|
||||
}
|
||||
}
|
||||
|
||||
// Show the HUD to the client.
|
||||
// We have to look for all current entities that can be electrified and toggle the HUD layer on if they are.
|
||||
private void ShowHUD()
|
||||
{
|
||||
var electrifiedQuery = AllEntityQuery<ElectrocutionHUDVisualsComponent, AppearanceComponent, SpriteComponent>();
|
||||
while (electrifiedQuery.MoveNext(out var uid, out var _, out var appearanceComp, out var spriteComp))
|
||||
{
|
||||
if (!AppearanceSystem.TryGetData<bool>(uid, ElectrifiedVisuals.IsElectrified, out var electrified, appearanceComp))
|
||||
continue;
|
||||
|
||||
if (electrified)
|
||||
spriteComp.LayerSetVisible(ElectrifiedLayers.HUD, true);
|
||||
else
|
||||
spriteComp.LayerSetVisible(ElectrifiedLayers.HUD, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the HUD from the client.
|
||||
// Find all current entities that can be electrified and hide the HUD layer.
|
||||
private void RemoveHUD()
|
||||
{
|
||||
var electrifiedQuery = AllEntityQuery<ElectrocutionHUDVisualsComponent, AppearanceComponent, SpriteComponent>();
|
||||
while (electrifiedQuery.MoveNext(out var uid, out var _, out var appearanceComp, out var spriteComp))
|
||||
{
|
||||
|
||||
spriteComp.LayerSetVisible(ElectrifiedLayers.HUD, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle the HUD layer if an entity becomes (de-)electrified
|
||||
protected override void OnAppearanceChange(EntityUid uid, ElectrocutionHUDVisualsComponent comp, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
if (!AppearanceSystem.TryGetData<bool>(uid, ElectrifiedVisuals.IsElectrified, out var electrified, args.Component))
|
||||
return;
|
||||
|
||||
var player = _playerMan.LocalEntity;
|
||||
if (electrified && HasComp<ShowElectrocutionHUDComponent>(player))
|
||||
args.Sprite.LayerSetVisible(ElectrifiedLayers.HUD, true);
|
||||
else
|
||||
args.Sprite.LayerSetVisible(ElectrifiedLayers.HUD, false);
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ using Content.Client.IoC;
|
||||
using Content.Client.Launcher;
|
||||
using Content.Client.Lobby;
|
||||
using Content.Client.MainMenu;
|
||||
using Content.Client.Overlays;
|
||||
using Content.Client.Parallax.Managers;
|
||||
using Content.Client.Players.PlayTimeTracking;
|
||||
using Content.Client.Radiation.Overlays;
|
||||
@@ -159,13 +160,14 @@ namespace Content.Client.Entry
|
||||
|
||||
_parallaxManager.LoadDefaultParallax();
|
||||
|
||||
_overlayManager.AddOverlay(new CP14BasePostProcessOverlay()); // CP14-PostProcess
|
||||
_overlayManager.AddOverlay(new SingularityOverlay());
|
||||
_overlayManager.AddOverlay(new RadiationPulseOverlay());
|
||||
_chatManager.Initialize();
|
||||
_clientPreferencesManager.Initialize();
|
||||
_euiManager.Initialize();
|
||||
_voteManager.Initialize();
|
||||
_userInterfaceManager.SetDefaultTheme(_configManager.GetCVar(CCVars.UIDefaultInterfaceTheme));
|
||||
_userInterfaceManager.SetDefaultTheme("SS14DefaultTheme");
|
||||
_userInterfaceManager.SetActiveTheme(_configManager.GetCVar(CVars.InterfaceTheme));
|
||||
_documentParsingManager.Initialize();
|
||||
_titleWindowManager.Initialize();
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace Content.Client.Eui
|
||||
/// </summary>
|
||||
protected void SendMessage(EuiMessageBase msg)
|
||||
{
|
||||
var netMsg = _netManager.CreateNetMessage<MsgEuiMessage>();
|
||||
var netMsg = new MsgEuiMessage();
|
||||
netMsg.Id = Id;
|
||||
netMsg.Message = msg;
|
||||
|
||||
|
||||
8
Content.Client/Explosion/ScatteringGrenadeSystem.cs
Normal file
8
Content.Client/Explosion/ScatteringGrenadeSystem.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Content.Shared.Explosion.EntitySystems;
|
||||
|
||||
namespace Content.Client.Explosion;
|
||||
|
||||
public sealed class ScatteringGrenadeSystem : SharedScatteringGrenadeSystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -112,7 +112,12 @@ namespace Content.Client.Ghost
|
||||
_actions.RemoveAction(uid, component.ToggleLightingActionEntity);
|
||||
_actions.RemoveAction(uid, component.ToggleFoVActionEntity);
|
||||
_actions.RemoveAction(uid, component.ToggleGhostsActionEntity);
|
||||
_actions.RemoveAction(uid, component.ToggleGhostHearingActionEntity);
|
||||
//_actions.RemoveAction(uid, component.ToggleGhostHearingActionEntity); //Dont need in CP14
|
||||
//CP14
|
||||
_actions.RemoveAction(uid, component.CP14ZLevelUpActionEntity);
|
||||
_actions.RemoveAction(uid, component.CP14ZLevelDownActionEntity);
|
||||
_actions.RemoveAction(uid, component.CP14ToggleRoofActionEntity);
|
||||
//CP14 end
|
||||
|
||||
if (uid != _playerManager.LocalEntity)
|
||||
return;
|
||||
|
||||
@@ -168,8 +168,8 @@ public sealed partial class GuidebookWindow : FancyWindow, ILinkClickHandler
|
||||
var parent = forcedRoot == null ? null : AddEntry(forcedRoot.Value, null, addedEntries);
|
||||
foreach (var entry in GetSortedEntries(roots))
|
||||
{
|
||||
if (!entry.CrystallPunkAllowed) continue; //CrystallPunk guidebook filter
|
||||
if (entry.LocFilter is not null && entry.LocFilter != ContentLocalizationManager.Culture) continue; //CrystallPunk guidebook filter
|
||||
if (!entry.CrystallPunkAllowed) continue; //CrystallEdge guidebook filter
|
||||
if (entry.LocFilter is not null && entry.LocFilter != ContentLocalizationManager.Culture) continue; //CrystallEdge guidebook filter
|
||||
|
||||
AddEntry(entry.Id, parent, addedEntries);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public sealed class GuidebookSystem : EntitySystem
|
||||
[Dependency] private readonly RgbLightControllerSystem _rgbLightControllerSystem = default!;
|
||||
[Dependency] private readonly SharedPointLightSystem _pointLightSystem = default!;
|
||||
[Dependency] private readonly TagSystem _tags = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!; //CrystallPunk guidebook filter
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!; //CrystallEdge guidebook filter
|
||||
|
||||
public event Action<List<ProtoId<GuideEntryPrototype>>,
|
||||
List<ProtoId<GuideEntryPrototype>>?,
|
||||
@@ -47,7 +47,7 @@ public sealed class GuidebookSystem : EntitySystem
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<GuideHelpComponent, MapInitEvent>(OnCrystallPunkMapInit); //CrystallPunk guidebook filter
|
||||
SubscribeLocalEvent<GuideHelpComponent, MapInitEvent>(OnCrystallEdgeMapInit); //CrystallEdge guidebook filter
|
||||
SubscribeLocalEvent<GuideHelpComponent, GetVerbsEvent<ExamineVerb>>(OnGetVerbs);
|
||||
SubscribeLocalEvent<GuideHelpComponent, ActivateInWorldEvent>(OnInteract);
|
||||
|
||||
@@ -57,12 +57,12 @@ public sealed class GuidebookSystem : EntitySystem
|
||||
OnGuidebookControlsTestGetAlternateVerbs);
|
||||
}
|
||||
|
||||
//CrystallPunk guidebook filter
|
||||
private void OnCrystallPunkMapInit(Entity<GuideHelpComponent> ent, ref MapInitEvent args)
|
||||
//CrystallEdge guidebook filter
|
||||
private void OnCrystallEdgeMapInit(Entity<GuideHelpComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
foreach (var guide in ent.Comp.Guides)
|
||||
{
|
||||
var guideProto = _proto.Index<GuideEntryPrototype>(guide);
|
||||
var guideProto = _proto.Index(guide);
|
||||
if (!guideProto.CrystallPunkAllowed) //REMOVE unnecessary guidebook
|
||||
{
|
||||
RemComp<GuideHelpComponent>(ent);
|
||||
@@ -70,7 +70,7 @@ public sealed class GuidebookSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
}
|
||||
//CrystallPunk guidebook filter end
|
||||
//CrystallEdge 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
|
||||
|
||||
101
Content.Client/Holopad/HolopadBoundUserInterface.cs
Normal file
101
Content.Client/Holopad/HolopadBoundUserInterface.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using Content.Shared.Holopad;
|
||||
using Content.Shared.Silicons.StationAi;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Player;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Content.Client.Holopad;
|
||||
|
||||
public sealed class HolopadBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IClyde _displayManager = default!;
|
||||
|
||||
[ViewVariables]
|
||||
private HolopadWindow? _window;
|
||||
|
||||
public HolopadBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = this.CreateWindow<HolopadWindow>();
|
||||
_window.Title = Loc.GetString("holopad-window-title", ("title", EntMan.GetComponent<MetaDataComponent>(Owner).EntityName));
|
||||
|
||||
if (this.UiKey is not HolopadUiKey)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
var uiKey = (HolopadUiKey)this.UiKey;
|
||||
|
||||
// AIs will see a different holopad interface to crew when interacting with them in the world
|
||||
if (uiKey == HolopadUiKey.InteractionWindow && EntMan.HasComponent<StationAiHeldComponent>(_playerManager.LocalEntity))
|
||||
uiKey = HolopadUiKey.InteractionWindowForAi;
|
||||
|
||||
_window.SetState(Owner, uiKey);
|
||||
_window.UpdateState(new Dictionary<NetEntity, string>());
|
||||
|
||||
// Set message actions
|
||||
_window.SendHolopadStartNewCallMessageAction += SendHolopadStartNewCallMessage;
|
||||
_window.SendHolopadAnswerCallMessageAction += SendHolopadAnswerCallMessage;
|
||||
_window.SendHolopadEndCallMessageAction += SendHolopadEndCallMessage;
|
||||
_window.SendHolopadStartBroadcastMessageAction += SendHolopadStartBroadcastMessage;
|
||||
_window.SendHolopadActivateProjectorMessageAction += SendHolopadActivateProjectorMessage;
|
||||
_window.SendHolopadRequestStationAiMessageAction += SendHolopadRequestStationAiMessage;
|
||||
|
||||
// If this call is addressed to an AI, open the window in the bottom right hand corner of the screen
|
||||
if (uiKey == HolopadUiKey.AiRequestWindow)
|
||||
_window.OpenCenteredAt(new Vector2(1f, 1f));
|
||||
|
||||
// Otherwise offset to the left so the holopad can still be seen
|
||||
else
|
||||
_window.OpenCenteredAt(new Vector2(0.3333f, 0.50f));
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
var castState = (HolopadBoundInterfaceState)state;
|
||||
EntMan.TryGetComponent<TransformComponent>(Owner, out var xform);
|
||||
|
||||
_window?.UpdateState(castState.Holopads);
|
||||
}
|
||||
|
||||
public void SendHolopadStartNewCallMessage(NetEntity receiver)
|
||||
{
|
||||
SendMessage(new HolopadStartNewCallMessage(receiver));
|
||||
}
|
||||
|
||||
public void SendHolopadAnswerCallMessage()
|
||||
{
|
||||
SendMessage(new HolopadAnswerCallMessage());
|
||||
}
|
||||
|
||||
public void SendHolopadEndCallMessage()
|
||||
{
|
||||
SendMessage(new HolopadEndCallMessage());
|
||||
}
|
||||
|
||||
public void SendHolopadStartBroadcastMessage()
|
||||
{
|
||||
SendMessage(new HolopadStartBroadcastMessage());
|
||||
}
|
||||
|
||||
public void SendHolopadActivateProjectorMessage()
|
||||
{
|
||||
SendMessage(new HolopadActivateProjectorMessage());
|
||||
}
|
||||
|
||||
public void SendHolopadRequestStationAiMessage()
|
||||
{
|
||||
SendMessage(new HolopadStationAiRequestMessage());
|
||||
}
|
||||
}
|
||||
172
Content.Client/Holopad/HolopadSystem.cs
Normal file
172
Content.Client/Holopad/HolopadSystem.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
using Content.Shared.Chat.TypingIndicator;
|
||||
using Content.Shared.Holopad;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Client.Holopad;
|
||||
|
||||
public sealed class HolopadSystem : SharedHolopadSystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<HolopadHologramComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<HolopadHologramComponent, BeforePostShaderRenderEvent>(OnShaderRender);
|
||||
SubscribeAllEvent<TypingChangedEvent>(OnTypingChanged);
|
||||
|
||||
SubscribeNetworkEvent<PlayerSpriteStateRequest>(OnPlayerSpriteStateRequest);
|
||||
SubscribeNetworkEvent<PlayerSpriteStateMessage>(OnPlayerSpriteStateMessage);
|
||||
}
|
||||
|
||||
private void OnComponentInit(EntityUid uid, HolopadHologramComponent component, ComponentInit ev)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||
return;
|
||||
|
||||
UpdateHologramSprite(uid);
|
||||
}
|
||||
|
||||
private void OnShaderRender(EntityUid uid, HolopadHologramComponent component, BeforePostShaderRenderEvent ev)
|
||||
{
|
||||
if (ev.Sprite.PostShader == null)
|
||||
return;
|
||||
|
||||
ev.Sprite.PostShader.SetParameter("t", (float)_timing.CurTime.TotalSeconds * component.ScrollRate);
|
||||
}
|
||||
|
||||
private void OnTypingChanged(TypingChangedEvent ev, EntitySessionEventArgs args)
|
||||
{
|
||||
var uid = args.SenderSession.AttachedEntity;
|
||||
|
||||
if (!Exists(uid))
|
||||
return;
|
||||
|
||||
if (!HasComp<HolopadUserComponent>(uid))
|
||||
return;
|
||||
|
||||
var netEv = new HolopadUserTypingChangedEvent(GetNetEntity(uid.Value), ev.IsTyping);
|
||||
RaiseNetworkEvent(netEv);
|
||||
}
|
||||
|
||||
private void OnPlayerSpriteStateRequest(PlayerSpriteStateRequest ev)
|
||||
{
|
||||
var targetPlayer = GetEntity(ev.TargetPlayer);
|
||||
var player = _playerManager.LocalSession?.AttachedEntity;
|
||||
|
||||
// Ignore the request if received by a player who isn't the target
|
||||
if (targetPlayer != player)
|
||||
return;
|
||||
|
||||
if (!TryComp<SpriteComponent>(player, out var playerSprite))
|
||||
return;
|
||||
|
||||
var spriteLayerData = new List<PrototypeLayerData>();
|
||||
|
||||
if (playerSprite.Visible)
|
||||
{
|
||||
// Record the RSI paths, state names and shader paramaters of all visible layers
|
||||
for (int i = 0; i < playerSprite.AllLayers.Count(); i++)
|
||||
{
|
||||
if (!playerSprite.TryGetLayer(i, out var layer))
|
||||
continue;
|
||||
|
||||
if (!layer.Visible ||
|
||||
string.IsNullOrEmpty(layer.ActualRsi?.Path.ToString()) ||
|
||||
string.IsNullOrEmpty(layer.State.Name))
|
||||
continue;
|
||||
|
||||
var layerDatum = new PrototypeLayerData();
|
||||
layerDatum.RsiPath = layer.ActualRsi.Path.ToString();
|
||||
layerDatum.State = layer.State.Name;
|
||||
|
||||
if (layer.CopyToShaderParameters != null)
|
||||
{
|
||||
var key = (string)layer.CopyToShaderParameters.LayerKey;
|
||||
|
||||
if (playerSprite.LayerMapTryGet(key, out var otherLayerIdx) &&
|
||||
playerSprite.TryGetLayer(otherLayerIdx, out var otherLayer) &&
|
||||
otherLayer.Visible)
|
||||
{
|
||||
layerDatum.MapKeys = new() { key };
|
||||
|
||||
layerDatum.CopyToShaderParameters = new PrototypeCopyToShaderParameters()
|
||||
{
|
||||
LayerKey = key,
|
||||
ParameterTexture = layer.CopyToShaderParameters.ParameterTexture,
|
||||
ParameterUV = layer.CopyToShaderParameters.ParameterUV
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
spriteLayerData.Add(layerDatum);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the recorded data to the server
|
||||
var evResponse = new PlayerSpriteStateMessage(ev.TargetPlayer, spriteLayerData.ToArray());
|
||||
RaiseNetworkEvent(evResponse);
|
||||
}
|
||||
|
||||
private void OnPlayerSpriteStateMessage(PlayerSpriteStateMessage ev)
|
||||
{
|
||||
UpdateHologramSprite(GetEntity(ev.SpriteEntity), ev.SpriteLayerData);
|
||||
}
|
||||
|
||||
private void UpdateHologramSprite(EntityUid uid, PrototypeLayerData[]? layerData = null)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(uid, out var hologramSprite))
|
||||
return;
|
||||
|
||||
if (!TryComp<HolopadHologramComponent>(uid, out var holopadhologram))
|
||||
return;
|
||||
|
||||
for (int i = hologramSprite.AllLayers.Count() - 1; i >= 0; i--)
|
||||
hologramSprite.RemoveLayer(i);
|
||||
|
||||
if (layerData == null || layerData.Length == 0)
|
||||
{
|
||||
layerData = new PrototypeLayerData[1];
|
||||
layerData[0] = new PrototypeLayerData()
|
||||
{
|
||||
RsiPath = holopadhologram.RsiPath,
|
||||
State = holopadhologram.RsiState
|
||||
};
|
||||
}
|
||||
|
||||
for (int i = 0; i < layerData.Length; i++)
|
||||
{
|
||||
var layer = layerData[i];
|
||||
layer.Shader = "unshaded";
|
||||
|
||||
hologramSprite.AddLayer(layerData[i], i);
|
||||
}
|
||||
|
||||
UpdateHologramShader(uid, hologramSprite, holopadhologram);
|
||||
}
|
||||
|
||||
private void UpdateHologramShader(EntityUid uid, SpriteComponent sprite, HolopadHologramComponent holopadHologram)
|
||||
{
|
||||
// Find the texture height of the largest layer
|
||||
float texHeight = sprite.AllLayers.Max(x => x.PixelSize.Y);
|
||||
|
||||
var instance = _prototypeManager.Index<ShaderPrototype>(holopadHologram.ShaderName).InstanceUnique();
|
||||
instance.SetParameter("color1", new Vector3(holopadHologram.Color1.R, holopadHologram.Color1.G, holopadHologram.Color1.B));
|
||||
instance.SetParameter("color2", new Vector3(holopadHologram.Color2.R, holopadHologram.Color2.G, holopadHologram.Color2.B));
|
||||
instance.SetParameter("alpha", holopadHologram.Alpha);
|
||||
instance.SetParameter("intensity", holopadHologram.Intensity);
|
||||
instance.SetParameter("texHeight", texHeight);
|
||||
instance.SetParameter("t", (float)_timing.CurTime.TotalSeconds * holopadHologram.ScrollRate);
|
||||
|
||||
sprite.PostShader = instance;
|
||||
sprite.RaiseShaderEvent = true;
|
||||
}
|
||||
}
|
||||
118
Content.Client/Holopad/HolopadWindow.xaml
Normal file
118
Content.Client/Holopad/HolopadWindow.xaml
Normal file
@@ -0,0 +1,118 @@
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
Resizable="False"
|
||||
MaxSize="400 800"
|
||||
MinSize="400 150">
|
||||
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
|
||||
|
||||
<BoxContainer Name="ControlsLockOutContainer" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" ReservesSpace="False" Visible="False">
|
||||
<!-- Header text -->
|
||||
<controls:StripeBack>
|
||||
<PanelContainer>
|
||||
<RichTextLabel Name="EmergencyBroadcastText" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="10 10 10 10" ReservesSpace="False"/>
|
||||
</PanelContainer>
|
||||
</controls:StripeBack>
|
||||
|
||||
<Label Text="{Loc 'holopad-window-controls-locked-out'}" HorizontalAlignment="Center" Margin="10 5 10 0" ReservesSpace="False"/>
|
||||
<RichTextLabel Name="LockOutIdText" HorizontalAlignment="Center" Margin="10 5 10 0" ReservesSpace="False"/>
|
||||
<Label Name="LockOutCountDownText" Text="{Loc 'holopad-window-controls-unlock-countdown'}" HorizontalAlignment="Center" Margin="10 15 10 10" ReservesSpace="False"/>
|
||||
</BoxContainer>
|
||||
|
||||
<BoxContainer Name="ControlsContainer" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" ReservesSpace="False">
|
||||
|
||||
<!-- Active call controls (either this or the call placement controls will be active) -->
|
||||
<BoxContainer Name="ActiveCallControlsContainer" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" ReservesSpace="False">
|
||||
|
||||
<!-- Header text -->
|
||||
<BoxContainer MinHeight="60" Orientation="Vertical" VerticalAlignment="Center">
|
||||
<Label Name="CallStatusText" Margin="10 5 10 0" ReservesSpace="False"/>
|
||||
<BoxContainer Name="CallerIdContainer" Orientation="Vertical" ReservesSpace="False">
|
||||
<RichTextLabel Name="CallerIdText" HorizontalAlignment="Center" Margin="0 0 0 0"/>
|
||||
<Label Text="{Loc 'holopad-window-relay-label'}" Margin="10 5 10 0" ReservesSpace="False"/>
|
||||
<RichTextLabel Name="HolopadIdText" HorizontalAlignment="Center" Margin="0 0 0 10"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Controls (the answer call button is absent when the phone is not ringing) -->
|
||||
<GridContainer Columns="2" ReservesSpace="False">
|
||||
<Control HorizontalExpand="True" Margin="10 0 2 5">
|
||||
<Button Name="AnswerCallButton" Text="{Loc 'holopad-window-answer-call'}" StyleClasses="OpenRight" Margin="0 0 0 5" Disabled="True"/>
|
||||
</Control>
|
||||
<Control HorizontalExpand="True" Margin="2 0 10 5">
|
||||
<Button Name="EndCallButton" Text="{Loc 'holopad-window-end-call'}" StyleClasses="OpenLeft" Margin="0 0 0 5" Disabled="True"/>
|
||||
</Control>
|
||||
</GridContainer>
|
||||
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Call placement controls (either this or the active call controls will be active) -->
|
||||
<BoxContainer Name="CallPlacementControlsContainer" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" ReservesSpace="False">
|
||||
|
||||
<controls:StripeBack>
|
||||
<PanelContainer>
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<RichTextLabel Name="SubtitleText" HorizontalAlignment="Center" Margin="0 5 0 0"/>
|
||||
<RichTextLabel Name="OptionsText" HorizontalAlignment="Center" Margin="0 0 0 5"/>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</controls:StripeBack>
|
||||
|
||||
<!-- Request the station AI or activate the holopad projector (only one of these should be active at a time) -->
|
||||
<BoxContainer Name="RequestStationAiContainer" Orientation="Vertical" ReservesSpace="False" Visible="False">
|
||||
<Button Name="RequestStationAiButton" Text="{Loc 'holopad-window-request-station-ai'}" Margin="10 5 10 5" Disabled="False"/>
|
||||
</BoxContainer>
|
||||
|
||||
<BoxContainer Name="ActivateProjectorContainer" Orientation="Vertical" ReservesSpace="False" Visible="False">
|
||||
<Button Name="ActivateProjectorButton" Text="{Loc 'holopad-window-activate-projector'}" Margin="10 5 10 5" Disabled="False"/>
|
||||
</BoxContainer>
|
||||
|
||||
<!-- List of contactable holopads (the list is created in C#) -->
|
||||
<BoxContainer Name="HolopadContactListContainer" Orientation="Vertical" Margin="10 0 10 5" ReservesSpace="False" Visible="False">
|
||||
<PanelContainer Name="HolopadContactListHeaderPanel">
|
||||
<Label Text="{Loc 'holopad-window-select-contact-from-list'}" HorizontalAlignment="Center" Margin="0 3 0 3"/>
|
||||
</PanelContainer>
|
||||
|
||||
<PanelContainer Name="HolopadContactListPanel">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
|
||||
<!-- Contact filter -->
|
||||
<LineEdit Name="SearchLineEdit" HorizontalExpand="True" Margin="4, 4, 4, 0"
|
||||
PlaceHolder="{Loc holopad-window-filter-line-placeholder}" />
|
||||
|
||||
<ScrollContainer HorizontalExpand="True" VerticalExpand="True" Margin="8, 8, 8, 8" MinHeight="256">
|
||||
|
||||
<!-- If there is no data yet, this will be displayed -->
|
||||
<BoxContainer Name="FetchingAvailableHolopadsContainer" HorizontalAlignment="Center" HorizontalExpand="True" VerticalExpand="True" ReservesSpace="False">
|
||||
<Label Text="{Loc 'holopad-window-fetching-contacts-list'}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Container for the contacts -->
|
||||
<BoxContainer Name="ContactsList" Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" Margin="10 0 10 0"/>
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Button to start an emergency broadcast (the user requires a certain level of access to interact with it) -->
|
||||
<BoxContainer Name="StartBroadcastContainer" Orientation="Vertical" ReservesSpace="False" Visible="False">
|
||||
<Button Name="StartBroadcastButton" Text="{Loc 'holopad-window-emergency-broadcast'}" Margin="10 0 10 5" Disabled="False" ReservesSpace="False"/>
|
||||
</BoxContainer>
|
||||
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Footer -->
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<PanelContainer StyleClasses="LowDivider" />
|
||||
<BoxContainer Orientation="Horizontal" Margin="10 2 5 0" VerticalAlignment="Bottom">
|
||||
<Label Text="{Loc 'holopad-window-flavor-left'}" StyleClasses="WindowFooterText" />
|
||||
<Label Text="{Loc 'holopad-window-flavor-right'}" StyleClasses="WindowFooterText"
|
||||
HorizontalAlignment="Right" HorizontalExpand="True" Margin="0 0 5 0" />
|
||||
<TextureRect StyleClasses="NTLogoDark" Stretch="KeepAspectCentered"
|
||||
VerticalAlignment="Center" HorizontalAlignment="Right" SetSize="19 19"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
</controls:FancyWindow>
|
||||
344
Content.Client/Holopad/HolopadWindow.xaml.cs
Normal file
344
Content.Client/Holopad/HolopadWindow.xaml.cs
Normal file
@@ -0,0 +1,344 @@
|
||||
using Content.Client.Popups;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Holopad;
|
||||
using Content.Shared.Telephone;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Client.Holopad;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class HolopadWindow : FancyWindow
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
private readonly SharedHolopadSystem _holopadSystem = default!;
|
||||
private readonly SharedTelephoneSystem _telephoneSystem = default!;
|
||||
private readonly AccessReaderSystem _accessReaderSystem = default!;
|
||||
private readonly PopupSystem _popupSystem = default!;
|
||||
|
||||
private EntityUid? _owner = null;
|
||||
private HolopadUiKey _currentUiKey;
|
||||
private TelephoneState _currentState;
|
||||
private TelephoneState _previousState;
|
||||
private TimeSpan _buttonUnlockTime;
|
||||
private float _updateTimer = 0.25f;
|
||||
|
||||
private const float UpdateTime = 0.25f;
|
||||
private TimeSpan _buttonUnlockDelay = TimeSpan.FromSeconds(0.5f);
|
||||
|
||||
public event Action<NetEntity>? SendHolopadStartNewCallMessageAction;
|
||||
public event Action? SendHolopadAnswerCallMessageAction;
|
||||
public event Action? SendHolopadEndCallMessageAction;
|
||||
public event Action? SendHolopadStartBroadcastMessageAction;
|
||||
public event Action? SendHolopadActivateProjectorMessageAction;
|
||||
public event Action? SendHolopadRequestStationAiMessageAction;
|
||||
|
||||
public HolopadWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_holopadSystem = _entManager.System<SharedHolopadSystem>();
|
||||
_telephoneSystem = _entManager.System<SharedTelephoneSystem>();
|
||||
_accessReaderSystem = _entManager.System<AccessReaderSystem>();
|
||||
_popupSystem = _entManager.System<PopupSystem>();
|
||||
|
||||
_buttonUnlockTime = _timing.CurTime + _buttonUnlockDelay;
|
||||
|
||||
// Assign button actions
|
||||
AnswerCallButton.OnPressed += args => { OnHolopadAnswerCallMessage(); };
|
||||
EndCallButton.OnPressed += args => { OnHolopadEndCallMessage(); };
|
||||
StartBroadcastButton.OnPressed += args => { OnHolopadStartBroadcastMessage(); };
|
||||
ActivateProjectorButton.OnPressed += args => { OnHolopadActivateProjectorMessage(); };
|
||||
RequestStationAiButton.OnPressed += args => { OnHolopadRequestStationAiMessage(); };
|
||||
|
||||
// XML formatting
|
||||
AnswerCallButton.AddStyleClass("ButtonAccept");
|
||||
EndCallButton.AddStyleClass("Caution");
|
||||
StartBroadcastButton.AddStyleClass("Caution");
|
||||
|
||||
HolopadContactListPanel.PanelOverride = new StyleBoxFlat
|
||||
{
|
||||
BackgroundColor = new Color(47, 47, 59) * Color.DarkGray,
|
||||
BorderColor = new Color(82, 82, 82), //new Color(70, 73, 102),
|
||||
BorderThickness = new Thickness(2),
|
||||
};
|
||||
|
||||
HolopadContactListHeaderPanel.PanelOverride = new StyleBoxFlat
|
||||
{
|
||||
BackgroundColor = new Color(82, 82, 82),
|
||||
};
|
||||
|
||||
EmergencyBroadcastText.SetMessage(FormattedMessage.FromMarkupOrThrow(Loc.GetString("holopad-window-emergency-broadcast-in-progress")));
|
||||
SubtitleText.SetMessage(FormattedMessage.FromMarkupOrThrow(Loc.GetString("holopad-window-subtitle")));
|
||||
OptionsText.SetMessage(FormattedMessage.FromMarkupOrThrow(Loc.GetString("holopad-window-options")));
|
||||
}
|
||||
|
||||
#region: Button actions
|
||||
|
||||
private void OnSendHolopadStartNewCallMessage(NetEntity receiver)
|
||||
{
|
||||
SendHolopadStartNewCallMessageAction?.Invoke(receiver);
|
||||
}
|
||||
|
||||
private void OnHolopadAnswerCallMessage()
|
||||
{
|
||||
SendHolopadAnswerCallMessageAction?.Invoke();
|
||||
}
|
||||
|
||||
private void OnHolopadEndCallMessage()
|
||||
{
|
||||
SendHolopadEndCallMessageAction?.Invoke();
|
||||
|
||||
if (_currentUiKey == HolopadUiKey.AiRequestWindow)
|
||||
Close();
|
||||
}
|
||||
|
||||
private void OnHolopadStartBroadcastMessage()
|
||||
{
|
||||
if (_playerManager.LocalSession?.AttachedEntity == null || _owner == null)
|
||||
return;
|
||||
|
||||
var player = _playerManager.LocalSession.AttachedEntity;
|
||||
|
||||
if (!_accessReaderSystem.IsAllowed(player.Value, _owner.Value))
|
||||
{
|
||||
_popupSystem.PopupClient(Loc.GetString("holopad-window-access-denied"), _owner.Value, player.Value);
|
||||
return;
|
||||
}
|
||||
|
||||
SendHolopadStartBroadcastMessageAction?.Invoke();
|
||||
}
|
||||
|
||||
private void OnHolopadActivateProjectorMessage()
|
||||
{
|
||||
SendHolopadActivateProjectorMessageAction?.Invoke();
|
||||
}
|
||||
|
||||
private void OnHolopadRequestStationAiMessage()
|
||||
{
|
||||
SendHolopadRequestStationAiMessageAction?.Invoke();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void SetState(EntityUid owner, HolopadUiKey uiKey)
|
||||
{
|
||||
_owner = owner;
|
||||
_currentUiKey = uiKey;
|
||||
|
||||
// Determines what UI containers are available to the user.
|
||||
// Components of these will be toggled on and off when
|
||||
// UpdateAppearance() is called
|
||||
|
||||
switch (uiKey)
|
||||
{
|
||||
case HolopadUiKey.InteractionWindow:
|
||||
RequestStationAiContainer.Visible = true;
|
||||
HolopadContactListContainer.Visible = true;
|
||||
StartBroadcastContainer.Visible = true;
|
||||
break;
|
||||
|
||||
case HolopadUiKey.InteractionWindowForAi:
|
||||
ActivateProjectorContainer.Visible = true;
|
||||
StartBroadcastContainer.Visible = true;
|
||||
break;
|
||||
|
||||
case HolopadUiKey.AiActionWindow:
|
||||
HolopadContactListContainer.Visible = true;
|
||||
StartBroadcastContainer.Visible = true;
|
||||
break;
|
||||
|
||||
case HolopadUiKey.AiRequestWindow:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateState(Dictionary<NetEntity, string> holopads)
|
||||
{
|
||||
if (_owner == null || !_entManager.TryGetComponent<TelephoneComponent>(_owner.Value, out var telephone))
|
||||
return;
|
||||
|
||||
// Caller ID text
|
||||
var callerId = _telephoneSystem.GetFormattedCallerIdForEntity(telephone.LastCallerId.Item1, telephone.LastCallerId.Item2, Color.LightGray, "Default", 11);
|
||||
var holoapdId = _telephoneSystem.GetFormattedDeviceIdForEntity(telephone.LastCallerId.Item3, Color.LightGray, "Default", 11);
|
||||
|
||||
CallerIdText.SetMessage(FormattedMessage.FromMarkupOrThrow(callerId));
|
||||
HolopadIdText.SetMessage(FormattedMessage.FromMarkupOrThrow(holoapdId));
|
||||
LockOutIdText.SetMessage(FormattedMessage.FromMarkupOrThrow(callerId));
|
||||
|
||||
// Sort holopads alphabetically
|
||||
var holopadArray = holopads.ToArray();
|
||||
Array.Sort(holopadArray, AlphabeticalSort);
|
||||
|
||||
// Clear excess children from the contact list
|
||||
while (ContactsList.ChildCount > holopadArray.Length)
|
||||
ContactsList.RemoveChild(ContactsList.GetChild(ContactsList.ChildCount - 1));
|
||||
|
||||
// Make / update required children
|
||||
for (int i = 0; i < holopadArray.Length; i++)
|
||||
{
|
||||
var (netEntity, label) = holopadArray[i];
|
||||
|
||||
if (i >= ContactsList.ChildCount)
|
||||
{
|
||||
var newContactButton = new HolopadContactButton();
|
||||
newContactButton.OnPressed += args => { OnSendHolopadStartNewCallMessage(newContactButton.NetEntity); };
|
||||
|
||||
ContactsList.AddChild(newContactButton);
|
||||
}
|
||||
|
||||
var child = ContactsList.GetChild(i);
|
||||
|
||||
if (child is not HolopadContactButton)
|
||||
continue;
|
||||
|
||||
var contactButton = (HolopadContactButton)child;
|
||||
contactButton.UpdateValues(netEntity, label);
|
||||
}
|
||||
|
||||
// Update buttons
|
||||
UpdateAppearance();
|
||||
}
|
||||
|
||||
private void UpdateAppearance()
|
||||
{
|
||||
if (_owner == null || !_entManager.TryGetComponent<TelephoneComponent>(_owner.Value, out var telephone))
|
||||
return;
|
||||
|
||||
if (_owner == null || !_entManager.TryGetComponent<HolopadComponent>(_owner.Value, out var holopad))
|
||||
return;
|
||||
|
||||
var hasBroadcastAccess = !_holopadSystem.IsHolopadBroadcastOnCoolDown((_owner.Value, holopad));
|
||||
var localPlayer = _playerManager.LocalSession?.AttachedEntity;
|
||||
|
||||
ControlsLockOutContainer.Visible = _holopadSystem.IsHolopadControlLocked((_owner.Value, holopad), localPlayer);
|
||||
ControlsContainer.Visible = !ControlsLockOutContainer.Visible;
|
||||
|
||||
// Temporarily disable the interface buttons when the call state changes to prevent any misclicks
|
||||
if (_currentState != telephone.CurrentState)
|
||||
{
|
||||
_previousState = _currentState;
|
||||
_currentState = telephone.CurrentState;
|
||||
_buttonUnlockTime = _timing.CurTime + _buttonUnlockDelay;
|
||||
}
|
||||
|
||||
var lockButtons = _timing.CurTime < _buttonUnlockTime;
|
||||
|
||||
// Make / update required children
|
||||
foreach (var child in ContactsList.Children)
|
||||
{
|
||||
if (child is not HolopadContactButton contactButton)
|
||||
continue;
|
||||
|
||||
var passesFilter = string.IsNullOrEmpty(SearchLineEdit.Text) ||
|
||||
contactButton.Text?.Contains(SearchLineEdit.Text, StringComparison.CurrentCultureIgnoreCase) == true;
|
||||
|
||||
contactButton.Visible = passesFilter;
|
||||
contactButton.Disabled = (_currentState != TelephoneState.Idle || lockButtons);
|
||||
}
|
||||
|
||||
// Update control text
|
||||
var cooldown = _holopadSystem.GetHolopadBroadcastCoolDown((_owner.Value, holopad));
|
||||
var cooldownString = $"{cooldown.Minutes:00}:{cooldown.Seconds:00}";
|
||||
|
||||
StartBroadcastButton.Text = _holopadSystem.IsHolopadBroadcastOnCoolDown((_owner.Value, holopad)) ?
|
||||
Loc.GetString("holopad-window-emergency-broadcast-with-countdown", ("countdown", cooldownString)) :
|
||||
Loc.GetString("holopad-window-emergency-broadcast");
|
||||
|
||||
var lockout = _holopadSystem.GetHolopadControlLockedPeriod((_owner.Value, holopad));
|
||||
var lockoutString = $"{lockout.Minutes:00}:{lockout.Seconds:00}";
|
||||
|
||||
LockOutCountDownText.Text = Loc.GetString("holopad-window-controls-unlock-countdown", ("countdown", lockoutString));
|
||||
|
||||
switch (_currentState)
|
||||
{
|
||||
case TelephoneState.Idle:
|
||||
CallStatusText.Text = Loc.GetString("holopad-window-no-calls-in-progress"); break;
|
||||
|
||||
case TelephoneState.Calling:
|
||||
CallStatusText.Text = Loc.GetString("holopad-window-outgoing-call"); break;
|
||||
|
||||
case TelephoneState.Ringing:
|
||||
CallStatusText.Text = (_currentUiKey == HolopadUiKey.AiRequestWindow) ?
|
||||
Loc.GetString("holopad-window-ai-request") : Loc.GetString("holopad-window-incoming-call"); break;
|
||||
|
||||
case TelephoneState.InCall:
|
||||
CallStatusText.Text = Loc.GetString("holopad-window-call-in-progress"); break;
|
||||
|
||||
case TelephoneState.EndingCall:
|
||||
if (_previousState == TelephoneState.Calling || _previousState == TelephoneState.Idle)
|
||||
CallStatusText.Text = Loc.GetString("holopad-window-call-rejected");
|
||||
else
|
||||
CallStatusText.Text = Loc.GetString("holopad-window-call-ending");
|
||||
break;
|
||||
}
|
||||
|
||||
// Update control disability
|
||||
AnswerCallButton.Disabled = (_currentState != TelephoneState.Ringing || lockButtons);
|
||||
EndCallButton.Disabled = (_currentState == TelephoneState.Idle || _currentState == TelephoneState.EndingCall || lockButtons);
|
||||
StartBroadcastButton.Disabled = (_currentState != TelephoneState.Idle || !hasBroadcastAccess || lockButtons);
|
||||
RequestStationAiButton.Disabled = (_currentState != TelephoneState.Idle || lockButtons);
|
||||
ActivateProjectorButton.Disabled = (_currentState != TelephoneState.Idle || lockButtons);
|
||||
|
||||
// Update control visibility
|
||||
FetchingAvailableHolopadsContainer.Visible = (ContactsList.ChildCount == 0);
|
||||
ActiveCallControlsContainer.Visible = (_currentState != TelephoneState.Idle || _currentUiKey == HolopadUiKey.AiRequestWindow);
|
||||
CallPlacementControlsContainer.Visible = !ActiveCallControlsContainer.Visible;
|
||||
CallerIdContainer.Visible = (_currentState == TelephoneState.Ringing);
|
||||
AnswerCallButton.Visible = (_currentState == TelephoneState.Ringing);
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
_updateTimer += args.DeltaSeconds;
|
||||
|
||||
if (_updateTimer >= UpdateTime)
|
||||
{
|
||||
_updateTimer -= UpdateTime;
|
||||
UpdateAppearance();
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class HolopadContactButton : Button
|
||||
{
|
||||
public NetEntity NetEntity;
|
||||
|
||||
public HolopadContactButton()
|
||||
{
|
||||
HorizontalExpand = true;
|
||||
SetHeight = 32;
|
||||
Margin = new Thickness(0f, 1f, 0f, 1f);
|
||||
ReservesSpace = false;
|
||||
}
|
||||
|
||||
public void UpdateValues(NetEntity netEntity, string label)
|
||||
{
|
||||
NetEntity = netEntity;
|
||||
Text = Loc.GetString("holopad-window-contact-label", ("label", label));
|
||||
}
|
||||
}
|
||||
|
||||
private int AlphabeticalSort(KeyValuePair<NetEntity, string> x, KeyValuePair<NetEntity, string> y)
|
||||
{
|
||||
if (string.IsNullOrEmpty(x.Value))
|
||||
return -1;
|
||||
|
||||
if (string.IsNullOrEmpty(y.Value))
|
||||
return 1;
|
||||
|
||||
return x.Value.CompareTo(y.Value);
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ namespace Content.Client.Info
|
||||
AddInfoButton("server-info-website-button", CCVars.InfoLinksWebsite);
|
||||
AddInfoButton("server-info-wiki-button", CCVars.InfoLinksWiki);
|
||||
AddInfoButton("server-info-forum-button", CCVars.InfoLinksForum);
|
||||
AddInfoButton("server-info-telegram-button", CCVars.InfoLinksTelegram);
|
||||
|
||||
var guidebookController = UserInterfaceManager.GetUIController<GuidebookUIController>();
|
||||
var guidebookButton = new Button() { Text = Loc.GetString("server-info-guidebook-button") };
|
||||
|
||||
@@ -235,9 +235,23 @@ namespace Content.Client.Inventory
|
||||
EntityManager.RaisePredictiveEvent(new InteractInventorySlotEvent(GetNetEntity(item.Value), altInteract: true));
|
||||
}
|
||||
|
||||
protected override void UpdateInventoryTemplate(Entity<InventoryComponent> ent)
|
||||
{
|
||||
base.UpdateInventoryTemplate(ent);
|
||||
|
||||
if (TryComp(ent, out InventorySlotsComponent? inventorySlots))
|
||||
{
|
||||
foreach (var slot in ent.Comp.Slots)
|
||||
{
|
||||
if (inventorySlots.SlotData.TryGetValue(slot.Name, out var slotData))
|
||||
slotData.SlotDef = slot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SlotData
|
||||
{
|
||||
public readonly SlotDefinition SlotDef;
|
||||
public SlotDefinition SlotDef;
|
||||
public EntityUid? HeldEntity => Container?.ContainedEntity;
|
||||
public bool Blocked;
|
||||
public bool Highlighted;
|
||||
|
||||
@@ -17,6 +17,7 @@ using Content.Shared.Inventory.VirtualItem;
|
||||
using Content.Shared.Strip.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Input;
|
||||
@@ -29,10 +30,13 @@ namespace Content.Client.Inventory
|
||||
[UsedImplicitly]
|
||||
public sealed class StrippableBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _ui = default!;
|
||||
|
||||
private readonly ExamineSystem _examine;
|
||||
private readonly InventorySystem _inv;
|
||||
private readonly SharedCuffableSystem _cuffable;
|
||||
private readonly StrippableSystem _strippable;
|
||||
|
||||
[ViewVariables]
|
||||
private const int ButtonSeparation = 4;
|
||||
@@ -51,6 +55,8 @@ namespace Content.Client.Inventory
|
||||
_examine = EntMan.System<ExamineSystem>();
|
||||
_inv = EntMan.System<InventorySystem>();
|
||||
_cuffable = EntMan.System<SharedCuffableSystem>();
|
||||
_strippable = EntMan.System<StrippableSystem>();
|
||||
|
||||
_virtualHiddenEntity = EntMan.SpawnEntity(HiddenPocketEntityId, MapCoordinates.Nullspace);
|
||||
}
|
||||
|
||||
@@ -185,9 +191,15 @@ namespace Content.Client.Inventory
|
||||
return;
|
||||
|
||||
if (ev.Function == ContentKeyFunctions.ExamineEntity)
|
||||
{
|
||||
_examine.DoExamine(slot.Entity.Value);
|
||||
ev.Handle();
|
||||
}
|
||||
else if (ev.Function == EngineKeyFunctions.UseSecondary)
|
||||
{
|
||||
_ui.GetUIController<VerbMenuUIController>().OpenVerbMenu(slot.Entity.Value);
|
||||
ev.Handle();
|
||||
}
|
||||
}
|
||||
|
||||
private void AddInventoryButton(EntityUid invUid, string slotId, InventoryComponent inv)
|
||||
@@ -198,7 +210,8 @@ namespace Content.Client.Inventory
|
||||
var entity = container.ContainedEntity;
|
||||
|
||||
// If this is a full pocket, obscure the real entity
|
||||
if (entity != null && slotDef.StripHidden)
|
||||
// this does not work for modified clients because they are still sent the real entity
|
||||
if (entity != null && _strippable.IsStripHidden(slotDef, _player.LocalEntity))
|
||||
entity = _virtualHiddenEntity;
|
||||
|
||||
var button = new SlotButton(new SlotData(slotDef, container));
|
||||
|
||||
@@ -21,6 +21,22 @@ public sealed class HandheldLightSystem : SharedHandheldLightSystem
|
||||
SubscribeLocalEvent<HandheldLightComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// TODO: Not properly predicted yet. Don't call this function if you want a the actual return value!
|
||||
/// </remarks>
|
||||
public override bool TurnOff(Entity<HandheldLightComponent> ent, bool makeNoise = true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// TODO: Not properly predicted yet. Don't call this function if you want a the actual return value!
|
||||
/// </remarks>
|
||||
public override bool TurnOn(EntityUid user, Entity<HandheldLightComponent> uid)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnAppearanceChange(EntityUid uid, HandheldLightComponent? component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
|
||||
@@ -116,7 +116,7 @@ namespace Content.Client.Lobby
|
||||
return;
|
||||
}
|
||||
|
||||
Lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-not-started");
|
||||
Lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-not-started");
|
||||
string text;
|
||||
|
||||
if (_gameTicker.Paused)
|
||||
@@ -136,6 +136,10 @@ namespace Content.Client.Lobby
|
||||
{
|
||||
text = Loc.GetString(seconds < -5 ? "lobby-state-right-now-question" : "lobby-state-right-now-confirmation");
|
||||
}
|
||||
else if (difference.TotalHours >= 1)
|
||||
{
|
||||
text = $"{Math.Floor(difference.TotalHours)}:{difference.Minutes:D2}:{difference.Seconds:D2}";
|
||||
}
|
||||
else
|
||||
{
|
||||
text = $"{difference.Minutes}:{difference.Seconds:D2}";
|
||||
|
||||
@@ -279,7 +279,7 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
|
||||
|
||||
_profileEditor.OnOpenGuidebook += _guide.OpenHelp;
|
||||
|
||||
_characterSetup = new CharacterSetupGui(EntityManager, _prototypeManager, _resourceCache, _preferencesManager, _profileEditor);
|
||||
_characterSetup = new CharacterSetupGui(_profileEditor);
|
||||
|
||||
_characterSetup.CloseButton.OnPressed += _ =>
|
||||
{
|
||||
@@ -455,7 +455,21 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
|
||||
{
|
||||
EntityUid dummyEnt;
|
||||
|
||||
if (humanoid is not null)
|
||||
EntProtoId? previewEntity = null;
|
||||
if (humanoid != null && jobClothes)
|
||||
{
|
||||
job ??= GetPreferredJob(humanoid);
|
||||
|
||||
previewEntity = job.JobPreviewEntity ?? (EntProtoId?)job?.JobEntity;
|
||||
}
|
||||
|
||||
if (previewEntity != null)
|
||||
{
|
||||
// Special type like borg or AI, do not spawn a human just spawn the entity.
|
||||
dummyEnt = EntityManager.SpawnEntity(previewEntity, MapCoordinates.Nullspace);
|
||||
return dummyEnt;
|
||||
}
|
||||
else if (humanoid is not null)
|
||||
{
|
||||
var dummy = _prototypeManager.Index<SpeciesPrototype>(humanoid.Species).DollPrototype;
|
||||
dummyEnt = EntityManager.SpawnEntity(dummy, MapCoordinates.Nullspace);
|
||||
@@ -469,7 +483,8 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
|
||||
|
||||
if (humanoid != null && jobClothes)
|
||||
{
|
||||
job ??= GetPreferredJob(humanoid);
|
||||
DebugTools.Assert(job != null);
|
||||
|
||||
GiveDummyJobClothes(dummyEnt, humanoid, job);
|
||||
|
||||
if (_prototypeManager.HasIndex<RoleLoadoutPrototype>(LoadoutSystem.GetJobPrototype(job.ID)))
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
SeparationOverride="0"
|
||||
Name="InternalHBox">
|
||||
<SpriteView Scale="2 2"
|
||||
Margin="0 4 4 4"
|
||||
OverrideDirection="South"
|
||||
Name="View"/>
|
||||
<Label Name="DescriptionLabel"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
xmlns:style="clr-namespace:Content.Client.Stylesheets"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
VerticalExpand="True">
|
||||
<Control>
|
||||
<PanelContainer Name="BackgroundPanel" />
|
||||
@@ -10,10 +11,15 @@
|
||||
<Label Text="{Loc 'character-setup-gui-character-setup-label'}"
|
||||
Margin="8 0 0 0" VAlign="Center"
|
||||
StyleClasses="LabelHeadingBigger" />
|
||||
|
||||
<Button Name="StatsButton" HorizontalExpand="True"
|
||||
Text="{Loc 'character-setup-gui-character-setup-stats-button'}"
|
||||
StyleClasses="ButtonBig"
|
||||
HorizontalAlignment="Right" />
|
||||
<cc:CommandButton Name="AdminRemarksButton"
|
||||
Command="adminremarks"
|
||||
Text="{Loc 'character-setup-gui-character-setup-adminremarks-button'}"
|
||||
StyleClasses="ButtonBig" />
|
||||
<Button Name="RulesButton"
|
||||
Text="{Loc 'character-setup-gui-character-setup-rules-button'}"
|
||||
StyleClasses="ButtonBig"/>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Client.Info;
|
||||
using Content.Client.Info.PlaytimeStats;
|
||||
using Content.Client.Resources;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
@@ -8,6 +9,7 @@ using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Lobby.UI
|
||||
@@ -18,28 +20,23 @@ namespace Content.Client.Lobby.UI
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class CharacterSetupGui : Control
|
||||
{
|
||||
private readonly IClientPreferencesManager _preferencesManager;
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IPrototypeManager _protomanager;
|
||||
[Dependency] private readonly IClientPreferencesManager _preferencesManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protomanager = default!;
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
private readonly Button _createNewCharacterButton;
|
||||
|
||||
public event Action<int>? SelectCharacter;
|
||||
public event Action<int>? DeleteCharacter;
|
||||
|
||||
public CharacterSetupGui(
|
||||
IEntityManager entManager,
|
||||
IPrototypeManager protoManager,
|
||||
IResourceCache resourceCache,
|
||||
IClientPreferencesManager preferencesManager,
|
||||
HumanoidProfileEditor profileEditor)
|
||||
public CharacterSetupGui(HumanoidProfileEditor profileEditor)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_preferencesManager = preferencesManager;
|
||||
_entManager = entManager;
|
||||
_protomanager = protoManager;
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
var panelTex = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
|
||||
var panelTex = _resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
|
||||
var back = new StyleBoxTexture
|
||||
{
|
||||
Texture = panelTex,
|
||||
@@ -56,7 +53,7 @@ namespace Content.Client.Lobby.UI
|
||||
|
||||
_createNewCharacterButton.OnPressed += args =>
|
||||
{
|
||||
preferencesManager.CreateCharacter(HumanoidCharacterProfile.Random());
|
||||
_preferencesManager.CreateCharacter(HumanoidCharacterProfile.Random());
|
||||
ReloadCharacterPickers();
|
||||
args.Event.Handle();
|
||||
};
|
||||
@@ -65,6 +62,8 @@ namespace Content.Client.Lobby.UI
|
||||
RulesButton.OnPressed += _ => new RulesAndInfoWindow().Open();
|
||||
|
||||
StatsButton.OnPressed += _ => new PlaytimeStatsWindow().OpenCentered();
|
||||
|
||||
_cfg.OnValueChanged(CCVars.SeeOwnNotes, p => AdminRemarksButton.Visible = p, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
</BoxContainer>
|
||||
<!-- Right side -->
|
||||
<BoxContainer Orientation="Vertical" VerticalExpand="True" VerticalAlignment="Center">
|
||||
<SpriteView Name="SpriteView" Scale="8 8" SizeFlagsStretchRatio="1" />
|
||||
<SpriteView Name="SpriteView" Scale="8 8" Margin="4" SizeFlagsStretchRatio="1" />
|
||||
<BoxContainer Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 5">
|
||||
<Button Name="SpriteRotateLeft" Text="◀" StyleClasses="OpenRight" />
|
||||
<cc:VSeparator Margin="2 0 3 0" />
|
||||
|
||||
@@ -36,17 +36,18 @@ public sealed partial class LoadoutContainer : BoxContainer
|
||||
|
||||
if (_protoManager.TryIndex(proto, out var loadProto))
|
||||
{
|
||||
var ent = _entManager.System<LoadoutSystem>().GetFirstOrNull(loadProto);
|
||||
var ent = loadProto.DummyEntity ?? _entManager.System<LoadoutSystem>().GetFirstOrNull(loadProto);
|
||||
|
||||
if (ent != null)
|
||||
{
|
||||
_entity = _entManager.SpawnEntity(ent, MapCoordinates.Nullspace);
|
||||
Sprite.SetEntity(_entity);
|
||||
if (ent == null)
|
||||
return;
|
||||
|
||||
var spriteTooltip = new Tooltip();
|
||||
spriteTooltip.SetMessage(FormattedMessage.FromUnformatted(_entManager.GetComponent<MetaDataComponent>(_entity.Value).EntityDescription));
|
||||
TooltipSupplier = _ => spriteTooltip;
|
||||
}
|
||||
_entity = _entManager.SpawnEntity(ent, MapCoordinates.Nullspace);
|
||||
Sprite.SetEntity(_entity);
|
||||
|
||||
var spriteTooltip = new Tooltip();
|
||||
spriteTooltip.SetMessage(FormattedMessage.FromUnformatted(_entManager.GetComponent<MetaDataComponent>(_entity.Value).EntityDescription));
|
||||
|
||||
TooltipSupplier = _ => spriteTooltip;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ using Content.Client.Message;
|
||||
using Content.Client.UserInterface.Systems.EscapeMenu;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Movement.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Controls the switching of motion and standing still animation
|
||||
/// </summary>
|
||||
public sealed class ClientSpriteMovementSystem : SharedSpriteMovementSystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
private EntityQuery<SpriteComponent> _spriteQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_spriteQuery = GetEntityQuery<SpriteComponent>();
|
||||
|
||||
SubscribeLocalEvent<SpriteMovementComponent, AfterAutoHandleStateEvent>(OnAfterAutoHandleState);
|
||||
}
|
||||
|
||||
private void OnAfterAutoHandleState(Entity<SpriteMovementComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
if (!_spriteQuery.TryGetComponent(ent, out var sprite))
|
||||
return;
|
||||
|
||||
if (ent.Comp.IsMoving)
|
||||
{
|
||||
foreach (var (layer, state) in ent.Comp.MovementLayers)
|
||||
{
|
||||
sprite.LayerSetData(layer, state);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var (layer, state) in ent.Comp.NoMovementLayers)
|
||||
{
|
||||
sprite.LayerSetData(layer, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Movement.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Handles setting sprite states based on whether an entity has movement input.
|
||||
/// </summary>
|
||||
public sealed class SpriteMovementSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
private EntityQuery<SpriteComponent> _spriteQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<SpriteMovementComponent, MoveInputEvent>(OnSpriteMoveInput);
|
||||
_spriteQuery = GetEntityQuery<SpriteComponent>();
|
||||
}
|
||||
|
||||
private void OnSpriteMoveInput(EntityUid uid, SpriteMovementComponent component, ref MoveInputEvent args)
|
||||
{
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
var oldMoving = (SharedMoverController.GetNormalizedMovement(args.OldMovement) & MoveButtons.AnyDirection) != MoveButtons.None;
|
||||
var moving = (SharedMoverController.GetNormalizedMovement(args.Entity.Comp.HeldMoveButtons) & MoveButtons.AnyDirection) != MoveButtons.None;
|
||||
|
||||
if (oldMoving == moving || !_spriteQuery.TryGetComponent(uid, out var sprite))
|
||||
return;
|
||||
|
||||
if (moving)
|
||||
{
|
||||
foreach (var (layer, state) in component.MovementLayers)
|
||||
{
|
||||
sprite.LayerSetData(layer, state);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var (layer, state) in component.NoMovementLayers)
|
||||
{
|
||||
sprite.LayerSetData(layer, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ public abstract class EquipmentHudSystem<T> : EntitySystem where T : IComponent
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
|
||||
[ViewVariables]
|
||||
protected bool IsActive;
|
||||
protected virtual SlotFlags TargetSlots => ~SlotFlags.POCKET;
|
||||
|
||||
@@ -102,7 +103,7 @@ public abstract class EquipmentHudSystem<T> : EntitySystem where T : IComponent
|
||||
args.Components.Add(component);
|
||||
}
|
||||
|
||||
private void RefreshOverlay(EntityUid uid)
|
||||
protected void RefreshOverlay(EntityUid uid)
|
||||
{
|
||||
if (uid != _player.LocalSession?.AttachedEntity)
|
||||
return;
|
||||
|
||||
@@ -21,9 +21,16 @@ public sealed class ShowHealthBarsSystem : EquipmentHudSystem<ShowHealthBarsComp
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ShowHealthBarsComponent, AfterAutoHandleStateEvent>(OnHandleState);
|
||||
|
||||
_overlay = new(EntityManager, _prototype);
|
||||
}
|
||||
|
||||
private void OnHandleState(Entity<ShowHealthBarsComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
RefreshOverlay(ent);
|
||||
}
|
||||
|
||||
protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowHealthBarsComponent> component)
|
||||
{
|
||||
base.UpdateInternal(component);
|
||||
|
||||
@@ -17,6 +17,7 @@ public sealed class ShowHealthIconsSystem : EquipmentHudSystem<ShowHealthIconsCo
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeMan = default!;
|
||||
|
||||
[ViewVariables]
|
||||
public HashSet<string> DamageContainers = new();
|
||||
|
||||
public override void Initialize()
|
||||
@@ -24,6 +25,7 @@ public sealed class ShowHealthIconsSystem : EquipmentHudSystem<ShowHealthIconsCo
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DamageableComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
SubscribeLocalEvent<ShowHealthIconsComponent, AfterAutoHandleStateEvent>(OnHandleState);
|
||||
}
|
||||
|
||||
protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowHealthIconsComponent> component)
|
||||
@@ -43,6 +45,11 @@ public sealed class ShowHealthIconsSystem : EquipmentHudSystem<ShowHealthIconsCo
|
||||
DamageContainers.Clear();
|
||||
}
|
||||
|
||||
private void OnHandleState(Entity<ShowHealthIconsComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
RefreshOverlay(ent);
|
||||
}
|
||||
|
||||
private void OnGetStatusIconsEvent(Entity<DamageableComponent> entity, ref GetStatusIconsEvent args)
|
||||
{
|
||||
if (!IsActive)
|
||||
|
||||
@@ -35,7 +35,7 @@ public sealed partial class StencilOverlay
|
||||
var matty = Matrix3x2.Multiply(matrix, invMatrix);
|
||||
worldHandle.SetTransform(matty);
|
||||
|
||||
foreach (var tile in grid.Comp.GetTilesIntersecting(worldAABB))
|
||||
foreach (var tile in _map.GetTilesIntersecting(grid.Owner, grid, worldAABB))
|
||||
{
|
||||
// Ignored tiles for stencil
|
||||
if (_weather.CanWeatherAffect(grid.Owner, grid, tile))
|
||||
|
||||
@@ -26,6 +26,7 @@ public sealed partial class StencilOverlay : Overlay
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
private readonly ParallaxSystem _parallax;
|
||||
private readonly SharedTransformSystem _transform;
|
||||
private readonly SharedMapSystem _map;
|
||||
private readonly SpriteSystem _sprite;
|
||||
private readonly WeatherSystem _weather;
|
||||
|
||||
@@ -35,11 +36,12 @@ public sealed partial class StencilOverlay : Overlay
|
||||
|
||||
private readonly ShaderInstance _shader;
|
||||
|
||||
public StencilOverlay(ParallaxSystem parallax, SharedTransformSystem transform, SpriteSystem sprite, WeatherSystem weather)
|
||||
public StencilOverlay(ParallaxSystem parallax, SharedTransformSystem transform, SharedMapSystem map, SpriteSystem sprite, WeatherSystem weather)
|
||||
{
|
||||
ZIndex = ParallaxSystem.ParallaxZIndex + 1;
|
||||
_parallax = parallax;
|
||||
_transform = transform;
|
||||
_map = map;
|
||||
_sprite = sprite;
|
||||
_weather = weather;
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
@@ -10,13 +10,14 @@ public sealed class StencilOverlaySystem : EntitySystem
|
||||
[Dependency] private readonly IOverlayManager _overlay = default!;
|
||||
[Dependency] private readonly ParallaxSystem _parallax = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
[Dependency] private readonly WeatherSystem _weather = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_overlay.AddOverlay(new StencilOverlay(_parallax, _transform, _sprite, _weather));
|
||||
_overlay.AddOverlay(new StencilOverlay(_parallax, _transform, _map, _sprite, _weather));
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
|
||||
@@ -9,6 +9,7 @@ using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Client.UserInterface.RichText;
|
||||
using Content.Client.UserInterface.RichText;
|
||||
using Robust.Shared.Input;
|
||||
|
||||
namespace Content.Client.Paper.UI
|
||||
@@ -43,7 +44,8 @@ namespace Content.Client.Paper.UI
|
||||
typeof(BulletTag),
|
||||
typeof(ColorTag),
|
||||
typeof(HeadingTag),
|
||||
typeof(ItalicTag)
|
||||
typeof(ItalicTag),
|
||||
typeof(MonoTag)
|
||||
};
|
||||
|
||||
public event Action<string>? OnSaved;
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Pulling.Components;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Physics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -14,6 +17,8 @@ public sealed class MoverController : SharedMoverController
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly AlertsSystem _alerts = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -135,4 +140,15 @@ public sealed class MoverController : SharedMoverController
|
||||
{
|
||||
return _timing is { IsFirstTimePredicted: true, InSimulation: true };
|
||||
}
|
||||
|
||||
public override void SetSprinting(Entity<InputMoverComponent> entity, ushort subTick, bool walking)
|
||||
{
|
||||
// Logger.Info($"[{_gameTiming.CurTick}/{subTick}] Sprint: {enabled}");
|
||||
base.SetSprinting(entity, subTick, walking);
|
||||
|
||||
if (walking && _cfg.GetCVar(CCVars.ToggleWalk))
|
||||
_alerts.ShowAlert(entity, WalkingAlert, showCooldown: false, autoRemove: false);
|
||||
else
|
||||
_alerts.ClearAlert(entity, WalkingAlert);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,26 +385,6 @@ public partial class NavMapControl : MapGridControl
|
||||
if (PostWallDrawingAction != null)
|
||||
PostWallDrawingAction.Invoke(handle);
|
||||
|
||||
// Beacons
|
||||
if (_beacons.Pressed)
|
||||
{
|
||||
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 font = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Bold.ttf"), fontSize);
|
||||
|
||||
foreach (var beacon in _navMap.Beacons.Values)
|
||||
{
|
||||
var position = beacon.Position - offset;
|
||||
position = ScalePosition(position with { Y = -position.Y });
|
||||
|
||||
var textDimensions = handle.GetDimensions(font, beacon.Text, 1f);
|
||||
handle.DrawRect(new UIBox2(position - textDimensions / 2 - rectBuffer, position + textDimensions / 2 + rectBuffer), BackgroundColor);
|
||||
handle.DrawString(font, position - textDimensions / 2, beacon.Text, beacon.Color);
|
||||
}
|
||||
}
|
||||
|
||||
var curTime = Timing.RealTime;
|
||||
var blinkFrequency = 1f / 1f;
|
||||
var lit = curTime.TotalSeconds % blinkFrequency > blinkFrequency / 2f;
|
||||
@@ -443,11 +423,31 @@ public partial class NavMapControl : MapGridControl
|
||||
position = ScalePosition(new Vector2(position.X, -position.Y));
|
||||
|
||||
var scalingCoefficient = MinmapScaleModifier * float.Sqrt(MinimapScale);
|
||||
var positionOffset = new Vector2(scalingCoefficient * blip.Texture.Width, scalingCoefficient * blip.Texture.Height);
|
||||
var positionOffset = new Vector2(scalingCoefficient * blip.Scale * blip.Texture.Width, scalingCoefficient * blip.Scale * blip.Texture.Height);
|
||||
|
||||
handle.DrawTextureRect(blip.Texture, new UIBox2(position - positionOffset, position + positionOffset), blip.Color);
|
||||
}
|
||||
}
|
||||
|
||||
// Beacons
|
||||
if (_beacons.Pressed)
|
||||
{
|
||||
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 font = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Bold.ttf"), fontSize);
|
||||
|
||||
foreach (var beacon in _navMap.Beacons.Values)
|
||||
{
|
||||
var position = beacon.Position - offset;
|
||||
position = ScalePosition(position with { Y = -position.Y });
|
||||
|
||||
var textDimensions = handle.GetDimensions(font, beacon.Text, 1f);
|
||||
handle.DrawRect(new UIBox2(position - textDimensions / 2 - rectBuffer, position + textDimensions / 2 + rectBuffer), BackgroundColor);
|
||||
handle.DrawString(font, position - textDimensions / 2, beacon.Text, beacon.Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
@@ -689,6 +689,9 @@ public partial class NavMapControl : MapGridControl
|
||||
Vector2i foundTermius;
|
||||
Vector2i foundOrigin;
|
||||
|
||||
if (origin == terminus)
|
||||
return;
|
||||
|
||||
// Does our new line end at the beginning of an existing line?
|
||||
if (lookup.Remove(terminus, out foundTermius))
|
||||
{
|
||||
@@ -739,13 +742,15 @@ public struct NavMapBlip
|
||||
public Color Color;
|
||||
public bool Blinks;
|
||||
public bool Selectable;
|
||||
public float Scale;
|
||||
|
||||
public NavMapBlip(EntityCoordinates coordinates, Texture texture, Color color, bool blinks, bool selectable = true)
|
||||
public NavMapBlip(EntityCoordinates coordinates, Texture texture, Color color, bool blinks, bool selectable = true, float scale = 1f)
|
||||
{
|
||||
Coordinates = coordinates;
|
||||
Texture = texture;
|
||||
Color = color;
|
||||
Blinks = blinks;
|
||||
Selectable = selectable;
|
||||
Scale = scale;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using Content.Client.Effects;
|
||||
using Content.Client.Smoking;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Polymorph.Components;
|
||||
using Content.Shared.Polymorph.Systems;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Client.Polymorph.Systems;
|
||||
|
||||
@@ -10,14 +13,20 @@ public sealed class ChameleonProjectorSystem : SharedChameleonProjectorSystem
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
|
||||
private EntityQuery<AppearanceComponent> _appearanceQuery;
|
||||
private EntityQuery<SpriteComponent> _spriteQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_appearanceQuery = GetEntityQuery<AppearanceComponent>();
|
||||
_spriteQuery = GetEntityQuery<SpriteComponent>();
|
||||
|
||||
SubscribeLocalEvent<ChameleonDisguiseComponent, AfterAutoHandleStateEvent>(OnHandleState);
|
||||
|
||||
SubscribeLocalEvent<ChameleonDisguisedComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<ChameleonDisguisedComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<ChameleonDisguisedComponent, GetFlashEffectTargetEvent>(OnGetFlashEffectTargetEvent);
|
||||
}
|
||||
|
||||
private void OnHandleState(Entity<ChameleonDisguiseComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
@@ -25,9 +34,30 @@ public sealed class ChameleonProjectorSystem : SharedChameleonProjectorSystem
|
||||
CopyComp<SpriteComponent>(ent);
|
||||
CopyComp<GenericVisualizerComponent>(ent);
|
||||
CopyComp<SolutionContainerVisualsComponent>(ent);
|
||||
CopyComp<BurnStateVisualsComponent>(ent);
|
||||
|
||||
// reload appearance to hopefully prevent any invisible layers
|
||||
if (_appearanceQuery.TryComp(ent, out var appearance))
|
||||
_appearance.QueueUpdate(ent, appearance);
|
||||
}
|
||||
|
||||
private void OnStartup(Entity<ChameleonDisguisedComponent> ent, ref ComponentStartup args)
|
||||
{
|
||||
if (!_spriteQuery.TryComp(ent, out var sprite))
|
||||
return;
|
||||
|
||||
ent.Comp.WasVisible = sprite.Visible;
|
||||
sprite.Visible = false;
|
||||
}
|
||||
|
||||
private void OnShutdown(Entity<ChameleonDisguisedComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
if (_spriteQuery.TryComp(ent, out var sprite))
|
||||
sprite.Visible = ent.Comp.WasVisible;
|
||||
}
|
||||
|
||||
private void OnGetFlashEffectTargetEvent(Entity<ChameleonDisguisedComponent> ent, ref GetFlashEffectTargetEvent args)
|
||||
{
|
||||
args.Target = ent.Comp.Disguise;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using Robust.Shared.Utility;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Content.Client.Power;
|
||||
|
||||
@@ -104,6 +105,26 @@ public sealed partial class PowerMonitoringWindow
|
||||
// Update power value
|
||||
// Don't use SI prefixes, just give the number in W, so that it is readily apparent which consumer is using a lot of power.
|
||||
button.PowerValue.Text = Loc.GetString("power-monitoring-window-button-value", ("value", Math.Round(entry.PowerValue).ToString("N0")));
|
||||
|
||||
// Update battery level if applicable
|
||||
if (entry.BatteryLevel != null)
|
||||
{
|
||||
button.BatteryLevel.Value = entry.BatteryLevel.Value;
|
||||
button.BatteryLevel.Visible = true;
|
||||
|
||||
button.BatteryPercentage.Text = entry.BatteryLevel.Value.ToString("P0");
|
||||
button.BatteryPercentage.Visible = true;
|
||||
|
||||
// Set progress bar color based on percentage
|
||||
var color = Color.FromHsv(new Vector4(entry.BatteryLevel.Value * 0.33f, 1, 1, 1));
|
||||
|
||||
button.BatteryLevel.ForegroundStyleBoxOverride = new StyleBoxFlat { BackgroundColor = color };
|
||||
}
|
||||
else
|
||||
{
|
||||
button.BatteryLevel.Visible = false;
|
||||
button.BatteryPercentage.Visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateEntrySourcesOrLoads(BoxContainer masterContainer, BoxContainer currentContainer, PowerMonitoringConsoleEntry[]? entries, SpriteSpecifier.Texture icon)
|
||||
@@ -443,6 +464,11 @@ public sealed class PowerMonitoringButton : Button
|
||||
public BoxContainer MainContainer;
|
||||
public TextureRect TextureRect;
|
||||
public Label NameLocalized;
|
||||
|
||||
public ProgressBar BatteryLevel;
|
||||
public PanelContainer BackgroundPanel;
|
||||
public Label BatteryPercentage;
|
||||
|
||||
public Label PowerValue;
|
||||
|
||||
public PowerMonitoringButton()
|
||||
@@ -478,6 +504,49 @@ public sealed class PowerMonitoringButton : Button
|
||||
|
||||
MainContainer.AddChild(NameLocalized);
|
||||
|
||||
BatteryLevel = new ProgressBar()
|
||||
{
|
||||
SetWidth = 47f,
|
||||
SetHeight = 20f,
|
||||
Margin = new Thickness(15, 0, 0, 0),
|
||||
MaxValue = 1,
|
||||
Visible = false,
|
||||
BackgroundStyleBoxOverride = new StyleBoxFlat { BackgroundColor = Color.Black },
|
||||
};
|
||||
|
||||
MainContainer.AddChild(BatteryLevel);
|
||||
|
||||
BackgroundPanel = new PanelContainer
|
||||
{
|
||||
// Draw a half-transparent box over the battery level to make the text more readable.
|
||||
PanelOverride = new StyleBoxFlat
|
||||
{
|
||||
BackgroundColor = new Color(0, 0, 0, 0.9f)
|
||||
},
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalExpand = true,
|
||||
VerticalExpand = true,
|
||||
// Box is undersized perfectly compared to the progress bar, so a little bit of the unaffected progress bar is visible.
|
||||
SetSize = new Vector2(43f, 16f)
|
||||
};
|
||||
|
||||
BatteryLevel.AddChild(BackgroundPanel);
|
||||
|
||||
BatteryPercentage = new Label()
|
||||
{
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
Align = Label.AlignMode.Center,
|
||||
SetWidth = 45f,
|
||||
MinWidth = 20f,
|
||||
Margin = new Thickness(10, -4, 10, 0),
|
||||
ClipText = true,
|
||||
Visible = false,
|
||||
};
|
||||
|
||||
BackgroundPanel.AddChild(BatteryPercentage);
|
||||
|
||||
PowerValue = new Label()
|
||||
{
|
||||
HorizontalAlignment = HAlignment.Right,
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace Content.Client.Sandbox
|
||||
}
|
||||
|
||||
// Try copy tile.
|
||||
|
||||
|
||||
if (!_map.TryFindGridAt(_transform.ToMapCoordinates(coords), out var gridUid, out var grid) || !_mapSystem.TryGetTileRef(gridUid, grid, coords, out var tileRef))
|
||||
return false;
|
||||
|
||||
@@ -157,10 +157,5 @@ namespace Content.Client.Sandbox
|
||||
{
|
||||
_consoleHost.ExecuteCommand("physics shapes");
|
||||
}
|
||||
|
||||
public void MachineLinking()
|
||||
{
|
||||
_consoleHost.ExecuteCommand("signallink");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ public sealed partial class SensorMonitoringWindow : FancyWindow, IComputerWindo
|
||||
public SensorMonitoringWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
public void UpdateState(ConsoleUIState state)
|
||||
|
||||
@@ -116,7 +116,7 @@ public partial class BaseShuttleControl : MapGridControl
|
||||
}
|
||||
}
|
||||
|
||||
protected void DrawGrid(DrawingHandleScreen handle, Matrix3x2 matrix, Entity<MapGridComponent> grid, Color color, float alpha = 0.01f)
|
||||
protected void DrawGrid(DrawingHandleScreen handle, Matrix3x2 gridToView, Entity<MapGridComponent> grid, Color color, float alpha = 0.01f)
|
||||
{
|
||||
var rator = Maps.GetAllTilesEnumerator(grid.Owner, grid.Comp);
|
||||
var minimapScale = MinimapScale;
|
||||
@@ -264,7 +264,7 @@ public partial class BaseShuttleControl : MapGridControl
|
||||
Extensions.EnsureLength(ref _allVertices, totalData);
|
||||
|
||||
_drawJob.MidPoint = midpoint;
|
||||
_drawJob.Matrix = matrix;
|
||||
_drawJob.Matrix = gridToView;
|
||||
_drawJob.MinimapScale = minimapScale;
|
||||
_drawJob.Vertices = gridData.Vertices;
|
||||
_drawJob.ScaledVertices = _allVertices;
|
||||
@@ -286,7 +286,7 @@ public partial class BaseShuttleControl : MapGridControl
|
||||
|
||||
private record struct GridDrawJob : IParallelRobustJob
|
||||
{
|
||||
public int BatchSize => 16;
|
||||
public int BatchSize => 64;
|
||||
|
||||
public float MinimapScale;
|
||||
public Vector2 MidPoint;
|
||||
@@ -297,12 +297,7 @@ public partial class BaseShuttleControl : MapGridControl
|
||||
|
||||
public void Execute(int index)
|
||||
{
|
||||
var vert = Vertices[index];
|
||||
var adjustedVert = Vector2.Transform(vert, Matrix);
|
||||
adjustedVert = adjustedVert with { Y = -adjustedVert.Y };
|
||||
|
||||
var scaledVert = ScalePosition(adjustedVert, MinimapScale, MidPoint);
|
||||
ScaledVertices[index] = scaledVert;
|
||||
ScaledVertices[index] = Vector2.Transform(Vertices[index], Matrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ public sealed partial class NavScreen : BoxContainer
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
private SharedTransformSystem _xformSystem;
|
||||
|
||||
private EntityUid? _consoleEntity; // Entity of controlling console
|
||||
private EntityUid? _shuttleEntity;
|
||||
|
||||
public NavScreen()
|
||||
@@ -35,6 +36,12 @@ public sealed partial class NavScreen : BoxContainer
|
||||
_shuttleEntity = shuttle;
|
||||
}
|
||||
|
||||
public void SetConsole(EntityUid? console)
|
||||
{
|
||||
_consoleEntity = console;
|
||||
NavRadar.SetConsole(console);
|
||||
}
|
||||
|
||||
private void OnIFFTogglePressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
NavRadar.ShowIFF ^= true;
|
||||
|
||||
@@ -138,6 +138,7 @@ public sealed partial class ShuttleConsoleWindow : FancyWindow,
|
||||
{
|
||||
var coordinates = _entManager.GetCoordinates(cState.NavState.Coordinates);
|
||||
NavContainer.SetShuttle(coordinates?.EntityId);
|
||||
NavContainer.SetConsole(owner);
|
||||
MapContainer.SetShuttle(coordinates?.EntityId);
|
||||
MapContainer.SetConsole(owner);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user