Compare commits
639 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ceb8cc8421 | ||
|
|
a90d7a645c | ||
|
|
2ea8bbf4eb | ||
|
|
41b72d5aa2 | ||
|
|
b6ab0298f4 | ||
|
|
ec771abfaa | ||
|
|
ad9d7573d6 | ||
|
|
0086e60b6a | ||
|
|
388cc8fdde | ||
|
|
211dd56f94 | ||
|
|
1df5be6570 | ||
|
|
ffee6bbd35 | ||
|
|
1132e5b6a7 | ||
|
|
15d81c1876 | ||
|
|
29b00fc633 | ||
|
|
e00a737285 | ||
|
|
dc6e65559b | ||
|
|
ad695702ac | ||
|
|
930cb61af8 | ||
|
|
2e230c089b | ||
|
|
aea7d01eaa | ||
|
|
448ee88357 | ||
|
|
c635aeba79 | ||
|
|
ff90bb4802 | ||
|
|
d906bcda03 | ||
|
|
a162564516 | ||
|
|
232e4951b0 | ||
|
|
824f5f6755 | ||
|
|
248e3f686b | ||
|
|
69f5600a51 | ||
|
|
1fbb5915aa | ||
|
|
4d202a7678 | ||
|
|
c7e9c4e857 | ||
|
|
409a7d9e01 | ||
|
|
5abcb680ab | ||
|
|
c675886713 | ||
|
|
f5d319bc3a | ||
|
|
5f276cd39d | ||
|
|
b34a68a519 | ||
|
|
ff3c1e6e82 | ||
|
|
18efec6472 | ||
|
|
1ba460e1a6 | ||
|
|
8cb3b9b2e6 | ||
|
|
c0717ed6a3 | ||
|
|
14d538997e | ||
|
|
42a41036ad | ||
|
|
1a92d08399 | ||
|
|
1f320eccd7 | ||
|
|
52a6d9ff43 | ||
|
|
8621d61278 | ||
|
|
96516fa288 | ||
|
|
90173f6147 | ||
|
|
22c5b6eb31 | ||
|
|
a3b0127328 | ||
|
|
b281cc28a1 | ||
|
|
40eca95661 | ||
|
|
6de6c6534c | ||
|
|
068c997519 | ||
|
|
c529f38517 | ||
|
|
76c5c911d9 | ||
|
|
d9077bde74 | ||
|
|
92668432a7 | ||
|
|
ce1eab9181 | ||
|
|
a9a99c1821 | ||
|
|
73511d9d8f | ||
|
|
4b9c4022b8 | ||
|
|
69f9da944d | ||
|
|
391d4a6c67 | ||
|
|
73e54d201d | ||
|
|
fb46949ea3 | ||
|
|
806e95b382 | ||
|
|
cdcafbada6 | ||
|
|
837a906538 | ||
|
|
4540034e26 | ||
|
|
546770ee70 | ||
|
|
b7d5297e9f | ||
|
|
08d0663307 | ||
|
|
715ddbaea2 | ||
|
|
b9215d214e | ||
|
|
0a6e5c6d01 | ||
|
|
558f6ab8a5 | ||
|
|
8ff4c22f42 | ||
|
|
215885a436 | ||
|
|
90701573ac | ||
|
|
4221fecff6 | ||
|
|
c4523a956d | ||
|
|
338f456c50 | ||
|
|
b9e4410c93 | ||
|
|
2c7c0b905f | ||
|
|
7cf2239eff | ||
|
|
6d3613672b | ||
|
|
f97977323a | ||
|
|
e8498d1bb2 | ||
|
|
ff4190b4e1 | ||
|
|
f81ca7ceba | ||
|
|
955f92298d | ||
|
|
10801af2f7 | ||
|
|
f1aeaaa640 | ||
|
|
f551bd32d6 | ||
|
|
4631e3c79a | ||
|
|
c156af34c4 | ||
|
|
1cfdfa12a0 | ||
|
|
9f58d8cc74 | ||
|
|
4a4fcad7ef | ||
|
|
50bc1bff48 | ||
|
|
dd13d969b2 | ||
|
|
400778eb73 | ||
|
|
da35a0f3c9 | ||
|
|
1426e516b5 | ||
|
|
e07c3c368f | ||
|
|
56bccdbc3e | ||
|
|
740c62330e | ||
|
|
de3d306487 | ||
|
|
32f4f99441 | ||
|
|
8bb4f526f5 | ||
|
|
93a34186cc | ||
|
|
e9de8ff9dd | ||
|
|
9c3587b00e | ||
|
|
996b45a04f | ||
|
|
241aa353e9 | ||
|
|
2afb515a8a | ||
|
|
9ae2cf6db4 | ||
|
|
ce9c8785d6 | ||
|
|
02e13d0182 | ||
|
|
fe483897a6 | ||
|
|
54449038ef | ||
|
|
b9f7c65c05 | ||
|
|
338a8e463e | ||
|
|
9c9a9db9a0 | ||
|
|
51c323963c | ||
|
|
7e703760c8 | ||
|
|
48b6a8c725 | ||
|
|
96daace170 | ||
|
|
196ec030f5 | ||
|
|
377bd1890e | ||
|
|
e40f9b20e3 | ||
|
|
ce163bdd80 | ||
|
|
d52e5ccbfb | ||
|
|
f489bee686 | ||
|
|
8c6f6f3fb0 | ||
|
|
877dcc996c | ||
|
|
7e30ffe007 | ||
|
|
916b1521a0 | ||
|
|
bc3942127b | ||
|
|
4bb02d64fb | ||
|
|
e40b775f7c | ||
|
|
ddcdeca4ea | ||
|
|
d81254e389 | ||
|
|
b64643ecd6 | ||
|
|
c197278c6f | ||
|
|
1a5c4ad83c | ||
|
|
818ee83440 | ||
|
|
91de6f80b1 | ||
|
|
792b219b04 | ||
|
|
2b0ca2dd8b | ||
|
|
a903ffb105 | ||
|
|
bec187d997 | ||
|
|
2917a87ce7 | ||
|
|
3fa2e0c976 | ||
|
|
51eae9d30c | ||
|
|
e68a5c8402 | ||
|
|
304b4d8542 | ||
|
|
cbca48ebbb | ||
|
|
a78a07d0c4 | ||
|
|
ad3b3c9f4f | ||
|
|
96fbde3413 | ||
|
|
88c29abe5e | ||
|
|
331cfaa1c5 | ||
|
|
9a1f37d476 | ||
|
|
57bcb2a16d | ||
|
|
fa8fbc49f5 | ||
|
|
c0edf2aeed | ||
|
|
e0e5f6355c | ||
|
|
eacfa34c99 | ||
|
|
296288e99d | ||
|
|
0ccefebc52 | ||
|
|
29304a7714 | ||
|
|
d15998ed16 | ||
|
|
90620db5f8 | ||
|
|
a014b40972 | ||
|
|
41b5c4dba2 | ||
|
|
1fa63179d1 | ||
|
|
e35d5390db | ||
|
|
030f1f2a57 | ||
|
|
61eb340945 | ||
|
|
b7d30f0870 | ||
|
|
23d8b92f94 | ||
|
|
5a5e201c7c | ||
|
|
1d3f61de3c | ||
|
|
c8cdde5d40 | ||
|
|
deaae06787 | ||
|
|
ff0f309f31 | ||
|
|
89b77bbed8 | ||
|
|
ec1a4c6601 | ||
|
|
71e2e2c768 | ||
|
|
dc2d3f94cb | ||
|
|
ea38075feb | ||
|
|
e00d4f7b26 | ||
|
|
cb7e5d4214 | ||
|
|
186138e9b5 | ||
|
|
50780f5cf0 | ||
|
|
f249c19eaf | ||
|
|
77d92675e2 | ||
|
|
02e35502a1 | ||
|
|
dc3369d198 | ||
|
|
25b50ebb27 | ||
|
|
6e5680b3c2 | ||
|
|
fe0414eda7 | ||
|
|
092539ae59 | ||
|
|
b16768fd0b | ||
|
|
8655dcaaf6 | ||
|
|
66621de270 | ||
|
|
a75ec704e6 | ||
|
|
5f00394f4f | ||
|
|
d3daa83b82 | ||
|
|
50f42d71a2 | ||
|
|
88d099a571 | ||
|
|
3e1f7d6d92 | ||
|
|
4c08c31149 | ||
|
|
29caf4cf53 | ||
|
|
0598578ed9 | ||
|
|
747cb15888 | ||
|
|
903961771b | ||
|
|
1fe24eeb12 | ||
|
|
8cd7e6e141 | ||
|
|
95c44d2a35 | ||
|
|
f0fd3186cd | ||
|
|
a18692bc46 | ||
|
|
b879418fa3 | ||
|
|
9b6acf6202 | ||
|
|
b1c81ed6e6 | ||
|
|
52af7d27da | ||
|
|
51caae7ebe | ||
|
|
9d7345892f | ||
|
|
6f032f678a | ||
|
|
d86020ceae | ||
|
|
2c7a66d8ec | ||
|
|
4eef8b9bd8 | ||
|
|
5251b30591 | ||
|
|
c283634efb | ||
|
|
e4676395c0 | ||
|
|
8c7e7e5510 | ||
|
|
69c1eff34e | ||
|
|
c3192e187c | ||
|
|
abd2c7642e | ||
|
|
49845d736b | ||
|
|
35f3cbe3f9 | ||
|
|
50433c7ab6 | ||
|
|
9f44870eb9 | ||
|
|
77753debeb | ||
|
|
9f1dd9f876 | ||
|
|
73ab6716a5 | ||
|
|
0deaa483dd | ||
|
|
f6af1a4ad5 | ||
|
|
43cef42fba | ||
|
|
66344c3ac7 | ||
|
|
c90d54664b | ||
|
|
7d85141c9b | ||
|
|
9b2be2ba50 | ||
|
|
ddde077d16 | ||
|
|
495315565a | ||
|
|
ee7a29326d | ||
|
|
6f298cab62 | ||
|
|
f5bb790edb | ||
|
|
85241a7dce | ||
|
|
f08d4bf7eb | ||
|
|
8ff58821ac | ||
|
|
ea581e67c8 | ||
|
|
0fe1407214 | ||
|
|
b8e7ae925f | ||
|
|
bdb6bf35bf | ||
|
|
9f3f09871e | ||
|
|
3b0ec7f695 | ||
|
|
fe01a7ccfc | ||
|
|
bd5f920151 | ||
|
|
b52b8d495e | ||
|
|
1af1ee2ad4 | ||
|
|
b94e015314 | ||
|
|
04942442aa | ||
|
|
eef0db6001 | ||
|
|
0e1b3607a4 | ||
|
|
89f175c73b | ||
|
|
25f680267c | ||
|
|
c06c9e92a5 | ||
|
|
d4fb4e8822 | ||
|
|
b23f3a119e | ||
|
|
1da3e89c7b | ||
|
|
bc98d3cef8 | ||
|
|
2bfb03cd29 | ||
|
|
2dbbd72a22 | ||
|
|
5391221391 | ||
|
|
1ad7b03023 | ||
|
|
66cec2a4bf | ||
|
|
a73c76cd02 | ||
|
|
e381920e86 | ||
|
|
5eefc7fd45 | ||
|
|
8792c06d87 | ||
|
|
0869b05ffd | ||
|
|
717cc575d1 | ||
|
|
ffd346af4b | ||
|
|
13274d63b0 | ||
|
|
72c856722b | ||
|
|
b69db1ccbf | ||
|
|
d2f01004a2 | ||
|
|
41357d44c1 | ||
|
|
56a7b9a305 | ||
|
|
12e4cdc43b | ||
|
|
d090e98bd4 | ||
|
|
8926669f3a | ||
|
|
3fb42ca4ee | ||
|
|
880d4b7249 | ||
|
|
00c59290fc | ||
|
|
b152832bf6 | ||
|
|
cd85e486e3 | ||
|
|
575b12dcdc | ||
|
|
c0caaaa8e5 | ||
|
|
c5e077efc1 | ||
|
|
842cb44cd2 | ||
|
|
de26699cfe | ||
|
|
38d11d0684 | ||
|
|
adf1c4d79e | ||
|
|
5b95aacb45 | ||
|
|
7be6a61b75 | ||
|
|
0577c90f81 | ||
|
|
995bb2f067 | ||
|
|
feccff9036 | ||
|
|
3b83ba9f65 | ||
|
|
068e8949c2 | ||
|
|
93eaff3d15 | ||
|
|
e3a39a21a2 | ||
|
|
a41bd181e1 | ||
|
|
4a5168f14c | ||
|
|
9a224398d3 | ||
|
|
e39ca8d8d2 | ||
|
|
b9e18b43e7 | ||
|
|
6d0c2ed362 | ||
|
|
d9ff72c907 | ||
|
|
03856b79b4 | ||
|
|
dcd74e8305 | ||
|
|
4f2ae14b3f | ||
|
|
5c234c1de4 | ||
|
|
0882435293 | ||
|
|
f0aec83be4 | ||
|
|
b3aa1f6dcd | ||
|
|
58e8aef5d8 | ||
|
|
5b0f49bab7 | ||
|
|
c27be2bf95 | ||
|
|
62eb7db0c7 | ||
|
|
1cfed8b6c7 | ||
|
|
1a33bb2d69 | ||
|
|
e2576e1770 | ||
|
|
40ce38770a | ||
|
|
1d0ed83967 | ||
|
|
6649d06fd8 | ||
|
|
4a40b5112c | ||
|
|
250a9646da | ||
|
|
4ba8848383 | ||
|
|
45b803ce41 | ||
|
|
e1ca54fc2e | ||
|
|
3b40b4aafa | ||
|
|
96868625cb | ||
|
|
5a9cc75ab9 | ||
|
|
fad429c922 | ||
|
|
8b694b1899 | ||
|
|
75614db5ce | ||
|
|
34f4c12e00 | ||
|
|
a7e63329b4 | ||
|
|
c17bc612ff | ||
|
|
d5559e44bb | ||
|
|
f83876e8f8 | ||
|
|
5797c6cb66 | ||
|
|
0a605781e9 | ||
|
|
d7ef38701b | ||
|
|
5e7b03d251 | ||
|
|
33a915fc67 | ||
|
|
83147922f4 | ||
|
|
c182ea944f | ||
|
|
e1f6a2bbd5 | ||
|
|
8aefe6c615 | ||
|
|
8e6c9e7c47 | ||
|
|
f0176c2801 | ||
|
|
9c4832bfbe | ||
|
|
6c66817502 | ||
|
|
694d6e5b33 | ||
|
|
f779755c11 | ||
|
|
53b59bd6f3 | ||
|
|
2c0d61111f | ||
|
|
0e686285f8 | ||
|
|
23975c100c | ||
|
|
84aa369809 | ||
|
|
1b3d3debc1 | ||
|
|
c1c4728961 | ||
|
|
7606c656a0 | ||
|
|
da3a7e47e3 | ||
|
|
43c0b512bd | ||
|
|
11eadd91af | ||
|
|
2234641e4a | ||
|
|
ccaa995702 | ||
|
|
0bcb6adf6a | ||
|
|
0030da78a6 | ||
|
|
5436d6831e | ||
|
|
7664fcf1f4 | ||
|
|
dfcfd9b04f | ||
|
|
9a938185d9 | ||
|
|
1ee556091c | ||
|
|
eee4b64c64 | ||
|
|
89a4b60f7e | ||
|
|
20846ef446 | ||
|
|
fd9db21ad7 | ||
|
|
3516392f06 | ||
|
|
54e0818fef | ||
|
|
f628fb3fd2 | ||
|
|
1116a0a647 | ||
|
|
f7be2c16c0 | ||
|
|
54cc252bd7 | ||
|
|
69ee137cea | ||
|
|
415b7e96fd | ||
|
|
e8e1c9dd1f | ||
|
|
474c4910e2 | ||
|
|
0e51d98c85 | ||
|
|
7ca90d11b3 | ||
|
|
69946c79d8 | ||
|
|
37df61113e | ||
|
|
b8becf4a56 | ||
|
|
5b7ff8ddee | ||
|
|
ef3007a603 | ||
|
|
0230323563 | ||
|
|
845d0f9182 | ||
|
|
f887d22a16 | ||
|
|
9780cf9062 | ||
|
|
10120bb0b6 | ||
|
|
574512f1bf | ||
|
|
744af3be3e | ||
|
|
2d07d89a73 | ||
|
|
f91488fa27 | ||
|
|
b0f212bad5 | ||
|
|
8038ebe37d | ||
|
|
4720182cf4 | ||
|
|
51bc17f76d | ||
|
|
d186f18c20 | ||
|
|
fc7493e149 | ||
|
|
3dc3031054 | ||
|
|
6cc88115b5 | ||
|
|
c33daddda2 | ||
|
|
952c58c226 | ||
|
|
49ea084336 | ||
|
|
1fe7e90da8 | ||
|
|
b92a9b6d1a | ||
|
|
43d5be40f0 | ||
|
|
2b026daf67 | ||
|
|
74541e23a4 | ||
|
|
c612806ef1 | ||
|
|
097882f658 | ||
|
|
24c9cf54af | ||
|
|
73712b4e39 | ||
|
|
2dd3f24fb2 | ||
|
|
7a91fb7512 | ||
|
|
1dabe49234 | ||
|
|
06ea07eca5 | ||
|
|
d414ea55f5 | ||
|
|
bb5a278fdb | ||
|
|
ef506006b3 | ||
|
|
7b7b5cc59f | ||
|
|
5ce370f18b | ||
|
|
73f4442410 | ||
|
|
ed39649721 | ||
|
|
cd4442b81e | ||
|
|
e8c4dc9a34 | ||
|
|
edb3eb5b2e | ||
|
|
ce5760d46c | ||
|
|
189a52c6d7 | ||
|
|
fe1252cca7 | ||
|
|
7b3fcb1d54 | ||
|
|
fa594fb3e9 | ||
|
|
830159390b | ||
|
|
b0a3f294c5 | ||
|
|
40ed16e118 | ||
|
|
6c5a0786c2 | ||
|
|
adda9136d0 | ||
|
|
cb224a973d | ||
|
|
d7074bf74f | ||
|
|
f051078c79 | ||
|
|
74b38c49fb | ||
|
|
cb439ba39a | ||
|
|
b34591ab59 | ||
|
|
8c874c76dc | ||
|
|
18ed1041ba | ||
|
|
ad5c82fec9 | ||
|
|
7629d447aa | ||
|
|
806addd3ae | ||
|
|
ad0c547f08 | ||
|
|
d92d4c2fa2 | ||
|
|
f163602942 | ||
|
|
2d23c6a45e | ||
|
|
86e600bec7 | ||
|
|
e4c378cb8f | ||
|
|
10999dc905 | ||
|
|
9b251d70e2 | ||
|
|
df538aa4d6 | ||
|
|
cd1d731b73 | ||
|
|
c355031a2c | ||
|
|
bd529a0478 | ||
|
|
7fcd424c71 | ||
|
|
439b8d772f | ||
|
|
d8540cdbd9 | ||
|
|
0dad2c863b | ||
|
|
179b2534d2 | ||
|
|
ff0b1bfd05 | ||
|
|
097c876578 | ||
|
|
d5ce4c6b7f | ||
|
|
7e5c764b50 | ||
|
|
77222824c3 | ||
|
|
e36033a3ac | ||
|
|
89d8208abd | ||
|
|
ab91d358ef | ||
|
|
147aad5064 | ||
|
|
f1ec10e3e1 | ||
|
|
b13d100107 | ||
|
|
13611e04ff | ||
|
|
92b7386e57 | ||
|
|
74367b1b74 | ||
|
|
dee2b12955 | ||
|
|
f6b4f23362 | ||
|
|
85b4be3ff4 | ||
|
|
20cf3a6a9b | ||
|
|
205a4fc530 | ||
|
|
2d9285f790 | ||
|
|
3915b735ae | ||
|
|
61a1e769d7 | ||
|
|
2ba705ffe9 | ||
|
|
78fa747973 | ||
|
|
c33c227d95 | ||
|
|
ea05c593aa | ||
|
|
74193d1182 | ||
|
|
41bda76980 | ||
|
|
ed411fa944 | ||
|
|
d6661e4558 | ||
|
|
9989d6db75 | ||
|
|
4a5a2ceb31 | ||
|
|
6704245a41 | ||
|
|
128728bfcb | ||
|
|
63b5d83728 | ||
|
|
bddd31b1b0 | ||
|
|
071ed3f1ed | ||
|
|
98bd1552b9 | ||
|
|
7554d51b0a | ||
|
|
3f89f3f0f7 | ||
|
|
b005d661f8 | ||
|
|
05d4b2793b | ||
|
|
cf29e4b7bd | ||
|
|
3dfce70fdc | ||
|
|
059832d324 | ||
|
|
1452502fbf | ||
|
|
1f22f8ab6a | ||
|
|
2eb30c9ba9 | ||
|
|
988b2b2bc5 | ||
|
|
5ee56a4cd3 | ||
|
|
37091d1254 | ||
|
|
68f4fd995e | ||
|
|
ba152fc61d | ||
|
|
074e6d8b62 | ||
|
|
283ed302db | ||
|
|
4b345c0ea6 | ||
|
|
fd4584519e | ||
|
|
de19cf652a | ||
|
|
7c840d71e2 | ||
|
|
0d0e6b2feb | ||
|
|
984617eed8 | ||
|
|
2d16205091 | ||
|
|
c41b867c7f | ||
|
|
7f196fc415 | ||
|
|
ec3e7968a6 | ||
|
|
82fbec02f0 | ||
|
|
9bd6200625 | ||
|
|
dac4f9cb3f | ||
|
|
8a11d2c649 | ||
|
|
2c99c8cb86 | ||
|
|
b6eb825c5c | ||
|
|
3601245d6f | ||
|
|
de46359742 | ||
|
|
de4583d14b | ||
|
|
60f975f0dc | ||
|
|
d38cc0ee82 | ||
|
|
36141f3b1a | ||
|
|
6f89d0672d | ||
|
|
7597cd9172 | ||
|
|
8c1fa84c6e | ||
|
|
7bc132bc0d | ||
|
|
ec4c864354 | ||
|
|
26e9c37be1 | ||
|
|
4c4687eb8e | ||
|
|
b8e01208af | ||
|
|
c7de994183 | ||
|
|
b0b220f085 | ||
|
|
5fd0e31920 | ||
|
|
bf10d34a8d | ||
|
|
1b44e84ef3 | ||
|
|
66ec5864d0 | ||
|
|
f40cffa746 | ||
|
|
4caa13f1c0 | ||
|
|
6864a457fe | ||
|
|
d345316db8 | ||
|
|
a2399f8842 | ||
|
|
92947a507c | ||
|
|
4f2d059de4 | ||
|
|
a464acf354 | ||
|
|
c85436118a | ||
|
|
fc1c29d14f | ||
|
|
17ea432604 | ||
|
|
3e0bcddd4d | ||
|
|
73ae408e20 | ||
|
|
66483bdd72 | ||
|
|
f6e265bccb | ||
|
|
60ff292f14 | ||
|
|
b2806dcb75 | ||
|
|
55b9e367b3 | ||
|
|
2d91344a5b | ||
|
|
eee05e1451 | ||
|
|
6d28ce7641 | ||
|
|
4e48f2d581 | ||
|
|
6d4e532ebe | ||
|
|
73c02f74f2 | ||
|
|
7add727db0 | ||
|
|
44cdd5ed55 | ||
|
|
7fda197391 | ||
|
|
8ca10e4f0b | ||
|
|
8a43a7022d | ||
|
|
e46e4a7993 | ||
|
|
3097106fe4 | ||
|
|
50cb6b4e30 | ||
|
|
e569eb276e | ||
|
|
17cd8c1d61 | ||
|
|
090fb0f417 | ||
|
|
e0941e2f45 | ||
|
|
b593220805 | ||
|
|
2b8d2d9c83 | ||
|
|
780fecff3b | ||
|
|
23381d936f | ||
|
|
a55ab49b6d |
24
.appveyor.yml
Normal file
24
.appveyor.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
version: 1.0.{build}
|
||||
image: Visual Studio 2017
|
||||
|
||||
platform: x64
|
||||
configuration: Debug
|
||||
|
||||
build:
|
||||
project: SpaceStation.sln
|
||||
parallel: false
|
||||
verbosity: minimal
|
||||
|
||||
before_build:
|
||||
- cmd: py -3 -m pip install --user requests
|
||||
- cmd: py -3 RUN_THIS.py --no-prompt
|
||||
- cmd: nuget restore SpaceStation14.sln
|
||||
|
||||
build_script:
|
||||
- ps: msbuild SpaceStation14.sln /v:m /nologo /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /p:Platform=x64 /p:Configuration=Debug /p:AppVeyor=yes
|
||||
|
||||
test:
|
||||
assemblies:
|
||||
only:
|
||||
- bin/Content.IntegrationTests/Content.IntegrationTests.dll
|
||||
- bin/Content.Tests/Content.Tests.dll
|
||||
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@@ -1 +1 @@
|
||||
* @PJB3005
|
||||
* @PJB3005 @Silvertorch5
|
||||
|
||||
17
.gitignore
vendored
17
.gitignore
vendored
@@ -262,3 +262,20 @@ __pycache__/
|
||||
|
||||
# Visual Studio Code workspace settings.
|
||||
.vscode/
|
||||
|
||||
# Release package files go here:
|
||||
release/
|
||||
|
||||
# Apple please go.
|
||||
.DS_Store
|
||||
# KDE, come in.
|
||||
.directory
|
||||
|
||||
BuildFiles/Mac/Space Station 14.app/Contents/MacOS/Godot
|
||||
BuildFiles/Mac/Space Station 14.app/Contents/MacOS/GodotSharpTools.dll
|
||||
BuildFiles/Mac/Space Station 14.app/Contents/MacOS/mscorlib.dll
|
||||
BuildFiles/Mac/Space Station 14.app/Contents/MacOS/libmonosgen-2.0.dylib
|
||||
BuildFiles/Windows/Godot/*
|
||||
|
||||
# Working on the tools scripts is getting annoying okay?
|
||||
.mypy_cache/
|
||||
|
||||
7
.gitmodules
vendored
7
.gitmodules
vendored
@@ -1,3 +1,4 @@
|
||||
[submodule "engine"]
|
||||
path = engine
|
||||
url = https://github.com/space-wizards/space-station-14.git
|
||||
[submodule "RobustToolbox"]
|
||||
path = RobustToolbox
|
||||
url = https://github.com/space-wizards/RobustToolbox.git
|
||||
branch = master
|
||||
38
.travis.yml
Normal file
38
.travis.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
language: csharp
|
||||
|
||||
dist: trusty
|
||||
sudo: false
|
||||
|
||||
os:
|
||||
- linux
|
||||
#- osx
|
||||
|
||||
addons:
|
||||
apt:
|
||||
#sources:
|
||||
#- deadsnakes
|
||||
|
||||
packages:
|
||||
- python3.5
|
||||
- python3-pip
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- packages/
|
||||
- RobustToolbox/Dependencies/
|
||||
- RobustToolbox/SS14.Client.Godot/.mono/assemblies/
|
||||
|
||||
#before_install:
|
||||
# - if [ $TRAVIS_OS_NAME = osx ]; then brew update && brew upgrade python; fi
|
||||
|
||||
before_script:
|
||||
#- "if [ $TRAVIS_OS_NAME = linux ]; then pyenv shell 3.6; fi"
|
||||
- "python3.5 -m pip install --user requests"
|
||||
- "nuget restore SpaceStation14.sln"
|
||||
- "python3.5 RUN_THIS.py --no-prompt"
|
||||
|
||||
script:
|
||||
- "msbuild /p:Configuration=Debug /p:Platform=x64 /nologo /m SpaceStation14.sln /p:Python=python3.5"
|
||||
- "mono packages/nunit.consolerunner/3.10.0/tools/nunit3-console.exe bin/Content.Tests/Content.Tests.dll bin/Content.IntegrationTests/Content.IntegrationTests.dll"
|
||||
|
||||
|
||||
1
BuildChecker/.gitignore
vendored
1
BuildChecker/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
INSTALLED_HOOKS_VERSION
|
||||
DISABLE_SUBMODULE_AUTOUPDATE
|
||||
|
||||
@@ -16,6 +16,17 @@ https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild
|
||||
<PropertyGroup>
|
||||
<Python>python3</Python>
|
||||
<Python Condition="'$(OS)'=='Windows_NT' Or '$(OS)'=='Windows'">py -3</Python>
|
||||
<ProjectGuid>{C899FCA4-7037-4E49-ABC2-44DE72487110}</ProjectGuid>
|
||||
<TargetFrameworkMoniker>.NETFramework, Version=v4.7.1</TargetFrameworkMoniker>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<Target Name="Build">
|
||||
<Exec Command="$(Python) git_helper.py" CustomErrorRegularExpression="^Error" />
|
||||
@@ -24,4 +35,8 @@ https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild
|
||||
<Target Name="Clean">
|
||||
<Message Importance="low" Text="Ignoring 'Clean' target." />
|
||||
</Target>
|
||||
<Target Name="Compile">
|
||||
</Target>
|
||||
<Target Name="CoreCompile">
|
||||
</Target>
|
||||
</Project>
|
||||
|
||||
@@ -8,10 +8,12 @@ import shutil
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
SOLUTION_PATH = Path("..") / "SpaceStation14Content.sln"
|
||||
CURRENT_HOOKS_VERSION = "1" # If this doesn't match the saved version we overwrite them all.
|
||||
SOLUTION_PATH = Path("..") / "SpaceStation14.sln"
|
||||
# If this doesn't match the saved version we overwrite them all.
|
||||
CURRENT_HOOKS_VERSION = "2"
|
||||
QUIET = len(sys.argv) == 2 and sys.argv[1] == "--quiet"
|
||||
|
||||
|
||||
def run_command(command: List[str], capture: bool = False) -> subprocess.CompletedProcess:
|
||||
"""
|
||||
Runs a command with pretty output.
|
||||
@@ -40,6 +42,9 @@ def update_submodules():
|
||||
Updates all submodules.
|
||||
"""
|
||||
|
||||
if os.path.isfile("DISABLE_SUBMODULE_AUTOUPDATE"):
|
||||
return
|
||||
|
||||
# If the status doesn't match, force VS to reload the solution.
|
||||
# status = run_command(["git", "submodule", "status"], capture=True)
|
||||
run_command(["git", "submodule", "update", "--init", "--recursive"])
|
||||
@@ -50,6 +55,7 @@ def update_submodules():
|
||||
# print("Git submodules changed. Reloading solution.")
|
||||
# reset_solution()
|
||||
|
||||
|
||||
def install_hooks():
|
||||
"""
|
||||
Installs the necessary git hooks into .git/hooks.
|
||||
@@ -77,7 +83,8 @@ def install_hooks():
|
||||
|
||||
for filename in os.listdir(str(hooks_source_dir)):
|
||||
print("Copying hook {}".format(filename))
|
||||
shutil.copyfile(str(hooks_source_dir/filename), str(hooks_target_dir/filename))
|
||||
shutil.copyfile(str(hooks_source_dir/filename),
|
||||
str(hooks_target_dir/filename))
|
||||
|
||||
|
||||
def reset_solution():
|
||||
@@ -91,6 +98,7 @@ def reset_solution():
|
||||
with SOLUTION_PATH.open("w") as f:
|
||||
f.write(content)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
install_hooks()
|
||||
update_submodules()
|
||||
|
||||
0
BuildChecker/hooks/post-checkout
Normal file → Executable file
0
BuildChecker/hooks/post-checkout
Normal file → Executable file
0
BuildChecker/hooks/post-merge
Normal file → Executable file
0
BuildChecker/hooks/post-merge
Normal file → Executable file
5
BuildFiles/Linux/SS14.Launcher
Executable file
5
BuildFiles/Linux/SS14.Launcher
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
exec mono bin/SS14.Launcher.exe
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key>
|
||||
<string>SS14L</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Space Station 14 Launcher</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>SS14</string>
|
||||
<!--
|
||||
Just a note about this icon.
|
||||
MacOS seems REALLY iffy about this and even when the file is correct,
|
||||
it can take forever before it decides to actually update it and display it.
|
||||
TL;DR Apple is stupid.
|
||||
-->
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>ss14</string>
|
||||
</dict>
|
||||
</plist>
|
||||
8
BuildFiles/Mac/Space Station 14 Launcher.app/Contents/MacOS/SS14
Executable file
8
BuildFiles/Mac/Space Station 14 Launcher.app/Contents/MacOS/SS14
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
# cd to file containing script or something?
|
||||
BASEDIR=$(dirname "$0")
|
||||
echo "$BASEDIR"
|
||||
cd "$BASEDIR"
|
||||
|
||||
exec /Library/Frameworks/Mono.framework/Versions/Current/Commands/mono ../Resources/SS14.Launcher.exe
|
||||
Binary file not shown.
20
BuildFiles/Mac/Space Station 14.app/Contents/Info.plist
Normal file
20
BuildFiles/Mac/Space Station 14.app/Contents/Info.plist
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key>
|
||||
<string>SS14</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Space Station 14</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>SS14</string>
|
||||
<!--
|
||||
Just a note about this icon.
|
||||
MacOS seems REALLY iffy about this and even when the file is correct,
|
||||
it can take forever before it decides to actually update it and display it.
|
||||
TL;DR Apple is stupid.
|
||||
-->
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>ss14</string>
|
||||
</dict>
|
||||
</plist>
|
||||
8
BuildFiles/Mac/Space Station 14.app/Contents/MacOS/SS14
Executable file
8
BuildFiles/Mac/Space Station 14.app/Contents/MacOS/SS14
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
# cd to file containing script or something?
|
||||
BASEDIR=$(dirname "$0")
|
||||
echo "$BASEDIR"
|
||||
cd "$BASEDIR"
|
||||
|
||||
exec /Library/Frameworks/Mono.framework/Versions/Current/Commands/mono ../Resources/Robust.Client.exe
|
||||
BIN
BuildFiles/Mac/Space Station 14.app/Contents/Resources/ss14.icns
Normal file
BIN
BuildFiles/Mac/Space Station 14.app/Contents/Resources/ss14.icns
Normal file
Binary file not shown.
1
BuildFiles/Windows/run_me.bat
Normal file
1
BuildFiles/Windows/run_me.bat
Normal file
@@ -0,0 +1 @@
|
||||
call bin\SS14.Launcher.exe
|
||||
73
Content.Benchmarks/ComponentManagerGetAllComponents.cs
Normal file
73
Content.Benchmarks/ComponentManagerGetAllComponents.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Moq;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Benchmarks
|
||||
{
|
||||
public class ComponentManagerGetAllComponents
|
||||
{
|
||||
private readonly List<IEntity> _entities = new List<IEntity>();
|
||||
|
||||
private IComponentManager _componentManager;
|
||||
|
||||
[Params(500, 1000, 5000)] public int N { get; set; }
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
// Initialize component manager.
|
||||
IoCManager.InitThread();
|
||||
|
||||
IoCManager.Register<IComponentManager, ComponentManager>();
|
||||
|
||||
var dummyReg = new Mock<IComponentRegistration>();
|
||||
dummyReg.SetupGet(p => p.Name).Returns("Dummy");
|
||||
dummyReg.SetupGet(p => p.Type).Returns(typeof(DummyComponent));
|
||||
dummyReg.SetupGet(p => p.NetID).Returns((uint?) null);
|
||||
dummyReg.SetupGet(p => p.NetworkSynchronizeExistence).Returns(false);
|
||||
dummyReg.SetupGet(p => p.References).Returns(new Type[] {typeof(DummyComponent)});
|
||||
|
||||
var componentFactory = new Mock<IComponentFactory>();
|
||||
componentFactory.Setup(p => p.GetComponent<DummyComponent>()).Returns(new DummyComponent());
|
||||
componentFactory.Setup(p => p.GetRegistration(It.IsAny<DummyComponent>())).Returns(dummyReg.Object);
|
||||
|
||||
IoCManager.RegisterInstance<IComponentFactory>(componentFactory.Object);
|
||||
|
||||
IoCManager.BuildGraph();
|
||||
|
||||
_componentManager = IoCManager.Resolve<IComponentManager>();
|
||||
|
||||
// Initialize N entities with one component.
|
||||
for (var i = 0; i < N; i++)
|
||||
{
|
||||
var entity = new Entity();
|
||||
entity.SetUid(new EntityUid(i + 1));
|
||||
_entities.Add(entity);
|
||||
|
||||
_componentManager.AddComponent<DummyComponent>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int Run()
|
||||
{
|
||||
var count = 0;
|
||||
|
||||
foreach (var _ in _componentManager.GetAllComponents<DummyComponent>())
|
||||
{
|
||||
count += 1;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private class DummyComponent : Component
|
||||
{
|
||||
public override string Name => "Dummy";
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Content.Benchmarks/Content.Benchmarks.csproj
Normal file
25
Content.Benchmarks/Content.Benchmarks.csproj
Normal file
@@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<OutputPath>..\bin\Content.Benchmarks\</OutputPath>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<Platforms>x86;x64</Platforms>
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.11.5" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Content.Client\Content.Client.csproj" />
|
||||
<ProjectReference Include="..\Content.Server\Content.Server.csproj" />
|
||||
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj" />
|
||||
<ProjectReference Include="..\Content.Tests\Content.Tests.csproj" />
|
||||
<ProjectReference Include="..\Content.IntegrationTests\Content.IntegrationTests.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Client\Robust.Client.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Server\Robust.Server.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared\Robust.Shared.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.UnitTesting\Robust.UnitTesting.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
13
Content.Benchmarks/Program.cs
Normal file
13
Content.Benchmarks/Program.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using BenchmarkDotNet.Configs;
|
||||
using BenchmarkDotNet.Running;
|
||||
|
||||
namespace Content.Benchmarks
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
BenchmarkRunner.Run<ComponentManagerGetAllComponents>();
|
||||
}
|
||||
}
|
||||
}
|
||||
232
Content.Client/Chat/ChatBox.cs
Normal file
232
Content.Client/Chat/ChatBox.cs
Normal file
@@ -0,0 +1,232 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Chat;
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Client.Chat
|
||||
{
|
||||
public class ChatBox : MarginContainer
|
||||
{
|
||||
public delegate void TextSubmitHandler(ChatBox chatBox, string text);
|
||||
|
||||
public delegate void FilterToggledHandler(ChatBox chatBox, BaseButton.ButtonToggledEventArgs e);
|
||||
|
||||
private const int MaxLinePixelLength = 500;
|
||||
|
||||
private readonly IList<string> _inputHistory = new List<string>();
|
||||
|
||||
private ILocalizationManager localize = IoCManager.Resolve<ILocalizationManager>();
|
||||
|
||||
public LineEdit Input { get; private set; }
|
||||
public OutputPanel contents;
|
||||
|
||||
// Buttons for filtering
|
||||
public Button AllButton;
|
||||
public Button LocalButton;
|
||||
public Button OOCButton;
|
||||
|
||||
/// <summary>
|
||||
/// Index while cycling through the input history. -1 means not going through history.
|
||||
/// </summary>
|
||||
private int _inputIndex = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Message that WAS being input before going through history began.
|
||||
/// </summary>
|
||||
private string _inputTemp;
|
||||
|
||||
/// <summary>
|
||||
/// Default formatting string for the ClientChatConsole.
|
||||
/// </summary>
|
||||
public string DefaultChatFormat { get; set; }
|
||||
|
||||
public bool ReleaseFocusOnEnter { get; set; } = true;
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
MarginLeft = -475.0f;
|
||||
MarginTop = 10.0f;
|
||||
MarginRight = -10.0f;
|
||||
MarginBottom = 235.0f;
|
||||
|
||||
AnchorLeft = 1.0f;
|
||||
AnchorRight = 1.0f;
|
||||
|
||||
var outerVBox = new VBoxContainer();
|
||||
|
||||
var panelContainer = new PanelContainer
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#25252aaa")},
|
||||
SizeFlagsVertical = SizeFlags.FillExpand
|
||||
};
|
||||
var vBox = new VBoxContainer();
|
||||
panelContainer.AddChild(vBox);
|
||||
var hBox = new HBoxContainer();
|
||||
|
||||
outerVBox.AddChild(panelContainer);
|
||||
outerVBox.AddChild(hBox);
|
||||
|
||||
var contentMargin = new MarginContainer
|
||||
{
|
||||
MarginLeftOverride = 4, MarginRightOverride = 4,
|
||||
SizeFlagsVertical = SizeFlags.FillExpand
|
||||
};
|
||||
contents = new OutputPanel();
|
||||
contentMargin.AddChild(contents);
|
||||
vBox.AddChild(contentMargin);
|
||||
|
||||
Input = new LineEdit();
|
||||
Input.OnKeyDown += InputKeyDown;
|
||||
Input.OnTextEntered += Input_OnTextEntered;
|
||||
vBox.AddChild(Input);
|
||||
|
||||
AllButton = new Button
|
||||
{
|
||||
Text = localize.GetString("All"),
|
||||
Name = "ALL",
|
||||
SizeFlagsHorizontal = SizeFlags.ShrinkEnd | SizeFlags.Expand,
|
||||
ToggleMode = true,
|
||||
};
|
||||
|
||||
LocalButton = new Button
|
||||
{
|
||||
Text = localize.GetString("Local"),
|
||||
Name = "Local",
|
||||
ToggleMode = true,
|
||||
};
|
||||
|
||||
OOCButton = new Button
|
||||
{
|
||||
Text = localize.GetString("OOC"),
|
||||
Name = "OOC",
|
||||
ToggleMode = true,
|
||||
};
|
||||
|
||||
AllButton.OnToggled += OnFilterToggled;
|
||||
LocalButton.OnToggled += OnFilterToggled;
|
||||
OOCButton.OnToggled += OnFilterToggled;
|
||||
|
||||
hBox.AddChild(AllButton);
|
||||
hBox.AddChild(LocalButton);
|
||||
hBox.AddChild(OOCButton);
|
||||
|
||||
AddChild(outerVBox);
|
||||
}
|
||||
|
||||
protected override void MouseDown(GUIMouseButtonEventArgs e)
|
||||
{
|
||||
base.MouseDown(e);
|
||||
|
||||
Input.GrabKeyboardFocus();
|
||||
}
|
||||
|
||||
private void InputKeyDown(GUIKeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Keyboard.Key.Escape)
|
||||
{
|
||||
Input.ReleaseKeyboardFocus();
|
||||
e.Handle();
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.Key == Keyboard.Key.Up)
|
||||
{
|
||||
if (_inputIndex == -1 && _inputHistory.Count != 0)
|
||||
{
|
||||
_inputTemp = Input.Text;
|
||||
_inputIndex++;
|
||||
}
|
||||
else if (_inputIndex + 1 < _inputHistory.Count)
|
||||
{
|
||||
_inputIndex++;
|
||||
}
|
||||
|
||||
if (_inputIndex != -1)
|
||||
{
|
||||
Input.Text = _inputHistory[_inputIndex];
|
||||
}
|
||||
|
||||
e.Handle();
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.Key == Keyboard.Key.Down)
|
||||
{
|
||||
if (_inputIndex == 0)
|
||||
{
|
||||
Input.Text = _inputTemp;
|
||||
_inputTemp = "";
|
||||
_inputIndex--;
|
||||
}
|
||||
else if (_inputIndex != -1)
|
||||
{
|
||||
_inputIndex--;
|
||||
Input.Text = _inputHistory[_inputIndex];
|
||||
}
|
||||
|
||||
e.Handle();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
TextSubmitted = null;
|
||||
Input = null;
|
||||
contents = null;
|
||||
}
|
||||
}
|
||||
|
||||
public event TextSubmitHandler TextSubmitted;
|
||||
|
||||
public event FilterToggledHandler FilterToggled;
|
||||
|
||||
public void AddLine(string message, ChatChannel channel, Color color)
|
||||
{
|
||||
if (Disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var formatted = new FormattedMessage(3);
|
||||
formatted.PushColor(color);
|
||||
formatted.AddText(message);
|
||||
formatted.Pop();
|
||||
contents.AddMessage(formatted);
|
||||
}
|
||||
|
||||
private void Input_OnTextEntered(LineEdit.LineEditEventArgs args)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(args.Text))
|
||||
{
|
||||
TextSubmitted?.Invoke(this, args.Text);
|
||||
_inputHistory.Insert(0, args.Text);
|
||||
}
|
||||
|
||||
_inputIndex = -1;
|
||||
|
||||
Input.Clear();
|
||||
|
||||
if (ReleaseFocusOnEnter)
|
||||
{
|
||||
Input.ReleaseKeyboardFocus();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFilterToggled(BaseButton.ButtonToggledEventArgs args)
|
||||
{
|
||||
FilterToggled?.Invoke(this, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
408
Content.Client/Chat/ChatManager.cs
Normal file
408
Content.Client/Chat/ChatManager.cs
Normal file
@@ -0,0 +1,408 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.Interfaces.Chat;
|
||||
using Content.Shared.Chat;
|
||||
using Robust.Client;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.Interfaces.Graphics.ClientEye;
|
||||
using Robust.Client.Interfaces.UserInterface;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Chat
|
||||
{
|
||||
internal sealed class ChatManager : IChatManager
|
||||
{
|
||||
/// <summary>
|
||||
/// The max amount of chars allowed to fit in a single speech bubble.
|
||||
/// </summary>
|
||||
private const int SingleBubbleCharLimit = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Base queue delay each speech bubble has.
|
||||
/// </summary>
|
||||
private const float BubbleDelayBase = 0.2f;
|
||||
|
||||
/// <summary>
|
||||
/// Factor multiplied by speech bubble char length to add to delay.
|
||||
/// </summary>
|
||||
private const float BubbleDelayFactor = 0.8f / SingleBubbleCharLimit;
|
||||
|
||||
/// <summary>
|
||||
/// The max amount of speech bubbles over a single entity at once.
|
||||
/// </summary>
|
||||
private const int SpeechBubbleCap = 4;
|
||||
|
||||
private const char ConCmdSlash = '/';
|
||||
private const char OOCAlias = '[';
|
||||
private const char MeAlias = '@';
|
||||
|
||||
private readonly List<StoredChatMessage> filteredHistory = new List<StoredChatMessage>();
|
||||
|
||||
// Filter Button States
|
||||
private bool _allState;
|
||||
private bool _localState;
|
||||
private bool _oocState;
|
||||
|
||||
// Flag Enums for holding filtered channels
|
||||
private ChatChannel _filteredChannels;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IClientNetManager _netManager;
|
||||
[Dependency] private readonly IClientConsole _console;
|
||||
[Dependency] private readonly IEntityManager _entityManager;
|
||||
[Dependency] private readonly IEyeManager _eyeManager;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
private ChatBox _currentChatBox;
|
||||
private Control _speechBubbleRoot;
|
||||
|
||||
/// <summary>
|
||||
/// Speech bubbles that are currently visible on screen.
|
||||
/// We track them to push them up when new ones get added.
|
||||
/// </summary>
|
||||
private readonly Dictionary<EntityUid, List<SpeechBubble>> _activeSpeechBubbles =
|
||||
new Dictionary<EntityUid, List<SpeechBubble>>();
|
||||
|
||||
/// <summary>
|
||||
/// Speech bubbles that are to-be-sent because of the "rate limit" they have.
|
||||
/// </summary>
|
||||
private readonly Dictionary<EntityUid, SpeechBubbleQueueData> _queuedSpeechBubbles
|
||||
= new Dictionary<EntityUid, SpeechBubbleQueueData>();
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_netManager.RegisterNetMessage<MsgChatMessage>(MsgChatMessage.NAME, _onChatMessage);
|
||||
|
||||
_speechBubbleRoot = new Control();
|
||||
_speechBubbleRoot.SetAnchorPreset(Control.LayoutPreset.Wide);
|
||||
_userInterfaceManager.StateRoot.AddChild(_speechBubbleRoot);
|
||||
_speechBubbleRoot.SetPositionFirst();
|
||||
}
|
||||
|
||||
public void FrameUpdate(RenderFrameEventArgs delta)
|
||||
{
|
||||
// Update queued speech bubbles.
|
||||
if (_queuedSpeechBubbles.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (entityUid, queueData) in _queuedSpeechBubbles.ShallowClone())
|
||||
{
|
||||
if (!_entityManager.TryGetEntity(entityUid, out var entity))
|
||||
{
|
||||
_queuedSpeechBubbles.Remove(entityUid);
|
||||
continue;
|
||||
}
|
||||
|
||||
queueData.TimeLeft -= delta.Elapsed;
|
||||
if (queueData.TimeLeft > 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (queueData.MessageQueue.Count == 0)
|
||||
{
|
||||
_queuedSpeechBubbles.Remove(entityUid);
|
||||
continue;
|
||||
}
|
||||
|
||||
var msg = queueData.MessageQueue.Dequeue();
|
||||
|
||||
queueData.TimeLeft += BubbleDelayBase + msg.Length * BubbleDelayFactor;
|
||||
|
||||
// We keep the queue around while it has 0 items. This allows us to keep the timer.
|
||||
// When the timer hits 0 and there's no messages left, THEN we can clear it up.
|
||||
CreateSpeechBubble(entity, msg);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetChatBox(ChatBox chatBox)
|
||||
{
|
||||
if (_currentChatBox != null)
|
||||
{
|
||||
_currentChatBox.TextSubmitted -= _onChatBoxTextSubmitted;
|
||||
_currentChatBox.FilterToggled -= _onFilterButtonToggled;
|
||||
}
|
||||
|
||||
_currentChatBox = chatBox;
|
||||
if (_currentChatBox != null)
|
||||
{
|
||||
_currentChatBox.TextSubmitted += _onChatBoxTextSubmitted;
|
||||
_currentChatBox.FilterToggled += _onFilterButtonToggled;
|
||||
}
|
||||
|
||||
RepopulateChat(filteredHistory);
|
||||
_currentChatBox.AllButton.Pressed = !_allState;
|
||||
_currentChatBox.LocalButton.Pressed = !_localState;
|
||||
_currentChatBox.OOCButton.Pressed = !_oocState;
|
||||
}
|
||||
|
||||
public void RemoveSpeechBubble(EntityUid entityUid, SpeechBubble bubble)
|
||||
{
|
||||
bubble.Dispose();
|
||||
|
||||
var list = _activeSpeechBubbles[entityUid];
|
||||
list.Remove(bubble);
|
||||
|
||||
if (list.Count == 0)
|
||||
{
|
||||
_activeSpeechBubbles.Remove(entityUid);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteChatMessage(StoredChatMessage message)
|
||||
{
|
||||
Logger.Debug($"{message.Channel}: {message.Message}");
|
||||
|
||||
if (IsFiltered(message.Channel))
|
||||
{
|
||||
Logger.Debug($"Message filtered: {message.Channel}: {message.Message}");
|
||||
return;
|
||||
}
|
||||
|
||||
var color = Color.DarkGray;
|
||||
var messageText = message.Message;
|
||||
if (!string.IsNullOrEmpty(message.MessageWrap))
|
||||
{
|
||||
messageText = string.Format(message.MessageWrap, messageText);
|
||||
}
|
||||
|
||||
switch (message.Channel)
|
||||
{
|
||||
case ChatChannel.Server:
|
||||
color = Color.Orange;
|
||||
break;
|
||||
case ChatChannel.OOC:
|
||||
color = Color.LightSkyBlue;
|
||||
break;
|
||||
}
|
||||
|
||||
_currentChatBox?.AddLine(messageText, message.Channel, color);
|
||||
}
|
||||
|
||||
private void _onChatBoxTextSubmitted(ChatBox chatBox, string text)
|
||||
{
|
||||
DebugTools.Assert(chatBox == _currentChatBox);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
return;
|
||||
|
||||
switch (text[0])
|
||||
{
|
||||
case ConCmdSlash:
|
||||
{
|
||||
// run locally
|
||||
var conInput = text.Substring(1);
|
||||
_console.ProcessCommand(conInput);
|
||||
break;
|
||||
}
|
||||
case OOCAlias:
|
||||
{
|
||||
var conInput = text.Substring(1);
|
||||
_console.ProcessCommand($"ooc \"{conInput}\"");
|
||||
break;
|
||||
}
|
||||
case MeAlias:
|
||||
{
|
||||
var conInput = text.Substring(1);
|
||||
_console.ProcessCommand($"me \"{conInput}\"");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
var conInput = _currentChatBox.DefaultChatFormat != null
|
||||
? string.Format(_currentChatBox.DefaultChatFormat, text)
|
||||
: text;
|
||||
_console.ProcessCommand(conInput);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void _onFilterButtonToggled(ChatBox chatBox, BaseButton.ButtonToggledEventArgs e)
|
||||
{
|
||||
switch (e.Button.Name)
|
||||
{
|
||||
case "Local":
|
||||
_localState = !_localState;
|
||||
if (_localState)
|
||||
{
|
||||
_filteredChannels |= ChatChannel.Local;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
_filteredChannels &= ~ChatChannel.Local;
|
||||
break;
|
||||
}
|
||||
|
||||
case "OOC":
|
||||
_oocState = !_oocState;
|
||||
if (_oocState)
|
||||
{
|
||||
_filteredChannels |= ChatChannel.OOC;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
_filteredChannels &= ~ChatChannel.OOC;
|
||||
break;
|
||||
}
|
||||
|
||||
case "ALL":
|
||||
chatBox.LocalButton.Pressed ^= true;
|
||||
chatBox.OOCButton.Pressed ^= true;
|
||||
_allState = !_allState;
|
||||
break;
|
||||
}
|
||||
|
||||
RepopulateChat(filteredHistory);
|
||||
}
|
||||
|
||||
private void RepopulateChat(IEnumerable<StoredChatMessage> filteredMessages)
|
||||
{
|
||||
_currentChatBox.contents.Clear();
|
||||
|
||||
foreach (var msg in filteredMessages)
|
||||
{
|
||||
WriteChatMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void _onChatMessage(MsgChatMessage msg)
|
||||
{
|
||||
Logger.Debug($"{msg.Channel}: {msg.Message}");
|
||||
|
||||
// Log all incoming chat to repopulate when filter is un-toggled
|
||||
var storedMessage = new StoredChatMessage(msg);
|
||||
filteredHistory.Add(storedMessage);
|
||||
WriteChatMessage(storedMessage);
|
||||
|
||||
// Local messages that have an entity attached get a speech bubble.
|
||||
if (msg.Channel == ChatChannel.Local && msg.SenderEntity != default)
|
||||
{
|
||||
AddSpeechBubble(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddSpeechBubble(MsgChatMessage msg)
|
||||
{
|
||||
if (!_entityManager.TryGetEntity(msg.SenderEntity, out var entity))
|
||||
{
|
||||
Logger.WarningS("chat", "Got local chat message with invalid sender entity: {0}", msg.SenderEntity);
|
||||
return;
|
||||
}
|
||||
|
||||
// Split message into words separated by spaces.
|
||||
var words = msg.Message.Split(' ');
|
||||
var messages = new List<string>();
|
||||
var currentBuffer = new List<string>();
|
||||
|
||||
// Really shoddy way to approximate word length.
|
||||
// Yes, I am aware of all the crimes here.
|
||||
// TODO: Improve this to use actual glyph width etc..
|
||||
var currentWordLength = 0;
|
||||
foreach (var word in words)
|
||||
{
|
||||
// +1 for the space.
|
||||
currentWordLength += word.Length + 1;
|
||||
|
||||
if (currentWordLength > SingleBubbleCharLimit)
|
||||
{
|
||||
// Too long for the current speech bubble, flush it.
|
||||
messages.Add(string.Join(" ", currentBuffer));
|
||||
currentBuffer.Clear();
|
||||
|
||||
currentWordLength = word.Length;
|
||||
|
||||
if (currentWordLength > SingleBubbleCharLimit)
|
||||
{
|
||||
// Word is STILL too long.
|
||||
// Truncate it with an ellipse.
|
||||
messages.Add($"{word.Substring(0, SingleBubbleCharLimit-3)}...");
|
||||
currentWordLength = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
currentBuffer.Add(word);
|
||||
}
|
||||
|
||||
if (currentBuffer.Count != 0)
|
||||
{
|
||||
// Don't forget the last bubble.
|
||||
messages.Add(string.Join(" ", currentBuffer));
|
||||
}
|
||||
|
||||
foreach (var message in messages)
|
||||
{
|
||||
EnqueueSpeechBubble(entity, message);
|
||||
}
|
||||
}
|
||||
|
||||
private void EnqueueSpeechBubble(IEntity entity, string contents)
|
||||
{
|
||||
if (!_queuedSpeechBubbles.TryGetValue(entity.Uid, out var queueData))
|
||||
{
|
||||
queueData = new SpeechBubbleQueueData();
|
||||
_queuedSpeechBubbles.Add(entity.Uid, queueData);
|
||||
}
|
||||
|
||||
queueData.MessageQueue.Enqueue(contents);
|
||||
}
|
||||
|
||||
private void CreateSpeechBubble(IEntity entity, string contents)
|
||||
{
|
||||
var bubble = new SpeechBubble(contents, entity, _eyeManager, this);
|
||||
|
||||
if (_activeSpeechBubbles.TryGetValue(entity.Uid, out var existing))
|
||||
{
|
||||
// Push up existing bubbles above the mob's head.
|
||||
foreach (var existingBubble in existing)
|
||||
{
|
||||
existingBubble.VerticalOffset += bubble.ContentHeight;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
existing = new List<SpeechBubble>();
|
||||
_activeSpeechBubbles.Add(entity.Uid, existing);
|
||||
}
|
||||
|
||||
existing.Add(bubble);
|
||||
_speechBubbleRoot.AddChild(bubble);
|
||||
|
||||
if (existing.Count > SpeechBubbleCap)
|
||||
{
|
||||
// Get the oldest to start fading fast.
|
||||
var last = existing[0];
|
||||
last.FadeNow();
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsFiltered(ChatChannel channel)
|
||||
{
|
||||
// _allState works as inverter.
|
||||
return _allState ^ _filteredChannels.HasFlag(channel);
|
||||
}
|
||||
|
||||
private sealed class SpeechBubbleQueueData
|
||||
{
|
||||
/// <summary>
|
||||
/// Time left until the next speech bubble can appear.
|
||||
/// </summary>
|
||||
public float TimeLeft { get; set; }
|
||||
|
||||
public Queue<string> MessageQueue { get; } = new Queue<string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
137
Content.Client/Chat/SpeechBubble.cs
Normal file
137
Content.Client/Chat/SpeechBubble.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using Content.Client.Interfaces.Chat;
|
||||
using Robust.Client;
|
||||
using Robust.Client.Interfaces.Graphics.ClientEye;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timers;
|
||||
|
||||
namespace Content.Client.Chat
|
||||
{
|
||||
public class SpeechBubble : Control
|
||||
{
|
||||
/// <summary>
|
||||
/// The total time a speech bubble stays on screen.
|
||||
/// </summary>
|
||||
private const float TotalTime = 4;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of time at the end of the bubble's life at which it starts fading.
|
||||
/// </summary>
|
||||
private const float FadeTime = 0.25f;
|
||||
|
||||
/// <summary>
|
||||
/// The distance in world space to offset the speech bubble from the center of the entity.
|
||||
/// i.e. greater -> higher above the mob's head.
|
||||
/// </summary>
|
||||
private const float EntityVerticalOffset = 0.5f;
|
||||
|
||||
private readonly IEyeManager _eyeManager;
|
||||
private readonly IEntity _senderEntity;
|
||||
private readonly IChatManager _chatManager;
|
||||
|
||||
private Control _panel;
|
||||
|
||||
private float _timeLeft = TotalTime;
|
||||
|
||||
public float VerticalOffset { get; set; }
|
||||
private float _verticalOffsetAchieved;
|
||||
|
||||
public float ContentHeight { get; }
|
||||
|
||||
public SpeechBubble(string text, IEntity senderEntity, IEyeManager eyeManager, IChatManager chatManager)
|
||||
{
|
||||
_chatManager = chatManager;
|
||||
_senderEntity = senderEntity;
|
||||
_eyeManager = eyeManager;
|
||||
|
||||
MouseFilter = MouseFilterMode.Ignore;
|
||||
// Use text clipping so new messages don't overlap old ones being pushed up.
|
||||
RectClipContent = true;
|
||||
|
||||
var label = new RichTextLabel
|
||||
{
|
||||
MaxWidth = 256,
|
||||
MouseFilter = MouseFilterMode.Ignore
|
||||
};
|
||||
label.SetMessage(text);
|
||||
|
||||
_panel = new PanelContainer
|
||||
{
|
||||
StyleClasses = { "tooltipBox" },
|
||||
Children = { label },
|
||||
MouseFilter = MouseFilterMode.Ignore,
|
||||
ModulateSelfOverride = Color.White.WithAlpha(0.75f)
|
||||
};
|
||||
|
||||
AddChild(_panel);
|
||||
|
||||
_panel.Size = _panel.CombinedMinimumSize;
|
||||
ContentHeight = _panel.Height;
|
||||
Size = (_panel.Width, 0);
|
||||
_verticalOffsetAchieved = -ContentHeight;
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(RenderFrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
_timeLeft -= args.Elapsed;
|
||||
|
||||
if (_timeLeft <= FadeTime)
|
||||
{
|
||||
// Update alpha if we're fading.
|
||||
Modulate = Color.White.WithAlpha(_timeLeft / FadeTime);
|
||||
}
|
||||
|
||||
if (_senderEntity.Deleted || _timeLeft <= 0)
|
||||
{
|
||||
// Timer spawn to prevent concurrent modification exception.
|
||||
Timer.Spawn(0, Die);
|
||||
return;
|
||||
}
|
||||
|
||||
// Lerp to our new vertical offset if it's been modified.
|
||||
if (FloatMath.CloseTo(_verticalOffsetAchieved - VerticalOffset, 0, 0.1))
|
||||
{
|
||||
_verticalOffsetAchieved = VerticalOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
_verticalOffsetAchieved = FloatMath.Lerp(_verticalOffsetAchieved, VerticalOffset, 10 * args.Elapsed);
|
||||
}
|
||||
|
||||
var worldPos = _senderEntity.Transform.WorldPosition;
|
||||
worldPos += (0, EntityVerticalOffset);
|
||||
|
||||
var lowerCenter = _eyeManager.WorldToScreen(worldPos) / UIScale;
|
||||
var screenPos = lowerCenter - (Width / 2, ContentHeight + _verticalOffsetAchieved);
|
||||
Position = screenPos;
|
||||
|
||||
var height = (lowerCenter.Y - screenPos.Y).Clamp(0, ContentHeight);
|
||||
Size = (Size.X, height);
|
||||
}
|
||||
|
||||
private void Die()
|
||||
{
|
||||
if (Disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_chatManager.RemoveSpeechBubble(_senderEntity.Uid, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Causes the speech bubble to start fading IMMEDIATELY.
|
||||
/// </summary>
|
||||
public void FadeNow()
|
||||
{
|
||||
if (_timeLeft > FadeTime)
|
||||
{
|
||||
_timeLeft = FadeTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
38
Content.Client/Chat/StoredChatMessage.cs
Normal file
38
Content.Client/Chat/StoredChatMessage.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using Content.Shared.Chat;
|
||||
|
||||
namespace Content.Client.Chat
|
||||
{
|
||||
public class StoredChatMessage
|
||||
{
|
||||
// TODO Make me reflected with respect to MsgChatMessage
|
||||
|
||||
/// <summary>
|
||||
/// Client's own copies of chat messages used in filtering locally
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Actual Message contents, i.e. words
|
||||
/// </summary>
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Message channel, used for filtering
|
||||
/// </summary>
|
||||
public ChatChannel Channel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What to "wrap" the message contents with. Example is stuff like 'Joe says: "{0}"'
|
||||
/// </summary>
|
||||
public string MessageWrap { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor to copy a net message into stored client variety
|
||||
/// </summary>
|
||||
public StoredChatMessage(MsgChatMessage netMsg)
|
||||
{
|
||||
Message = netMsg.Message;
|
||||
Channel = netMsg.Channel;
|
||||
MessageWrap = netMsg.MessageWrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
Content.Client/ClientModuleTestingCallbacks.cs
Normal file
10
Content.Client/ClientModuleTestingCallbacks.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using Content.Shared;
|
||||
|
||||
namespace Content.Client
|
||||
{
|
||||
public sealed class ClientModuleTestingCallbacks : SharedModuleTestingCallbacks
|
||||
{
|
||||
public Action ClientBeforeIoC { get; set; }
|
||||
}
|
||||
}
|
||||
127
Content.Client/ClientNotifyManager.cs
Normal file
127
Content.Client/ClientNotifyManager.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.Interfaces;
|
||||
using Content.Shared;
|
||||
using Robust.Client;
|
||||
using Robust.Client.Interfaces.Console;
|
||||
using Robust.Client.Interfaces.Graphics.ClientEye;
|
||||
using Robust.Client.Interfaces.Input;
|
||||
using Robust.Client.Interfaces.UserInterface;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client
|
||||
{
|
||||
public class ClientNotifyManager : SharedNotifyManager, IClientNotifyManager
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private IPlayerManager _playerManager;
|
||||
[Dependency] private IUserInterfaceManager _userInterfaceManager;
|
||||
[Dependency] private IInputManager _inputManager;
|
||||
[Dependency] private IEyeManager _eyeManager;
|
||||
[Dependency] private IClientNetManager _netManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
private readonly List<PopupLabel> _aliveLabels = new List<PopupLabel>();
|
||||
private bool _initialized;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
DebugTools.Assert(!_initialized);
|
||||
|
||||
_netManager.RegisterNetMessage<MsgDoNotify>(nameof(MsgDoNotify), DoNotifyMessage);
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
private void DoNotifyMessage(MsgDoNotify message)
|
||||
{
|
||||
PopupMessage(_eyeManager.WorldToScreen(message.Coordinates), message.Message);
|
||||
}
|
||||
|
||||
public override void PopupMessage(GridCoordinates coordinates, IEntity viewer, string message)
|
||||
{
|
||||
if (viewer != _playerManager.LocalPlayer.ControlledEntity)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PopupMessage(_eyeManager.WorldToScreen(coordinates), message);
|
||||
}
|
||||
|
||||
public void PopupMessage(ScreenCoordinates coordinates, string message)
|
||||
{
|
||||
var label = new PopupLabel {Text = message};
|
||||
var minimumSize = label.CombinedMinimumSize;
|
||||
label.InitialPos = label.Position = coordinates.Position - minimumSize / 2;
|
||||
_userInterfaceManager.StateRoot.AddChild(label);
|
||||
_aliveLabels.Add(label);
|
||||
}
|
||||
|
||||
public void PopupMessage(string message)
|
||||
{
|
||||
PopupMessage(new ScreenCoordinates(_inputManager.MouseScreenPosition), message);
|
||||
}
|
||||
|
||||
public void FrameUpdate(RenderFrameEventArgs eventArgs)
|
||||
{
|
||||
foreach (var label in _aliveLabels)
|
||||
{
|
||||
label.Update(eventArgs);
|
||||
}
|
||||
|
||||
_aliveLabels.RemoveAll(l => l.Disposed);
|
||||
}
|
||||
|
||||
private class PopupLabel : Label
|
||||
{
|
||||
private float _timeLeft;
|
||||
public Vector2 InitialPos { get; set; }
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
ShadowOffsetXOverride = 1;
|
||||
ShadowOffsetYOverride = 1;
|
||||
FontColorShadowOverride = Color.Black;
|
||||
|
||||
}
|
||||
|
||||
public void Update(RenderFrameEventArgs eventArgs)
|
||||
{
|
||||
_timeLeft += eventArgs.Elapsed;
|
||||
Position = InitialPos - new Vector2(0, 20 * (_timeLeft * _timeLeft + _timeLeft));
|
||||
if (_timeLeft > 0.5f)
|
||||
{
|
||||
Modulate = Color.White.WithAlpha(1f - 0.2f * (float)Math.Pow(_timeLeft - 0.5f, 3f));
|
||||
if (_timeLeft > 3f)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class PopupMessageCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "popupmsg";
|
||||
public string Description => "";
|
||||
public string Help => "";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
{
|
||||
var arg = args[0];
|
||||
var mgr = IoCManager.Resolve<IClientNotifyManager>();
|
||||
mgr.PopupMessage(arg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
57
Content.Client/Commands/DebugCommands.cs
Normal file
57
Content.Client/Commands/DebugCommands.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using Content.Client.Interfaces;
|
||||
using Content.Shared.GameObjects.Components.Markers;
|
||||
using Robust.Client.Interfaces.Console;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Client.Commands
|
||||
{
|
||||
internal sealed class ShowMarkersCommand : IConsoleCommand
|
||||
{
|
||||
// ReSharper disable once StringLiteralTypo
|
||||
public string Command => "togglemarkers";
|
||||
public string Description => "Toggles visibility of markers such as spawn points.";
|
||||
public string Help => "";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
{
|
||||
bool? whichToSet = null;
|
||||
foreach (var entity in IoCManager.Resolve<IEntityManager>()
|
||||
.GetEntities(new TypeEntityQuery(typeof(SharedSpawnPointComponent))))
|
||||
{
|
||||
if (!entity.TryGetComponent(out ISpriteComponent sprite))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!whichToSet.HasValue)
|
||||
{
|
||||
whichToSet = !sprite.Visible;
|
||||
}
|
||||
|
||||
sprite.Visible = whichToSet.Value;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class NotifyCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "notify";
|
||||
public string Description => "Send a notify client side.";
|
||||
public string Help => "notify <message>";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
{
|
||||
var message = args[0];
|
||||
|
||||
var notifyManager = IoCManager.Resolve<IClientNotifyManager>();
|
||||
notifyManager.PopupMessage(message);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
391
Content.Client/Construction/ConstructionMenu.cs
Normal file
391
Content.Client/Construction/ConstructionMenu.cs
Normal file
@@ -0,0 +1,391 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Client.GameObjects.Components.Construction;
|
||||
using Content.Shared.Construction;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Interfaces.GameObjects;
|
||||
using Robust.Client.Interfaces.Graphics;
|
||||
using Robust.Client.Interfaces.Placement;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.Placement;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Construction
|
||||
{
|
||||
public class ConstructionMenu : SS14Window
|
||||
{
|
||||
|
||||
#pragma warning disable CS0649
|
||||
[Dependency]
|
||||
readonly IPrototypeManager PrototypeManager;
|
||||
[Dependency]
|
||||
readonly IResourceCache ResourceCache;
|
||||
#pragma warning restore
|
||||
|
||||
public ConstructorComponent Owner { get; set; }
|
||||
Button BuildButton;
|
||||
Button EraseButton;
|
||||
LineEdit SearchBar;
|
||||
Tree RecipeList;
|
||||
TextureRect InfoIcon;
|
||||
Label InfoLabel;
|
||||
ItemList StepList;
|
||||
|
||||
CategoryNode RootCategory;
|
||||
// This list is flattened in such a way that the top most deepest category is first.
|
||||
List<CategoryNode> FlattenedCategories;
|
||||
PlacementManager Placement;
|
||||
|
||||
public ConstructionMenu()
|
||||
{
|
||||
Size = new Vector2(500.0f, 350.0f);
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
IoCManager.InjectDependencies(this);
|
||||
Placement = (PlacementManager)IoCManager.Resolve<IPlacementManager>();
|
||||
Placement.PlacementCanceled += OnPlacementCanceled;
|
||||
|
||||
Title = "Construction";
|
||||
|
||||
var hSplitContainer = new HSplitContainer();
|
||||
|
||||
// Left side
|
||||
var recipes = new VBoxContainer("Recipes") {CustomMinimumSize = new Vector2(150.0f, 0.0f)};
|
||||
SearchBar = new LineEdit("Search") {PlaceHolder = "Search"};
|
||||
RecipeList = new Tree("Tree") {SizeFlagsVertical = SizeFlags.FillExpand, HideRoot = true};
|
||||
recipes.AddChild(SearchBar);
|
||||
recipes.AddChild(RecipeList);
|
||||
hSplitContainer.AddChild(recipes);
|
||||
|
||||
// Right side
|
||||
var guide = new VBoxContainer("Guide");
|
||||
var info = new HBoxContainer("Info");
|
||||
InfoIcon = new TextureRect("TextureRect");
|
||||
InfoLabel = new Label("Label")
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand, SizeFlagsVertical = SizeFlags.ShrinkCenter
|
||||
};
|
||||
info.AddChild(InfoIcon);
|
||||
info.AddChild(InfoLabel);
|
||||
guide.AddChild(info);
|
||||
|
||||
var stepsLabel = new Label("Label")
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.ShrinkCenter,
|
||||
SizeFlagsVertical = SizeFlags.ShrinkCenter,
|
||||
Text = "Steps"
|
||||
};
|
||||
guide.AddChild(stepsLabel);
|
||||
|
||||
StepList = new ItemList("StepsList")
|
||||
{
|
||||
SizeFlagsVertical = SizeFlags.FillExpand, SelectMode = ItemList.ItemListSelectMode.None
|
||||
};
|
||||
guide.AddChild(StepList);
|
||||
|
||||
var buttonsContainer = new HBoxContainer("Buttons");
|
||||
BuildButton = new Button("BuildButton")
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
TextAlign = Button.AlignMode.Center,
|
||||
Text = "Build!",
|
||||
Disabled = true,
|
||||
ToggleMode = false
|
||||
};
|
||||
EraseButton = new Button("EraseButton")
|
||||
{
|
||||
TextAlign = Button.AlignMode.Center, Text = "Clear Ghosts", ToggleMode = true
|
||||
};
|
||||
buttonsContainer.AddChild(BuildButton);
|
||||
buttonsContainer.AddChild(EraseButton);
|
||||
guide.AddChild(buttonsContainer);
|
||||
|
||||
hSplitContainer.AddChild(guide);
|
||||
Contents.AddChild(hSplitContainer);
|
||||
|
||||
BuildButton.OnPressed += OnBuildPressed;
|
||||
EraseButton.OnToggled += OnEraseToggled;
|
||||
SearchBar.OnTextChanged += OnTextEntered;
|
||||
RecipeList.OnItemSelected += OnItemSelected;
|
||||
|
||||
PopulatePrototypeList();
|
||||
PopulateTree();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
Placement.PlacementCanceled -= OnPlacementCanceled;
|
||||
}
|
||||
}
|
||||
|
||||
void OnItemSelected()
|
||||
{
|
||||
var prototype = (ConstructionPrototype)RecipeList.Selected.Metadata;
|
||||
|
||||
if (prototype == null)
|
||||
{
|
||||
InfoLabel.Text = "";
|
||||
InfoIcon.Texture = null;
|
||||
StepList.Clear();
|
||||
BuildButton.Disabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
BuildButton.Disabled = false;
|
||||
InfoLabel.Text = prototype.Description;
|
||||
InfoIcon.Texture = prototype.Icon.Frame0();
|
||||
|
||||
StepList.Clear();
|
||||
|
||||
foreach (var forward in prototype.Stages.Select(a => a.Forward))
|
||||
{
|
||||
if (forward == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Texture icon;
|
||||
string text;
|
||||
switch (forward)
|
||||
{
|
||||
case ConstructionStepMaterial mat:
|
||||
switch (mat.Material)
|
||||
{
|
||||
case ConstructionStepMaterial.MaterialType.Metal:
|
||||
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/sheet_metal.png");
|
||||
text = $"Metal x{mat.Amount}";
|
||||
break;
|
||||
case ConstructionStepMaterial.MaterialType.Glass:
|
||||
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/sheet_glass.png");
|
||||
text = $"Glass x{mat.Amount}";
|
||||
break;
|
||||
case ConstructionStepMaterial.MaterialType.Cable:
|
||||
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/cable_coil.png");
|
||||
text = $"Cable Coil x{mat.Amount}";
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
break;
|
||||
case ConstructionStepTool tool:
|
||||
switch (tool.Tool)
|
||||
{
|
||||
case ConstructionStepTool.ToolType.Wrench:
|
||||
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/wrench.png");
|
||||
text = "Wrench";
|
||||
break;
|
||||
case ConstructionStepTool.ToolType.Crowbar:
|
||||
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/crowbar.png");
|
||||
text = "Crowbar";
|
||||
break;
|
||||
case ConstructionStepTool.ToolType.Screwdriver:
|
||||
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/screwdriver.png");
|
||||
text = "Screwdriver";
|
||||
break;
|
||||
case ConstructionStepTool.ToolType.Welder:
|
||||
icon = ResourceCache.GetResource<RSIResource>("/Textures/Objects/tools.rsi").RSI["welder"].Frame0;
|
||||
text = $"Welding tool ({tool.Amount} fuel)";
|
||||
break;
|
||||
case ConstructionStepTool.ToolType.Wirecutters:
|
||||
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/wirecutter.png");
|
||||
text = "Wirecutters";
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
StepList.AddItem(text, icon, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnTextEntered(LineEdit.LineEditEventArgs args)
|
||||
{
|
||||
var str = args.Text;
|
||||
PopulateTree(string.IsNullOrWhiteSpace(str) ? null : str.ToLowerInvariant());
|
||||
}
|
||||
|
||||
void OnBuildPressed(Button.ButtonEventArgs args)
|
||||
{
|
||||
var prototype = (ConstructionPrototype)RecipeList.Selected.Metadata;
|
||||
if (prototype == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (prototype.Type != ConstructionType.Structure)
|
||||
{
|
||||
// In-hand attackby doesn't exist so this is the best alternative.
|
||||
var loc = Owner.Owner.GetComponent<ITransformComponent>().GridPosition;
|
||||
Owner.SpawnGhost(prototype, loc, Direction.North);
|
||||
return;
|
||||
}
|
||||
|
||||
var hijack = new ConstructionPlacementHijack(prototype, Owner);
|
||||
var info = new PlacementInformation
|
||||
{
|
||||
IsTile = false,
|
||||
PlacementOption = prototype.PlacementMode,
|
||||
};
|
||||
|
||||
|
||||
Placement.BeginHijackedPlacing(info, hijack);
|
||||
}
|
||||
|
||||
private void OnEraseToggled(BaseButton.ButtonToggledEventArgs args)
|
||||
{
|
||||
var hijack = new ConstructionPlacementHijack(null, Owner);
|
||||
Placement.ToggleEraserHijacked(hijack);
|
||||
}
|
||||
|
||||
void PopulatePrototypeList()
|
||||
{
|
||||
RootCategory = new CategoryNode("", null);
|
||||
int count = 1;
|
||||
|
||||
foreach (var prototype in PrototypeManager.EnumeratePrototypes<ConstructionPrototype>())
|
||||
{
|
||||
var currentNode = RootCategory;
|
||||
|
||||
foreach (var category in prototype.CategorySegments)
|
||||
{
|
||||
if (!currentNode.ChildCategories.TryGetValue(category, out var subNode))
|
||||
{
|
||||
count++;
|
||||
subNode = new CategoryNode(category, currentNode);
|
||||
currentNode.ChildCategories.Add(category, subNode);
|
||||
}
|
||||
currentNode = subNode;
|
||||
}
|
||||
|
||||
currentNode.Prototypes.Add(prototype);
|
||||
}
|
||||
|
||||
// Do a pass to sort the prototype lists and flatten the hierarchy.
|
||||
void Recurse(CategoryNode node)
|
||||
{
|
||||
// I give up we're using recursion to flatten this.
|
||||
// There probably IS a way to do it.
|
||||
// I'm too stupid to think of what that way is.
|
||||
foreach (var child in node.ChildCategories.Values)
|
||||
{
|
||||
Recurse(child);
|
||||
}
|
||||
|
||||
node.Prototypes.Sort(ComparePrototype);
|
||||
FlattenedCategories.Add(node);
|
||||
node.FlattenedIndex = FlattenedCategories.Count - 1;
|
||||
}
|
||||
|
||||
FlattenedCategories = new List<CategoryNode>(count);
|
||||
Recurse(RootCategory);
|
||||
}
|
||||
|
||||
void PopulateTree(string searchTerm = null)
|
||||
{
|
||||
RecipeList.Clear();
|
||||
|
||||
var categoryItems = new Tree.Item[FlattenedCategories.Count];
|
||||
categoryItems[RootCategory.FlattenedIndex] = RecipeList.CreateItem();
|
||||
|
||||
// Yay more recursion.
|
||||
Tree.Item ItemForNode(CategoryNode node)
|
||||
{
|
||||
if (categoryItems[node.FlattenedIndex] != null)
|
||||
{
|
||||
return categoryItems[node.FlattenedIndex];
|
||||
}
|
||||
|
||||
var item = RecipeList.CreateItem(ItemForNode(node.Parent));
|
||||
item.Text = node.Name;
|
||||
item.Selectable = false;
|
||||
categoryItems[node.FlattenedIndex] = item;
|
||||
return item;
|
||||
}
|
||||
|
||||
foreach (var node in FlattenedCategories)
|
||||
{
|
||||
foreach (var prototype in node.Prototypes)
|
||||
{
|
||||
if (searchTerm != null)
|
||||
{
|
||||
var found = false;
|
||||
// TODO: don't run ToLowerInvariant() constantly.
|
||||
if (prototype.Name.ToLowerInvariant().IndexOf(searchTerm) != -1)
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var keyw in prototype.Keywords.Concat(prototype.CategorySegments))
|
||||
{
|
||||
// TODO: don't run ToLowerInvariant() constantly.
|
||||
if (keyw.ToLowerInvariant().IndexOf(searchTerm) != -1)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
var subItem = RecipeList.CreateItem(ItemForNode(node));
|
||||
subItem.Text = prototype.Name;
|
||||
subItem.Metadata = prototype;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPlacementCanceled(object sender, EventArgs e)
|
||||
{
|
||||
EraseButton.Pressed = false;
|
||||
}
|
||||
|
||||
private static int ComparePrototype(ConstructionPrototype x, ConstructionPrototype y)
|
||||
{
|
||||
return x.Name.CompareTo(y.Name);
|
||||
}
|
||||
|
||||
class CategoryNode
|
||||
{
|
||||
public readonly string Name;
|
||||
public readonly CategoryNode Parent;
|
||||
public SortedDictionary<string, CategoryNode> ChildCategories = new SortedDictionary<string, CategoryNode>();
|
||||
public List<ConstructionPrototype> Prototypes = new List<ConstructionPrototype>();
|
||||
public int FlattenedIndex = -1;
|
||||
|
||||
public CategoryNode(string name, CategoryNode parent)
|
||||
{
|
||||
Name = name;
|
||||
Parent = parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
53
Content.Client/Construction/ConstructionPlacementHijack.cs
Normal file
53
Content.Client/Construction/ConstructionPlacementHijack.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using Content.Client.GameObjects.Components.Construction;
|
||||
using Content.Shared.Construction;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.Placement;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.Construction
|
||||
{
|
||||
public class ConstructionPlacementHijack : PlacementHijack
|
||||
{
|
||||
private readonly ConstructionPrototype Prototype;
|
||||
private readonly ConstructorComponent Owner;
|
||||
|
||||
public ConstructionPlacementHijack(ConstructionPrototype prototype, ConstructorComponent owner)
|
||||
{
|
||||
Prototype = prototype;
|
||||
Owner = owner;
|
||||
}
|
||||
|
||||
public override bool HijackPlacementRequest(GridCoordinates coords)
|
||||
{
|
||||
if (Prototype != null)
|
||||
{
|
||||
var dir = Manager.Direction;
|
||||
Owner.SpawnGhost(Prototype, coords, dir);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool HijackDeletion(IEntity entity)
|
||||
{
|
||||
if (entity.TryGetComponent(out ConstructionGhostComponent ghost))
|
||||
{
|
||||
Owner.ClearGhost(ghost.GhostID);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void StartHijack(PlacementManager manager)
|
||||
{
|
||||
base.StartHijack(manager);
|
||||
|
||||
var res = IoCManager.Resolve<IResourceCache>();
|
||||
manager.CurrentBaseSprite = Prototype.Icon.DirFrame0();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,87 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{A2E5F175-78AF-4DDD-8F97-E2D2552372ED}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Content.Client</RootNamespace>
|
||||
<AssemblyName>Content.Client</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ContentAssemblyTarget>..\bin\Client\Assemblies\</ContentAssemblyTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<!--
|
||||
This copies all dependencies,
|
||||
but on the plus side it's automatically located in the right place.
|
||||
-->
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<Platforms>x64;x86</Platforms>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>..\bin\Content.Client\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<!--
|
||||
This copies all dependencies,
|
||||
but on the plus side it's automatically located in the right place.
|
||||
-->
|
||||
<OutputPath>..\bin\Content.Client\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\RobustToolbox\MSBuild\Robust.DefineConstants.targets" />
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
<PackageReference Include="Nett" Version="0.11.0" />
|
||||
<PackageReference Include="SixLabors.Core" Version="1.0.0-beta0007" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.0-beta0006" />
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
<PackageReference Include="YamlDotNet" Version="6.0.0" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.1.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="EntryPoint.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj">
|
||||
<Project>{26aeebb3-dde7-443a-9f43-7bc7f4acf6b5}</Project>
|
||||
<Name>Content.Shared</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\engine\Lidgren.Network\Lidgren.Network.csproj">
|
||||
<Project>{59250baf-0000-0000-0000-000000000000}</Project>
|
||||
<Name>Lidgren.Network</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\engine\SS14.Client.Graphics\SS14.Client.Graphics.csproj">
|
||||
<Project>{302b877e-0000-0000-0000-000000000000}</Project>
|
||||
<Name>SS14.Client.Graphics</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\engine\SS14.Client\SS14.Client.csproj">
|
||||
<Project>{0c31dfdf-0000-0000-0000-000000000000}</Project>
|
||||
<Name>SS14.Client</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\engine\SS14.Shared\SS14.Shared.csproj">
|
||||
<Project>{0529f740-0000-0000-0000-000000000000}</Project>
|
||||
<Name>SS14.Shared</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="..\SS14.Content.targets" />
|
||||
<Target Name="AfterBuild" DependsOnTargets="CopyContentAssemblies" />
|
||||
<ItemGroup>
|
||||
<!-- Files to be copied into Client/Assemblies -->
|
||||
<ContentAssemblies Include="$(OutputPath)Content.Client.dll" />
|
||||
<ContentAssemblies Include="$(OutputPath)Content.Shared.dll" />
|
||||
<ContentAssemblies Include="$(OutputPath)Content.Client.pdb" Condition="'$(Configuration)' == 'Debug'" />
|
||||
<ContentAssemblies Include="$(OutputPath)Content.Shared.pdb" Condition="'$(Configuration)' == 'Debug'" />
|
||||
<ProjectReference Include="..\RobustToolbox\Lidgren.Network\Lidgren.Network.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared\Robust.Shared.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Client\Robust.Client.csproj" />
|
||||
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,12 +1,194 @@
|
||||
using SS14.Shared.ContentPack;
|
||||
using System;
|
||||
using Content.Client.Chat;
|
||||
using Content.Client.GameObjects.Components.Actor;
|
||||
using Content.Client.GameTicking;
|
||||
using Content.Client.Input;
|
||||
using Content.Client.Interfaces;
|
||||
using Content.Client.Interfaces.Chat;
|
||||
using Content.Client.Interfaces.Parallax;
|
||||
using Content.Client.Parallax;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared.GameObjects.Components.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Markers;
|
||||
using Content.Shared.GameObjects.Components.Research;
|
||||
using Content.Shared.Interfaces;
|
||||
using Robust.Client;
|
||||
using Robust.Client.Interfaces;
|
||||
using Robust.Client.Interfaces.Graphics.Overlays;
|
||||
using Robust.Client.Interfaces.Input;
|
||||
using Robust.Client.Interfaces.UserInterface;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client
|
||||
{
|
||||
public class EntryPoint : GameClient
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IPlayerManager _playerManager;
|
||||
[Dependency] private readonly IEscapeMenuOwner _escapeMenuOwner;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
// TODO: Anything at all.
|
||||
var factory = IoCManager.Resolve<IComponentFactory>();
|
||||
var prototypes = IoCManager.Resolve<IPrototypeManager>();
|
||||
|
||||
factory.DoAutoRegistrations();
|
||||
|
||||
var registerIgnore = new[]
|
||||
{
|
||||
"Interactable",
|
||||
"Destructible",
|
||||
"Temperature",
|
||||
"PowerTransfer",
|
||||
"PowerNode",
|
||||
"PowerProvider",
|
||||
"PowerDevice",
|
||||
"PowerStorage",
|
||||
"PowerGenerator",
|
||||
"Explosive",
|
||||
"OnUseTimerTrigger",
|
||||
"ToolboxElectricalFill",
|
||||
"ToolLockerFill",
|
||||
"EmitSoundOnUse",
|
||||
"FootstepModifier",
|
||||
"HeatResistance",
|
||||
"CombatMode",
|
||||
"Teleportable",
|
||||
"ItemTeleporter",
|
||||
"Portal",
|
||||
"EntityStorage",
|
||||
"PlaceableSurface",
|
||||
"Wirecutter",
|
||||
"Screwdriver",
|
||||
"Multitool",
|
||||
"Welder",
|
||||
"Wrench",
|
||||
"Crowbar",
|
||||
"HitscanWeapon",
|
||||
"ProjectileWeapon",
|
||||
"Projectile",
|
||||
"MeleeWeapon",
|
||||
"Storeable",
|
||||
"Stack",
|
||||
"Dice",
|
||||
"Construction",
|
||||
"Apc",
|
||||
"Door",
|
||||
"PoweredLight",
|
||||
"Smes",
|
||||
"Powercell",
|
||||
"HandheldLight",
|
||||
"LightBulb",
|
||||
"Healing",
|
||||
"Catwalk",
|
||||
"BallisticMagazine",
|
||||
"BallisticMagazineWeapon",
|
||||
"BallisticBullet",
|
||||
"HitscanWeaponCapacitor",
|
||||
"PowerCell",
|
||||
"AiController",
|
||||
"PlayerInputMover",
|
||||
};
|
||||
|
||||
foreach (var ignoreName in registerIgnore)
|
||||
{
|
||||
factory.RegisterIgnore(ignoreName);
|
||||
}
|
||||
|
||||
factory.Register<SharedLatheComponent>();
|
||||
factory.Register<SharedSpawnPointComponent>();
|
||||
factory.Register<SolutionComponent>();
|
||||
|
||||
prototypes.RegisterIgnore("material");
|
||||
|
||||
IoCManager.Register<IGameHud, GameHud>();
|
||||
IoCManager.Register<IClientNotifyManager, ClientNotifyManager>();
|
||||
IoCManager.Register<ISharedNotifyManager, ClientNotifyManager>();
|
||||
IoCManager.Register<IClientGameTicker, ClientGameTicker>();
|
||||
IoCManager.Register<IParallaxManager, ParallaxManager>();
|
||||
IoCManager.Register<IChatManager, ChatManager>();
|
||||
IoCManager.Register<IEscapeMenuOwner, EscapeMenuOwner>();
|
||||
if (TestingCallbacks != null)
|
||||
{
|
||||
var cast = (ClientModuleTestingCallbacks) TestingCallbacks;
|
||||
cast.ClientBeforeIoC?.Invoke();
|
||||
}
|
||||
|
||||
IoCManager.BuildGraph();
|
||||
|
||||
IoCManager.Resolve<IParallaxManager>().LoadParallax();
|
||||
IoCManager.Resolve<IBaseClient>().PlayerJoinedServer += SubscribePlayerAttachmentEvents;
|
||||
|
||||
var stylesheet = new NanoStyle();
|
||||
|
||||
IoCManager.Resolve<IUserInterfaceManager>().Stylesheet = stylesheet.Stylesheet;
|
||||
IoCManager.Resolve<IUserInterfaceManager>().Stylesheet = stylesheet.Stylesheet;
|
||||
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_escapeMenuOwner.Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe events to the player manager after the player manager is set up
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
public void SubscribePlayerAttachmentEvents(object sender, EventArgs args)
|
||||
{
|
||||
_playerManager.LocalPlayer.EntityAttached += AttachPlayerToEntity;
|
||||
_playerManager.LocalPlayer.EntityDetached += DetachPlayerFromEntity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add the character interface master which combines all character interfaces into one window
|
||||
/// </summary>
|
||||
public static void AttachPlayerToEntity(EntityAttachedEventArgs eventArgs)
|
||||
{
|
||||
eventArgs.NewEntity.AddComponent<CharacterInterface>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the character interface master from this entity now that we have detached ourselves from it
|
||||
/// </summary>
|
||||
public static void DetachPlayerFromEntity(EntityDetachedEventArgs eventArgs)
|
||||
{
|
||||
eventArgs.OldEntity.RemoveComponent<CharacterInterface>();
|
||||
}
|
||||
|
||||
public override void PostInit()
|
||||
{
|
||||
base.PostInit();
|
||||
|
||||
// Setup key contexts
|
||||
var inputMan = IoCManager.Resolve<IInputManager>();
|
||||
ContentContexts.SetupContexts(inputMan.Contexts);
|
||||
|
||||
IoCManager.Resolve<IGameHud>().Initialize();
|
||||
IoCManager.Resolve<IClientNotifyManager>().Initialize();
|
||||
IoCManager.Resolve<IClientGameTicker>().Initialize();
|
||||
IoCManager.Resolve<IOverlayManager>().AddOverlay(new ParallaxOverlay());
|
||||
IoCManager.Resolve<IChatManager>().Initialize();
|
||||
}
|
||||
|
||||
public override void Update(ModUpdateLevel level, float frameTime)
|
||||
{
|
||||
base.Update(level, frameTime);
|
||||
|
||||
switch (level)
|
||||
{
|
||||
case ModUpdateLevel.FramePreEngine:
|
||||
var renderFrameEventArgs = new RenderFrameEventArgs(frameTime);
|
||||
IoCManager.Resolve<IClientNotifyManager>().FrameUpdate(renderFrameEventArgs);
|
||||
IoCManager.Resolve<IClientGameTicker>().FrameUpdate(renderFrameEventArgs);
|
||||
IoCManager.Resolve<IChatManager>().FrameUpdate(renderFrameEventArgs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
104
Content.Client/EscapeMenuOwner.cs
Normal file
104
Content.Client/EscapeMenuOwner.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using Content.Client.UserInterface;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.Interfaces.Input;
|
||||
using Robust.Client.Interfaces.Placement;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.Interfaces.State;
|
||||
using Robust.Client.State.States;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Interfaces.Configuration;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client
|
||||
{
|
||||
internal sealed class EscapeMenuOwner : IEscapeMenuOwner
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IClientConsole _clientConsole;
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager;
|
||||
[Dependency] private readonly IInputManager _inputManager;
|
||||
[Dependency] private readonly IPlacementManager _placementManager;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
||||
[Dependency] private readonly IResourceCache _resourceCache;
|
||||
[Dependency] private readonly IStateManager _stateManager;
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager;
|
||||
[Dependency] private readonly IGameHud _gameHud;
|
||||
[Dependency] private readonly ILocalizationManager _localizationManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
private EscapeMenu _escapeMenu;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_stateManager.OnStateChanged += StateManagerOnOnStateChanged;
|
||||
|
||||
_gameHud.EscapeButtonToggled += _setOpenValue;
|
||||
}
|
||||
|
||||
private void StateManagerOnOnStateChanged(StateChangedEventArgs obj)
|
||||
{
|
||||
if (obj.NewState is GameScreen)
|
||||
{
|
||||
// Switched TO GameScreen.
|
||||
_escapeMenu = new EscapeMenu(_clientConsole, _tileDefinitionManager, _placementManager,
|
||||
_prototypeManager, _resourceCache, _configurationManager, _localizationManager);
|
||||
|
||||
_escapeMenu.OnClose += () => _gameHud.EscapeButtonDown = false;
|
||||
|
||||
var escapeMenuCommand = InputCmdHandler.FromDelegate(Enabled);
|
||||
|
||||
_inputManager.SetInputCommand(EngineKeyFunctions.EscapeMenu, escapeMenuCommand);
|
||||
}
|
||||
else if (obj.OldState is GameScreen)
|
||||
{
|
||||
// Switched FROM GameScreen.
|
||||
_escapeMenu.Dispose();
|
||||
_escapeMenu = null;
|
||||
|
||||
_inputManager.SetInputCommand(EngineKeyFunctions.EscapeMenu, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void Enabled(ICommonSession session)
|
||||
{
|
||||
if (_escapeMenu.IsOpen)
|
||||
{
|
||||
if (_escapeMenu.IsAtFront())
|
||||
{
|
||||
_setOpenValue(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_escapeMenu.MoveToFront();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_setOpenValue(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void _setOpenValue(bool value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
_gameHud.EscapeButtonDown = true;
|
||||
_escapeMenu.OpenCentered();
|
||||
}
|
||||
else
|
||||
{
|
||||
_gameHud.EscapeButtonDown = false;
|
||||
_escapeMenu.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface IEscapeMenuOwner
|
||||
{
|
||||
void Initialize();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using Content.Client.GameObjects.Components.Mobs;
|
||||
using Content.Client.UserInterface;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Actor
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class CharacterInfoComponent : Component, ICharacterUI
|
||||
{
|
||||
private CharacterInfoControl _control;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly ILocalizationManager _loc;
|
||||
[Dependency] private readonly IResourceCache _resourceCache;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override string Name => "CharacterInfo";
|
||||
|
||||
public Control Scene { get; private set; }
|
||||
public UIPriority Priority => UIPriority.Info;
|
||||
|
||||
public override void OnAdd()
|
||||
{
|
||||
base.OnAdd();
|
||||
|
||||
Scene = _control = new CharacterInfoControl(_resourceCache, _loc);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
if (Owner.TryGetComponent(out ISpriteComponent spriteComponent))
|
||||
{
|
||||
_control.SpriteView.Sprite = spriteComponent;
|
||||
}
|
||||
|
||||
_control.NameLabel.Text = Owner.Name;
|
||||
// ReSharper disable once StringLiteralTypo
|
||||
_control.SubText.Text = _loc.GetString("Professional Greyshirt");
|
||||
}
|
||||
|
||||
private sealed class CharacterInfoControl : VBoxContainer
|
||||
{
|
||||
public SpriteView SpriteView { get; }
|
||||
public Label NameLabel { get; }
|
||||
public Label SubText { get; }
|
||||
|
||||
public CharacterInfoControl(IResourceCache resourceCache, ILocalizationManager loc)
|
||||
{
|
||||
AddChild(new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
(SpriteView = new SpriteView { Scale = (2, 2)}),
|
||||
new VBoxContainer
|
||||
{
|
||||
SizeFlagsVertical = SizeFlags.None,
|
||||
Children =
|
||||
{
|
||||
(NameLabel = new Label()),
|
||||
(SubText = new Label
|
||||
{
|
||||
SizeFlagsVertical = SizeFlags.None,
|
||||
StyleClasses = {NanoStyle.StyleClassLabelSubText}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
AddChild(new Placeholder(resourceCache)
|
||||
{
|
||||
PlaceholderText = loc.GetString("Health & status effects")
|
||||
});
|
||||
|
||||
AddChild(new Placeholder(resourceCache)
|
||||
{
|
||||
PlaceholderText = loc.GetString("Objectives")
|
||||
});
|
||||
|
||||
AddChild(new Placeholder(resourceCache)
|
||||
{
|
||||
PlaceholderText = loc.GetString("Antagonist Roles")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Client.GameObjects.Components.Mobs;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.Input;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Actor
|
||||
{
|
||||
/// <summary>
|
||||
/// A semi-abstract component which gets added to entities upon attachment and collects all character
|
||||
/// user interfaces into a single window and keybind for the user
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class CharacterInterface : Component
|
||||
{
|
||||
public override string Name => "Character Interface Component";
|
||||
|
||||
[Dependency]
|
||||
#pragma warning disable 649
|
||||
private readonly IGameHud _gameHud;
|
||||
#pragma warning restore 649
|
||||
|
||||
/// <summary>
|
||||
/// Window to hold each of the character interfaces
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Null if it would otherwise be empty.
|
||||
/// </remarks>
|
||||
public SS14Window Window { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create the window with all character UIs and bind it to a keypress
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
//Use all the character ui interfaced components to create the character window
|
||||
var uiComponents = Owner.GetAllComponents<ICharacterUI>().ToList();
|
||||
if (uiComponents.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Window = new CharacterWindow(uiComponents);
|
||||
Window.OnClose += () => _gameHud.CharacterButtonDown = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose of window and the keypress binding
|
||||
/// </summary>
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
|
||||
Window?.Dispose();
|
||||
Window = null;
|
||||
|
||||
var inputMgr = IoCManager.Resolve<IInputManager>();
|
||||
inputMgr.SetInputCommand(ContentKeyFunctions.OpenCharacterMenu, null);
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null,
|
||||
IComponent component = null)
|
||||
{
|
||||
base.HandleMessage(message, netChannel, component);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case PlayerAttachedMsg _:
|
||||
if (Window != null)
|
||||
{
|
||||
_gameHud.CharacterButtonVisible = true;
|
||||
_gameHud.CharacterButtonToggled = b =>
|
||||
{
|
||||
if (b)
|
||||
{
|
||||
Window.Open();
|
||||
}
|
||||
else
|
||||
{
|
||||
Window.Close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PlayerDetachedMsg _:
|
||||
if (Window != null)
|
||||
{
|
||||
_gameHud.CharacterButtonVisible = false;
|
||||
Window.Close();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A window that collects and shows all the individual character user interfaces
|
||||
/// </summary>
|
||||
public class CharacterWindow : SS14Window
|
||||
{
|
||||
private readonly VBoxContainer _contentsVBox;
|
||||
|
||||
public CharacterWindow(List<ICharacterUI> windowComponents)
|
||||
{
|
||||
Title = "Character";
|
||||
|
||||
_contentsVBox = new VBoxContainer();
|
||||
Contents.AddChild(_contentsVBox);
|
||||
|
||||
windowComponents.Sort((a, b) => ((int) a.Priority).CompareTo((int) b.Priority));
|
||||
foreach (var element in windowComponents)
|
||||
{
|
||||
_contentsVBox.AddChild(element.Scene);
|
||||
}
|
||||
|
||||
Size = CombinedMinimumSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines ordering of the character user interface, small values come sooner
|
||||
/// </summary>
|
||||
public enum UIPriority
|
||||
{
|
||||
First = 0,
|
||||
Info = 5,
|
||||
Species = 100,
|
||||
Last = 99999
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Inventory;
|
||||
using Content.Shared.GameObjects.Components.Items;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Clothing
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(ItemComponent))]
|
||||
public class ClothingComponent : ItemComponent
|
||||
{
|
||||
public override string Name => "Clothing";
|
||||
public override uint? NetID => ContentNetIDs.CLOTHING;
|
||||
public override Type StateType => typeof(ClothingComponentState);
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string ClothingEquippedPrefix { get; set; }
|
||||
|
||||
public (RSI rsi, RSI.StateId stateId)? GetEquippedStateInfo(EquipmentSlotDefines.SlotFlags slot)
|
||||
{
|
||||
if (RsiPath == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var rsi = GetRSI();
|
||||
var prefix = ClothingEquippedPrefix ?? EquippedPrefix;
|
||||
var stateId = prefix != null ? $"{prefix}-equipped-{slot}" : $"equipped-{slot}";
|
||||
if (rsi.TryGetState(stateId, out _))
|
||||
{
|
||||
return (rsi, stateId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
{
|
||||
if (curState == null)
|
||||
return;
|
||||
|
||||
var clothingComponentState = (ClothingComponentState)curState;
|
||||
ClothingEquippedPrefix = clothingComponentState.ClothingEquippedPrefix;
|
||||
EquippedPrefix = clothingComponentState.EquippedPrefix;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using Content.Shared.Construction;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Construction
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ConstructionGhostComponent : Component
|
||||
{
|
||||
public override string Name => "ConstructionGhost";
|
||||
|
||||
[ViewVariables] public ConstructionPrototype Prototype { get; set; }
|
||||
[ViewVariables] public ConstructorComponent Master { get; set; }
|
||||
[ViewVariables] public int GhostID { get; set; }
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null,
|
||||
IComponent component = null)
|
||||
{
|
||||
base.HandleMessage(message, netChannel, component);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case ClientEntityClickMsg clickMsg:
|
||||
Master.TryStartConstruction(GhostID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.Construction;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared.Construction;
|
||||
using Content.Shared.GameObjects.Components.Construction;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Construction
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ConstructorComponent : SharedConstructorComponent
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IGameHud _gameHud;
|
||||
#pragma warning restore 649
|
||||
|
||||
private int nextId;
|
||||
private readonly Dictionary<int, ConstructionGhostComponent> Ghosts = new Dictionary<int, ConstructionGhostComponent>();
|
||||
public ConstructionMenu ConstructionMenu { get; private set; }
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
Owner.GetComponent<ITransformComponent>();
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
|
||||
{
|
||||
base.HandleMessage(message, netChannel, component);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case PlayerAttachedMsg _:
|
||||
if (ConstructionMenu == null)
|
||||
{
|
||||
ConstructionMenu = new ConstructionMenu {Owner = this};
|
||||
ConstructionMenu.OnClose += () => _gameHud.CraftingButtonDown = false;
|
||||
}
|
||||
|
||||
_gameHud.CraftingButtonVisible = true;
|
||||
_gameHud.CraftingButtonToggled = b =>
|
||||
{
|
||||
if (b)
|
||||
{
|
||||
ConstructionMenu.Open();
|
||||
}
|
||||
else
|
||||
{
|
||||
ConstructionMenu.Close();
|
||||
}
|
||||
};
|
||||
break;
|
||||
|
||||
case PlayerDetachedMsg _:
|
||||
_gameHud.CraftingButtonVisible = false;
|
||||
break;
|
||||
|
||||
case AckStructureConstructionMessage ackMsg:
|
||||
ClearGhost(ackMsg.Ack);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
ConstructionMenu?.Dispose();
|
||||
}
|
||||
|
||||
public void SpawnGhost(ConstructionPrototype prototype, GridCoordinates loc, Direction dir)
|
||||
{
|
||||
var entMgr = IoCManager.Resolve<IClientEntityManager>();
|
||||
var ghost = entMgr.SpawnEntityAt("constructionghost", loc);
|
||||
var comp = ghost.GetComponent<ConstructionGhostComponent>();
|
||||
comp.Prototype = prototype;
|
||||
comp.Master = this;
|
||||
comp.GhostID = nextId++;
|
||||
ghost.GetComponent<ITransformComponent>().LocalRotation = dir.ToAngle();
|
||||
var sprite = ghost.GetComponent<SpriteComponent>();
|
||||
sprite.LayerSetSprite(0, prototype.Icon);
|
||||
sprite.LayerSetVisible(0, true);
|
||||
|
||||
Ghosts.Add(comp.GhostID, comp);
|
||||
}
|
||||
|
||||
public void TryStartConstruction(int ghostId)
|
||||
{
|
||||
var ghost = Ghosts[ghostId];
|
||||
var transform = ghost.Owner.GetComponent<ITransformComponent>();
|
||||
var msg = new TryStartStructureConstructionMessage(transform.GridPosition, ghost.Prototype.ID, transform.LocalRotation, ghostId);
|
||||
SendNetworkMessage(msg);
|
||||
}
|
||||
|
||||
public void ClearGhost(int ghostId)
|
||||
{
|
||||
if (Ghosts.TryGetValue(ghostId, out var ghost))
|
||||
{
|
||||
ghost.Owner.Delete();
|
||||
Ghosts.Remove(ghostId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
Content.Client/GameObjects/Components/DamageableComponent.cs
Normal file
30
Content.Client/GameObjects/Components/DamageableComponent.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Fuck I really hate doing this
|
||||
/// TODO: make sure the client only gets damageable component on the clientside entity for its player mob
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class DamageableComponent : SharedDamageableComponent
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Damageable";
|
||||
|
||||
public Dictionary<DamageType, int> CurrentDamage = new Dictionary<DamageType, int>();
|
||||
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if(curState is DamageComponentState)
|
||||
{
|
||||
var damagestate = (DamageComponentState)curState;
|
||||
CurrentDamage = damagestate.CurrentDamage;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Doors;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.GameObjects.Components.Animations;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Doors
|
||||
{
|
||||
public class AirlockVisualizer2D : AppearanceVisualizer
|
||||
{
|
||||
private const string AnimationKey = "airlock_animation";
|
||||
|
||||
private Animation CloseAnimation;
|
||||
private Animation OpenAnimation;
|
||||
|
||||
public override void LoadData(YamlMappingNode node)
|
||||
{
|
||||
base.LoadData(node);
|
||||
|
||||
var openSound = node.GetNode("open_sound").AsString();
|
||||
var closeSound = node.GetNode("close_sound").AsString();
|
||||
|
||||
CloseAnimation = new Animation {Length = TimeSpan.FromSeconds(1.2f)};
|
||||
{
|
||||
var flick = new AnimationTrackSpriteFlick();
|
||||
CloseAnimation.AnimationTracks.Add(flick);
|
||||
flick.LayerKey = DoorVisualLayers.Base;
|
||||
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("closing", 0f));
|
||||
|
||||
var flickUnlit = new AnimationTrackSpriteFlick();
|
||||
CloseAnimation.AnimationTracks.Add(flickUnlit);
|
||||
flickUnlit.LayerKey = DoorVisualLayers.BaseUnlit;
|
||||
flickUnlit.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("closing_unlit", 0f));
|
||||
|
||||
var sound = new AnimationTrackPlaySound();
|
||||
CloseAnimation.AnimationTracks.Add(sound);
|
||||
sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(closeSound, 0));
|
||||
}
|
||||
|
||||
OpenAnimation = new Animation {Length = TimeSpan.FromSeconds(1.2f)};
|
||||
{
|
||||
var flick = new AnimationTrackSpriteFlick();
|
||||
OpenAnimation.AnimationTracks.Add(flick);
|
||||
flick.LayerKey = DoorVisualLayers.Base;
|
||||
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("opening", 0f));
|
||||
|
||||
var flickUnlit = new AnimationTrackSpriteFlick();
|
||||
OpenAnimation.AnimationTracks.Add(flickUnlit);
|
||||
flickUnlit.LayerKey = DoorVisualLayers.BaseUnlit;
|
||||
flickUnlit.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("opening_unlit", 0f));
|
||||
|
||||
var sound = new AnimationTrackPlaySound();
|
||||
OpenAnimation.AnimationTracks.Add(sound);
|
||||
sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(openSound, 0));
|
||||
}
|
||||
}
|
||||
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
if (!entity.HasComponent<AnimationPlayerComponent>())
|
||||
{
|
||||
entity.AddComponent<AnimationPlayerComponent>();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
var animPlayer = component.Owner.GetComponent<AnimationPlayerComponent>();
|
||||
if (!component.TryGetData(DoorVisuals.VisualState, out DoorVisualState state))
|
||||
{
|
||||
state = DoorVisualState.Closed;
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case DoorVisualState.Closed:
|
||||
sprite.LayerSetState(DoorVisualLayers.Base, "closed");
|
||||
sprite.LayerSetState(DoorVisualLayers.BaseUnlit, "closed_unlit");
|
||||
sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, true);
|
||||
break;
|
||||
case DoorVisualState.Closing:
|
||||
sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, true);
|
||||
if (!animPlayer.HasRunningAnimation(AnimationKey))
|
||||
{
|
||||
animPlayer.Play(CloseAnimation, AnimationKey);
|
||||
}
|
||||
break;
|
||||
case DoorVisualState.Opening:
|
||||
sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, true);
|
||||
if (!animPlayer.HasRunningAnimation(AnimationKey))
|
||||
{
|
||||
animPlayer.Play(OpenAnimation, AnimationKey);
|
||||
}
|
||||
|
||||
break;
|
||||
case DoorVisualState.Open:
|
||||
sprite.LayerSetState(DoorVisualLayers.Base, "open");
|
||||
sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, false);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum DoorVisualLayers
|
||||
{
|
||||
Base,
|
||||
BaseUnlit
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Client.GameObjects.Components.Clothing;
|
||||
using Content.Shared.GameObjects;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
|
||||
using static Content.Shared.GameObjects.SharedInventoryComponent.ClientInventoryMessage;
|
||||
|
||||
namespace Content.Client.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// A character UI which shows items the user has equipped within his inventory
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class ClientInventoryComponent : SharedInventoryComponent
|
||||
{
|
||||
private readonly Dictionary<Slots, IEntity> _slots = new Dictionary<Slots, IEntity>();
|
||||
|
||||
[ViewVariables]
|
||||
public InventoryInterfaceController InterfaceController { get; private set; }
|
||||
|
||||
private ISpriteComponent _sprite;
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
|
||||
InterfaceController?.Dispose();
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
var controllerType = ReflectionManager.LooseGetType(InventoryInstance.InterfaceControllerTypeName);
|
||||
var args = new object[] {this};
|
||||
InterfaceController = DynamicTypeFactory.CreateInstance<InventoryInterfaceController>(controllerType, args);
|
||||
InterfaceController.Initialize();
|
||||
|
||||
if (Owner.TryGetComponent(out _sprite))
|
||||
{
|
||||
foreach (var mask in InventoryInstance.SlotMasks.OrderBy(s => InventoryInstance.SlotDrawingOrder(s)))
|
||||
{
|
||||
if (mask == Slots.NONE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_sprite.LayerMapReserveBlank(mask);
|
||||
}
|
||||
}
|
||||
|
||||
// Component state already came in but we couldn't set anything visually because, well, we didn't initialize yet.
|
||||
foreach (var (slot, entity) in _slots)
|
||||
{
|
||||
_setSlot(slot, entity);
|
||||
}
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if (curState == null)
|
||||
return;
|
||||
|
||||
var cast = (InventoryComponentState) curState;
|
||||
|
||||
var doneSlots = new HashSet<Slots>();
|
||||
|
||||
foreach (var (slot, entityUid) in cast.Entities)
|
||||
{
|
||||
if (_slots.ContainsKey(slot))
|
||||
{
|
||||
_slots.Remove(slot);
|
||||
_clearSlot(slot);
|
||||
}
|
||||
|
||||
var entity = Owner.EntityManager.GetEntity(entityUid);
|
||||
_slots[slot] = entity;
|
||||
_setSlot(slot, entity);
|
||||
doneSlots.Add(slot);
|
||||
}
|
||||
|
||||
foreach (var slot in _slots.Keys.ToList())
|
||||
{
|
||||
if (!doneSlots.Contains(slot))
|
||||
{
|
||||
_clearSlot(slot);
|
||||
_slots.Remove(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void _setSlot(Slots slot, IEntity entity)
|
||||
{
|
||||
if (_sprite != null && entity.TryGetComponent(out ClothingComponent clothing))
|
||||
{
|
||||
var flag = SlotMasks[slot];
|
||||
var data = clothing.GetEquippedStateInfo(flag);
|
||||
if (data == null)
|
||||
{
|
||||
_sprite.LayerSetVisible(slot, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var (rsi, state) = data.Value;
|
||||
_sprite.LayerSetVisible(slot, true);
|
||||
_sprite.LayerSetRSI(slot, rsi);
|
||||
_sprite.LayerSetState(slot, state);
|
||||
}
|
||||
}
|
||||
|
||||
InterfaceController?.AddToSlot(slot, entity);
|
||||
}
|
||||
|
||||
private void _clearSlot(Slots slot)
|
||||
{
|
||||
InterfaceController?.RemoveFromSlot(slot);
|
||||
_sprite?.LayerSetVisible(slot, false);
|
||||
}
|
||||
|
||||
public void SendUnequipMessage(Slots slot)
|
||||
{
|
||||
var unequipmessage = new ClientInventoryMessage(slot, ClientInventoryUpdate.Unequip);
|
||||
SendNetworkMessage(unequipmessage);
|
||||
}
|
||||
|
||||
public void SendEquipMessage(Slots slot)
|
||||
{
|
||||
var equipmessage = new ClientInventoryMessage(slot, ClientInventoryUpdate.Equip);
|
||||
SendNetworkMessage(equipmessage);
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null,
|
||||
IComponent component = null)
|
||||
{
|
||||
base.HandleMessage(message, netChannel, component);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case PlayerAttachedMsg _:
|
||||
InterfaceController.PlayerAttached();
|
||||
break;
|
||||
|
||||
case PlayerDetachedMsg _:
|
||||
InterfaceController.PlayerDetached();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.Utility;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
|
||||
|
||||
namespace Content.Client.GameObjects
|
||||
{
|
||||
// Dynamically instantiated by ClientInventoryComponent.
|
||||
[UsedImplicitly]
|
||||
public class HumanInventoryInterfaceController : InventoryInterfaceController
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly ILocalizationManager _loc;
|
||||
[Dependency] private readonly IResourceCache _resourceCache;
|
||||
#pragma warning restore 649
|
||||
|
||||
private readonly Dictionary<Slots, List<InventoryButton>> _inventoryButtons
|
||||
= new Dictionary<Slots, List<InventoryButton>>();
|
||||
|
||||
private InventoryButton _hudButtonPocket1;
|
||||
private InventoryButton _hudButtonPocket2;
|
||||
private InventoryButton _hudButtonBelt;
|
||||
private InventoryButton _hudButtonBack;
|
||||
private Control _quickButtonsContainer;
|
||||
|
||||
public HumanInventoryInterfaceController(ClientInventoryComponent owner) : base(owner)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_window = new HumanInventoryWindow(_loc, _resourceCache);
|
||||
|
||||
foreach (var (slot, button) in _window.Buttons)
|
||||
{
|
||||
button.OnPressed = AddToInventory;
|
||||
_inventoryButtons.Add(slot, new List<InventoryButton> {button});
|
||||
}
|
||||
|
||||
void AddButton(out InventoryButton variable, Slots slot, string textureName)
|
||||
{
|
||||
var texture = _resourceCache.GetTexture($"/Textures/UserInterface/Inventory/{textureName}.png");
|
||||
variable = new InventoryButton(slot, texture)
|
||||
{
|
||||
OnPressed = AddToInventory
|
||||
};
|
||||
_inventoryButtons[slot].Add(variable);
|
||||
}
|
||||
|
||||
AddButton(out _hudButtonPocket1, Slots.POCKET1, "pocket");
|
||||
AddButton(out _hudButtonPocket2, Slots.POCKET2, "pocket");
|
||||
AddButton(out _hudButtonBack, Slots.BACKPACK, "back");
|
||||
AddButton(out _hudButtonBelt, Slots.BELT, "belt");
|
||||
|
||||
_quickButtonsContainer = new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
_hudButtonBelt,
|
||||
_hudButtonBack,
|
||||
_hudButtonPocket1,
|
||||
_hudButtonPocket2,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public override SS14Window Window => _window;
|
||||
private HumanInventoryWindow _window;
|
||||
|
||||
public override void AddToSlot(Slots slot, IEntity entity)
|
||||
{
|
||||
base.AddToSlot(slot, entity);
|
||||
|
||||
if (!_inventoryButtons.TryGetValue(slot, out var buttons))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
entity.TryGetComponent(out ISpriteComponent sprite);
|
||||
|
||||
foreach (var button in buttons)
|
||||
{
|
||||
button.SpriteView.Sprite = sprite;
|
||||
button.OnPressed = RemoveFromInventory;
|
||||
}
|
||||
}
|
||||
|
||||
public override void RemoveFromSlot(Slots slot)
|
||||
{
|
||||
base.RemoveFromSlot(slot);
|
||||
|
||||
if (!_inventoryButtons.TryGetValue(slot, out var buttons))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var button in buttons)
|
||||
{
|
||||
button.SpriteView.Sprite = null;
|
||||
button.OnPressed = AddToInventory;
|
||||
}
|
||||
}
|
||||
|
||||
public override void PlayerAttached()
|
||||
{
|
||||
base.PlayerAttached();
|
||||
|
||||
GameHud.InventoryQuickButtonContainer.AddChild(_quickButtonsContainer);
|
||||
}
|
||||
|
||||
public override void PlayerDetached()
|
||||
{
|
||||
base.PlayerDetached();
|
||||
|
||||
GameHud.InventoryQuickButtonContainer.RemoveChild(_quickButtonsContainer);
|
||||
}
|
||||
|
||||
private class HumanInventoryWindow : SS14Window
|
||||
{
|
||||
private const int ButtonSize = 64;
|
||||
private const int ButtonSeparation = 2;
|
||||
private const int RightSeparation = 2;
|
||||
|
||||
public IReadOnlyDictionary<Slots, InventoryButton> Buttons { get; }
|
||||
|
||||
public HumanInventoryWindow(ILocalizationManager loc, IResourceCache resourceCache)
|
||||
{
|
||||
Title = loc.GetString("Your Inventory");
|
||||
Resizable = false;
|
||||
|
||||
var buttonDict = new Dictionary<Slots, InventoryButton>();
|
||||
Buttons = buttonDict;
|
||||
|
||||
const int width = ButtonSize * 4 + ButtonSeparation * 3 + RightSeparation;
|
||||
const int height = ButtonSize * 4 + ButtonSeparation * 3;
|
||||
|
||||
var windowContents = new Control {CustomMinimumSize = (width, height)};
|
||||
Contents.AddChild(windowContents);
|
||||
|
||||
void AddButton(Slots slot, string textureName, Vector2 position)
|
||||
{
|
||||
var texture = resourceCache.GetTexture($"/Textures/UserInterface/Inventory/{textureName}.png");
|
||||
var button = new InventoryButton(slot, texture)
|
||||
{
|
||||
Position = position
|
||||
};
|
||||
|
||||
windowContents.AddChild(button);
|
||||
buttonDict.Add(slot, button);
|
||||
}
|
||||
|
||||
const int size = ButtonSize;
|
||||
const int sep = ButtonSeparation;
|
||||
const int rSep = RightSeparation;
|
||||
|
||||
// Left column.
|
||||
AddButton(Slots.EYES, "glasses", (0, size + sep));
|
||||
AddButton(Slots.INNERCLOTHING, "uniform", (0, 2 * (size + sep)));
|
||||
AddButton(Slots.EXOSUITSLOT1, "suit_storage", (0, 3 * (size + sep)));
|
||||
|
||||
// Middle column.
|
||||
AddButton(Slots.HEAD, "head", (size + sep, 0));
|
||||
AddButton(Slots.MASK, "mask", (size + sep, size + sep));
|
||||
AddButton(Slots.OUTERCLOTHING, "suit", (size + sep, 2 * (size + sep)));
|
||||
AddButton(Slots.SHOES, "shoes", (size + sep, 3 * (size + sep)));
|
||||
|
||||
// Right column
|
||||
AddButton(Slots.EARS, "ears", (2 * (size + sep), 0));
|
||||
AddButton(Slots.IDCARD, "mask", (2 * (size + sep), size + sep));
|
||||
AddButton(Slots.GLOVES, "gloves", (2 * (size + sep), 2 * (size + sep)));
|
||||
|
||||
// Far right column.
|
||||
AddButton(Slots.BACKPACK, "back", (rSep + 3 * (size + sep), 0));
|
||||
AddButton(Slots.BELT, "belt", (rSep + 3 * (size + sep), size + sep));
|
||||
AddButton(Slots.POCKET1, "pocket", (rSep + 3 * (size + sep), 2 * (size + sep)));
|
||||
AddButton(Slots.POCKET2, "pocket", (rSep + 3 * (size + sep), 3 * (size + sep)));
|
||||
|
||||
Size = CombinedMinimumSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Inventory;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects
|
||||
{
|
||||
public sealed class InventoryButton : MarginContainer
|
||||
{
|
||||
public EquipmentSlotDefines.Slots Slot { get; }
|
||||
public EntityUid EntityUid { get; set; }
|
||||
|
||||
public BaseButton Button { get; }
|
||||
public SpriteView SpriteView { get; }
|
||||
|
||||
public Action<BaseButton.ButtonEventArgs> OnPressed { get; set; }
|
||||
|
||||
public InventoryButton(EquipmentSlotDefines.Slots slot, Texture texture)
|
||||
{
|
||||
Slot = slot;
|
||||
|
||||
CustomMinimumSize = (64, 64);
|
||||
|
||||
AddChild(Button = new TextureButton
|
||||
{
|
||||
TextureNormal = texture,
|
||||
Scale = (2, 2),
|
||||
});
|
||||
|
||||
Button.OnPressed += e => OnPressed?.Invoke(e);
|
||||
|
||||
AddChild(SpriteView = new SpriteView
|
||||
{
|
||||
MouseFilter = MouseFilterMode.Ignore,
|
||||
Scale = (2, 2)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared.GameObjects.Components.Inventory;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Client.GameObjects
|
||||
{
|
||||
public abstract class InventoryInterfaceController : IDisposable
|
||||
{
|
||||
// ReSharper disable once UnassignedGetOnlyAutoProperty
|
||||
[field: Dependency] protected IGameHud GameHud { get; }
|
||||
|
||||
protected InventoryInterfaceController(ClientInventoryComponent owner)
|
||||
{
|
||||
Owner = owner;
|
||||
}
|
||||
|
||||
public virtual void Initialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public abstract SS14Window Window { get; }
|
||||
protected ClientInventoryComponent Owner { get; }
|
||||
|
||||
public virtual void PlayerAttached()
|
||||
{
|
||||
GameHud.InventoryButtonVisible = true;
|
||||
GameHud.InventoryButtonToggled = b =>
|
||||
{
|
||||
if (b)
|
||||
{
|
||||
Window.Open();
|
||||
}
|
||||
else
|
||||
{
|
||||
Window.Close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public virtual void PlayerDetached()
|
||||
{
|
||||
GameHud.InventoryButtonVisible = false;
|
||||
Window.Close();
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void AddToSlot(EquipmentSlotDefines.Slots slot, IEntity entity)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void RemoveFromSlot(EquipmentSlotDefines.Slots slot)
|
||||
{
|
||||
}
|
||||
|
||||
protected void RemoveFromInventory(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
args.Button.Pressed = false;
|
||||
var control = (InventoryButton) args.Button.Parent;
|
||||
|
||||
Owner.SendUnequipMessage(control.Slot);
|
||||
}
|
||||
|
||||
protected void AddToInventory(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
args.Button.Pressed = false;
|
||||
var control = (InventoryButton) args.Button.Parent;
|
||||
|
||||
Owner.SendEquipMessage(control.Slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Client.GameObjects.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.Transform;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using static Robust.Client.GameObjects.SpriteComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.IconSmoothing
|
||||
{
|
||||
// TODO: Potential improvements:
|
||||
// Defer updating of these.
|
||||
// Get told by somebody to use a loop.
|
||||
/// <summary>
|
||||
/// Makes sprites of other grid-aligned entities like us connect.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The system is based on Baystation12's smoothwalling, and thus will work with those.
|
||||
/// To use, set <c>base</c> equal to the prefix of the corner states in the sprite base RSI.
|
||||
/// Any objects with the same <c>key</c> will connect.
|
||||
/// </remarks>
|
||||
[RegisterComponent]
|
||||
public class IconSmoothComponent : Component
|
||||
{
|
||||
private string _smoothKey;
|
||||
private string _stateBase;
|
||||
private IconSmoothingMode _mode;
|
||||
|
||||
public override string Name => "IconSmooth";
|
||||
|
||||
internal ISpriteComponent Sprite { get; private set; }
|
||||
internal SnapGridComponent SnapGrid { get; private set; }
|
||||
private (GridId, MapIndices) _lastPosition;
|
||||
|
||||
/// <summary>
|
||||
/// We will smooth with other objects with the same key.
|
||||
/// </summary>
|
||||
public string SmoothKey => _smoothKey;
|
||||
|
||||
/// <summary>
|
||||
/// Prepended to the RSI state.
|
||||
/// </summary>
|
||||
public string StateBase => _stateBase;
|
||||
|
||||
/// <summary>
|
||||
/// Mode that controls how the icon should be selected.
|
||||
/// </summary>
|
||||
public IconSmoothingMode Mode => _mode;
|
||||
|
||||
/// <summary>
|
||||
/// Used by <see cref="IconSmoothSystem"/> to reduce redundant updates.
|
||||
/// </summary>
|
||||
internal int UpdateGeneration { get; set; }
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SnapGrid = Owner.GetComponent<SnapGridComponent>();
|
||||
Sprite = Owner.GetComponent<ISpriteComponent>();
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataFieldCached(ref _stateBase, "base", "");
|
||||
serializer.DataFieldCached(ref _smoothKey, "key", null);
|
||||
serializer.DataFieldCached(ref _mode, "mode", IconSmoothingMode.Corners);
|
||||
}
|
||||
|
||||
public override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
SnapGrid.OnPositionChanged += SnapGridOnPositionChanged;
|
||||
Owner.EntityManager.RaiseEvent(Owner, new IconSmoothDirtyEvent(null, SnapGrid.Offset, Mode));
|
||||
if (Mode == IconSmoothingMode.Corners)
|
||||
{
|
||||
var state0 = $"{StateBase}0";
|
||||
Sprite.LayerMapSet(CornerLayers.SE, Sprite.AddLayerState(state0));
|
||||
Sprite.LayerSetDirOffset(CornerLayers.SE, DirectionOffset.None);
|
||||
Sprite.LayerMapSet(CornerLayers.NE, Sprite.AddLayerState(state0));
|
||||
Sprite.LayerSetDirOffset(CornerLayers.NE, DirectionOffset.CounterClockwise);
|
||||
Sprite.LayerMapSet(CornerLayers.NW, Sprite.AddLayerState(state0));
|
||||
Sprite.LayerSetDirOffset(CornerLayers.NW, DirectionOffset.Flip);
|
||||
Sprite.LayerMapSet(CornerLayers.SW, Sprite.AddLayerState(state0));
|
||||
Sprite.LayerSetDirOffset(CornerLayers.SW, DirectionOffset.Clockwise);
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual void CalculateNewSprite()
|
||||
{
|
||||
switch (Mode)
|
||||
{
|
||||
case IconSmoothingMode.Corners:
|
||||
CalculateNewSpriteCorers();
|
||||
break;
|
||||
|
||||
case IconSmoothingMode.CardinalFlags:
|
||||
CalculateNewSpriteCardinal();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
private void CalculateNewSpriteCardinal()
|
||||
{
|
||||
var dirs = CardinalConnectDirs.None;
|
||||
|
||||
if (MatchingEntity(SnapGrid.GetInDir(Direction.North)))
|
||||
dirs |= CardinalConnectDirs.North;
|
||||
if (MatchingEntity(SnapGrid.GetInDir(Direction.South)))
|
||||
dirs |= CardinalConnectDirs.South;
|
||||
if (MatchingEntity(SnapGrid.GetInDir(Direction.East)))
|
||||
dirs |= CardinalConnectDirs.East;
|
||||
if (MatchingEntity(SnapGrid.GetInDir(Direction.West)))
|
||||
dirs |= CardinalConnectDirs.West;
|
||||
|
||||
Sprite.LayerSetState(0, $"{StateBase}{(int) dirs}");
|
||||
}
|
||||
|
||||
private void CalculateNewSpriteCorers()
|
||||
{
|
||||
var n = MatchingEntity(SnapGrid.GetInDir(Direction.North));
|
||||
var ne = MatchingEntity(SnapGrid.GetInDir(Direction.NorthEast));
|
||||
var e = MatchingEntity(SnapGrid.GetInDir(Direction.East));
|
||||
var se = MatchingEntity(SnapGrid.GetInDir(Direction.SouthEast));
|
||||
var s = MatchingEntity(SnapGrid.GetInDir(Direction.South));
|
||||
var sw = MatchingEntity(SnapGrid.GetInDir(Direction.SouthWest));
|
||||
var w = MatchingEntity(SnapGrid.GetInDir(Direction.West));
|
||||
var nw = MatchingEntity(SnapGrid.GetInDir(Direction.NorthWest));
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
var cornerNE = CornerFill.None;
|
||||
var cornerSE = CornerFill.None;
|
||||
var cornerSW = CornerFill.None;
|
||||
var cornerNW = CornerFill.None;
|
||||
// ReSharper restore InconsistentNaming
|
||||
|
||||
if (n)
|
||||
{
|
||||
cornerNE |= CornerFill.CounterClockwise;
|
||||
cornerNW |= CornerFill.Clockwise;
|
||||
}
|
||||
|
||||
if (ne)
|
||||
{
|
||||
cornerNE |= CornerFill.Diagonal;
|
||||
}
|
||||
|
||||
if (e)
|
||||
{
|
||||
cornerNE |= CornerFill.Clockwise;
|
||||
cornerSE |= CornerFill.CounterClockwise;
|
||||
}
|
||||
|
||||
if (se)
|
||||
{
|
||||
cornerSE |= CornerFill.Diagonal;
|
||||
}
|
||||
|
||||
if (s)
|
||||
{
|
||||
cornerSE |= CornerFill.Clockwise;
|
||||
cornerSW |= CornerFill.CounterClockwise;
|
||||
}
|
||||
|
||||
if (sw)
|
||||
{
|
||||
cornerSW |= CornerFill.Diagonal;
|
||||
}
|
||||
|
||||
if (w)
|
||||
{
|
||||
cornerSW |= CornerFill.Clockwise;
|
||||
cornerNW |= CornerFill.CounterClockwise;
|
||||
}
|
||||
|
||||
if (nw)
|
||||
{
|
||||
cornerNW |= CornerFill.Diagonal;
|
||||
}
|
||||
|
||||
Sprite.LayerSetState(CornerLayers.NE, $"{StateBase}{(int) cornerNE}");
|
||||
Sprite.LayerSetState(CornerLayers.SE, $"{StateBase}{(int) cornerSE}");
|
||||
Sprite.LayerSetState(CornerLayers.SW, $"{StateBase}{(int) cornerSW}");
|
||||
Sprite.LayerSetState(CornerLayers.NW, $"{StateBase}{(int) cornerNW}");
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
SnapGrid.OnPositionChanged -= SnapGridOnPositionChanged;
|
||||
Owner.EntityManager.RaiseEvent(Owner, new IconSmoothDirtyEvent(_lastPosition, SnapGrid.Offset, Mode));
|
||||
|
||||
base.Shutdown();
|
||||
}
|
||||
|
||||
private void SnapGridOnPositionChanged()
|
||||
{
|
||||
Owner.EntityManager.RaiseEvent(Owner, new IconSmoothDirtyEvent(_lastPosition, SnapGrid.Offset, Mode));
|
||||
_lastPosition = (Owner.Transform.GridID, SnapGrid.Position);
|
||||
}
|
||||
|
||||
[System.Diagnostics.Contracts.Pure]
|
||||
protected bool MatchingEntity(IEnumerable<IEntity> candidates)
|
||||
{
|
||||
foreach (var entity in candidates)
|
||||
{
|
||||
if (!entity.TryGetComponent(out IconSmoothComponent other))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (other.SmoothKey == SmoothKey)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum CardinalConnectDirs : byte
|
||||
{
|
||||
None = 0,
|
||||
North = 1,
|
||||
South = 2,
|
||||
East = 4,
|
||||
West = 8
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum CornerFill : byte
|
||||
{
|
||||
// These values are pulled from Baystation12.
|
||||
// I'm too lazy to convert the state names.
|
||||
None = 0,
|
||||
|
||||
// The cardinal tile counter-clockwise of this corner is filled.
|
||||
CounterClockwise = 1,
|
||||
|
||||
// The diagonal tile in the direction of this corner.
|
||||
Diagonal = 2,
|
||||
|
||||
// The cardinal tile clockwise of this corner is filled.
|
||||
Clockwise = 4,
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
public enum CornerLayers
|
||||
{
|
||||
SE,
|
||||
NE,
|
||||
NW,
|
||||
SW,
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Controls the mode with which icon smoothing is calculated.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public enum IconSmoothingMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Each icon is made up of 4 corners, each of which can get a different state depending on
|
||||
/// adjacent entities clockwise, counter-clockwise and diagonal with the corner.
|
||||
/// </summary>
|
||||
Corners,
|
||||
|
||||
/// <summary>
|
||||
/// There are 16 icons, only one of which is used at once.
|
||||
/// The icon selected is a bit field made up of the cardinal direction flags that have adjacent entities.
|
||||
/// </summary>
|
||||
CardinalFlags,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Client.Interfaces.GameObjects;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared.GameObjects;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IHandsComponent))]
|
||||
public class HandsComponent : SharedHandsComponent, IHandsComponent
|
||||
{
|
||||
private HandsGui _gui;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IGameHud _gameHud;
|
||||
#pragma warning restore 649
|
||||
|
||||
[ViewVariables] private readonly Dictionary<string, IEntity> _hands = new Dictionary<string, IEntity>();
|
||||
|
||||
[ViewVariables] public string ActiveIndex { get; private set; }
|
||||
|
||||
[ViewVariables] private ISpriteComponent _sprite;
|
||||
|
||||
[ViewVariables] public IEntity ActiveHand => GetEntity(ActiveIndex);
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
|
||||
_gui?.Dispose();
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
if (Owner.TryGetComponent(out _sprite))
|
||||
{
|
||||
foreach (var slot in _hands.Keys)
|
||||
{
|
||||
_sprite.LayerMapReserveBlank($"hand-{slot}");
|
||||
_setHand(slot, _hands[slot]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEntity GetEntity(string index)
|
||||
{
|
||||
if (_hands.TryGetValue(index, out var entity))
|
||||
{
|
||||
return entity;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
{
|
||||
if (curState == null)
|
||||
return;
|
||||
|
||||
var cast = (HandsComponentState) curState;
|
||||
foreach (var (slot, uid) in cast.Hands)
|
||||
{
|
||||
IEntity entity = null;
|
||||
try
|
||||
{
|
||||
entity = Owner.EntityManager.GetEntity(uid);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Nothing.
|
||||
}
|
||||
|
||||
_hands[slot] = entity;
|
||||
_setHand(slot, entity);
|
||||
}
|
||||
|
||||
foreach (var slot in _hands.Keys.ToList())
|
||||
{
|
||||
if (!cast.Hands.ContainsKey(slot))
|
||||
{
|
||||
_hands[slot] = null;
|
||||
_setHand(slot, null);
|
||||
}
|
||||
}
|
||||
|
||||
ActiveIndex = cast.ActiveIndex;
|
||||
|
||||
_gui?.UpdateHandIcons();
|
||||
}
|
||||
|
||||
private void _setHand(string hand, IEntity entity)
|
||||
{
|
||||
if (_sprite == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity == null)
|
||||
{
|
||||
_sprite.LayerSetVisible($"hand-{hand}", false);
|
||||
return;
|
||||
}
|
||||
|
||||
var item = entity.GetComponent<ItemComponent>();
|
||||
var maybeInhands = item.GetInHandStateInfo(hand);
|
||||
if (!maybeInhands.HasValue)
|
||||
{
|
||||
_sprite.LayerSetVisible($"hand-{hand}", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var (rsi, state) = maybeInhands.Value;
|
||||
_sprite.LayerSetVisible($"hand-{hand}", true);
|
||||
_sprite.LayerSetState($"hand-{hand}", state, rsi);
|
||||
}
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
if (!serializer.Reading)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var slot in serializer.ReadDataFieldCached("hands", new List<string>()))
|
||||
{
|
||||
_hands.Add(slot, null);
|
||||
}
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null,
|
||||
IComponent component = null)
|
||||
{
|
||||
base.HandleMessage(message, netChannel, component);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case PlayerAttachedMsg _:
|
||||
if (_gui == null)
|
||||
{
|
||||
_gui = new HandsGui();
|
||||
}
|
||||
else
|
||||
{
|
||||
_gui.Parent?.RemoveChild(_gui);
|
||||
}
|
||||
|
||||
_gameHud.HandsContainer.AddChild(_gui);
|
||||
_gui.UpdateHandIcons();
|
||||
break;
|
||||
|
||||
case PlayerDetachedMsg _:
|
||||
_gui.Parent?.RemoveChild(_gui);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void SendChangeHand(string index)
|
||||
{
|
||||
SendNetworkMessage(new ClientChangedHandMsg(index));
|
||||
}
|
||||
|
||||
public void AttackByInHand(string index)
|
||||
{
|
||||
SendNetworkMessage(new ClientAttackByInHandMsg(index));
|
||||
}
|
||||
|
||||
public void UseActiveHand()
|
||||
{
|
||||
if (GetEntity(ActiveIndex) != null)
|
||||
{
|
||||
SendNetworkMessage(new ActivateInhandMsg());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
74
Content.Client/GameObjects/Components/Items/ItemComponent.cs
Normal file
74
Content.Client/GameObjects/Components/Items/ItemComponent.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Items;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.Renderable;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ItemComponent : Component
|
||||
{
|
||||
public override string Name => "Item";
|
||||
public override uint? NetID => ContentNetIDs.ITEM;
|
||||
public override Type StateType => typeof(ItemComponentState);
|
||||
|
||||
[ViewVariables] protected ResourcePath RsiPath;
|
||||
|
||||
private string _equippedPrefix;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string EquippedPrefix
|
||||
{
|
||||
get => _equippedPrefix;
|
||||
set => _equippedPrefix = value;
|
||||
}
|
||||
|
||||
public (RSI rsi, RSI.StateId stateId)? GetInHandStateInfo(string hand)
|
||||
{
|
||||
if (RsiPath == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var rsi = GetRSI();
|
||||
var stateId = EquippedPrefix != null ? $"{EquippedPrefix}-inhand-{hand}" : $"inhand-{hand}";
|
||||
if (rsi.TryGetState(stateId, out _))
|
||||
{
|
||||
return (rsi, stateId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataFieldCached(ref RsiPath, "sprite", null);
|
||||
serializer.DataFieldCached(ref _equippedPrefix, "prefix", null);
|
||||
}
|
||||
|
||||
protected RSI GetRSI()
|
||||
{
|
||||
var resourceCache = IoCManager.Resolve<IResourceCache>();
|
||||
return resourceCache.GetResource<RSIResource>(SharedSpriteComponent.TextureRoot / RsiPath).RSI;
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
{
|
||||
if(curState == null)
|
||||
return;
|
||||
|
||||
var itemComponentState = (ItemComponentState)curState;
|
||||
EquippedPrefix = itemComponentState.EquippedPrefix;
|
||||
}
|
||||
}
|
||||
}
|
||||
209
Content.Client/GameObjects/Components/LowWallComponent.cs
Normal file
209
Content.Client/GameObjects/Components/LowWallComponent.cs
Normal file
@@ -0,0 +1,209 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Client.GameObjects.Components.IconSmoothing;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using static Robust.Client.GameObjects.SpriteComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components
|
||||
{
|
||||
// TODO: Over layers should be placed ABOVE the window itself too.
|
||||
// This is gonna require a client entity & parenting,
|
||||
// so IsMapTransform being naive is gonna be a problem.
|
||||
|
||||
/// <summary>
|
||||
/// Override of icon smoothing to handle the specific complexities of low walls.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IconSmoothComponent))]
|
||||
public class LowWallComponent : IconSmoothComponent
|
||||
{
|
||||
public override string Name => "LowWall";
|
||||
|
||||
public CornerFill LastCornerNE { get; private set; }
|
||||
public CornerFill LastCornerSE { get; private set; }
|
||||
public CornerFill LastCornerSW { get; private set; }
|
||||
public CornerFill LastCornerNW { get; private set; }
|
||||
|
||||
public override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
var overState0 = $"{StateBase}over_0";
|
||||
Sprite.LayerMapSet(OverCornerLayers.SE, Sprite.AddLayerState(overState0));
|
||||
Sprite.LayerSetDirOffset(OverCornerLayers.SE, DirectionOffset.None);
|
||||
Sprite.LayerMapSet(OverCornerLayers.NE, Sprite.AddLayerState(overState0));
|
||||
Sprite.LayerSetDirOffset(OverCornerLayers.NE, DirectionOffset.CounterClockwise);
|
||||
Sprite.LayerMapSet(OverCornerLayers.NW, Sprite.AddLayerState(overState0));
|
||||
Sprite.LayerSetDirOffset(OverCornerLayers.NW, DirectionOffset.Flip);
|
||||
Sprite.LayerMapSet(OverCornerLayers.SW, Sprite.AddLayerState(overState0));
|
||||
Sprite.LayerSetDirOffset(OverCornerLayers.SW, DirectionOffset.Clockwise);
|
||||
}
|
||||
|
||||
internal override void CalculateNewSprite()
|
||||
{
|
||||
base.CalculateNewSprite();
|
||||
|
||||
var (n, nl) = MatchingWall(SnapGrid.GetInDir(Direction.North));
|
||||
var (ne, nel) = MatchingWall(SnapGrid.GetInDir(Direction.NorthEast));
|
||||
var (e, el) = MatchingWall(SnapGrid.GetInDir(Direction.East));
|
||||
var (se, sel) = MatchingWall(SnapGrid.GetInDir(Direction.SouthEast));
|
||||
var (s, sl) = MatchingWall(SnapGrid.GetInDir(Direction.South));
|
||||
var (sw, swl) = MatchingWall(SnapGrid.GetInDir(Direction.SouthWest));
|
||||
var (w, wl) = MatchingWall(SnapGrid.GetInDir(Direction.West));
|
||||
var (nw, nwl) = MatchingWall(SnapGrid.GetInDir(Direction.NorthWest));
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
var cornerNE = CornerFill.None;
|
||||
var cornerSE = CornerFill.None;
|
||||
var cornerSW = CornerFill.None;
|
||||
var cornerNW = CornerFill.None;
|
||||
|
||||
var lowCornerNE = CornerFill.None;
|
||||
var lowCornerSE = CornerFill.None;
|
||||
var lowCornerSW = CornerFill.None;
|
||||
var lowCornerNW = CornerFill.None;
|
||||
// ReSharper restore InconsistentNaming
|
||||
|
||||
if (n)
|
||||
{
|
||||
cornerNE |= CornerFill.CounterClockwise;
|
||||
cornerNW |= CornerFill.Clockwise;
|
||||
|
||||
if (!nl)
|
||||
{
|
||||
lowCornerNE |= CornerFill.CounterClockwise;
|
||||
lowCornerNW |= CornerFill.Clockwise;
|
||||
}
|
||||
}
|
||||
|
||||
if (ne)
|
||||
{
|
||||
cornerNE |= CornerFill.Diagonal;
|
||||
|
||||
if (!nel && (nl || el || n && e))
|
||||
{
|
||||
lowCornerNE |= CornerFill.Diagonal;
|
||||
}
|
||||
}
|
||||
|
||||
if (e)
|
||||
{
|
||||
cornerNE |= CornerFill.Clockwise;
|
||||
cornerSE |= CornerFill.CounterClockwise;
|
||||
|
||||
if (!el)
|
||||
{
|
||||
lowCornerNE |= CornerFill.Clockwise;
|
||||
lowCornerSE |= CornerFill.CounterClockwise;
|
||||
}
|
||||
}
|
||||
|
||||
if (se)
|
||||
{
|
||||
cornerSE |= CornerFill.Diagonal;
|
||||
|
||||
if (!sel && (sl || el || s && e))
|
||||
{
|
||||
lowCornerSE |= CornerFill.Diagonal;
|
||||
}
|
||||
}
|
||||
|
||||
if (s)
|
||||
{
|
||||
cornerSE |= CornerFill.Clockwise;
|
||||
cornerSW |= CornerFill.CounterClockwise;
|
||||
|
||||
if (!sl)
|
||||
{
|
||||
lowCornerSE |= CornerFill.Clockwise;
|
||||
lowCornerSW |= CornerFill.CounterClockwise;
|
||||
}
|
||||
}
|
||||
|
||||
if (sw)
|
||||
{
|
||||
cornerSW |= CornerFill.Diagonal;
|
||||
|
||||
if (!swl && (sl || wl || s && w))
|
||||
{
|
||||
lowCornerSW |= CornerFill.Diagonal;
|
||||
}
|
||||
}
|
||||
|
||||
if (w)
|
||||
{
|
||||
cornerSW |= CornerFill.Clockwise;
|
||||
cornerNW |= CornerFill.CounterClockwise;
|
||||
|
||||
if (!wl)
|
||||
{
|
||||
lowCornerSW |= CornerFill.Clockwise;
|
||||
lowCornerNW |= CornerFill.CounterClockwise;
|
||||
}
|
||||
}
|
||||
|
||||
if (nw)
|
||||
{
|
||||
cornerNW |= CornerFill.Diagonal;
|
||||
|
||||
if (!nwl && (nl || wl || n && w))
|
||||
{
|
||||
lowCornerNW |= CornerFill.Diagonal;
|
||||
}
|
||||
}
|
||||
|
||||
Sprite.LayerSetState(CornerLayers.NE, $"{StateBase}{(int) cornerNE}");
|
||||
Sprite.LayerSetState(CornerLayers.SE, $"{StateBase}{(int) cornerSE}");
|
||||
Sprite.LayerSetState(CornerLayers.SW, $"{StateBase}{(int) cornerSW}");
|
||||
Sprite.LayerSetState(CornerLayers.NW, $"{StateBase}{(int) cornerNW}");
|
||||
|
||||
Sprite.LayerSetState(OverCornerLayers.NE, $"{StateBase}over_{(int) lowCornerNE}");
|
||||
Sprite.LayerSetState(OverCornerLayers.SE, $"{StateBase}over_{(int) lowCornerSE}");
|
||||
Sprite.LayerSetState(OverCornerLayers.SW, $"{StateBase}over_{(int) lowCornerSW}");
|
||||
Sprite.LayerSetState(OverCornerLayers.NW, $"{StateBase}over_{(int) lowCornerNW}");
|
||||
|
||||
LastCornerNE = cornerNE;
|
||||
LastCornerSE = cornerSE;
|
||||
LastCornerSW = cornerSW;
|
||||
LastCornerNW = cornerNW;
|
||||
|
||||
foreach (var entity in SnapGrid.GetLocal())
|
||||
{
|
||||
if (entity.TryGetComponent(out WindowComponent window))
|
||||
{
|
||||
window.UpdateSprite();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.Contracts.Pure]
|
||||
private (bool connected, bool lowWall) MatchingWall(IEnumerable<IEntity> candidates)
|
||||
{
|
||||
foreach (var entity in candidates)
|
||||
{
|
||||
if (!entity.TryGetComponent(out IconSmoothComponent other))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (other.SmoothKey == SmoothKey)
|
||||
{
|
||||
return (true, other is LowWallComponent);
|
||||
}
|
||||
}
|
||||
|
||||
return (false, false);
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
private enum OverCornerLayers
|
||||
{
|
||||
SE,
|
||||
NE,
|
||||
NW,
|
||||
SW,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Mobs
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedCameraRecoilComponent))]
|
||||
public sealed class CameraRecoilComponent : SharedCameraRecoilComponent
|
||||
{
|
||||
// Maximum rate of magnitude restore towards 0 kick.
|
||||
private const float RestoreRateMax = 1.5f;
|
||||
|
||||
// Minimum rate of magnitude restore towards 0 kick.
|
||||
private const float RestoreRateMin = 0.5f;
|
||||
|
||||
// Time in seconds since the last kick that lerps RestoreRateMin and RestoreRateMax
|
||||
private const float RestoreRateRamp = 0.05f;
|
||||
|
||||
// The maximum magnitude of the kick applied to the camera at any point.
|
||||
private const float KickMagnitudeMax = 0.25f;
|
||||
|
||||
private Vector2 _currentKick;
|
||||
private float _lastKickTime;
|
||||
|
||||
private EyeComponent _eye;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_eye = Owner.GetComponent<EyeComponent>();
|
||||
}
|
||||
|
||||
public override void Kick(Vector2 recoil)
|
||||
{
|
||||
// Use really bad math to "dampen" kicks when we're already kicked.
|
||||
var existing = _currentKick.Length;
|
||||
var dampen = existing/KickMagnitudeMax;
|
||||
_currentKick += recoil * (1-dampen);
|
||||
if (_currentKick.Length > KickMagnitudeMax)
|
||||
{
|
||||
_currentKick = _currentKick.Normalized * KickMagnitudeMax;
|
||||
}
|
||||
|
||||
_lastKickTime = 0;
|
||||
_updateEye();
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
|
||||
{
|
||||
base.HandleMessage(message, netChannel, component);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case RecoilKickMessage msg:
|
||||
Kick(msg.Recoil);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void FrameUpdate(float frameTime)
|
||||
{
|
||||
var magnitude = _currentKick.Length;
|
||||
if (magnitude <= 0.005f)
|
||||
{
|
||||
_currentKick = Vector2.Zero;
|
||||
_updateEye();
|
||||
return;
|
||||
}
|
||||
|
||||
// Continually restore camera to 0.
|
||||
var normalized = _currentKick.Normalized;
|
||||
var restoreRate = FloatMath.Lerp(RestoreRateMin, RestoreRateMax, Math.Min(1, _lastKickTime/RestoreRateRamp));
|
||||
var restore = normalized * restoreRate * frameTime;
|
||||
_currentKick -= restore;
|
||||
_updateEye();
|
||||
}
|
||||
|
||||
private void _updateEye()
|
||||
{
|
||||
_eye.Offset = _currentKick;
|
||||
}
|
||||
}
|
||||
}
|
||||
21
Content.Client/GameObjects/Components/Mobs/ICharacterUI.cs
Normal file
21
Content.Client/GameObjects/Components/Mobs/ICharacterUI.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Content.Client.GameObjects.Components.Actor;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Mobs
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface which is gathered to assemble the character window from multiple components
|
||||
/// </summary>
|
||||
public interface ICharacterUI
|
||||
{
|
||||
/// <summary>
|
||||
/// The godot control which holds the character user interface to be included in the window
|
||||
/// </summary>
|
||||
Control Scene { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The order it will appear in the character UI, higher is lower
|
||||
/// </summary>
|
||||
UIPriority Priority { get; }
|
||||
}
|
||||
}
|
||||
171
Content.Client/GameObjects/Components/Mobs/SpeciesUI.cs
Normal file
171
Content.Client/GameObjects/Components/Mobs/SpeciesUI.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.GameObjects.Components.Actor;
|
||||
using Content.Client.Graphics.Overlays;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Client.Utility;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Graphics.Overlays;
|
||||
using Robust.Client.Interfaces.Graphics.Overlays;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.Interfaces.UserInterface;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.Renderable;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Client.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// A character UI component which shows the current damage state of the mob (living/dead)
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class SpeciesUI : SharedSpeciesComponent//, ICharacterUI
|
||||
{
|
||||
private StatusEffectsUI _ui;
|
||||
|
||||
/// <summary>
|
||||
/// Holds the godot control for the species window
|
||||
/// </summary>
|
||||
private SpeciesWindow _window;
|
||||
|
||||
/// <summary>
|
||||
/// An enum representing the current state being applied to the user
|
||||
/// </summary>
|
||||
private ScreenEffects _currentEffect = ScreenEffects.None;
|
||||
|
||||
#pragma warning disable 649
|
||||
// Required dependencies
|
||||
[Dependency] private readonly IOverlayManager _overlayManager;
|
||||
[Dependency] private readonly IPlayerManager _playerManager;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager;
|
||||
[Dependency] private readonly IResourceCache _resourceCache;
|
||||
#pragma warning restore 649
|
||||
|
||||
//Relevant interface implementation for the character UI controller
|
||||
public Control Scene => _window;
|
||||
public UIPriority Priority => UIPriority.Species;
|
||||
|
||||
/// <summary>
|
||||
/// Allows calculating if we need to act due to this component being controlled by the current mob
|
||||
/// </summary>
|
||||
private bool CurrentlyControlled => _playerManager.LocalPlayer.ControlledEntity == Owner;
|
||||
|
||||
/// <summary>
|
||||
/// Holds the screen effects that can be applied mapped ot their relevant overlay
|
||||
/// </summary>
|
||||
private Dictionary<ScreenEffects, Overlay> EffectsDictionary;
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
|
||||
_window.Dispose();
|
||||
}
|
||||
|
||||
public override void OnAdd()
|
||||
{
|
||||
base.OnAdd();
|
||||
|
||||
_window = new SpeciesWindow();
|
||||
_ui = new StatusEffectsUI();
|
||||
|
||||
EffectsDictionary = new Dictionary<ScreenEffects, Overlay>()
|
||||
{
|
||||
{ ScreenEffects.CircleMask, new CircleMaskOverlay() },
|
||||
{ ScreenEffects.GradientCircleMask, new GradientCircleMask() }
|
||||
};
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case HudStateChange msg:
|
||||
if (CurrentlyControlled)
|
||||
{
|
||||
ChangeHudIcon(msg);
|
||||
}
|
||||
break;
|
||||
|
||||
case PlayerAttachedMsg _:
|
||||
_ui.Parent?.RemoveChild(_ui);
|
||||
|
||||
_userInterfaceManager.StateRoot.AddChild(_ui);
|
||||
ApplyOverlay();
|
||||
break;
|
||||
|
||||
case PlayerDetachedMsg _:
|
||||
_ui.Parent?.RemoveChild(_ui);
|
||||
RemoveOverlay();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ChangeHudIcon(HudStateChange changeMessage)
|
||||
{
|
||||
var path = SharedSpriteComponent.TextureRoot / changeMessage.StateSprite;
|
||||
var texture = _resourceCache.GetTexture(path);
|
||||
|
||||
_window.SetIcon(texture);
|
||||
_ui.SetHealthIcon(texture);
|
||||
|
||||
SetOverlay(changeMessage);
|
||||
}
|
||||
|
||||
private void SetOverlay(HudStateChange message)
|
||||
{
|
||||
RemoveOverlay();
|
||||
|
||||
_currentEffect = message.effect;
|
||||
|
||||
ApplyOverlay();
|
||||
}
|
||||
|
||||
private void RemoveOverlay()
|
||||
{
|
||||
if (_currentEffect != ScreenEffects.None)
|
||||
{
|
||||
var appliedEffect = EffectsDictionary[_currentEffect];
|
||||
_overlayManager.RemoveOverlay(appliedEffect.ID);
|
||||
}
|
||||
|
||||
_currentEffect = ScreenEffects.None;
|
||||
}
|
||||
|
||||
private void ApplyOverlay()
|
||||
{
|
||||
if (_currentEffect != ScreenEffects.None)
|
||||
{
|
||||
var overlay = EffectsDictionary[_currentEffect];
|
||||
if (_overlayManager.HasOverlay(overlay.ID))
|
||||
{
|
||||
return;
|
||||
}
|
||||
_overlayManager.AddOverlay(overlay);
|
||||
}
|
||||
}
|
||||
|
||||
private class SpeciesWindow : TextureRect
|
||||
{
|
||||
public SpeciesWindow()
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.ShrinkCenter;
|
||||
SizeFlagsVertical = SizeFlags.None;
|
||||
|
||||
Texture = IoCManager.Resolve<IResourceCache>().GetTexture("/Textures/Mob/UI/Human/human0.png");
|
||||
}
|
||||
|
||||
public void SetIcon(Texture texture)
|
||||
{
|
||||
Texture = texture;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Mobs
|
||||
{
|
||||
public class SpeciesVisualizer2D : AppearanceVisualizer
|
||||
{
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
if (component.TryGetData<SharedSpeciesComponent.MobState>(SharedSpeciesComponent.MobVisuals.RotationState, out var state))
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case SharedSpeciesComponent.MobState.Stand:
|
||||
sprite.Rotation = 0;
|
||||
break;
|
||||
case SharedSpeciesComponent.MobState.Down:
|
||||
sprite.Rotation = Angle.FromDegrees(90);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Movement
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class HandTeleporterVisualizer2D : AppearanceVisualizer
|
||||
{
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
if (!component.TryGetData(TeleporterVisuals.VisualState, out TeleporterVisualState state))
|
||||
{
|
||||
state = TeleporterVisualState.Ready;
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case TeleporterVisualState.Charging:
|
||||
sprite.LayerSetState(0, "charging");
|
||||
break;
|
||||
case TeleporterVisualState.Ready:
|
||||
sprite.LayerSetState(0, "ready");
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Movement
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class PortalVisualizer2D : AppearanceVisualizer
|
||||
{
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
|
||||
var sprite = entity.GetComponent<ISpriteComponent>();
|
||||
|
||||
sprite.LayerMapSet(Layers.Portal, sprite.AddLayerState("portal-pending"));
|
||||
sprite.LayerSetShader(Layers.Portal, "unshaded");
|
||||
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
if (component.TryGetData<PortalState>(PortalVisuals.State, out var state))
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case PortalState.Pending:
|
||||
sprite.LayerSetState(Layers.Portal, "portal-pending");
|
||||
break;
|
||||
// TODO: Spritework here?
|
||||
case PortalState.UnableToTeleport:
|
||||
sprite.LayerSetState(Layers.Portal, "portal-unconnected");
|
||||
break;
|
||||
case PortalState.RecentlyTeleported:
|
||||
sprite.LayerSetState(Layers.Portal, "portal-unconnected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetState(Layers.Portal, "portal-pending");
|
||||
}
|
||||
}
|
||||
|
||||
enum Layers
|
||||
{
|
||||
Portal
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared.GameObjects.Components.Power;
|
||||
using NJsonSchema.Validation;
|
||||
using OpenTK.Graphics.OpenGL4;
|
||||
using Robust.Client.GameObjects.Components.UserInterface;
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
using Robust.Client.Interfaces.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Power
|
||||
{
|
||||
public class ApcBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private ApcWindow _window;
|
||||
private BaseButton _breakerButton;
|
||||
private Label _externalPowerStateLabel;
|
||||
private ProgressBar _chargeBar;
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new ApcWindow
|
||||
{
|
||||
MarginRight = 426.0f, MarginBottom = 270.0f
|
||||
};
|
||||
_window.OnClose += Close;
|
||||
_window.OpenCenteredMinSize();
|
||||
|
||||
_breakerButton = _window.BreakerButton;
|
||||
_breakerButton.OnPressed += _ => SendMessage(new ApcToggleMainBreakerMessage());
|
||||
|
||||
_externalPowerStateLabel = _window.ExternalPowerStateLabel;
|
||||
_chargeBar = _window.ChargeBar;
|
||||
}
|
||||
|
||||
public ApcBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
var castState = (ApcBoundInterfaceState) state;
|
||||
|
||||
_breakerButton.Pressed = castState.MainBreaker;
|
||||
switch (castState.ApcExternalPower)
|
||||
{
|
||||
case ApcExternalPowerState.None:
|
||||
_externalPowerStateLabel.Text = "None";
|
||||
_externalPowerStateLabel.SetOnlyStyleClass(NanoStyle.StyleClassPowerStateNone);
|
||||
break;
|
||||
case ApcExternalPowerState.Low:
|
||||
_externalPowerStateLabel.Text = "Low";
|
||||
_externalPowerStateLabel.SetOnlyStyleClass(NanoStyle.StyleClassPowerStateLow);
|
||||
break;
|
||||
case ApcExternalPowerState.Good:
|
||||
_externalPowerStateLabel.Text = "Good";
|
||||
_externalPowerStateLabel.SetOnlyStyleClass(NanoStyle.StyleClassPowerStateGood);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
_chargeBar.Value = castState.Charge;
|
||||
UpdateChargeBarColor(castState.Charge);
|
||||
float ChargePercentage = (castState.Charge / _chargeBar.MaxValue) * 100.0f;
|
||||
_window.ChargePercentage.Text = " " + ChargePercentage.ToString("0.00") + "%";
|
||||
}
|
||||
|
||||
private void UpdateChargeBarColor(float charge)
|
||||
{
|
||||
float normalizedCharge = charge / _chargeBar.MaxValue;
|
||||
|
||||
float leftHue = 0.0f;// Red
|
||||
float middleHue = 0.066f;// Orange
|
||||
float rightHue = 0.33f;// Green
|
||||
float saturation = 1.0f;// Uniform saturation
|
||||
float value = 0.8f;// Uniform value / brightness
|
||||
float alpha = 1.0f;// Uniform alpha
|
||||
|
||||
// These should add up to 1.0 or your transition won't be smooth
|
||||
float leftSideSize = 0.5f;// Fraction of _chargeBar lerped from leftHue to middleHue
|
||||
float rightSideSize = 0.5f;// Fraction of _chargeBar lerped from middleHue to rightHue
|
||||
|
||||
float finalHue;
|
||||
if (normalizedCharge <= leftSideSize)
|
||||
{
|
||||
normalizedCharge /= leftSideSize;// Adjust range to 0.0 to 1.0
|
||||
finalHue = FloatMath.Lerp(leftHue, middleHue, normalizedCharge);
|
||||
}
|
||||
else
|
||||
{
|
||||
normalizedCharge = (normalizedCharge - leftSideSize) / rightSideSize;// Adjust range to 0.0 to 1.0.
|
||||
finalHue = FloatMath.Lerp(middleHue, rightHue, normalizedCharge);
|
||||
}
|
||||
|
||||
// Check if null first to avoid repeatedly creating this.
|
||||
if (_chargeBar.ForegroundStyleBoxOverride == null)
|
||||
{
|
||||
_chargeBar.ForegroundStyleBoxOverride = new StyleBoxFlat();
|
||||
}
|
||||
|
||||
var foregroundStyleBoxOverride = (StyleBoxFlat)_chargeBar.ForegroundStyleBoxOverride;
|
||||
foregroundStyleBoxOverride.BackgroundColor =
|
||||
Color.FromHsv(new Vector4(finalHue, saturation, value, alpha));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_window.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private class ApcWindow : SS14Window
|
||||
{
|
||||
public Button BreakerButton { get; set; }
|
||||
public Label ExternalPowerStateLabel { get; set; }
|
||||
public ProgressBar ChargeBar { get; set; }
|
||||
public Label ChargePercentage { get; set; }
|
||||
|
||||
public ApcWindow()
|
||||
{
|
||||
Title = "APC";
|
||||
var rows = new VBoxContainer("Rows");
|
||||
|
||||
var statusHeader = new Label("StatusHeader") { Text = "Power Status: " };
|
||||
rows.AddChild(statusHeader);
|
||||
|
||||
var breaker = new HBoxContainer("Breaker");
|
||||
var breakerLabel = new Label("Label") { Text = "Main Breaker: " };
|
||||
BreakerButton = new CheckButton {Name = "Breaker", Text = "Toggle"};
|
||||
breaker.AddChild(breakerLabel);
|
||||
breaker.AddChild(BreakerButton);
|
||||
rows.AddChild(breaker);
|
||||
|
||||
var externalStatus = new HBoxContainer("ExternalStatus");
|
||||
var externalStatusLabel = new Label("Label") { Text = "External Power: " };
|
||||
ExternalPowerStateLabel = new Label("Status") { Text = "Good" };
|
||||
ExternalPowerStateLabel.SetOnlyStyleClass(NanoStyle.StyleClassPowerStateGood);
|
||||
externalStatus.AddChild(externalStatusLabel);
|
||||
externalStatus.AddChild(ExternalPowerStateLabel);
|
||||
rows.AddChild(externalStatus);
|
||||
|
||||
var charge = new HBoxContainer("Charge");
|
||||
var chargeLabel = new Label("Label") { Text = "Charge:" };
|
||||
ChargeBar = new ProgressBar("Charge")
|
||||
{
|
||||
SizeFlagsHorizontal = Control.SizeFlags.FillExpand,
|
||||
MinValue = 0.0f,
|
||||
MaxValue = 1.0f,
|
||||
Page = 0.0f,
|
||||
Value = 0.5f
|
||||
};
|
||||
ChargePercentage = new Label("ChargePercentage");
|
||||
charge.AddChild(chargeLabel);
|
||||
charge.AddChild(ChargeBar);
|
||||
charge.AddChild(ChargePercentage);
|
||||
rows.AddChild(charge);
|
||||
|
||||
Contents.AddChild(rows);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using Content.Shared.GameObjects.Components.Power;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Power
|
||||
{
|
||||
public class ApcVisualizer2D : AppearanceVisualizer
|
||||
{
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
|
||||
var sprite = entity.GetComponent<ISpriteComponent>();
|
||||
|
||||
sprite.LayerMapSet(Layers.ChargeState, sprite.AddLayerState("apco3-0"));
|
||||
sprite.LayerSetShader(Layers.ChargeState, "unshaded");
|
||||
|
||||
sprite.LayerMapSet(Layers.Lock, sprite.AddLayerState("apcox-0"));
|
||||
sprite.LayerSetShader(Layers.Lock, "unshaded");
|
||||
|
||||
sprite.LayerMapSet(Layers.Equipment, sprite.AddLayerState("apco0-3"));
|
||||
sprite.LayerSetShader(Layers.Equipment, "unshaded");
|
||||
|
||||
sprite.LayerMapSet(Layers.Lighting, sprite.AddLayerState("apco1-3"));
|
||||
sprite.LayerSetShader(Layers.Lighting, "unshaded");
|
||||
|
||||
sprite.LayerMapSet(Layers.Environment, sprite.AddLayerState("apco2-3"));
|
||||
sprite.LayerSetShader(Layers.Environment, "unshaded");
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
if (component.TryGetData<ApcChargeState>(ApcVisuals.ChargeState, out var state))
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case ApcChargeState.Lack:
|
||||
sprite.LayerSetState(Layers.ChargeState, "apco3-0");
|
||||
break;
|
||||
case ApcChargeState.Charging:
|
||||
sprite.LayerSetState(Layers.ChargeState, "apco3-1");
|
||||
break;
|
||||
case ApcChargeState.Full:
|
||||
sprite.LayerSetState(Layers.ChargeState, "apco3-2");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetState(Layers.ChargeState, "apco3-0");
|
||||
}
|
||||
}
|
||||
|
||||
enum Layers
|
||||
{
|
||||
ChargeState,
|
||||
Lock,
|
||||
Equipment,
|
||||
Lighting,
|
||||
Environment,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using Content.Shared.GameObjects.Components.Power;
|
||||
using Content.Shared.Utility;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Power
|
||||
{
|
||||
public class PowerCellVisualizer2D : AppearanceVisualizer
|
||||
{
|
||||
private string _prefix;
|
||||
|
||||
public override void LoadData(YamlMappingNode node)
|
||||
{
|
||||
base.LoadData(node);
|
||||
|
||||
_prefix = node.GetNode("prefix").AsString();
|
||||
}
|
||||
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
|
||||
var sprite = entity.GetComponent<ISpriteComponent>();
|
||||
|
||||
sprite.LayerMapSet(Layers.Charge, sprite.AddLayerState($"{_prefix}_100"));
|
||||
sprite.LayerSetShader(Layers.Charge, "unshaded");
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
if (component.TryGetData(PowerCellVisuals.ChargeLevel, out float fraction))
|
||||
{
|
||||
sprite.LayerSetState(Layers.Charge, $"{_prefix}_{ContentHelpers.RoundToLevels(fraction, 1, 5) * 25}");
|
||||
}
|
||||
}
|
||||
|
||||
private enum Layers
|
||||
{
|
||||
Charge
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Content.Shared.GameObjects.Components.Power;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Power
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class PowerDebugTool : SharedPowerDebugTool
|
||||
{
|
||||
SS14Window LastWindow;
|
||||
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
|
||||
{
|
||||
base.HandleMessage(message, netChannel, component);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case OpenDataWindowMsg msg:
|
||||
if (LastWindow != null && !LastWindow.Disposed)
|
||||
{
|
||||
LastWindow.Dispose();
|
||||
}
|
||||
LastWindow = new SS14Window()
|
||||
{
|
||||
Title = "Power Debug Tool",
|
||||
};
|
||||
LastWindow.Contents.AddChild(new Label() { Text = msg.Data });
|
||||
LastWindow.Open();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
using Content.Shared.GameObjects.Components.Power;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Power
|
||||
{
|
||||
public class SmesVisualizer2D : AppearanceVisualizer
|
||||
{
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
|
||||
var sprite = entity.GetComponent<ISpriteComponent>();
|
||||
|
||||
sprite.LayerMapSet(Layers.Input, sprite.AddLayerState("smes-oc0"));
|
||||
sprite.LayerSetShader(Layers.Input, "unshaded");
|
||||
sprite.LayerMapSet(Layers.Charge, sprite.AddLayerState("smes-og1"));
|
||||
sprite.LayerSetShader(Layers.Charge, "unshaded");
|
||||
sprite.LayerSetVisible(Layers.Charge, false);
|
||||
sprite.LayerMapSet(Layers.Output, sprite.AddLayerState("smes-op0"));
|
||||
sprite.LayerSetShader(Layers.Output, "unshaded");
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
if (!component.TryGetData<int>(SmesVisuals.LastChargeLevel, out var level) || level == 0)
|
||||
{
|
||||
sprite.LayerSetVisible(Layers.Charge, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetVisible(Layers.Charge, true);
|
||||
sprite.LayerSetState(Layers.Charge, $"smes-og{level}");
|
||||
}
|
||||
|
||||
if (component.TryGetData<ChargeState>(SmesVisuals.LastChargeState, out var state))
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case ChargeState.Still:
|
||||
sprite.LayerSetState(Layers.Input, "smes-oc0");
|
||||
sprite.LayerSetState(Layers.Output, "smes-op1");
|
||||
break;
|
||||
case ChargeState.Charging:
|
||||
sprite.LayerSetState(Layers.Input, "smes-oc1");
|
||||
sprite.LayerSetState(Layers.Output, "smes-op1");
|
||||
break;
|
||||
case ChargeState.Discharging:
|
||||
sprite.LayerSetState(Layers.Input, "smes-oc0");
|
||||
sprite.LayerSetState(Layers.Output, "smes-op2");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetState(Layers.Input, "smes-oc0");
|
||||
sprite.LayerSetState(Layers.Output, "smes-op1");
|
||||
}
|
||||
}
|
||||
|
||||
enum Layers
|
||||
{
|
||||
Input,
|
||||
Charge,
|
||||
Output,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.Research;
|
||||
using Content.Shared.GameObjects.Components.Research;
|
||||
using Content.Shared.Research;
|
||||
using Robust.Client.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Research
|
||||
{
|
||||
public class LatheBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
[Dependency]
|
||||
private IPrototypeManager _prototypeManager;
|
||||
#pragma warning restore
|
||||
[ViewVariables]
|
||||
private LatheMenu menu;
|
||||
[ViewVariables]
|
||||
private LatheQueueMenu queueMenu;
|
||||
|
||||
public MaterialStorageComponent Storage { get; private set; }
|
||||
public SharedLatheComponent Lathe { get; private set; }
|
||||
public LatheDatabaseComponent Database { get; private set; }
|
||||
|
||||
[ViewVariables]
|
||||
public Queue<LatheRecipePrototype> QueuedRecipes => _queuedRecipes;
|
||||
private Queue<LatheRecipePrototype> _queuedRecipes = new Queue<LatheRecipePrototype>();
|
||||
|
||||
public LatheBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
SendMessage(new SharedLatheComponent.LatheSyncRequestMessage());
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
if (!Owner.Owner.TryGetComponent(out MaterialStorageComponent storage)
|
||||
|| !Owner.Owner.TryGetComponent(out SharedLatheComponent lathe)
|
||||
|| !Owner.Owner.TryGetComponent(out LatheDatabaseComponent database)) return;
|
||||
|
||||
Storage = storage;
|
||||
Lathe = lathe;
|
||||
Database = database;
|
||||
|
||||
menu = new LatheMenu {Owner = this};
|
||||
queueMenu = new LatheQueueMenu { Owner = this };
|
||||
|
||||
menu.OnClose += Close;
|
||||
|
||||
menu.Populate();
|
||||
menu.PopulateMaterials();
|
||||
|
||||
menu.QueueButton.OnPressed += (args) => { queueMenu.OpenCentered(); };
|
||||
|
||||
storage.OnMaterialStorageChanged += menu.PopulateDisabled;
|
||||
storage.OnMaterialStorageChanged += menu.PopulateMaterials;
|
||||
|
||||
menu.OpenCentered();
|
||||
}
|
||||
|
||||
public void Queue(LatheRecipePrototype recipe, int quantity = 1)
|
||||
{
|
||||
SendMessage(new SharedLatheComponent.LatheQueueRecipeMessage(recipe.ID, quantity));
|
||||
}
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case SharedLatheComponent.LatheProducingRecipeMessage msg:
|
||||
if (!_prototypeManager.TryIndex(msg.ID, out LatheRecipePrototype recipe)) break;
|
||||
queueMenu.SetInfo(recipe);
|
||||
break;
|
||||
case SharedLatheComponent.LatheStoppedProducingRecipeMessage msg:
|
||||
queueMenu.ClearInfo();
|
||||
break;
|
||||
case SharedLatheComponent.LatheFullQueueMessage msg:
|
||||
_queuedRecipes.Clear();
|
||||
foreach (var id in msg.Recipes)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex(id, out LatheRecipePrototype recipePrototype)) break;
|
||||
_queuedRecipes.Enqueue(recipePrototype);
|
||||
}
|
||||
queueMenu.PopulateList();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing) return;
|
||||
menu?.Dispose();
|
||||
queueMenu?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Content.Shared.GameObjects.Components.Research;
|
||||
using Content.Shared.Research;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Research
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedLatheDatabaseComponent))]
|
||||
public class LatheDatabaseComponent : SharedLatheDatabaseComponent
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
[Dependency]
|
||||
private IPrototypeManager _prototypeManager;
|
||||
#pragma warning restore
|
||||
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
if (!(curState is LatheDatabaseState state)) return;
|
||||
Clear();
|
||||
foreach (var ID in state.Recipes)
|
||||
{
|
||||
if(!_prototypeManager.TryIndex(ID, out LatheRecipePrototype recipe)) continue;
|
||||
AddRecipe(recipe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.GameObjects.Components.Research;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Research
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedMaterialStorageComponent))]
|
||||
public class MaterialStorageComponent : SharedMaterialStorageComponent
|
||||
{
|
||||
protected override Dictionary<string, int> Storage { get; set; } = new Dictionary<string, int>();
|
||||
|
||||
public event Action OnMaterialStorageChanged;
|
||||
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
if (!(curState is MaterialStorageState state)) return;
|
||||
Storage = state.Storage;
|
||||
OnMaterialStorageChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
108
Content.Client/GameObjects/Components/Sound/SoundComponent.cs
Normal file
108
Content.Client/GameObjects/Components/Sound/SoundComponent.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.GameObjects.Components.Sound;
|
||||
using Robust.Client.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timers;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Sound
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class SoundComponent : SharedSoundComponent
|
||||
{
|
||||
private readonly List<ScheduledSound> _schedules = new List<ScheduledSound>();
|
||||
private AudioSystem _audioSystem;
|
||||
private Random Random;
|
||||
|
||||
public override void StopAllSounds()
|
||||
{
|
||||
foreach (var schedule in _schedules)
|
||||
{
|
||||
schedule.Play = false;
|
||||
}
|
||||
_schedules.Clear();
|
||||
}
|
||||
|
||||
public override void StopScheduledSound(string filename)
|
||||
{
|
||||
foreach (var schedule in _schedules.ToArray())
|
||||
{
|
||||
if (schedule.Filename != filename) continue;
|
||||
schedule.Play = false;
|
||||
_schedules.Remove(schedule);
|
||||
}
|
||||
}
|
||||
|
||||
public override void AddScheduledSound(ScheduledSound schedule)
|
||||
{
|
||||
_schedules.Add(schedule);
|
||||
Play(schedule);
|
||||
}
|
||||
|
||||
public void Play(ScheduledSound schedule)
|
||||
{
|
||||
if (!schedule.Play) return;
|
||||
if (Random == null) Random = new Random(Owner.Uid.GetHashCode() ^ DateTime.Now.GetHashCode());
|
||||
|
||||
Timer.Spawn((int) schedule.Delay + (Random.Next((int) schedule.RandomDelay)),() =>
|
||||
{
|
||||
if (!schedule.Play) return; // We make sure this hasn't changed.
|
||||
if (_audioSystem == null) _audioSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>();
|
||||
_audioSystem.Play(schedule.Filename, Owner, schedule.AudioParams);
|
||||
|
||||
if (schedule.Times == 0)
|
||||
{
|
||||
_schedules.Remove(schedule);
|
||||
return;
|
||||
}
|
||||
|
||||
if (schedule.Times > 0)
|
||||
schedule.Times--;
|
||||
|
||||
Play(schedule);
|
||||
});
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
|
||||
{
|
||||
base.HandleMessage(message, netChannel, component);
|
||||
switch (message)
|
||||
{
|
||||
case ScheduledSoundMessage msg:
|
||||
AddScheduledSound(msg.Schedule);
|
||||
break;
|
||||
|
||||
case StopSoundScheduleMessage msg:
|
||||
StopScheduledSound(msg.Filename);
|
||||
break;
|
||||
|
||||
case StopAllSoundsMessage msg:
|
||||
StopAllSounds();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
IoCManager.Resolve<IEntitySystemManager>().TryGetEntitySystem(out _audioSystem);
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
if (serializer.Writing) return;
|
||||
serializer.TryReadDataField("schedules", out List<ScheduledSound> schedules);
|
||||
if (schedules == null) return;
|
||||
foreach (var schedule in schedules)
|
||||
{
|
||||
if (schedule == null) continue;
|
||||
AddScheduledSound(schedule);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.GameObjects.Components.Storage;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Storage
|
||||
{
|
||||
/// <summary>
|
||||
/// Client version of item storage containers, contains a UI which displays stored entities and their size
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class ClientStorageComponent : SharedStorageComponent
|
||||
{
|
||||
private Dictionary<EntityUid, int> StoredEntities { get; set; } = new Dictionary<EntityUid, int>();
|
||||
private int StorageSizeUsed;
|
||||
private int StorageCapacityMax;
|
||||
private StorageWindow Window;
|
||||
|
||||
public override void OnAdd()
|
||||
{
|
||||
base.OnAdd();
|
||||
|
||||
Window = new StorageWindow()
|
||||
{ StorageEntity = this};
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
Window.Dispose();
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
//Updates what we are storing for the UI
|
||||
case StorageHeldItemsMessage msg:
|
||||
HandleStorageMessage(msg);
|
||||
break;
|
||||
//Opens the UI
|
||||
case OpenStorageUIMessage msg:
|
||||
OpenUI();
|
||||
break;
|
||||
case CloseStorageUIMessage msg:
|
||||
CloseUI();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies received values from server about contents of storage container
|
||||
/// </summary>
|
||||
/// <param name="storagestate"></param>
|
||||
private void HandleStorageMessage(StorageHeldItemsMessage storagestate)
|
||||
{
|
||||
StoredEntities = new Dictionary<EntityUid, int>(storagestate.StoredEntities);
|
||||
StorageSizeUsed = storagestate.StorageSizeUsed;
|
||||
StorageCapacityMax = storagestate.StorageSizeMax;
|
||||
Window.BuildEntityList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the storage UI
|
||||
/// </summary>
|
||||
private void OpenUI()
|
||||
{
|
||||
Window.Open();
|
||||
}
|
||||
|
||||
private void CloseUI()
|
||||
{
|
||||
Window.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Function for clicking one of the stored entity buttons in the UI, tells server to remove that entity
|
||||
/// </summary>
|
||||
/// <param name="entityuid"></param>
|
||||
private void Interact(EntityUid entityuid)
|
||||
{
|
||||
SendNetworkMessage(new RemoveEntityMessage(entityuid));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GUI class for client storage component
|
||||
/// </summary>
|
||||
private class StorageWindow : SS14Window
|
||||
{
|
||||
private Control VSplitContainer;
|
||||
private VBoxContainer EntityList;
|
||||
private Label Information;
|
||||
public ClientStorageComponent StorageEntity;
|
||||
|
||||
public StorageWindow()
|
||||
{
|
||||
Size = new Vector2(180.0f, 320.0f);
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
Title = "Storage Item";
|
||||
RectClipContent = true;
|
||||
|
||||
VSplitContainer = new VBoxContainer("VSplitContainer");
|
||||
Information = new Label("Information")
|
||||
{
|
||||
Text = "Items: 0 Volume: 0/0 Stuff",
|
||||
SizeFlagsVertical = SizeFlags.ShrinkCenter
|
||||
};
|
||||
VSplitContainer.AddChild(Information);
|
||||
|
||||
var listScrollContainer = new ScrollContainer("ListScrollContainer")
|
||||
{
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
HScrollEnabled = true,
|
||||
VScrollEnabled = true
|
||||
};
|
||||
EntityList = new VBoxContainer("EntityList")
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand
|
||||
};
|
||||
listScrollContainer.AddChild(EntityList);
|
||||
VSplitContainer.AddChild(listScrollContainer);
|
||||
Contents.AddChild(VSplitContainer);
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
StorageEntity.SendNetworkMessage(new CloseStorageUIMessage());
|
||||
base.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loops through stored entities creating buttons for each, updates information labels
|
||||
/// </summary>
|
||||
public void BuildEntityList()
|
||||
{
|
||||
EntityList.DisposeAllChildren();
|
||||
|
||||
var storagelist = StorageEntity.StoredEntities;
|
||||
|
||||
foreach (var entityuid in storagelist)
|
||||
{
|
||||
var entity = IoCManager.Resolve<IEntityManager>().GetEntity(entityuid.Key);
|
||||
|
||||
var button = new EntityButton()
|
||||
{
|
||||
EntityuID = entityuid.Key
|
||||
};
|
||||
button.ActualButton.OnToggled += OnItemButtonToggled;
|
||||
//Name and Size labels set
|
||||
button.EntityName.Text = entity.Name;
|
||||
button.EntitySize.Text = string.Format("{0}", entityuid.Value);
|
||||
|
||||
//Gets entity sprite and assigns it to button texture
|
||||
if (entity.TryGetComponent(out ISpriteComponent sprite))
|
||||
{
|
||||
button.EntitySpriteView.Sprite = sprite;
|
||||
}
|
||||
|
||||
EntityList.AddChild(button);
|
||||
}
|
||||
|
||||
//Sets information about entire storage container current capacity
|
||||
if (StorageEntity.StorageCapacityMax != 0)
|
||||
{
|
||||
Information.Text = String.Format("Items: {0}, Stored: {1}/{2}", storagelist.Count, StorageEntity.StorageSizeUsed, StorageEntity.StorageCapacityMax);
|
||||
}
|
||||
else
|
||||
{
|
||||
Information.Text = String.Format("Items: {0}", storagelist.Count);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Function assigned to button toggle which removes the entity from storage
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
private void OnItemButtonToggled(BaseButton.ButtonToggledEventArgs args)
|
||||
{
|
||||
var control = (EntityButton)args.Button.Parent;
|
||||
args.Button.Pressed = false;
|
||||
StorageEntity.Interact(control.EntityuID);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Button created for each entity that represents that item in the storage UI, with a texture, and name and size label
|
||||
/// </summary>
|
||||
private class EntityButton : PanelContainer
|
||||
{
|
||||
public EntityUid EntityuID { get; set; }
|
||||
public Button ActualButton { get; private set; }
|
||||
public SpriteView EntitySpriteView { get; private set; }
|
||||
public Control EntityControl { get; private set; }
|
||||
public Label EntityName { get; private set; }
|
||||
public Label EntitySize { get; private set; }
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
ActualButton = new Button("Button")
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
ToggleMode = true,
|
||||
MouseFilter = MouseFilterMode.Stop
|
||||
};
|
||||
AddChild(ActualButton);
|
||||
|
||||
var hBoxContainer = new HBoxContainer("HBoxContainer") {MouseFilter = MouseFilterMode.Ignore};
|
||||
EntitySpriteView = new SpriteView("SpriteView")
|
||||
{
|
||||
CustomMinimumSize = new Vector2(32.0f, 32.0f), MouseFilter = MouseFilterMode.Ignore
|
||||
};
|
||||
EntityName = new Label("Name")
|
||||
{
|
||||
SizeFlagsVertical = SizeFlags.ShrinkCenter,
|
||||
Text = "Backpack",
|
||||
MouseFilter = MouseFilterMode.Ignore
|
||||
};
|
||||
hBoxContainer.AddChild(EntitySpriteView);
|
||||
hBoxContainer.AddChild(EntityName);
|
||||
|
||||
EntityControl = new Control("Control")
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand, MouseFilter = MouseFilterMode.Ignore
|
||||
};
|
||||
EntitySize = new Label("Size")
|
||||
{
|
||||
SizeFlagsVertical = SizeFlags.ShrinkCenter,
|
||||
Text = "Size 6",
|
||||
Align = Label.AlignMode.Right,
|
||||
AnchorLeft = 1.0f,
|
||||
AnchorRight = 1.0f,
|
||||
AnchorBottom = 0.5f,
|
||||
AnchorTop = 0.5f,
|
||||
MarginLeft = -38.0f,
|
||||
MarginTop = -7.0f,
|
||||
MarginRight = -5.0f,
|
||||
MarginBottom = 7.0f
|
||||
};
|
||||
|
||||
EntityControl.AddChild(EntitySize);
|
||||
hBoxContainer.AddChild(EntityControl);
|
||||
AddChild(hBoxContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using Content.Shared.GameObjects.Components.Storage;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Storage
|
||||
{
|
||||
public sealed class StorageVisualizer2D : AppearanceVisualizer
|
||||
{
|
||||
private string _stateOpen;
|
||||
private string _stateClosed;
|
||||
|
||||
public override void LoadData(YamlMappingNode node)
|
||||
{
|
||||
base.LoadData(node);
|
||||
|
||||
if (node.TryGetNode("state_open", out var child))
|
||||
{
|
||||
_stateOpen = child.AsString();
|
||||
}
|
||||
|
||||
if (node.TryGetNode("state_closed", out child))
|
||||
{
|
||||
_stateClosed = child.AsString();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
if (!component.Owner.TryGetComponent(out ISpriteComponent sprite))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
component.TryGetData(StorageVisuals.Open, out bool open);
|
||||
sprite.LayerSetState(StorageVisualLayers.Door, open ? _stateOpen : _stateClosed);
|
||||
}
|
||||
}
|
||||
|
||||
public enum StorageVisualLayers
|
||||
{
|
||||
Door
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.Transform;
|
||||
|
||||
namespace Content.Client.GameObjects.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple component that automatically hides the sibling <see cref="ISpriteComponent"/> when the tile it's on
|
||||
/// is not a sub floor (plating).
|
||||
/// </summary>
|
||||
/// <seealso cref="ContentTileDefinition.IsSubFloor"/>
|
||||
[RegisterComponent]
|
||||
public sealed class SubFloorHideComponent : Component
|
||||
{
|
||||
private SnapGridComponent _snapGridComponent;
|
||||
|
||||
public override string Name => "SubFloorHide";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_snapGridComponent = Owner.GetComponent<SnapGridComponent>();
|
||||
}
|
||||
|
||||
public override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
_snapGridComponent.OnPositionChanged += SnapGridOnPositionChanged;
|
||||
Owner.EntityManager.RaiseEvent(Owner, new SubFloorHideDirtyEvent());
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
_snapGridComponent.OnPositionChanged -= SnapGridOnPositionChanged;
|
||||
}
|
||||
|
||||
private void SnapGridOnPositionChanged()
|
||||
{
|
||||
Owner.EntityManager.RaiseEvent(Owner, new SubFloorHideDirtyEvent());
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SubFloorHideDirtyEvent : EntitySystemMessage
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Triggers;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.GameObjects.Components.Animations;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Doors
|
||||
{
|
||||
public class TimerTriggerVisualizer2D : AppearanceVisualizer
|
||||
{
|
||||
private const string AnimationKey = "priming_animation";
|
||||
|
||||
private Animation PrimingAnimation;
|
||||
|
||||
public override void LoadData(YamlMappingNode node)
|
||||
{
|
||||
base.LoadData(node);
|
||||
|
||||
var countdownSound = node.GetNode("countdown_sound").AsString();
|
||||
|
||||
|
||||
PrimingAnimation = new Animation { Length = TimeSpan.MaxValue };
|
||||
{
|
||||
var flick = new AnimationTrackSpriteFlick();
|
||||
PrimingAnimation.AnimationTracks.Add(flick);
|
||||
flick.LayerKey = TriggerVisualLayers.Base;
|
||||
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("primed", 0f));
|
||||
|
||||
var sound = new AnimationTrackPlaySound();
|
||||
PrimingAnimation.AnimationTracks.Add(sound);
|
||||
sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(countdownSound, 0));
|
||||
}
|
||||
}
|
||||
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
if (!entity.HasComponent<AnimationPlayerComponent>())
|
||||
{
|
||||
entity.AddComponent<AnimationPlayerComponent>();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
var animPlayer = component.Owner.GetComponent<AnimationPlayerComponent>();
|
||||
if (!component.TryGetData(TriggerVisuals.VisualState, out TriggerVisualState state))
|
||||
{
|
||||
state = TriggerVisualState.Unprimed;
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case TriggerVisualState.Primed:
|
||||
if (!animPlayer.HasRunningAnimation(AnimationKey))
|
||||
{
|
||||
animPlayer.Play(PrimingAnimation, AnimationKey);
|
||||
}
|
||||
break;
|
||||
case TriggerVisualState.Unprimed:
|
||||
sprite.LayerSetState(0, "icon");
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
public enum TriggerVisualLayers
|
||||
{
|
||||
Base
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using Content.Shared.GameObjects.Components.Weapons.Ranged;
|
||||
using Content.Shared.Utility;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Weapons.Ranged
|
||||
{
|
||||
public sealed class BallisticMagazineVisualizer2D : AppearanceVisualizer
|
||||
{
|
||||
private string _baseState;
|
||||
private int _steps;
|
||||
|
||||
public override void LoadData(YamlMappingNode node)
|
||||
{
|
||||
base.LoadData(node);
|
||||
|
||||
_baseState = node.GetNode("base_state").AsString();
|
||||
_steps = node.GetNode("steps").AsInt();
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
|
||||
if (!component.TryGetData(BallisticMagazineVisuals.AmmoCapacity, out int capacity))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!component.TryGetData(BallisticMagazineVisuals.AmmoLeft, out int current))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var step = ContentHelpers.RoundToLevels(current, capacity, _steps);
|
||||
|
||||
sprite.LayerSetState(0, $"{_baseState}-{step}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using Content.Shared.GameObjects.Components.Weapons.Ranged;
|
||||
using Content.Shared.Utility;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Weapons.Ranged
|
||||
{
|
||||
public sealed class BallisticMagazineWeaponVisualizer2D : AppearanceVisualizer
|
||||
{
|
||||
private string _baseState;
|
||||
private int _steps;
|
||||
|
||||
public override void LoadData(YamlMappingNode node)
|
||||
{
|
||||
base.LoadData(node);
|
||||
|
||||
_baseState = node.GetNode("base_state").AsString();
|
||||
_steps = node.GetNode("steps").AsInt();
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
|
||||
component.TryGetData(BallisticMagazineWeaponVisuals.MagazineLoaded, out bool loaded);
|
||||
|
||||
if (loaded)
|
||||
{
|
||||
if (!component.TryGetData(BallisticMagazineWeaponVisuals.AmmoCapacity, out int capacity))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!component.TryGetData(BallisticMagazineWeaponVisuals.AmmoLeft, out int current))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var step = ContentHelpers.RoundToLevels(current, capacity, _steps);
|
||||
|
||||
sprite.LayerSetState(0, $"{_baseState}-{step}");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetState(0, _baseState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Weapons.Ranged;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.Timing;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Weapons.Ranged
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class ClientRangedWeaponComponent : SharedRangedWeaponComponent
|
||||
{
|
||||
private TimeSpan _lastFireTime;
|
||||
private int _tick;
|
||||
|
||||
public void TryFire(GridCoordinates worldPos)
|
||||
{
|
||||
var curTime = IoCManager.Resolve<IGameTiming>().CurTime;
|
||||
var span = curTime - _lastFireTime;
|
||||
if (span.TotalSeconds < 1 / FireRate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_lastFireTime = curTime;
|
||||
SendNetworkMessage(new FireMessage(worldPos, _tick++));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using Content.Shared.GameObjects.Components.Power;
|
||||
using Content.Shared.Utility;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Power
|
||||
{
|
||||
public class HitscanWeaponVisualizer2D : AppearanceVisualizer
|
||||
{
|
||||
private string _prefix;
|
||||
|
||||
public override void LoadData(YamlMappingNode node)
|
||||
{
|
||||
base.LoadData(node);
|
||||
|
||||
_prefix = node.GetNode("prefix").AsString();
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
if (component.TryGetData(PowerCellVisuals.ChargeLevel, out float fraction))
|
||||
{
|
||||
sprite.LayerSetState(0, $"{_prefix}_{ContentHelpers.RoundToLevels(fraction, 1, 5) * 25}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
92
Content.Client/GameObjects/Components/WindowComponent.cs
Normal file
92
Content.Client/GameObjects/Components/WindowComponent.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using Content.Client.GameObjects.EntitySystems;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.Transform;
|
||||
using Robust.Shared.Serialization;
|
||||
using static Content.Client.GameObjects.Components.IconSmoothing.IconSmoothComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class WindowComponent : Component
|
||||
{
|
||||
public override string Name => "Window";
|
||||
|
||||
private string _stateBase;
|
||||
private ISpriteComponent _sprite;
|
||||
private SnapGridComponent _snapGrid;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_sprite = Owner.GetComponent<ISpriteComponent>();
|
||||
_snapGrid = Owner.GetComponent<SnapGridComponent>();
|
||||
}
|
||||
|
||||
public override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
_snapGrid.OnPositionChanged += SnapGridOnPositionChanged;
|
||||
Owner.EntityManager.RaiseEvent(Owner, new WindowSmoothDirtyEvent());
|
||||
|
||||
var state0 = $"{_stateBase}0";
|
||||
_sprite.LayerMapSet(CornerLayers.SE, _sprite.AddLayerState(state0));
|
||||
_sprite.LayerSetDirOffset(CornerLayers.SE, SpriteComponent.DirectionOffset.None);
|
||||
_sprite.LayerMapSet(CornerLayers.NE, _sprite.AddLayerState(state0));
|
||||
_sprite.LayerSetDirOffset(CornerLayers.NE, SpriteComponent.DirectionOffset.CounterClockwise);
|
||||
_sprite.LayerMapSet(CornerLayers.NW, _sprite.AddLayerState(state0));
|
||||
_sprite.LayerSetDirOffset(CornerLayers.NW, SpriteComponent.DirectionOffset.Flip);
|
||||
_sprite.LayerMapSet(CornerLayers.SW, _sprite.AddLayerState(state0));
|
||||
_sprite.LayerSetDirOffset(CornerLayers.SW, SpriteComponent.DirectionOffset.Clockwise);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
_snapGrid.OnPositionChanged -= SnapGridOnPositionChanged;
|
||||
|
||||
base.Shutdown();
|
||||
}
|
||||
|
||||
private void SnapGridOnPositionChanged()
|
||||
{
|
||||
Owner.EntityManager.RaiseEvent(Owner, new WindowSmoothDirtyEvent());
|
||||
}
|
||||
|
||||
public void UpdateSprite()
|
||||
{
|
||||
var lowWall = FindLowWall();
|
||||
if (lowWall == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_sprite.LayerSetState(CornerLayers.NE, $"{_stateBase}{(int) lowWall.LastCornerNE}");
|
||||
_sprite.LayerSetState(CornerLayers.SE, $"{_stateBase}{(int) lowWall.LastCornerSE}");
|
||||
_sprite.LayerSetState(CornerLayers.SW, $"{_stateBase}{(int) lowWall.LastCornerSW}");
|
||||
_sprite.LayerSetState(CornerLayers.NW, $"{_stateBase}{(int) lowWall.LastCornerNW}");
|
||||
}
|
||||
|
||||
private LowWallComponent FindLowWall()
|
||||
{
|
||||
foreach (var entity in _snapGrid.GetLocal())
|
||||
{
|
||||
if (entity.TryGetComponent(out LowWallComponent lowWall))
|
||||
{
|
||||
return lowWall;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(ref _stateBase, "base", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Content.Client.GameObjects.Components.Mobs;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
|
||||
namespace Content.Client.GameObjects.EntitySystems
|
||||
{
|
||||
public sealed class CameraRecoilSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
EntityQuery = new TypeEntityQuery(typeof(CameraRecoilComponent));
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
base.FrameUpdate(frameTime);
|
||||
|
||||
foreach (var entity in RelevantEntities)
|
||||
{
|
||||
var recoil = entity.GetComponent<CameraRecoilComponent>();
|
||||
recoil.FrameUpdate(frameTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using Content.Client.GameObjects.Components.Actor;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.GameObjects.EntitySystems;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Client.GameObjects.EntitySystems
|
||||
{
|
||||
public sealed class CharacterInterfaceSystem : EntitySystem
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IGameHud _gameHud;
|
||||
[Dependency] private readonly IPlayerManager _playerManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
var inputSys = EntitySystemManager.GetEntitySystem<InputSystem>();
|
||||
inputSys.BindMap.BindFunction(ContentKeyFunctions.OpenCharacterMenu,
|
||||
InputCmdHandler.FromDelegate(s => HandleOpenCharacterMenu()));
|
||||
}
|
||||
|
||||
private void HandleOpenCharacterMenu()
|
||||
{
|
||||
if (_playerManager.LocalPlayer.ControlledEntity == null
|
||||
|| !_playerManager.LocalPlayer.ControlledEntity.TryGetComponent(out CharacterInterface characterInterface))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var menu = characterInterface.Window;
|
||||
|
||||
if (menu == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (menu.IsOpen)
|
||||
{
|
||||
if (menu.IsAtFront())
|
||||
{
|
||||
_setOpenValue(menu, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
menu.MoveToFront();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_setOpenValue(menu, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void _setOpenValue(SS14Window menu, bool value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
_gameHud.CharacterButtonDown = true;
|
||||
menu.OpenCentered();
|
||||
}
|
||||
else
|
||||
{
|
||||
_gameHud.CharacterButtonDown = false;
|
||||
menu.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.GameObjects.EntitySystems;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Client.GameObjects.EntitySystems
|
||||
{
|
||||
public sealed class ClientInventorySystem : EntitySystem
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IGameHud _gameHud;
|
||||
[Dependency] private readonly IPlayerManager _playerManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
var inputSys = EntitySystemManager.GetEntitySystem<InputSystem>();
|
||||
inputSys.BindMap.BindFunction(ContentKeyFunctions.OpenInventoryMenu,
|
||||
InputCmdHandler.FromDelegate(s => HandleOpenInventoryMenu()));
|
||||
}
|
||||
|
||||
private void HandleOpenInventoryMenu()
|
||||
{
|
||||
if (_playerManager.LocalPlayer.ControlledEntity == null
|
||||
|| !_playerManager.LocalPlayer.ControlledEntity.TryGetComponent(out ClientInventoryComponent clientInventory))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var menu = clientInventory.InterfaceController.Window;
|
||||
|
||||
if (menu.IsOpen)
|
||||
{
|
||||
if (menu.IsAtFront())
|
||||
{
|
||||
_setOpenValue(menu, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
menu.MoveToFront();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_setOpenValue(menu, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void _setOpenValue(SS14Window menu, bool value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
_gameHud.InventoryButtonDown = true;
|
||||
menu.OpenCentered();
|
||||
}
|
||||
else
|
||||
{
|
||||
_gameHud.InventoryButtonDown = false;
|
||||
menu.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Content.Client.GameObjects.EntitySystems
|
||||
{
|
||||
public class ClientNotifySystem
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using Content.Client.Construction;
|
||||
using Content.Client.GameObjects.Components.Construction;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.GameObjects.EntitySystems;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Client.GameObjects.EntitySystems
|
||||
{
|
||||
public sealed class ConstructorSystem : EntitySystem
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IGameHud _gameHud;
|
||||
[Dependency] private readonly IPlayerManager _playerManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
var inputSys = EntitySystemManager.GetEntitySystem<InputSystem>();
|
||||
inputSys.BindMap.BindFunction(ContentKeyFunctions.OpenCraftingMenu,
|
||||
new PointerInputCmdHandler(HandleOpenCraftingMenu));
|
||||
}
|
||||
|
||||
private void HandleOpenCraftingMenu(in PointerInputCmdHandler.PointerInputCmdArgs args)
|
||||
{
|
||||
if (_playerManager.LocalPlayer.ControlledEntity == null
|
||||
|| !_playerManager.LocalPlayer.ControlledEntity.TryGetComponent(out ConstructorComponent constructor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var menu = constructor.ConstructionMenu;
|
||||
|
||||
if (menu.IsOpen)
|
||||
{
|
||||
if (menu.IsAtFront())
|
||||
{
|
||||
_setOpenValue(menu, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
menu.MoveToFront();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_setOpenValue(menu, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void _setOpenValue(ConstructionMenu menu, bool value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
_gameHud.CraftingButtonDown = true;
|
||||
menu.OpenCentered();
|
||||
}
|
||||
else
|
||||
{
|
||||
_gameHud.CraftingButtonDown = false;
|
||||
menu.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
149
Content.Client/GameObjects/EntitySystems/ExamineSystem.cs
Normal file
149
Content.Client/GameObjects/EntitySystems/ExamineSystem.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Shared.GameObjects.EntitySystemMessages;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Input;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects.EntitySystems;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Client.Interfaces.Input;
|
||||
using Robust.Client.Interfaces.UserInterface;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Players;
|
||||
|
||||
namespace Content.Client.GameObjects.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
internal sealed class ExamineSystem : ExamineSystemShared
|
||||
{
|
||||
public const string StyleClassEntityTooltip = "entity-tooltip";
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private IInputManager _inputManager;
|
||||
[Dependency] private IUserInterfaceManager _userInterfaceManager;
|
||||
[Dependency] private IEntityManager _entityManager;
|
||||
[Dependency] private IPlayerManager _playerManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
private Popup _examineTooltipOpen;
|
||||
private CancellationTokenSource _requestCancelTokenSource;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
var inputSys = EntitySystemManager.GetEntitySystem<InputSystem>();
|
||||
inputSys.BindMap.BindFunction(ContentKeyFunctions.ExamineEntity, new PointerInputCmdHandler(HandleExamine));
|
||||
}
|
||||
|
||||
public override void RegisterMessageTypes()
|
||||
{
|
||||
base.RegisterMessageTypes();
|
||||
|
||||
RegisterMessageType<ExamineSystemMessages.ExamineInfoResponseMessage>();
|
||||
}
|
||||
|
||||
private void HandleExamine(ICommonSession session, GridCoordinates coords, EntityUid uid)
|
||||
{
|
||||
if (!uid.IsValid() || !_entityManager.TryGetEntity(uid, out var examined))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var playerEntity = _playerManager.LocalPlayer.ControlledEntity;
|
||||
|
||||
if (playerEntity == null || !CanExamine(playerEntity, examined))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DoExamine(examined);
|
||||
}
|
||||
|
||||
public async void DoExamine(IEntity entity)
|
||||
{
|
||||
CloseTooltip();
|
||||
|
||||
var mousePos = _inputManager.MouseScreenPosition;
|
||||
|
||||
// Actually open the tooltip.
|
||||
_examineTooltipOpen = new Popup();
|
||||
_userInterfaceManager.StateRoot.AddChild(_examineTooltipOpen);
|
||||
var panel = new PanelContainer();
|
||||
panel.AddStyleClass(StyleClassEntityTooltip);
|
||||
panel.ModulateSelfOverride = Color.LightGray.WithAlpha(0.90f);
|
||||
_examineTooltipOpen.AddChild(panel);
|
||||
panel.SetAnchorAndMarginPreset(Control.LayoutPreset.Wide);
|
||||
var vBox = new VBoxContainer();
|
||||
panel.AddChild(vBox);
|
||||
var hBox = new HBoxContainer { SeparationOverride = 5};
|
||||
vBox.AddChild(hBox);
|
||||
if (entity.TryGetComponent(out ISpriteComponent sprite))
|
||||
{
|
||||
hBox.AddChild(new SpriteView {Sprite = sprite});
|
||||
}
|
||||
|
||||
hBox.AddChild(new Label
|
||||
{
|
||||
Text = entity.Name,
|
||||
SizeFlagsHorizontal = Control.SizeFlags.FillExpand,
|
||||
});
|
||||
|
||||
const float minWidth = 300;
|
||||
var size = Vector2.ComponentMax((minWidth, 0), panel.CombinedMinimumSize);
|
||||
_examineTooltipOpen.Open(UIBox2.FromDimensions(mousePos, size));
|
||||
|
||||
if (entity.Uid.IsClientSide())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Ask server for extra examine info.
|
||||
RaiseNetworkEvent(new ExamineSystemMessages.RequestExamineInfoMessage(entity.Uid));
|
||||
|
||||
ExamineSystemMessages.ExamineInfoResponseMessage response;
|
||||
try
|
||||
{
|
||||
_requestCancelTokenSource = new CancellationTokenSource();
|
||||
response =
|
||||
await AwaitNetMessage<ExamineSystemMessages.ExamineInfoResponseMessage>(_requestCancelTokenSource
|
||||
.Token);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_requestCancelTokenSource = null;
|
||||
}
|
||||
|
||||
var richLabel = new RichTextLabel();
|
||||
richLabel.SetMessage(response.Message);
|
||||
vBox.AddChild(richLabel);
|
||||
}
|
||||
|
||||
public void CloseTooltip()
|
||||
{
|
||||
if (_examineTooltipOpen != null)
|
||||
{
|
||||
_examineTooltipOpen.Dispose();
|
||||
_examineTooltipOpen = null;
|
||||
}
|
||||
|
||||
if (_requestCancelTokenSource != null)
|
||||
{
|
||||
_requestCancelTokenSource.Cancel();
|
||||
_requestCancelTokenSource = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
155
Content.Client/GameObjects/EntitySystems/IconSmoothSystem.cs
Normal file
155
Content.Client/GameObjects/EntitySystems/IconSmoothSystem.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Client.GameObjects.Components.IconSmoothing;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.Transform;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.GameObjects.EntitySystems
|
||||
{
|
||||
/// <summary>
|
||||
/// Entity system implementing the logic for <see cref="IconSmoothComponent"/>
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
internal sealed class IconSmoothSystem : EntitySystem
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IMapManager _mapManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
private readonly Queue<IEntity> _dirtyEntities = new Queue<IEntity>();
|
||||
|
||||
private int _generation;
|
||||
|
||||
public override void SubscribeEvents()
|
||||
{
|
||||
base.SubscribeEvents();
|
||||
|
||||
SubscribeEvent<IconSmoothDirtyEvent>(HandleDirtyEvent);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
base.FrameUpdate(frameTime);
|
||||
|
||||
if (_dirtyEntities.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_generation += 1;
|
||||
|
||||
// Performance: This could be spread over multiple updates, or made parallel.
|
||||
while (_dirtyEntities.Count > 0)
|
||||
{
|
||||
CalculateNewSprite(_dirtyEntities.Dequeue());
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDirtyEvent(object sender, IconSmoothDirtyEvent ev)
|
||||
{
|
||||
// Yes, we updates ALL smoothing entities surrounding us even if they would never smooth with us.
|
||||
// This is simpler to implement. If you want to optimize it be my guest.
|
||||
if (sender is IEntity senderEnt && senderEnt.IsValid() &&
|
||||
senderEnt.HasComponent<IconSmoothComponent>())
|
||||
{
|
||||
var snapGrid = senderEnt.GetComponent<SnapGridComponent>();
|
||||
|
||||
_dirtyEntities.Enqueue(senderEnt);
|
||||
AddValidEntities(snapGrid.GetInDir(Direction.North));
|
||||
AddValidEntities(snapGrid.GetInDir(Direction.South));
|
||||
AddValidEntities(snapGrid.GetInDir(Direction.East));
|
||||
AddValidEntities(snapGrid.GetInDir(Direction.West));
|
||||
if (ev.Mode == IconSmoothingMode.Corners)
|
||||
{
|
||||
|
||||
AddValidEntities(snapGrid.GetInDir(Direction.NorthEast));
|
||||
AddValidEntities(snapGrid.GetInDir(Direction.SouthEast));
|
||||
AddValidEntities(snapGrid.GetInDir(Direction.SouthWest));
|
||||
AddValidEntities(snapGrid.GetInDir(Direction.NorthWest));
|
||||
}
|
||||
}
|
||||
else if (ev.LastPosition.HasValue)
|
||||
{
|
||||
// Entity is no longer valid, update around the last position it was at.
|
||||
var grid = _mapManager.GetGrid(ev.LastPosition.Value.grid);
|
||||
var pos = ev.LastPosition.Value.pos;
|
||||
|
||||
AddValidEntities(grid.GetSnapGridCell(pos + new MapIndices(1, 0), ev.Offset));
|
||||
AddValidEntities(grid.GetSnapGridCell(pos + new MapIndices(-1, 0), ev.Offset));
|
||||
AddValidEntities(grid.GetSnapGridCell(pos + new MapIndices(0, 1), ev.Offset));
|
||||
AddValidEntities(grid.GetSnapGridCell(pos + new MapIndices(0, -1), ev.Offset));
|
||||
if (ev.Mode == IconSmoothingMode.Corners)
|
||||
{
|
||||
AddValidEntities(grid.GetSnapGridCell(pos + new MapIndices(1, 1), ev.Offset));
|
||||
AddValidEntities(grid.GetSnapGridCell(pos + new MapIndices(-1, -1), ev.Offset));
|
||||
AddValidEntities(grid.GetSnapGridCell(pos + new MapIndices(-1, 1), ev.Offset));
|
||||
AddValidEntities(grid.GetSnapGridCell(pos + new MapIndices(1, -1), ev.Offset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddValidEntities(IEnumerable<IEntity> candidates)
|
||||
{
|
||||
foreach (var entity in candidates)
|
||||
{
|
||||
if (entity.HasComponent<IconSmoothComponent>())
|
||||
{
|
||||
_dirtyEntities.Enqueue(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddValidEntities(IEnumerable<IComponent> candidates)
|
||||
{
|
||||
AddValidEntities(candidates.Select(c => c.Owner));
|
||||
}
|
||||
|
||||
private void CalculateNewSprite(IEntity entity)
|
||||
{
|
||||
// The generation check prevents updating an entity multiple times per tick.
|
||||
// As it stands now, it's totally possible for something to get queued twice.
|
||||
// Generation on the component is set after an update so we can cull updates that happened this generation.
|
||||
if (!entity.IsValid()
|
||||
|| !entity.TryGetComponent(out IconSmoothComponent smoothing)
|
||||
|| smoothing.UpdateGeneration == _generation)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
smoothing.CalculateNewSprite();
|
||||
|
||||
smoothing.UpdateGeneration = _generation;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event raised by a <see cref="IconSmoothComponent"/> when it needs to be recalculated.
|
||||
/// </summary>
|
||||
public sealed class IconSmoothDirtyEvent : EntitySystemMessage
|
||||
{
|
||||
public IconSmoothDirtyEvent((GridId grid, MapIndices pos)? lastPosition, SnapGridOffset offset, IconSmoothingMode mode)
|
||||
{
|
||||
LastPosition = lastPosition;
|
||||
Offset = offset;
|
||||
Mode = mode;
|
||||
}
|
||||
|
||||
public (GridId grid, MapIndices pos)? LastPosition { get; }
|
||||
public SnapGridOffset Offset { get; }
|
||||
public IconSmoothingMode Mode { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using Content.Client.GameObjects.Components.Weapons.Ranged;
|
||||
using Content.Client.Interfaces.GameObjects;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.GameObjects.EntitySystems;
|
||||
using Robust.Client.Interfaces.Graphics.ClientEye;
|
||||
using Robust.Client.Interfaces.Input;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Client.GameObjects.EntitySystems
|
||||
{
|
||||
public class RangedWeaponSystem : EntitySystem
|
||||
{
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IPlayerManager _playerManager;
|
||||
[Dependency] private readonly IEyeManager _eyeManager;
|
||||
[Dependency] private readonly IInputManager _inputManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
private InputSystem _inputSystem;
|
||||
private bool _isFirstShot;
|
||||
private bool _blocked;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
IoCManager.InjectDependencies(this);
|
||||
_inputSystem = EntitySystemManager.GetEntitySystem<InputSystem>();
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var canFireSemi = _isFirstShot;
|
||||
var state = _inputSystem.CmdStates.GetState(ContentKeyFunctions.UseItemInHand);
|
||||
if (state != BoundKeyState.Down)
|
||||
{
|
||||
_isFirstShot = true;
|
||||
_blocked = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_isFirstShot = false;
|
||||
|
||||
var entity = _playerManager.LocalPlayer.ControlledEntity;
|
||||
if (entity == null || !entity.TryGetComponent(out IHandsComponent hands))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var held = hands.ActiveHand;
|
||||
if (held == null || !held.TryGetComponent(out ClientRangedWeaponComponent weapon))
|
||||
{
|
||||
_blocked = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_blocked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var worldPos = _eyeManager.ScreenToWorld(_inputManager.MouseScreenPosition);
|
||||
|
||||
if (weapon.Automatic || canFireSemi)
|
||||
{
|
||||
weapon.TryFire(worldPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using Content.Client.GameObjects.Components;
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects.Components.Transform;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Client.GameObjects.EntitySystems
|
||||
{
|
||||
/// <summary>
|
||||
/// Entity system backing <see cref="SubFloorHideComponent"/>.
|
||||
/// </summary>
|
||||
internal sealed class SubFloorHideSystem : EntitySystem
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IMapManager _mapManager;
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_mapManager.GridChanged += MapManagerOnGridChanged;
|
||||
_mapManager.TileChanged += MapManagerOnTileChanged;
|
||||
|
||||
SubscribeEvent<SubFloorHideDirtyEvent>(HandleDirtyEvent);
|
||||
}
|
||||
|
||||
private void HandleDirtyEvent(object sender, SubFloorHideDirtyEvent ev)
|
||||
{
|
||||
if (!(sender is IEntity senderEnt))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sprite = senderEnt.GetComponent<ISpriteComponent>();
|
||||
var grid = _mapManager.GetGrid(senderEnt.Transform.GridID);
|
||||
var position = senderEnt.Transform.GridPosition;
|
||||
var tileRef = grid.GetTileRef(position);
|
||||
var tileDef = (ContentTileDefinition) _tileDefinitionManager[tileRef.Tile.TypeId];
|
||||
sprite.Visible = tileDef.IsSubFloor;
|
||||
}
|
||||
|
||||
private void MapManagerOnTileChanged(object sender, TileChangedEventArgs e)
|
||||
{
|
||||
UpdateTile(_mapManager.GetGrid(e.NewTile.GridIndex), e.NewTile.GridIndices);
|
||||
}
|
||||
|
||||
private void MapManagerOnGridChanged(object sender, GridChangedEventArgs e)
|
||||
{
|
||||
foreach (var modified in e.Modified)
|
||||
{
|
||||
UpdateTile(e.Grid, modified.position);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTile(IMapGrid grid, MapIndices position)
|
||||
{
|
||||
var tile = grid.GetTileRef(position);
|
||||
var tileDef = (ContentTileDefinition) _tileDefinitionManager[tile.Tile.TypeId];
|
||||
foreach (var snapGridComponent in grid.GetSnapGridCell(position, SnapGridOffset.Center))
|
||||
{
|
||||
var entity = snapGridComponent.Owner;
|
||||
if (!entity.HasComponent<SubFloorHideComponent>() ||
|
||||
!entity.TryGetComponent(out ISpriteComponent spriteComponent))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
spriteComponent.Visible = tileDef.IsSubFloor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
235
Content.Client/GameObjects/EntitySystems/VerbSystem.cs
Normal file
235
Content.Client/GameObjects/EntitySystems/VerbSystem.cs
Normal file
@@ -0,0 +1,235 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.EntitySystemMessages;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.GameObjects.EntitySystems;
|
||||
using Robust.Client.Interfaces.Input;
|
||||
using Robust.Client.Interfaces.State;
|
||||
using Robust.Client.Interfaces.UserInterface;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.State.States;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.GameObjects.EntitySystems
|
||||
{
|
||||
public class VerbSystem : EntitySystem
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IStateManager _stateManager;
|
||||
[Dependency] private readonly IEntityManager _entityManager;
|
||||
[Dependency] private readonly IPlayerManager _playerManager;
|
||||
[Dependency] private readonly IInputManager _inputManager;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
private Popup _currentPopup;
|
||||
private EntityUid _currentEntity;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
var input = EntitySystemManager.GetEntitySystem<InputSystem>();
|
||||
input.BindMap.BindFunction(ContentKeyFunctions.OpenContextMenu,
|
||||
new PointerInputCmdHandler(OnOpenContextMenu));
|
||||
}
|
||||
|
||||
public override void RegisterMessageTypes()
|
||||
{
|
||||
base.RegisterMessageTypes();
|
||||
|
||||
RegisterMessageType<VerbSystemMessages.VerbsResponseMessage>();
|
||||
}
|
||||
|
||||
public void OpenContextMenu(IEntity entity, ScreenCoordinates screenCoordinates)
|
||||
{
|
||||
if (_currentPopup != null)
|
||||
{
|
||||
_closeContextMenu();
|
||||
}
|
||||
|
||||
_currentEntity = entity.Uid;
|
||||
_currentPopup = new Popup();
|
||||
_currentPopup.UserInterfaceManager.StateRoot.AddChild(_currentPopup);
|
||||
_currentPopup.OnPopupHide += _closeContextMenu;
|
||||
var vBox = new VBoxContainer("ButtonBox");
|
||||
_currentPopup.AddChild(vBox);
|
||||
|
||||
vBox.AddChild(new Label {Text = "Waiting on Server..."});
|
||||
RaiseNetworkEvent(new VerbSystemMessages.RequestVerbsMessage(_currentEntity));
|
||||
|
||||
var size = vBox.CombinedMinimumSize;
|
||||
var box = UIBox2.FromDimensions(screenCoordinates.Position, size);
|
||||
_currentPopup.Open(box);
|
||||
}
|
||||
|
||||
private void OnOpenContextMenu(in PointerInputCmdHandler.PointerInputCmdArgs args)
|
||||
{
|
||||
if (_currentPopup != null)
|
||||
{
|
||||
_closeContextMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(_stateManager.CurrentState is GameScreen gameScreen))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var entities = gameScreen.GetEntitiesUnderPosition(args.Coordinates);
|
||||
|
||||
_currentPopup = new Popup();
|
||||
_currentPopup.OnPopupHide += _closeContextMenu;
|
||||
var vBox = new VBoxContainer("ButtonBox");
|
||||
_currentPopup.AddChild(vBox);
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
var button = new Button {Text = entity.Name};
|
||||
vBox.AddChild(button);
|
||||
button.OnPressed += _ => OnContextButtonPressed(entity);
|
||||
}
|
||||
|
||||
_currentPopup.UserInterfaceManager.StateRoot.AddChild(_currentPopup);
|
||||
|
||||
var size = vBox.CombinedMinimumSize;
|
||||
var box = UIBox2.FromDimensions(args.ScreenCoordinates.Position, size);
|
||||
_currentPopup.Open(box);
|
||||
}
|
||||
|
||||
private void OnContextButtonPressed(IEntity entity)
|
||||
{
|
||||
OpenContextMenu(entity, new ScreenCoordinates(_inputManager.MouseScreenPosition));
|
||||
}
|
||||
|
||||
public override void HandleNetMessage(INetChannel channel, EntitySystemMessage message)
|
||||
{
|
||||
base.HandleNetMessage(channel, message);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case VerbSystemMessages.VerbsResponseMessage resp:
|
||||
_fillEntityPopup(resp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void _fillEntityPopup(VerbSystemMessages.VerbsResponseMessage msg)
|
||||
{
|
||||
if (_currentEntity != msg.Entity || !_entityManager.TryGetEntity(_currentEntity, out var entity))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DebugTools.AssertNotNull(_currentPopup);
|
||||
|
||||
var buttons = new List<Button>();
|
||||
|
||||
var vBox = _currentPopup.GetChild<VBoxContainer>("ButtonBox");
|
||||
vBox.DisposeAllChildren();
|
||||
foreach (var data in msg.Verbs)
|
||||
{
|
||||
var button = new Button {Text = data.Text, Disabled = !data.Available};
|
||||
if (data.Available)
|
||||
{
|
||||
button.OnPressed += _ =>
|
||||
{
|
||||
RaiseNetworkEvent(new VerbSystemMessages.UseVerbMessage(_currentEntity, data.Key));
|
||||
_closeContextMenu();
|
||||
};
|
||||
}
|
||||
|
||||
buttons.Add(button);
|
||||
}
|
||||
|
||||
var user = GetUserEntity();
|
||||
foreach (var (component, verb) in VerbUtility.GetVerbs(entity))
|
||||
{
|
||||
if (verb.RequireInteractionRange)
|
||||
{
|
||||
var distanceSquared = (user.Transform.WorldPosition - entity.Transform.WorldPosition)
|
||||
.LengthSquared;
|
||||
if (distanceSquared > Verb.InteractionRangeSquared)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
var disabled = verb.GetVisibility(user, component) != VerbVisibility.Visible;
|
||||
var button = new Button
|
||||
{
|
||||
Text = verb.GetText(user, component),
|
||||
Disabled = disabled
|
||||
};
|
||||
if (!disabled)
|
||||
{
|
||||
button.OnPressed += _ =>
|
||||
{
|
||||
_closeContextMenu();
|
||||
try
|
||||
{
|
||||
verb.Activate(user, component);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorS("verb", "Exception in verb {0} on {1}:\n{2}", verb, entity, e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
buttons.Add(button);
|
||||
}
|
||||
|
||||
if (buttons.Count > 0)
|
||||
{
|
||||
buttons.Sort((a, b) => string.Compare(a.Text, b.Text, StringComparison.Ordinal));
|
||||
|
||||
foreach (var button in buttons)
|
||||
{
|
||||
vBox.AddChild(button);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var panel = new PanelContainer();
|
||||
panel.AddChild(new Label {Text = "No verbs!"});
|
||||
vBox.AddChild(panel);
|
||||
}
|
||||
|
||||
_currentPopup.Size = vBox.CombinedMinimumSize;
|
||||
|
||||
// If we're at the bottom of the window and the menu would go below the bottom of the window,
|
||||
// shift it up so it extends UP.
|
||||
var bottomCoord = vBox.CombinedMinimumSize.Y + _currentPopup.Position.Y;
|
||||
if (bottomCoord > _userInterfaceManager.StateRoot.Size.Y)
|
||||
{
|
||||
_currentPopup.Position = _currentPopup.Position - new Vector2(0, vBox.CombinedMinimumSize.Y);
|
||||
}
|
||||
}
|
||||
|
||||
private void _closeContextMenu()
|
||||
{
|
||||
_currentPopup?.Dispose();
|
||||
_currentPopup = null;
|
||||
_currentEntity = EntityUid.Invalid;
|
||||
}
|
||||
|
||||
private IEntity GetUserEntity()
|
||||
{
|
||||
return _playerManager.LocalPlayer.ControlledEntity;
|
||||
}
|
||||
}
|
||||
}
|
||||
51
Content.Client/GameObjects/EntitySystems/WindowSystem.cs
Normal file
51
Content.Client/GameObjects/EntitySystems/WindowSystem.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.GameObjects.Components;
|
||||
using Content.Client.GameObjects.Components.IconSmoothing;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.Transform;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Client.GameObjects.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class WindowSystem : EntitySystem
|
||||
{
|
||||
private readonly Queue<IEntity> _dirtyEntities = new Queue<IEntity>();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeEvent<WindowSmoothDirtyEvent>(HandleDirtyEvent);
|
||||
}
|
||||
|
||||
private void HandleDirtyEvent(object sender, WindowSmoothDirtyEvent ev)
|
||||
{
|
||||
if (sender is IEntity senderEnt && senderEnt.HasComponent<WindowComponent>())
|
||||
{
|
||||
_dirtyEntities.Enqueue(senderEnt);
|
||||
}
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
base.FrameUpdate(frameTime);
|
||||
|
||||
// Performance: This could be spread over multiple updates, or made parallel.
|
||||
while (_dirtyEntities.Count > 0)
|
||||
{
|
||||
_dirtyEntities.Dequeue().GetComponent<WindowComponent>().UpdateSprite();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event raised by a <see cref="WindowComponent"/> when it needs to be recalculated.
|
||||
/// </summary>
|
||||
public sealed class WindowSmoothDirtyEvent : EntitySystemMessage
|
||||
{
|
||||
}
|
||||
}
|
||||
270
Content.Client/GameTicking/ClientGameTicker.cs
Normal file
270
Content.Client/GameTicking/ClientGameTicker.cs
Normal file
@@ -0,0 +1,270 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Content.Client.Chat;
|
||||
using Content.Client.Interfaces;
|
||||
using Content.Client.Interfaces.Chat;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.Interfaces;
|
||||
using Robust.Client.Interfaces.Input;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.Interfaces.UserInterface;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.GameTicking
|
||||
{
|
||||
public class ClientGameTicker : SharedGameTicker, IClientGameTicker
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private IClientNetManager _netManager;
|
||||
[Dependency] private IUserInterfaceManager _userInterfaceManager;
|
||||
[Dependency] private IInputManager _inputManager;
|
||||
[Dependency] private IBaseClient _baseClient;
|
||||
[Dependency] private IChatManager _chatManager;
|
||||
[Dependency] private IClientConsole _console;
|
||||
[Dependency] private ILocalizationManager _localization;
|
||||
[Dependency] private IResourceCache _resourceCache;
|
||||
[Dependency] private IPlayerManager _playerManager;
|
||||
[Dependency] private IGameHud _gameHud;
|
||||
#pragma warning restore 649
|
||||
|
||||
[ViewVariables] private bool _areWeReady;
|
||||
[ViewVariables] private bool _initialized;
|
||||
[ViewVariables] private TickerState _tickerState;
|
||||
[ViewVariables] private ChatBox _gameChat;
|
||||
[ViewVariables] private LobbyGui _lobby;
|
||||
[ViewVariables] private bool _gameStarted;
|
||||
[ViewVariables] private DateTime _startTime;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
DebugTools.Assert(!_initialized);
|
||||
|
||||
_netManager.RegisterNetMessage<MsgTickerJoinLobby>(nameof(MsgTickerJoinLobby), _joinLobby);
|
||||
_netManager.RegisterNetMessage<MsgTickerJoinGame>(nameof(MsgTickerJoinGame), _joinGame);
|
||||
_netManager.RegisterNetMessage<MsgTickerLobbyStatus>(nameof(MsgTickerLobbyStatus), _lobbyStatus);
|
||||
|
||||
_baseClient.RunLevelChanged += BaseClientOnRunLevelChanged;
|
||||
_playerManager.PlayerListUpdated += PlayerManagerOnPlayerListUpdated;
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
private void PlayerManagerOnPlayerListUpdated(object sender, EventArgs e)
|
||||
{
|
||||
if (_lobby == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_updatePlayerList();
|
||||
}
|
||||
|
||||
private void _updatePlayerList()
|
||||
{
|
||||
_lobby.OnlinePlayerItemList.Clear();
|
||||
foreach (var session in _playerManager.Sessions.OrderBy(s => s.Name))
|
||||
{
|
||||
_lobby.OnlinePlayerItemList.AddItem(session.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private void BaseClientOnRunLevelChanged(object sender, RunLevelChangedEventArgs e)
|
||||
{
|
||||
if (e.NewLevel != ClientRunLevel.Initialize)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_tickerState = TickerState.Unset;
|
||||
_lobby?.Dispose();
|
||||
_lobby = null;
|
||||
_gameChat?.Dispose();
|
||||
_gameChat = null;
|
||||
_gameHud.RootControl.Orphan();
|
||||
}
|
||||
|
||||
public void FrameUpdate(RenderFrameEventArgs renderFrameEventArgs)
|
||||
{
|
||||
if (_lobby == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_gameStarted)
|
||||
{
|
||||
_lobby.StartTime.Text = "";
|
||||
return;
|
||||
}
|
||||
|
||||
string text;
|
||||
var difference = _startTime - DateTime.UtcNow;
|
||||
if (difference.Ticks < 0)
|
||||
{
|
||||
if (difference.TotalSeconds < -5)
|
||||
{
|
||||
text = _localization.GetString("Right Now?");
|
||||
}
|
||||
else
|
||||
{
|
||||
text = _localization.GetString("Right Now");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
text = $"{(int) Math.Floor(difference.TotalMinutes)}:{difference.Seconds:D2}";
|
||||
}
|
||||
|
||||
_lobby.StartTime.Text = _localization.GetString("Round Starts In: {0}", text);
|
||||
}
|
||||
|
||||
private void _lobbyStatus(MsgTickerLobbyStatus message)
|
||||
{
|
||||
_startTime = message.StartTime;
|
||||
_gameStarted = message.IsRoundStarted;
|
||||
_areWeReady = message.YouAreReady;
|
||||
|
||||
_updateLobbyUi();
|
||||
}
|
||||
|
||||
private void _updateLobbyUi()
|
||||
{
|
||||
if (_lobby == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_gameStarted)
|
||||
{
|
||||
_lobby.ReadyButton.Text = _localization.GetString("Join");
|
||||
_lobby.ReadyButton.ToggleMode = false;
|
||||
_lobby.ReadyButton.Pressed = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lobby.StartTime.Text = "";
|
||||
_lobby.ReadyButton.Text = _localization.GetString("Ready Up");
|
||||
_lobby.ReadyButton.ToggleMode = true;
|
||||
_lobby.ReadyButton.Pressed = _areWeReady;
|
||||
}
|
||||
}
|
||||
|
||||
private void _joinLobby(MsgTickerJoinLobby message)
|
||||
{
|
||||
if (_tickerState == TickerState.InLobby)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_gameChat != null)
|
||||
{
|
||||
_gameChat.Dispose();
|
||||
_gameChat = null;
|
||||
}
|
||||
|
||||
_gameHud.RootControl.Orphan();
|
||||
|
||||
_tickerState = TickerState.InLobby;
|
||||
|
||||
_lobby = new LobbyGui(_localization, _resourceCache);
|
||||
_userInterfaceManager.StateRoot.AddChild(_lobby);
|
||||
|
||||
_lobby.SetAnchorAndMarginPreset(Control.LayoutPreset.Wide, margin: 20);
|
||||
|
||||
_chatManager.SetChatBox(_lobby.Chat);
|
||||
_lobby.Chat.DefaultChatFormat = "ooc \"{0}\"";
|
||||
|
||||
_lobby.ServerName.Text = _baseClient.GameInfo.ServerName;
|
||||
|
||||
_inputManager.SetInputCommand(ContentKeyFunctions.FocusChat,
|
||||
InputCmdHandler.FromDelegate(session =>
|
||||
{
|
||||
_lobby.Chat.Input.IgnoreNext = true;
|
||||
_lobby.Chat.Input.GrabKeyboardFocus();
|
||||
}));
|
||||
|
||||
_updateLobbyUi();
|
||||
|
||||
_lobby.ObserveButton.OnPressed += args => _console.ProcessCommand("observe");
|
||||
_lobby.ReadyButton.OnPressed += args =>
|
||||
{
|
||||
if (!_gameStarted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_console.ProcessCommand("joingame");
|
||||
};
|
||||
|
||||
_lobby.ReadyButton.OnToggled += args =>
|
||||
{
|
||||
if (_gameStarted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_console.ProcessCommand($"toggleready {args.Pressed}");
|
||||
};
|
||||
|
||||
_lobby.LeaveButton.OnPressed += args => _console.ProcessCommand("disconnect");
|
||||
|
||||
_updatePlayerList();
|
||||
}
|
||||
|
||||
private void _joinGame(MsgTickerJoinGame message)
|
||||
{
|
||||
if (_tickerState == TickerState.InGame)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_tickerState = TickerState.InGame;
|
||||
|
||||
if (_lobby != null)
|
||||
{
|
||||
_lobby.Dispose();
|
||||
_lobby = null;
|
||||
}
|
||||
|
||||
_inputManager.SetInputCommand(ContentKeyFunctions.FocusChat,
|
||||
InputCmdHandler.FromDelegate(session =>
|
||||
{
|
||||
_gameChat.Input.IgnoreNext = true;
|
||||
_gameChat.Input.GrabKeyboardFocus();
|
||||
}));
|
||||
|
||||
_gameChat = new ChatBox();
|
||||
_userInterfaceManager.StateRoot.AddChild(_gameChat);
|
||||
_userInterfaceManager.StateRoot.AddChild(_gameHud.RootControl);
|
||||
_chatManager.SetChatBox(_gameChat);
|
||||
_gameChat.DefaultChatFormat = "say \"{0}\"";
|
||||
_gameChat.Input.PlaceHolder = _localization.GetString("Say something! [ for OOC");
|
||||
}
|
||||
|
||||
private enum TickerState
|
||||
{
|
||||
Unset = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The client is in the lobby.
|
||||
/// </summary>
|
||||
InLobby = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The client is NOT in the lobby.
|
||||
/// Do not confuse this with the client session status.
|
||||
/// </summary>
|
||||
InGame = 2
|
||||
}
|
||||
}
|
||||
}
|
||||
34
Content.Client/Graphics/Overlays/CircleMaskOverlay.cs
Normal file
34
Content.Client/Graphics/Overlays/CircleMaskOverlay.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
using Robust.Client.Graphics.Overlays;
|
||||
using Robust.Client.Graphics.Shaders;
|
||||
using Robust.Client.Interfaces.Graphics.ClientEye;
|
||||
using Robust.Client.Interfaces.Graphics.Overlays;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Graphics.Overlays
|
||||
{
|
||||
public class CircleMaskOverlay : Overlay
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
||||
[Dependency] private readonly IEyeManager _eyeManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public CircleMaskOverlay() : base(nameof(CircleMaskOverlay))
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
Shader = _prototypeManager.Index<ShaderPrototype>("circlemask").Instance();
|
||||
}
|
||||
|
||||
protected override void Draw(DrawingHandleBase handle)
|
||||
{
|
||||
var worldHandle = (DrawingHandleWorld)handle;
|
||||
var viewport = _eyeManager.GetWorldViewport();
|
||||
worldHandle.DrawRect(viewport, Color.White);
|
||||
}
|
||||
}
|
||||
}
|
||||
33
Content.Client/Graphics/Overlays/GradientCircleMask.cs
Normal file
33
Content.Client/Graphics/Overlays/GradientCircleMask.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
using Robust.Client.Graphics.Overlays;
|
||||
using Robust.Client.Graphics.Shaders;
|
||||
using Robust.Client.Interfaces.Graphics.ClientEye;
|
||||
using Robust.Client.Interfaces.Graphics.Overlays;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Graphics.Overlays
|
||||
{
|
||||
public class GradientCircleMask : Overlay
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
||||
[Dependency] private readonly IEyeManager _eyeManager;
|
||||
#pragma warning restore 649
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public GradientCircleMask() : base(nameof(GradientCircleMask))
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
Shader = _prototypeManager.Index<ShaderPrototype>("gradientcirclemask").Instance();
|
||||
}
|
||||
|
||||
protected override void Draw(DrawingHandleBase handle)
|
||||
{
|
||||
var worldHandle = (DrawingHandleWorld)handle;
|
||||
var viewport = _eyeManager.GetWorldViewport();
|
||||
worldHandle.DrawRect(viewport, Color.White);
|
||||
}
|
||||
}
|
||||
}
|
||||
42
Content.Client/Input/ContentContexts.cs
Normal file
42
Content.Client/Input/ContentContexts.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Content.Shared.Input;
|
||||
using Robust.Shared.Input;
|
||||
|
||||
namespace Content.Client.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains a helper function for setting up all content
|
||||
/// contexts, and modifying existing engine ones.
|
||||
/// </summary>
|
||||
public static class ContentContexts
|
||||
{
|
||||
public static void SetupContexts(IInputContextContainer contexts)
|
||||
{
|
||||
var common = contexts.GetContext("common");
|
||||
common.AddFunction(ContentKeyFunctions.FocusChat);
|
||||
common.AddFunction(ContentKeyFunctions.ExamineEntity);
|
||||
common.AddFunction(ContentKeyFunctions.OpenTutorial);
|
||||
|
||||
var human = contexts.GetContext("human");
|
||||
human.AddFunction(ContentKeyFunctions.SwapHands);
|
||||
human.AddFunction(ContentKeyFunctions.Drop);
|
||||
human.AddFunction(ContentKeyFunctions.ActivateItemInHand);
|
||||
human.AddFunction(ContentKeyFunctions.OpenCharacterMenu);
|
||||
human.AddFunction(ContentKeyFunctions.UseItemInHand);
|
||||
human.AddFunction(ContentKeyFunctions.ActivateItemInWorld);
|
||||
human.AddFunction(ContentKeyFunctions.ThrowItemInHand);
|
||||
human.AddFunction(ContentKeyFunctions.OpenContextMenu);
|
||||
human.AddFunction(ContentKeyFunctions.OpenCraftingMenu);
|
||||
human.AddFunction(ContentKeyFunctions.OpenInventoryMenu);
|
||||
// Disabled until there is feedback, so hitting tab doesn't suddenly break interaction.
|
||||
// human.AddFunction(ContentKeyFunctions.ToggleCombatMode);
|
||||
|
||||
var ghost = contexts.New("ghost", "common");
|
||||
ghost.AddFunction(EngineKeyFunctions.MoveUp);
|
||||
ghost.AddFunction(EngineKeyFunctions.MoveDown);
|
||||
ghost.AddFunction(EngineKeyFunctions.MoveLeft);
|
||||
ghost.AddFunction(EngineKeyFunctions.MoveRight);
|
||||
ghost.AddFunction(EngineKeyFunctions.Run);
|
||||
ghost.AddFunction(ContentKeyFunctions.OpenContextMenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
Content.Client/Interfaces/Chat/IChatManager.cs
Normal file
17
Content.Client/Interfaces/Chat/IChatManager.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Content.Client.Chat;
|
||||
using Robust.Client;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.Interfaces.Chat
|
||||
{
|
||||
public interface IChatManager
|
||||
{
|
||||
void Initialize();
|
||||
|
||||
void FrameUpdate(RenderFrameEventArgs delta);
|
||||
|
||||
void SetChatBox(ChatBox chatBox);
|
||||
|
||||
void RemoveSpeechBubble(EntityUid entityUid, SpeechBubble bubble);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Client.Interfaces.GameObjects
|
||||
{
|
||||
// HYPER SIMPLE HANDS API CLIENT SIDE.
|
||||
// To allow for showing the HUD, mostly.
|
||||
public interface IHandsComponent
|
||||
{
|
||||
IEntity GetEntity(string index);
|
||||
string ActiveIndex { get; }
|
||||
IEntity ActiveHand { get; }
|
||||
|
||||
void SendChangeHand(string index);
|
||||
void AttackByInHand(string index);
|
||||
}
|
||||
}
|
||||
11
Content.Client/Interfaces/IClientGameTicker.cs
Normal file
11
Content.Client/Interfaces/IClientGameTicker.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Content.Client.UserInterface;
|
||||
using Robust.Client;
|
||||
|
||||
namespace Content.Client.Interfaces
|
||||
{
|
||||
public interface IClientGameTicker
|
||||
{
|
||||
void Initialize();
|
||||
void FrameUpdate(RenderFrameEventArgs renderFrameEventArgs);
|
||||
}
|
||||
}
|
||||
14
Content.Client/Interfaces/IClientNotifyManager.cs
Normal file
14
Content.Client/Interfaces/IClientNotifyManager.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Content.Shared.Interfaces;
|
||||
using Robust.Client;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Client.Interfaces
|
||||
{
|
||||
public interface IClientNotifyManager : ISharedNotifyManager
|
||||
{
|
||||
void Initialize();
|
||||
void PopupMessage(ScreenCoordinates coordinates, string message);
|
||||
void PopupMessage(string message);
|
||||
void FrameUpdate(RenderFrameEventArgs eventArgs);
|
||||
}
|
||||
}
|
||||
12
Content.Client/Interfaces/Parallax/IParallaxManager.cs
Normal file
12
Content.Client/Interfaces/Parallax/IParallaxManager.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using Robust.Client.Graphics;
|
||||
|
||||
namespace Content.Client.Interfaces.Parallax
|
||||
{
|
||||
public interface IParallaxManager
|
||||
{
|
||||
event Action<Texture> OnTextureLoaded;
|
||||
Texture ParallaxTexture { get; }
|
||||
void LoadParallax();
|
||||
}
|
||||
}
|
||||
418
Content.Client/Parallax/ParallaxGenerator.cs
Normal file
418
Content.Client/Parallax/ParallaxGenerator.cs
Normal file
@@ -0,0 +1,418 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Nett;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.Primitives;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Interfaces.Log;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Noise;
|
||||
using BlendFactor = Robust.Shared.Maths.Color.BlendFactor;
|
||||
|
||||
namespace Content.Client.Parallax
|
||||
{
|
||||
public class ParallaxGenerator
|
||||
{
|
||||
private readonly List<Layer> Layers = new List<Layer>();
|
||||
|
||||
public static Image<Rgba32> GenerateParallax(TomlTable config, Size size, ISawmill sawmill)
|
||||
{
|
||||
sawmill.Debug("Generating parallax!");
|
||||
var generator = new ParallaxGenerator();
|
||||
generator._loadConfig(config);
|
||||
|
||||
var image = new Image<Rgba32>(Configuration.Default, size.Width, size.Height, Rgba32.Black);
|
||||
var count = 0;
|
||||
foreach (var layer in generator.Layers)
|
||||
{
|
||||
layer.Apply(image);
|
||||
sawmill.Debug("Layer {0} done!", count++);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private void _loadConfig(TomlTable config)
|
||||
{
|
||||
foreach (var layerArray in config.Get<TomlTableArray>("layers").Items)
|
||||
{
|
||||
var layer = layerArray.Get<TomlTable>();
|
||||
switch (layer.Get<string>("type"))
|
||||
{
|
||||
case "noise":
|
||||
var layerNoise = new LayerNoise(layer);
|
||||
Layers.Add(layerNoise);
|
||||
break;
|
||||
|
||||
case "points":
|
||||
var layerPoint = new LayerPoints(layer);
|
||||
Layers.Add(layerPoint);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class Layer
|
||||
{
|
||||
public abstract void Apply(Image<Rgba32> bitmap);
|
||||
}
|
||||
|
||||
private class LayerNoise : Layer
|
||||
{
|
||||
private readonly Color InnerColor = Color.White;
|
||||
private readonly Color OuterColor = Color.Black;
|
||||
private readonly NoiseGenerator.NoiseType NoiseType = NoiseGenerator.NoiseType.Fbm;
|
||||
private readonly uint Seed = 1234;
|
||||
private readonly double Persistence = 0.5;
|
||||
private readonly double Lacunarity = Math.PI * 2 / 3;
|
||||
private readonly double Frequency = 1;
|
||||
private readonly uint Octaves = 3;
|
||||
private readonly double Threshold;
|
||||
private readonly double Power = 1;
|
||||
private readonly BlendFactor SrcFactor = BlendFactor.One;
|
||||
private readonly BlendFactor DstFactor = BlendFactor.One;
|
||||
|
||||
public LayerNoise(TomlTable table)
|
||||
{
|
||||
if (table.TryGetValue("innercolor", out var tomlObject))
|
||||
{
|
||||
InnerColor = Color.FromHex(tomlObject.Get<string>());
|
||||
}
|
||||
|
||||
if (table.TryGetValue("outercolor", out tomlObject))
|
||||
{
|
||||
OuterColor = Color.FromHex(tomlObject.Get<string>());
|
||||
}
|
||||
|
||||
if (table.TryGetValue("seed", out tomlObject))
|
||||
{
|
||||
Seed = (uint) tomlObject.Get<int>();
|
||||
}
|
||||
|
||||
if (table.TryGetValue("persistence", out tomlObject))
|
||||
{
|
||||
Persistence = double.Parse(tomlObject.Get<string>(), CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (table.TryGetValue("lacunarity", out tomlObject))
|
||||
{
|
||||
Lacunarity = double.Parse(tomlObject.Get<string>(), CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (table.TryGetValue("frequency", out tomlObject))
|
||||
{
|
||||
Frequency = double.Parse(tomlObject.Get<string>(), CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (table.TryGetValue("octaves", out tomlObject))
|
||||
{
|
||||
Octaves = (uint) tomlObject.Get<int>();
|
||||
}
|
||||
|
||||
if (table.TryGetValue("threshold", out tomlObject))
|
||||
{
|
||||
Threshold = double.Parse(tomlObject.Get<string>(), CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (table.TryGetValue("sourcefactor", out tomlObject))
|
||||
{
|
||||
SrcFactor = (BlendFactor) Enum.Parse(typeof(BlendFactor), tomlObject.Get<string>());
|
||||
}
|
||||
|
||||
if (table.TryGetValue("destfactor", out tomlObject))
|
||||
{
|
||||
DstFactor = (BlendFactor) Enum.Parse(typeof(BlendFactor), tomlObject.Get<string>());
|
||||
}
|
||||
|
||||
if (table.TryGetValue("power", out tomlObject))
|
||||
{
|
||||
Power = double.Parse(tomlObject.Get<string>(), CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (table.TryGetValue("noise_type", out tomlObject))
|
||||
{
|
||||
switch (tomlObject.Get<string>())
|
||||
{
|
||||
case "fbm":
|
||||
NoiseType = NoiseGenerator.NoiseType.Fbm;
|
||||
break;
|
||||
case "ridged":
|
||||
NoiseType = NoiseGenerator.NoiseType.Ridged;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Apply(Image<Rgba32> bitmap)
|
||||
{
|
||||
var noise = new NoiseGenerator(NoiseType);
|
||||
noise.SetSeed(Seed);
|
||||
noise.SetFrequency(Frequency);
|
||||
noise.SetPersistence(Persistence);
|
||||
noise.SetLacunarity(Lacunarity);
|
||||
noise.SetOctaves(Octaves);
|
||||
noise.SetPeriodX(bitmap.Width);
|
||||
noise.SetPeriodY(bitmap.Height);
|
||||
var threshVal = 1 / (1 - Threshold);
|
||||
var powFactor = 1 / Power;
|
||||
for (var x = 0; x < bitmap.Width; x++)
|
||||
{
|
||||
for (var y = 0; y < bitmap.Height; y++)
|
||||
{
|
||||
// Do noise calculations.
|
||||
var noiseVal = Math.Min(1, Math.Max(0, (noise.GetNoiseTiled(x, y) + 1) / 2));
|
||||
|
||||
// Threshold
|
||||
noiseVal = Math.Max(0, noiseVal - Threshold);
|
||||
noiseVal *= threshVal;
|
||||
noiseVal = Math.Pow(noiseVal, powFactor);
|
||||
|
||||
// Get colors based on noise values.
|
||||
var srcColor = Color.InterpolateBetween(InnerColor, OuterColor, (float) noiseVal)
|
||||
.WithAlpha((float) noiseVal);
|
||||
|
||||
// Apply blending factors & write back.
|
||||
var dstColor = bitmap[x, y].ConvertImgSharp();
|
||||
bitmap[x, y] = Color.Blend(dstColor, srcColor, DstFactor, SrcFactor).ConvertImgSharp();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class LayerPoints : Layer
|
||||
{
|
||||
private readonly int Seed = 1234;
|
||||
private readonly int PointCount = 100;
|
||||
|
||||
private readonly Color CloseColor = Color.White;
|
||||
private readonly Color FarColor = Color.Black;
|
||||
|
||||
private readonly BlendFactor SrcFactor = BlendFactor.One;
|
||||
private readonly BlendFactor DstFactor = BlendFactor.One;
|
||||
|
||||
// Noise mask stuff.
|
||||
private readonly bool Masked;
|
||||
private readonly NoiseGenerator.NoiseType MaskNoiseType = NoiseGenerator.NoiseType.Fbm;
|
||||
private readonly uint MaskSeed = 1234;
|
||||
private readonly double MaskPersistence = 0.5;
|
||||
private readonly double MaskLacunarity = Math.PI * 2 / 3;
|
||||
private readonly double MaskFrequency = 1;
|
||||
private readonly uint MaskOctaves = 3;
|
||||
private readonly double MaskThreshold;
|
||||
private readonly int PointSize = 1;
|
||||
private readonly double MaskPower = 1;
|
||||
|
||||
|
||||
public LayerPoints(TomlTable table)
|
||||
{
|
||||
if (table.TryGetValue("seed", out var tomlObject))
|
||||
{
|
||||
Seed = tomlObject.Get<int>();
|
||||
}
|
||||
|
||||
if (table.TryGetValue("count", out tomlObject))
|
||||
{
|
||||
PointCount = tomlObject.Get<int>();
|
||||
}
|
||||
|
||||
if (table.TryGetValue("sourcefactor", out tomlObject))
|
||||
{
|
||||
SrcFactor = (BlendFactor) Enum.Parse(typeof(BlendFactor), tomlObject.Get<string>());
|
||||
}
|
||||
|
||||
if (table.TryGetValue("destfactor", out tomlObject))
|
||||
{
|
||||
DstFactor = (BlendFactor) Enum.Parse(typeof(BlendFactor), tomlObject.Get<string>());
|
||||
}
|
||||
|
||||
if (table.TryGetValue("farcolor", out tomlObject))
|
||||
{
|
||||
FarColor = Color.FromHex(tomlObject.Get<string>());
|
||||
}
|
||||
|
||||
if (table.TryGetValue("closecolor", out tomlObject))
|
||||
{
|
||||
CloseColor = Color.FromHex(tomlObject.Get<string>());
|
||||
}
|
||||
|
||||
if (table.TryGetValue("pointsize", out tomlObject))
|
||||
{
|
||||
PointSize = tomlObject.Get<int>();
|
||||
}
|
||||
|
||||
// Noise mask stuff.
|
||||
if (table.TryGetValue("mask", out tomlObject))
|
||||
{
|
||||
Masked = tomlObject.Get<bool>();
|
||||
}
|
||||
|
||||
if (table.TryGetValue("maskseed", out tomlObject))
|
||||
{
|
||||
MaskSeed = (uint) tomlObject.Get<int>();
|
||||
}
|
||||
|
||||
if (table.TryGetValue("maskpersistence", out tomlObject))
|
||||
{
|
||||
MaskPersistence = double.Parse(tomlObject.Get<string>(), CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (table.TryGetValue("masklacunarity", out tomlObject))
|
||||
{
|
||||
MaskLacunarity = double.Parse(tomlObject.Get<string>(), CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (table.TryGetValue("maskfrequency", out tomlObject))
|
||||
{
|
||||
MaskFrequency = double.Parse(tomlObject.Get<string>(), CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (table.TryGetValue("maskoctaves", out tomlObject))
|
||||
{
|
||||
MaskOctaves = (uint) tomlObject.Get<int>();
|
||||
}
|
||||
|
||||
if (table.TryGetValue("maskthreshold", out tomlObject))
|
||||
{
|
||||
MaskThreshold = double.Parse(tomlObject.Get<string>(), CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (table.TryGetValue("masknoise_type", out tomlObject))
|
||||
{
|
||||
switch (tomlObject.Get<string>())
|
||||
{
|
||||
case "fbm":
|
||||
MaskNoiseType = NoiseGenerator.NoiseType.Fbm;
|
||||
break;
|
||||
case "ridged":
|
||||
MaskNoiseType = NoiseGenerator.NoiseType.Ridged;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
if (table.TryGetValue("maskpower", out tomlObject))
|
||||
{
|
||||
MaskPower = double.Parse(tomlObject.Get<string>(), CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Apply(Image<Rgba32> bitmap)
|
||||
{
|
||||
// Temporary buffer so we don't mess up blending.
|
||||
var buffer = new Image<Rgba32>(Configuration.Default, bitmap.Width, bitmap.Height, Rgba32.Black);
|
||||
|
||||
if (Masked)
|
||||
{
|
||||
GenPointsMasked(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
GenPoints(buffer);
|
||||
}
|
||||
|
||||
for (var x = 0; x < bitmap.Width; x++)
|
||||
{
|
||||
for (var y = 0; y < bitmap.Height; y++)
|
||||
{
|
||||
var dstColor = bitmap[x, y].ConvertImgSharp();
|
||||
var srcColor = buffer[x, y].ConvertImgSharp();
|
||||
|
||||
bitmap[x, y] = Color.Blend(dstColor, srcColor, DstFactor, SrcFactor).ConvertImgSharp();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GenPoints(Image<Rgba32> buffer)
|
||||
{
|
||||
var o = PointSize - 1;
|
||||
var random = new Random(Seed);
|
||||
for (var i = 0; i < PointCount; i++)
|
||||
{
|
||||
var relX = random.NextDouble();
|
||||
var relY = random.NextDouble();
|
||||
|
||||
var x = (int) (relX * buffer.Width);
|
||||
var y = (int) (relY * buffer.Height);
|
||||
|
||||
var dist = random.NextDouble();
|
||||
|
||||
for (var ox = x - o; ox <= x + o; ox++)
|
||||
{
|
||||
for (var oy = y - o; oy <= y + o; oy++)
|
||||
{
|
||||
var color = Color.InterpolateBetween(FarColor, CloseColor, (float) dist).ConvertImgSharp();
|
||||
buffer[MathHelper.Mod(ox, buffer.Width), MathHelper.Mod(oy, buffer.Width)] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GenPointsMasked(Image<Rgba32> buffer)
|
||||
{
|
||||
var o = PointSize - 1;
|
||||
var random = new Random(Seed);
|
||||
var noise = new NoiseGenerator(MaskNoiseType);
|
||||
noise.SetSeed(MaskSeed);
|
||||
noise.SetFrequency(MaskFrequency);
|
||||
noise.SetPersistence(MaskPersistence);
|
||||
noise.SetLacunarity(MaskLacunarity);
|
||||
noise.SetOctaves(MaskOctaves);
|
||||
noise.SetPeriodX(buffer.Width);
|
||||
noise.SetPeriodY(buffer.Height);
|
||||
|
||||
var threshVal = 1 / (1 - MaskThreshold);
|
||||
var powFactor = 1 / MaskPower;
|
||||
|
||||
const int maxPointAttemptCount = 9999;
|
||||
var pointAttemptCount = 0;
|
||||
|
||||
for (var i = 0; i < PointCount; i++)
|
||||
{
|
||||
var relX = random.NextDouble();
|
||||
var relY = random.NextDouble();
|
||||
|
||||
var x = (int) (relX * buffer.Width);
|
||||
var y = (int) (relY * buffer.Height);
|
||||
|
||||
// Grab noise at this point.
|
||||
var noiseVal = Math.Min(1, Math.Max(0, (noise.GetNoiseTiled(x, y) + 1) / 2));
|
||||
// Threshold
|
||||
noiseVal = Math.Max(0, noiseVal - MaskThreshold);
|
||||
noiseVal *= threshVal;
|
||||
noiseVal = Math.Pow(noiseVal, powFactor);
|
||||
|
||||
var randomThresh = random.NextDouble();
|
||||
if (randomThresh > noiseVal)
|
||||
{
|
||||
if (++pointAttemptCount <= maxPointAttemptCount)
|
||||
{
|
||||
i--;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var dist = random.NextDouble();
|
||||
|
||||
for (var ox = x - o; ox <= x + o; ox++)
|
||||
{
|
||||
for (var oy = y - o; oy <= y + o; oy++)
|
||||
{
|
||||
var color = Color.InterpolateBetween(FarColor, CloseColor, (float) dist).ConvertImgSharp();
|
||||
buffer[MathHelper.Mod(ox, buffer.Width), MathHelper.Mod(oy, buffer.Height)] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
113
Content.Client/Parallax/ParallaxManager.cs
Normal file
113
Content.Client/Parallax/ParallaxManager.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Client.Interfaces.Parallax;
|
||||
using Nett;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.Primitives;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Shared.Interfaces.Configuration;
|
||||
using Robust.Shared.Interfaces.Log;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Parallax
|
||||
{
|
||||
internal sealed class ParallaxManager : IParallaxManager, IPostInjectInit
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IResourceCache _resourceCache;
|
||||
[Dependency] private readonly ILogManager _logManager;
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
private static readonly ResourcePath ParallaxConfigPath = new ResourcePath("/parallax_config.toml");
|
||||
|
||||
// Both of these below are in the user directory.
|
||||
private static readonly ResourcePath ParallaxPath = new ResourcePath("/parallax_cache.png");
|
||||
private static readonly ResourcePath ParallaxConfigOld = new ResourcePath("/parallax_config_old");
|
||||
|
||||
public event Action<Texture> OnTextureLoaded;
|
||||
public Texture ParallaxTexture { get; private set; }
|
||||
|
||||
public async void LoadParallax()
|
||||
{
|
||||
if (!_configurationManager.GetCVar<bool>("parallax.enabled"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MemoryStream configStream = null;
|
||||
string contents;
|
||||
TomlTable table;
|
||||
try
|
||||
{
|
||||
// Load normal config into memory
|
||||
if (!_resourceCache.TryContentFileRead(ParallaxConfigPath, out configStream))
|
||||
{
|
||||
Logger.ErrorS("parallax", "Parallax config not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
using (var reader = new StreamReader(configStream, EncodingHelpers.UTF8))
|
||||
{
|
||||
contents = reader.ReadToEnd();
|
||||
}
|
||||
|
||||
if (_resourceCache.UserData.Exists(ParallaxConfigOld))
|
||||
{
|
||||
bool match;
|
||||
using (var data = _resourceCache.UserData.Open(ParallaxConfigOld, FileMode.Open))
|
||||
using (var reader = new StreamReader(data, EncodingHelpers.UTF8))
|
||||
{
|
||||
match = reader.ReadToEnd() == contents;
|
||||
}
|
||||
|
||||
if (match)
|
||||
{
|
||||
using (var stream = _resourceCache.UserData.Open(ParallaxPath, FileMode.Open))
|
||||
{
|
||||
ParallaxTexture = Texture.LoadFromPNGStream(stream, "Parallax");
|
||||
}
|
||||
|
||||
OnTextureLoaded?.Invoke(ParallaxTexture);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
table = Toml.ReadString(contents);
|
||||
}
|
||||
finally
|
||||
{
|
||||
configStream?.Dispose();
|
||||
}
|
||||
|
||||
var sawmill = _logManager.GetSawmill("parallax");
|
||||
// Generate the parallax in the thread pool.
|
||||
var image = await Task.Run(() => ParallaxGenerator.GenerateParallax(table, new Size(1920, 1080), sawmill));
|
||||
// And load it in the main thread for safety reasons.
|
||||
ParallaxTexture = Texture.LoadFromImage(image, "Parallax");
|
||||
|
||||
// Store it and CRC so further game starts don't need to regenerate it.
|
||||
using (var stream = _resourceCache.UserData.Open(ParallaxPath, FileMode.Create))
|
||||
{
|
||||
image.SaveAsPng(stream);
|
||||
}
|
||||
|
||||
using (var stream = _resourceCache.UserData.Open(ParallaxConfigOld, FileMode.Create))
|
||||
using (var writer = new StreamWriter(stream, EncodingHelpers.UTF8))
|
||||
{
|
||||
writer.Write(contents);
|
||||
}
|
||||
|
||||
OnTextureLoaded?.Invoke(ParallaxTexture);
|
||||
}
|
||||
|
||||
public void PostInject()
|
||||
{
|
||||
_configurationManager.RegisterCVar("parallax.enabled", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
67
Content.Client/Parallax/ParallaxOverlay.cs
Normal file
67
Content.Client/Parallax/ParallaxOverlay.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using Content.Client.Interfaces.Parallax;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
using Robust.Client.Graphics.Overlays;
|
||||
using Robust.Client.Graphics.Shaders;
|
||||
using Robust.Client.Interfaces.Graphics.ClientEye;
|
||||
using Robust.Client.Interfaces.Graphics.Overlays;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Parallax
|
||||
{
|
||||
public class ParallaxOverlay : Overlay
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IParallaxManager _parallaxManager;
|
||||
[Dependency] private readonly IEyeManager _eyeManager;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
||||
[Dependency] private readonly IMapManager _mapManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override bool AlwaysDirty => true;
|
||||
private const float Slowness = 0.5f;
|
||||
|
||||
private Texture _parallaxTexture;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.ScreenSpaceBelowWorld;
|
||||
|
||||
public ParallaxOverlay() : base(nameof(ParallaxOverlay))
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
Shader = _prototypeManager.Index<ShaderPrototype>("unshaded").Instance();
|
||||
|
||||
if (_parallaxManager.ParallaxTexture == null)
|
||||
{
|
||||
_parallaxManager.OnTextureLoaded += texture => _parallaxTexture = texture;
|
||||
}
|
||||
else
|
||||
{
|
||||
_parallaxTexture = _parallaxManager.ParallaxTexture;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Draw(DrawingHandleBase handle)
|
||||
{
|
||||
if (_parallaxTexture == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var screenHandle = (DrawingHandleScreen) handle;
|
||||
|
||||
var (sizeX, sizeY) = _parallaxTexture.Size;
|
||||
var (posX, posY) = _eyeManager.ScreenToWorld(Vector2.Zero).ToWorld(_mapManager).Position;
|
||||
var (ox, oy) = (Vector2i) new Vector2(-posX / Slowness, posY / Slowness);
|
||||
ox = MathHelper.Mod(ox, sizeX);
|
||||
oy = MathHelper.Mod(oy, sizeY);
|
||||
|
||||
screenHandle.DrawTexture(_parallaxTexture, new Vector2(ox, oy));
|
||||
screenHandle.DrawTexture(_parallaxTexture, new Vector2(ox - sizeX, oy));
|
||||
screenHandle.DrawTexture(_parallaxTexture, new Vector2(ox, oy - sizeY));
|
||||
screenHandle.DrawTexture(_parallaxTexture, new Vector2(ox - sizeX, oy - sizeY));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Content.Client")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Content.Client")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("a2e5f175-78af-4ddd-8f97-e2d2552372ed")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
251
Content.Client/Research/LatheMenu.cs
Normal file
251
Content.Client/Research/LatheMenu.cs
Normal file
@@ -0,0 +1,251 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.GameObjects.Components.Research;
|
||||
using Content.Shared.Materials;
|
||||
using Content.Shared.Research;
|
||||
using Robust.Client.Interfaces.Graphics;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timers;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Research
|
||||
{
|
||||
public class LatheMenu : SS14Window
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
[Dependency]
|
||||
private IPrototypeManager PrototypeManager;
|
||||
#pragma warning restore
|
||||
|
||||
private ItemList Items;
|
||||
private ItemList Materials;
|
||||
private LineEdit AmountLineEdit;
|
||||
private LineEdit SearchBar;
|
||||
public Button QueueButton;
|
||||
protected override Vector2? CustomSize => (300, 450);
|
||||
|
||||
public LatheBoundUserInterface Owner { get; set; }
|
||||
|
||||
private List<LatheRecipePrototype> _recipes = new List<LatheRecipePrototype>();
|
||||
private List<LatheRecipePrototype> _shownRecipes = new List<LatheRecipePrototype>();
|
||||
|
||||
public LatheMenu()
|
||||
{
|
||||
}
|
||||
|
||||
public LatheMenu(string name) : base(name)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
Title = "Lathe Menu";
|
||||
|
||||
var margin = new MarginContainer()
|
||||
{
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
MarginTop = 5f,
|
||||
MarginLeft = 5f,
|
||||
MarginRight = -5f,
|
||||
MarginBottom = -5f,
|
||||
};
|
||||
|
||||
margin.SetAnchorAndMarginPreset(LayoutPreset.Wide);
|
||||
|
||||
var vbox = new VBoxContainer()
|
||||
{
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
SeparationOverride = 5,
|
||||
};
|
||||
|
||||
vbox.SetAnchorAndMarginPreset(LayoutPreset.Wide);
|
||||
|
||||
var hboxButtons = new HBoxContainer()
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
SizeFlagsStretchRatio = 1,
|
||||
};
|
||||
|
||||
QueueButton = new Button()
|
||||
{
|
||||
Text = "Queue",
|
||||
TextAlign = Button.AlignMode.Center,
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsStretchRatio = 1,
|
||||
};
|
||||
|
||||
var spacer = new Control()
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsStretchRatio = 3,
|
||||
};
|
||||
|
||||
spacer.SetAnchorAndMarginPreset(LayoutPreset.Wide);
|
||||
|
||||
var hboxFilter = new HBoxContainer()
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
SizeFlagsStretchRatio = 1
|
||||
};
|
||||
|
||||
SearchBar = new LineEdit()
|
||||
{
|
||||
PlaceHolder = "Search Designs",
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsStretchRatio = 3
|
||||
};
|
||||
|
||||
SearchBar.OnTextChanged += Populate;
|
||||
|
||||
var filterButton = new Button()
|
||||
{
|
||||
Text = "Filter",
|
||||
TextAlign = Button.AlignMode.Center,
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsStretchRatio = 1,
|
||||
Disabled = true,
|
||||
};
|
||||
|
||||
Items = new ItemList()
|
||||
{
|
||||
SizeFlagsStretchRatio = 8,
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
};
|
||||
|
||||
|
||||
|
||||
Items.OnItemSelected += ItemSelected;
|
||||
|
||||
AmountLineEdit = new LineEdit()
|
||||
{
|
||||
PlaceHolder = "Amount",
|
||||
Text = "1",
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
};
|
||||
|
||||
AmountLineEdit.OnTextChanged += PopulateDisabled;
|
||||
|
||||
Materials = new ItemList()
|
||||
{
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
SizeFlagsStretchRatio = 3
|
||||
};
|
||||
|
||||
hboxButtons.AddChild(spacer);
|
||||
hboxButtons.AddChild(QueueButton);
|
||||
|
||||
hboxFilter.AddChild(SearchBar);
|
||||
hboxFilter.AddChild(filterButton);
|
||||
|
||||
vbox.AddChild(hboxButtons);
|
||||
vbox.AddChild(hboxFilter);
|
||||
vbox.AddChild(Items);
|
||||
vbox.AddChild(AmountLineEdit);
|
||||
vbox.AddChild(Materials);
|
||||
|
||||
margin.AddChild(vbox);
|
||||
|
||||
Contents.AddChild(margin);
|
||||
}
|
||||
|
||||
public void ItemSelected(ItemList.ItemListSelectedEventArgs args)
|
||||
{
|
||||
int.TryParse(AmountLineEdit.Text, out var quantity);
|
||||
if (quantity <= 0) quantity = 1;
|
||||
Owner.Queue(_shownRecipes[args.ItemIndex], quantity);
|
||||
Items.SelectMode = ItemList.ItemListSelectMode.None;
|
||||
Timer.Spawn(100, () =>
|
||||
{
|
||||
Items.Unselect(args.ItemIndex);
|
||||
Items.SelectMode = ItemList.ItemListSelectMode.Single;
|
||||
});
|
||||
}
|
||||
|
||||
public void PopulateMaterials()
|
||||
{
|
||||
Materials.Clear();
|
||||
|
||||
foreach (var (id, amount) in Owner.Storage)
|
||||
{
|
||||
if (!PrototypeManager.TryIndex(id, out MaterialPrototype materialPrototype)) continue;
|
||||
var material = materialPrototype.Material;
|
||||
Materials.AddItem($"{material.Name} {amount} cm³", material.Icon.Frame0(), false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables or enables shown recipes depending on whether there are enough materials for it or not.
|
||||
/// </summary>
|
||||
public void PopulateDisabled()
|
||||
{
|
||||
int.TryParse(AmountLineEdit.Text, out var quantity);
|
||||
if (quantity <= 0) quantity = 1;
|
||||
for (var i = 0; i < _shownRecipes.Count; i++)
|
||||
{
|
||||
var prototype = _shownRecipes[i];
|
||||
Items.SetItemDisabled(i, !Owner.Lathe.CanProduce(prototype, quantity));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="PopulateDisabled()"/>
|
||||
public void PopulateDisabled(LineEdit.LineEditEventArgs args)
|
||||
{
|
||||
PopulateDisabled();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds shown recipes to the ItemList control.
|
||||
/// </summary>
|
||||
public void PopulateList()
|
||||
{
|
||||
Items.Clear();
|
||||
for (var i = 0; i < _shownRecipes.Count; i++)
|
||||
{
|
||||
var prototype = _shownRecipes[i];
|
||||
Items.AddItem(prototype.Name, prototype.Icon.Frame0());
|
||||
}
|
||||
|
||||
PopulateDisabled();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates the list of recipes that will actually be shown, using the current filters.
|
||||
/// </summary>
|
||||
public void Populate()
|
||||
{
|
||||
_shownRecipes.Clear();
|
||||
|
||||
foreach (var prototype in Owner.Database)
|
||||
{
|
||||
if (SearchBar.Text.Trim().Length != 0)
|
||||
{
|
||||
if (prototype.Name.ToLowerInvariant().Contains(SearchBar.Text.Trim().ToLowerInvariant()))
|
||||
_shownRecipes.Add(prototype);
|
||||
continue;
|
||||
}
|
||||
|
||||
_shownRecipes.Add(prototype);
|
||||
}
|
||||
|
||||
PopulateList();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Populate"/>
|
||||
public void Populate(LineEdit.LineEditEventArgs args)
|
||||
{
|
||||
Populate();
|
||||
}
|
||||
}
|
||||
}
|
||||
142
Content.Client/Research/LatheQueueMenu.cs
Normal file
142
Content.Client/Research/LatheQueueMenu.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using Content.Client.GameObjects.Components.Research;
|
||||
using Content.Shared.Research;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
using Robust.Client.Interfaces.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Client.Research
|
||||
{
|
||||
public class LatheQueueMenu : SS14Window
|
||||
{
|
||||
protected override Vector2? CustomSize => (300, 450);
|
||||
|
||||
public LatheBoundUserInterface Owner { get; set; }
|
||||
|
||||
[ViewVariables]
|
||||
private ItemList QueueList;
|
||||
private Label NameLabel;
|
||||
private Label Description;
|
||||
private TextureRect Icon;
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
Title = "Lathe Queue";
|
||||
|
||||
var margin = new MarginContainer()
|
||||
{
|
||||
MarginTop = 5f,
|
||||
MarginLeft = 5f,
|
||||
MarginRight = -5f,
|
||||
MarginBottom = -5f,
|
||||
};
|
||||
|
||||
margin.SetAnchorAndMarginPreset(LayoutPreset.Wide);
|
||||
|
||||
var vbox = new VBoxContainer();
|
||||
|
||||
vbox.SetAnchorAndMarginPreset(LayoutPreset.Wide);
|
||||
|
||||
var descMargin = new MarginContainer()
|
||||
{
|
||||
MarginTop = 5f,
|
||||
MarginLeft = 5f,
|
||||
MarginRight = -5f,
|
||||
MarginBottom = -5f,
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsStretchRatio = 2,
|
||||
};
|
||||
|
||||
var hbox = new HBoxContainer()
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
};
|
||||
|
||||
Icon = new TextureRect()
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsStretchRatio = 2,
|
||||
};
|
||||
|
||||
var vboxInfo = new VBoxContainer()
|
||||
{
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
SizeFlagsStretchRatio = 3,
|
||||
};
|
||||
|
||||
NameLabel = new Label()
|
||||
{
|
||||
RectClipContent = true,
|
||||
SizeFlagsHorizontal = SizeFlags.Fill,
|
||||
};
|
||||
|
||||
Description = new Label()
|
||||
{
|
||||
RectClipContent = true,
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
SizeFlagsHorizontal = SizeFlags.Fill,
|
||||
|
||||
};
|
||||
|
||||
QueueList = new ItemList()
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.Fill,
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
SizeFlagsStretchRatio = 3,
|
||||
SelectMode = ItemList.ItemListSelectMode.None
|
||||
};
|
||||
|
||||
vboxInfo.AddChild(NameLabel);
|
||||
vboxInfo.AddChild(Description);
|
||||
|
||||
hbox.AddChild(Icon);
|
||||
hbox.AddChild(vboxInfo);
|
||||
|
||||
descMargin.AddChild(hbox);
|
||||
|
||||
vbox.AddChild(descMargin);
|
||||
vbox.AddChild(QueueList);
|
||||
|
||||
margin.AddChild(vbox);
|
||||
|
||||
Contents.AddChild(margin);
|
||||
|
||||
ClearInfo();
|
||||
}
|
||||
|
||||
public void SetInfo(LatheRecipePrototype recipe)
|
||||
{
|
||||
Icon.Texture = recipe.Icon.Frame0();
|
||||
if (recipe.Name != null)
|
||||
NameLabel.Text = recipe.Name;
|
||||
if (recipe.Description != null)
|
||||
Description.Text = recipe.Description;
|
||||
}
|
||||
|
||||
public void ClearInfo()
|
||||
{
|
||||
Icon.Texture = Texture.Transparent;
|
||||
NameLabel.Text = "-------";
|
||||
Description.Text = "Not producing anything.";
|
||||
}
|
||||
|
||||
public void PopulateList()
|
||||
{
|
||||
QueueList.Clear();
|
||||
var idx = 1;
|
||||
foreach (var recipe in Owner.QueuedRecipes)
|
||||
{
|
||||
QueueList.AddItem($"{idx}. {recipe.Name}", recipe.Icon.Frame0(), false);
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
114
Content.Client/UserInterface/EscapeMenu.cs
Normal file
114
Content.Client/UserInterface/EscapeMenu.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.Interfaces.Graphics;
|
||||
using Robust.Client.Interfaces.Placement;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Interfaces.Configuration;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.UserInterface
|
||||
{
|
||||
internal sealed class EscapeMenu : SS14Window
|
||||
{
|
||||
private readonly IClientConsole _console;
|
||||
private readonly ITileDefinitionManager __tileDefinitionManager;
|
||||
private readonly IPlacementManager _placementManager;
|
||||
private readonly IPrototypeManager _prototypeManager;
|
||||
private readonly IResourceCache _resourceCache;
|
||||
private readonly IConfigurationManager _configSystem;
|
||||
private readonly ILocalizationManager _localizationManager;
|
||||
|
||||
private BaseButton QuitButton;
|
||||
private BaseButton OptionsButton;
|
||||
private BaseButton SpawnEntitiesButton;
|
||||
private BaseButton SpawnTilesButton;
|
||||
private OptionsMenu optionsMenu;
|
||||
|
||||
public EscapeMenu(IClientConsole console,
|
||||
ITileDefinitionManager tileDefinitionManager,
|
||||
IPlacementManager placementManager,
|
||||
IPrototypeManager prototypeManager,
|
||||
IResourceCache resourceCache,
|
||||
IConfigurationManager configSystem, ILocalizationManager localizationManager)
|
||||
{
|
||||
_configSystem = configSystem;
|
||||
_localizationManager = localizationManager;
|
||||
_console = console;
|
||||
__tileDefinitionManager = tileDefinitionManager;
|
||||
_placementManager = placementManager;
|
||||
_prototypeManager = prototypeManager;
|
||||
_resourceCache = resourceCache;
|
||||
|
||||
PerformLayout();
|
||||
}
|
||||
|
||||
private void PerformLayout()
|
||||
{
|
||||
optionsMenu = new OptionsMenu(_configSystem);
|
||||
|
||||
Resizable = false;
|
||||
|
||||
Title = "Menu";
|
||||
|
||||
var vBox = new VBoxContainer {SeparationOverride = 2};
|
||||
Contents.AddChild(vBox);
|
||||
|
||||
SpawnEntitiesButton = new Button {Text = "Spawn Entities"};
|
||||
SpawnEntitiesButton.OnPressed += OnSpawnEntitiesButtonClicked;
|
||||
vBox.AddChild(SpawnEntitiesButton);
|
||||
|
||||
SpawnTilesButton = new Button {Text = "Spawn Tiles"};
|
||||
SpawnTilesButton.OnPressed += OnSpawnTilesButtonClicked;
|
||||
vBox.AddChild(SpawnTilesButton);
|
||||
|
||||
// Add a spacer.
|
||||
vBox.AddChild(new Control { CustomMinimumSize = (0, 5)});
|
||||
|
||||
OptionsButton = new Button {Text = "Options"};
|
||||
OptionsButton.OnPressed += OnOptionsButtonClicked;
|
||||
vBox.AddChild(OptionsButton);
|
||||
|
||||
QuitButton = new Button {Text = "Quit"};
|
||||
QuitButton.OnPressed += OnQuitButtonClicked;
|
||||
vBox.AddChild(QuitButton);
|
||||
|
||||
Size = CombinedMinimumSize;
|
||||
}
|
||||
|
||||
private void OnQuitButtonClicked(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
_console.ProcessCommand("disconnect");
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void OnOptionsButtonClicked(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
optionsMenu.OpenCentered();
|
||||
}
|
||||
|
||||
private void OnSpawnEntitiesButtonClicked(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
var window = new EntitySpawnWindow(_placementManager, _prototypeManager, _resourceCache, _localizationManager);
|
||||
window.OpenToLeft();
|
||||
}
|
||||
|
||||
private void OnSpawnTilesButtonClicked(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
var window = new TileSpawnWindow(__tileDefinitionManager, _placementManager, _resourceCache);
|
||||
window.OpenToLeft();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (disposing)
|
||||
{
|
||||
optionsMenu.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
391
Content.Client/UserInterface/GameHud.cs
Normal file
391
Content.Client/UserInterface/GameHud.cs
Normal file
@@ -0,0 +1,391 @@
|
||||
using System;
|
||||
using Content.Client.Utility;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
using Robust.Client.Interfaces.Input;
|
||||
using Robust.Client.Interfaces.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.UserInterface
|
||||
{
|
||||
/// <summary>
|
||||
/// Responsible for laying out the default game HUD.
|
||||
/// </summary>
|
||||
public interface IGameHud
|
||||
{
|
||||
Control RootControl { get; }
|
||||
|
||||
// Escape top button.
|
||||
bool EscapeButtonDown { get; set; }
|
||||
Action<bool> EscapeButtonToggled { get; set; }
|
||||
|
||||
// Character top button.
|
||||
bool CharacterButtonDown { get; set; }
|
||||
bool CharacterButtonVisible { get; set; }
|
||||
Action<bool> CharacterButtonToggled { get; set; }
|
||||
|
||||
// Inventory top button.
|
||||
bool InventoryButtonDown { get; set; }
|
||||
bool InventoryButtonVisible { get; set; }
|
||||
Action<bool> InventoryButtonToggled { get; set; }
|
||||
|
||||
// Crafting top button.
|
||||
bool CraftingButtonDown { get; set; }
|
||||
bool CraftingButtonVisible { get; set; }
|
||||
Action<bool> CraftingButtonToggled { get; set; }
|
||||
|
||||
// Sandbox top button.
|
||||
bool SandboxButtonDown { get; set; }
|
||||
bool SandboxButtonVisible { get; set; }
|
||||
Action<bool> SandboxButtonToggled { get; set; }
|
||||
|
||||
Control HandsContainer { get; }
|
||||
Control InventoryQuickButtonContainer { get; }
|
||||
|
||||
// Init logic.
|
||||
void Initialize();
|
||||
}
|
||||
|
||||
internal sealed class GameHud : IGameHud
|
||||
{
|
||||
private HBoxContainer _topButtonsContainer;
|
||||
private TopButton _buttonEscapeMenu;
|
||||
private TopButton _buttonTutorial;
|
||||
private TopButton _buttonCharacterMenu;
|
||||
private TopButton _buttonInventoryMenu;
|
||||
private TopButton _buttonCraftingMenu;
|
||||
private TopButton _buttonSandboxMenu;
|
||||
private TutorialWindow _tutorialWindow;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IResourceCache _resourceCache;
|
||||
[Dependency] private readonly ILocalizationManager _loc;
|
||||
[Dependency] private readonly IInputManager _inputManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
public Control HandsContainer { get; private set; }
|
||||
public Control InventoryQuickButtonContainer { get; private set; }
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
RootControl = new Control {MouseFilter = Control.MouseFilterMode.Ignore};
|
||||
|
||||
RootControl.SetAnchorPreset(Control.LayoutPreset.Wide);
|
||||
|
||||
var escapeTexture = _resourceCache.GetTexture("/Textures/UserInterface/hamburger.svg.96dpi.png");
|
||||
var characterTexture = _resourceCache.GetTexture("/Textures/UserInterface/character.svg.96dpi.png");
|
||||
var inventoryTexture = _resourceCache.GetTexture("/Textures/UserInterface/inventory.svg.96dpi.png");
|
||||
var craftingTexture = _resourceCache.GetTexture("/Textures/UserInterface/hammer.svg.96dpi.png");
|
||||
var tutorialTexture = _resourceCache.GetTexture("/Textures/UserInterface/students-cap.svg.96dpi.png");
|
||||
var sandboxTexture = _resourceCache.GetTexture("/Textures/UserInterface/sandbox.svg.96dpi.png");
|
||||
|
||||
_topButtonsContainer = new HBoxContainer
|
||||
{
|
||||
SeparationOverride = 4
|
||||
};
|
||||
|
||||
RootControl.AddChild(_topButtonsContainer);
|
||||
_topButtonsContainer.SetAnchorAndMarginPreset(Control.LayoutPreset.TopLeft, margin: 10);
|
||||
|
||||
// TODO: Pull key names here from the actual key binding config.
|
||||
// Escape
|
||||
_buttonEscapeMenu = new TopButton(escapeTexture, "Esc")
|
||||
{
|
||||
ToolTip = _loc.GetString("Open escape menu.")
|
||||
};
|
||||
|
||||
_topButtonsContainer.AddChild(_buttonEscapeMenu);
|
||||
|
||||
_buttonEscapeMenu.OnToggled += args => EscapeButtonToggled?.Invoke(args.Pressed);
|
||||
|
||||
// Tutorial
|
||||
_buttonTutorial = new TopButton(tutorialTexture, "F1")
|
||||
{
|
||||
ToolTip = _loc.GetString("Open tutorial.")
|
||||
};
|
||||
|
||||
_topButtonsContainer.AddChild(_buttonTutorial);
|
||||
|
||||
_buttonTutorial.OnToggled += a => ButtonTutorialOnOnToggled();
|
||||
|
||||
// Character
|
||||
_buttonCharacterMenu = new TopButton(characterTexture, "C")
|
||||
{
|
||||
ToolTip = _loc.GetString("Open character menu."),
|
||||
Visible = false
|
||||
};
|
||||
|
||||
_topButtonsContainer.AddChild(_buttonCharacterMenu);
|
||||
|
||||
_buttonCharacterMenu.OnToggled += args => CharacterButtonToggled?.Invoke(args.Pressed);
|
||||
|
||||
// Inventory
|
||||
_buttonInventoryMenu = new TopButton(inventoryTexture, "I")
|
||||
{
|
||||
ToolTip = _loc.GetString("Open inventory menu."),
|
||||
Visible = false
|
||||
};
|
||||
|
||||
_topButtonsContainer.AddChild(_buttonInventoryMenu);
|
||||
|
||||
_buttonInventoryMenu.OnToggled += args => InventoryButtonToggled?.Invoke(args.Pressed);
|
||||
|
||||
// Crafting
|
||||
_buttonCraftingMenu = new TopButton(craftingTexture, "G")
|
||||
{
|
||||
ToolTip = _loc.GetString("Open crafting menu."),
|
||||
Visible = false
|
||||
};
|
||||
|
||||
_topButtonsContainer.AddChild(_buttonCraftingMenu);
|
||||
|
||||
_buttonCraftingMenu.OnToggled += args => CraftingButtonToggled?.Invoke(args.Pressed);
|
||||
|
||||
// Sandbox
|
||||
_buttonSandboxMenu = new TopButton(sandboxTexture, "B")
|
||||
{
|
||||
ToolTip = _loc.GetString("Open sandbox menu."),
|
||||
Visible = true
|
||||
};
|
||||
|
||||
_topButtonsContainer.AddChild(_buttonSandboxMenu);
|
||||
|
||||
_buttonSandboxMenu.OnToggled += args => SandboxButtonToggled?.Invoke(args.Pressed);
|
||||
|
||||
_tutorialWindow = new TutorialWindow();
|
||||
|
||||
_tutorialWindow.OnClose += () => _buttonTutorial.Pressed = false;
|
||||
|
||||
_inputManager.SetInputCommand(ContentKeyFunctions.OpenTutorial,
|
||||
InputCmdHandler.FromDelegate(s => ButtonTutorialOnOnToggled()));
|
||||
|
||||
var inventoryContainer = new HBoxContainer
|
||||
{
|
||||
GrowHorizontal = Control.GrowDirection.Begin,
|
||||
GrowVertical = Control.GrowDirection.Begin,
|
||||
SeparationOverride = 10
|
||||
};
|
||||
|
||||
RootControl.AddChild(inventoryContainer);
|
||||
inventoryContainer.SetAnchorAndMarginPreset(Control.LayoutPreset.BottomRight);
|
||||
|
||||
InventoryQuickButtonContainer = new MarginContainer
|
||||
{
|
||||
GrowHorizontal = Control.GrowDirection.Begin,
|
||||
GrowVertical = Control.GrowDirection.Begin,
|
||||
};
|
||||
|
||||
HandsContainer = new MarginContainer
|
||||
{
|
||||
GrowHorizontal = Control.GrowDirection.Both,
|
||||
GrowVertical = Control.GrowDirection.Begin
|
||||
};
|
||||
|
||||
inventoryContainer.Children.Add(HandsContainer);
|
||||
inventoryContainer.Children.Add(InventoryQuickButtonContainer);
|
||||
}
|
||||
|
||||
private void ButtonTutorialOnOnToggled()
|
||||
{
|
||||
if (_tutorialWindow.IsOpen)
|
||||
{
|
||||
if (!_tutorialWindow.IsAtFront())
|
||||
{
|
||||
_tutorialWindow.MoveToFront();
|
||||
_buttonTutorial.Pressed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_tutorialWindow.Close();
|
||||
_buttonTutorial.Pressed = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_tutorialWindow.OpenCentered();
|
||||
_buttonTutorial.Pressed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public Control RootControl { get; private set; }
|
||||
|
||||
public bool EscapeButtonDown
|
||||
{
|
||||
get => _buttonEscapeMenu.Pressed;
|
||||
set => _buttonEscapeMenu.Pressed = value;
|
||||
}
|
||||
|
||||
public Action<bool> EscapeButtonToggled { get; set; }
|
||||
|
||||
public bool CharacterButtonDown
|
||||
{
|
||||
get => _buttonCharacterMenu.Pressed;
|
||||
set => _buttonCharacterMenu.Pressed = value;
|
||||
}
|
||||
|
||||
public bool CharacterButtonVisible
|
||||
{
|
||||
get => _buttonCharacterMenu.Visible;
|
||||
set => _buttonCharacterMenu.Visible = value;
|
||||
}
|
||||
|
||||
public Action<bool> CharacterButtonToggled { get; set; }
|
||||
|
||||
public bool InventoryButtonDown
|
||||
{
|
||||
get => _buttonInventoryMenu.Pressed;
|
||||
set => _buttonInventoryMenu.Pressed = value;
|
||||
}
|
||||
|
||||
public bool InventoryButtonVisible
|
||||
{
|
||||
get => _buttonInventoryMenu.Visible;
|
||||
set => _buttonInventoryMenu.Visible = value;
|
||||
}
|
||||
|
||||
public Action<bool> InventoryButtonToggled { get; set; }
|
||||
|
||||
public bool CraftingButtonDown
|
||||
{
|
||||
get => _buttonCraftingMenu.Pressed;
|
||||
set => _buttonCraftingMenu.Pressed = value;
|
||||
}
|
||||
|
||||
public bool CraftingButtonVisible
|
||||
{
|
||||
get => _buttonCraftingMenu.Visible;
|
||||
set => _buttonCraftingMenu.Visible = value;
|
||||
}
|
||||
|
||||
public Action<bool> CraftingButtonToggled { get; set; }
|
||||
|
||||
public bool SandboxButtonDown
|
||||
{
|
||||
get => _buttonSandboxMenu.Pressed;
|
||||
set => _buttonSandboxMenu.Pressed = value;
|
||||
}
|
||||
|
||||
public bool SandboxButtonVisible
|
||||
{
|
||||
get => _buttonSandboxMenu.Visible;
|
||||
set => _buttonSandboxMenu.Visible = value;
|
||||
}
|
||||
|
||||
public Action<bool> SandboxButtonToggled { get; set; }
|
||||
|
||||
public sealed class TopButton : BaseButton
|
||||
{
|
||||
public const string StyleClassLabelTopButton = "topButtonLabel";
|
||||
|
||||
private static readonly Color ColorNormal = Color.FromHex("#7b7e9e");
|
||||
private static readonly Color ColorHovered = Color.FromHex("#9699bb");
|
||||
private static readonly Color ColorPressed = Color.FromHex("#789B8C");
|
||||
|
||||
private readonly VBoxContainer _container;
|
||||
private readonly TextureRect _textureRect;
|
||||
private readonly Label _label;
|
||||
|
||||
public TopButton(Texture texture, string keyName)
|
||||
{
|
||||
ToggleMode = true;
|
||||
|
||||
_container = new VBoxContainer {MouseFilter = MouseFilterMode.Ignore};
|
||||
AddChild(_container);
|
||||
_container.AddChild(_textureRect = new TextureRect
|
||||
{
|
||||
Texture = texture,
|
||||
SizeFlagsHorizontal = SizeFlags.ShrinkCenter,
|
||||
SizeFlagsVertical = SizeFlags.Expand | SizeFlags.ShrinkCenter,
|
||||
MouseFilter = MouseFilterMode.Ignore,
|
||||
ModulateSelfOverride = ColorNormal,
|
||||
CustomMinimumSize = (0, 32),
|
||||
Stretch = TextureRect.StretchMode.KeepCentered
|
||||
});
|
||||
|
||||
_container.AddChild(_label = new Label
|
||||
{
|
||||
Text = keyName,
|
||||
SizeFlagsHorizontal = SizeFlags.ShrinkCenter,
|
||||
MouseFilter = MouseFilterMode.Ignore,
|
||||
ModulateSelfOverride = ColorNormal
|
||||
});
|
||||
|
||||
_label.AddStyleClass(StyleClassLabelTopButton);
|
||||
|
||||
_container.SetAnchorAndMarginPreset(LayoutPreset.Wide);
|
||||
|
||||
DrawModeChanged();
|
||||
}
|
||||
|
||||
protected override Vector2 CalculateMinimumSize()
|
||||
{
|
||||
var styleSize = ActualStyleBox?.MinimumSize ?? Vector2.Zero;
|
||||
return (0, 4) + styleSize + _container?.CombinedMinimumSize ?? Vector2.Zero;
|
||||
}
|
||||
|
||||
protected override void Draw(DrawingHandleScreen handle)
|
||||
{
|
||||
ActualStyleBox?.Draw(handle, PixelSizeBox);
|
||||
}
|
||||
|
||||
private StyleBox ActualStyleBox
|
||||
{
|
||||
get
|
||||
{
|
||||
TryGetStyleProperty(Button.StylePropertyStyleBox, out StyleBox ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DrawModeChanged()
|
||||
{
|
||||
switch (DrawMode)
|
||||
{
|
||||
case DrawModeEnum.Normal:
|
||||
StylePseudoClass = Button.StylePseudoClassNormal;
|
||||
_textureRect.ModulateSelfOverride = ColorNormal;
|
||||
_label.ModulateSelfOverride = ColorNormal;
|
||||
break;
|
||||
|
||||
case DrawModeEnum.Pressed:
|
||||
StylePseudoClass = Button.StylePseudoClassPressed;
|
||||
_textureRect.ModulateSelfOverride = ColorPressed;
|
||||
_label.ModulateSelfOverride = ColorPressed;
|
||||
break;
|
||||
|
||||
case DrawModeEnum.Hover:
|
||||
StylePseudoClass = Button.StylePseudoClassHover;
|
||||
_textureRect.ModulateSelfOverride = ColorHovered;
|
||||
_label.ModulateSelfOverride = ColorHovered;
|
||||
break;
|
||||
|
||||
case DrawModeEnum.Disabled:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void StylePropertiesChanged()
|
||||
{
|
||||
base.StylePropertiesChanged();
|
||||
|
||||
if (_container == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var box = ActualStyleBox ?? new StyleBoxEmpty();
|
||||
|
||||
_container.MarginLeft = box.GetContentMargin(StyleBox.Margin.Left);
|
||||
_container.MarginRight = -box.GetContentMargin(StyleBox.Margin.Right);
|
||||
_container.MarginTop = box.GetContentMargin(StyleBox.Margin.Top) + 4;
|
||||
_container.MarginBottom = -box.GetContentMargin(StyleBox.Margin.Bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user