Compare commits
1100 Commits
devtest-20
...
devtest-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
157db79ae7 | ||
|
|
c01c39954a | ||
|
|
c48eb572e3 | ||
|
|
b03ab1212f | ||
|
|
8a9b5e7e01 | ||
|
|
9b90e4f25a | ||
|
|
8a4401bdcb | ||
|
|
d52e90cf23 | ||
|
|
14ef0a7740 | ||
|
|
c1f79b348a | ||
|
|
62166a50d9 | ||
|
|
283b330403 | ||
|
|
2cf6b74295 | ||
|
|
1a9f707d18 | ||
|
|
06a1c88e86 | ||
|
|
f358b566b1 | ||
|
|
15fc27d142 | ||
|
|
ad20597d74 | ||
|
|
6d409a7c97 | ||
|
|
36d5ae5421 | ||
|
|
db9744ea7f | ||
|
|
5e62fe86fc | ||
|
|
6cfa27c33b | ||
|
|
a405969199 | ||
|
|
9a6f3b4c05 | ||
|
|
7be059a79b | ||
|
|
29b55de042 | ||
|
|
235fb19aa8 | ||
|
|
c0f54fa4fc | ||
|
|
cdc216aca0 | ||
|
|
4505053618 | ||
|
|
e0d53126d6 | ||
|
|
12ff1dd14c | ||
|
|
50db3152f6 | ||
|
|
9c4fd0e3d3 | ||
|
|
8d27d22100 | ||
|
|
27f1a7ab27 | ||
|
|
d52e4793fe | ||
|
|
544ac6cb33 | ||
|
|
06fbc1a6cf | ||
|
|
edab10e6a6 | ||
|
|
dd99fc93e4 | ||
|
|
dbe824d4e5 | ||
|
|
6e73d7f5c2 | ||
|
|
960056d300 | ||
|
|
3efac3287e | ||
|
|
c4b4a8c8a5 | ||
|
|
b833f033bf | ||
|
|
ad75e2be89 | ||
|
|
7ee4fbeb0d | ||
|
|
9886f0ca9a | ||
|
|
46cf56d6ff | ||
|
|
e7af295b5e | ||
|
|
9d179d9a1a | ||
|
|
15010f9567 | ||
|
|
a7f4f6c1cf | ||
|
|
3eeb677f14 | ||
|
|
ef69a3de66 | ||
|
|
0aa5e07252 | ||
|
|
07d58337f1 | ||
|
|
b5e3f25418 | ||
|
|
19b02875c7 | ||
|
|
8a74f6ea18 | ||
|
|
a847f3eafa | ||
|
|
a751f074e7 | ||
|
|
75cb5c2166 | ||
|
|
70a86bed7a | ||
|
|
f67f8ed05e | ||
|
|
1ae53220d6 | ||
|
|
c546cb552e | ||
|
|
10f8836d7b | ||
|
|
7ecd4124ce | ||
|
|
54cd77be8e | ||
|
|
43388cb7fc | ||
|
|
4cc5104fde | ||
|
|
d519cabae3 | ||
|
|
9852e29835 | ||
|
|
3a427c3630 | ||
|
|
336656e8f7 | ||
|
|
06ad9666e8 | ||
|
|
728e0c6600 | ||
|
|
657e690bdd | ||
|
|
2d36d0a659 | ||
|
|
c42fd5d2e2 | ||
|
|
10bf97eff6 | ||
|
|
ea9992247d | ||
|
|
b90fecff76 | ||
|
|
a4fc9fea3b | ||
|
|
b7c7eff2a2 | ||
|
|
8c10dc406a | ||
|
|
05c3861426 | ||
|
|
1ef5db8896 | ||
|
|
006a87692a | ||
|
|
e019b70420 | ||
|
|
70ec5b0344 | ||
|
|
5401ace540 | ||
|
|
ab9081c852 | ||
|
|
3a9b35980c | ||
|
|
150439d215 | ||
|
|
b01a534a98 | ||
|
|
758b0b08d0 | ||
|
|
f87ba1d8a4 | ||
|
|
67fa7bdcc9 | ||
|
|
0b03aca104 | ||
|
|
3bf61f1043 | ||
|
|
ac975f4139 | ||
|
|
6d12301f88 | ||
|
|
914950c4a5 | ||
|
|
b417b267dd | ||
|
|
aae497eff1 | ||
|
|
3c9db4c2ac | ||
|
|
8c3793e7ea | ||
|
|
60a7f53491 | ||
|
|
04bfd62f2f | ||
|
|
117b8b3653 | ||
|
|
70cb0d2924 | ||
|
|
c5ea496c45 | ||
|
|
01e955ca37 | ||
|
|
fdb66c769c | ||
|
|
38b3fbbdbe | ||
|
|
3bc5d2d02c | ||
|
|
ac7eda8ca2 | ||
|
|
baf58f53b3 | ||
|
|
8513a83331 | ||
|
|
bf7fecff10 | ||
|
|
6e1f2f636c | ||
|
|
8b3db6f3d6 | ||
|
|
39d0abe982 | ||
|
|
c2026dc254 | ||
|
|
cae6c28754 | ||
|
|
3b99924799 | ||
|
|
b2b548b103 | ||
|
|
c2e3806a77 | ||
|
|
25500a7dda | ||
|
|
8c394a4cb5 | ||
|
|
2e7bd4de4b | ||
|
|
7261322e41 | ||
|
|
cada396733 | ||
|
|
9f9709f058 | ||
|
|
26fc65209d | ||
|
|
f642cead44 | ||
|
|
616d9421d6 | ||
|
|
20e5219cf4 | ||
|
|
9edda21b06 | ||
|
|
6d6822ca15 | ||
|
|
a2269e7ee7 | ||
|
|
e8f443f4a9 | ||
|
|
67f8452178 | ||
|
|
47f6e407d9 | ||
|
|
889e2152a4 | ||
|
|
173aae1f81 | ||
|
|
baed80983b | ||
|
|
b00423dc76 | ||
|
|
376ed15079 | ||
|
|
a6d8d6cd8e | ||
|
|
b3ee8b447e | ||
|
|
5f588561b6 | ||
|
|
83c53e17e0 | ||
|
|
c028488894 | ||
|
|
b066005f7e | ||
|
|
87e33a75c6 | ||
|
|
4961b0943b | ||
|
|
eda1e9c266 | ||
|
|
bc7bf174d8 | ||
|
|
2e06d5790b | ||
|
|
ab8790e8f1 | ||
|
|
7d630e63e7 | ||
|
|
77899191f3 | ||
|
|
6871873e93 | ||
|
|
fc4bd131cd | ||
|
|
d866286f82 | ||
|
|
76dfda164e | ||
|
|
134d47e48c | ||
|
|
a2dbd5e013 | ||
|
|
c27412c83a | ||
|
|
4143aba595 | ||
|
|
39ccac4022 | ||
|
|
7943f4deb6 | ||
|
|
2dda2d7689 | ||
|
|
b8f2a14ea0 | ||
|
|
595809f090 | ||
|
|
477db9cd4a | ||
|
|
67ff292d62 | ||
|
|
ae882b85a9 | ||
|
|
5e92915095 | ||
|
|
95809db03c | ||
|
|
6d5a5121bc | ||
|
|
86992751c7 | ||
|
|
2eba8b6c37 | ||
|
|
e1523e939d | ||
|
|
15a92f443d | ||
|
|
b57c68e392 | ||
|
|
e95fcb6bc0 | ||
|
|
6581fcb6a7 | ||
|
|
6551337bd8 | ||
|
|
137df52fdd | ||
|
|
b79aa7eb6a | ||
|
|
1c8c49dc8e | ||
|
|
f0c808d2fc | ||
|
|
26d9ae88df | ||
|
|
3c7f119bb1 | ||
|
|
fea35923f0 | ||
|
|
9627776318 | ||
|
|
6dcde3af72 | ||
|
|
43717a89b5 | ||
|
|
c7ba359688 | ||
|
|
dc3dbf6d85 | ||
|
|
71664c85ff | ||
|
|
cb41be113a | ||
|
|
4fe7daa85e | ||
|
|
f75afc6ee4 | ||
|
|
67fd71ab92 | ||
|
|
385e70552e | ||
|
|
1e2c67bfca | ||
|
|
56739f87fb | ||
|
|
571eb7614f | ||
|
|
c6c3a8c60d | ||
|
|
ae7cfa56b7 | ||
|
|
b856613194 | ||
|
|
86305879cb | ||
|
|
e5a1a8a706 | ||
|
|
27602a4a97 | ||
|
|
a98e460257 | ||
|
|
0349435650 | ||
|
|
c3fbdca18f | ||
|
|
acb5245a28 | ||
|
|
23561cd76b | ||
|
|
d3ab3d7d78 | ||
|
|
5b870be83f | ||
|
|
6130d5622c | ||
|
|
318c4e3456 | ||
|
|
ab701449e2 | ||
|
|
9a3447d863 | ||
|
|
5280637adf | ||
|
|
3bce55ac44 | ||
|
|
a3f79503ed | ||
|
|
02d462a82c | ||
|
|
3d17328d0d | ||
|
|
0e81abc21b | ||
|
|
803b930405 | ||
|
|
6adf45bcb4 | ||
|
|
01417c88c5 | ||
|
|
60bbbe0d93 | ||
|
|
888dfd3654 | ||
|
|
12de56ff62 | ||
|
|
b7cee41c54 | ||
|
|
10aac03f75 | ||
|
|
0eb0041f90 | ||
|
|
b38018af9c | ||
|
|
4df5ac0385 | ||
|
|
a7476bc303 | ||
|
|
cc4b3cb361 | ||
|
|
31a965b29a | ||
|
|
7a213338a2 | ||
|
|
fb27a25e52 | ||
|
|
534b09ae4a | ||
|
|
341a9f370c | ||
|
|
607d9b2d5c | ||
|
|
507ce40ad2 | ||
|
|
f58c3aed32 | ||
|
|
55e85bd9ca | ||
|
|
4bf614c5cd | ||
|
|
96b06c75d1 | ||
|
|
d261648ab0 | ||
|
|
7b81b9e806 | ||
|
|
a93aea3e4e | ||
|
|
2cfacc2c7d | ||
|
|
6d6b21a0eb | ||
|
|
7a78c37851 | ||
|
|
b8a9f41892 | ||
|
|
227567dfe1 | ||
|
|
d5ff5c672b | ||
|
|
393f6eca3a | ||
|
|
d193ef856e | ||
|
|
44d3691fa1 | ||
|
|
27d0465891 | ||
|
|
52a9fcef3c | ||
|
|
7386816f52 | ||
|
|
9c0075b233 | ||
|
|
e4c5700baf | ||
|
|
e1b7df8b6a | ||
|
|
c999b2d778 | ||
|
|
bfb6c671fb | ||
|
|
7c6ec577dc | ||
|
|
626b40f31b | ||
|
|
5af12440ba | ||
|
|
4614f6febe | ||
|
|
1354ffc32e | ||
|
|
3723939c99 | ||
|
|
e099739e13 | ||
|
|
21a48cc41d | ||
|
|
4740266308 | ||
|
|
78139413d7 | ||
|
|
f0578a75f4 | ||
|
|
7a0e55a02a | ||
|
|
f132bac80d | ||
|
|
109ea4fe5b | ||
|
|
f33feafd0e | ||
|
|
9195356e3a | ||
|
|
eff91108f4 | ||
|
|
e40c0516e6 | ||
|
|
c580a94ab7 | ||
|
|
6a545bb942 | ||
|
|
672bd2d9fe | ||
|
|
3ab4a584ab | ||
|
|
150d02ac0d | ||
|
|
6d26f60904 | ||
|
|
b42276953f | ||
|
|
7c290b9f76 | ||
|
|
51fe1d6629 | ||
|
|
8f558d2b47 | ||
|
|
3f5fadf2e9 | ||
|
|
07c16cee1d | ||
|
|
24130dfcdc | ||
|
|
15a2341a91 | ||
|
|
86f61298e6 | ||
|
|
3cd7ec3878 | ||
|
|
8b13d3e4c7 | ||
|
|
327d451abc | ||
|
|
2dac16ee02 | ||
|
|
d4b92a19d7 | ||
|
|
e0357596f5 | ||
|
|
1ef27d18c1 | ||
|
|
1d2d8ed107 | ||
|
|
1bf01bc214 | ||
|
|
0015deca47 | ||
|
|
ddfdc6e90f | ||
|
|
5db2ad54f2 | ||
|
|
b8a5750529 | ||
|
|
98d5b8c7cc | ||
|
|
3f34154a1e | ||
|
|
3119f831b3 | ||
|
|
8a07b762a2 | ||
|
|
551ab2fc59 | ||
|
|
99957e57b9 | ||
|
|
be2c59bc6e | ||
|
|
57f9a49b66 | ||
|
|
54bd0eb99d | ||
|
|
485faac294 | ||
|
|
d531d6f3ef | ||
|
|
3a9fdb82f5 | ||
|
|
5024ae1156 | ||
|
|
0135dd9ed3 | ||
|
|
a6e9b86bbe | ||
|
|
237c4444b5 | ||
|
|
a68467292e | ||
|
|
978c69d0c3 | ||
|
|
3eabc59921 | ||
|
|
2b3d99fac2 | ||
|
|
2bdefe0e9e | ||
|
|
de81fc2aca | ||
|
|
b514e0a6e7 | ||
|
|
715dfa4541 | ||
|
|
ac57a37224 | ||
|
|
afd620b092 | ||
|
|
552bceb07c | ||
|
|
fe58ed1283 | ||
|
|
361e2d463c | ||
|
|
259c8d2c98 | ||
|
|
e12c1dc9aa | ||
|
|
7fb49e383d | ||
|
|
1df3e28253 | ||
|
|
bacec2689d | ||
|
|
bf397591f9 | ||
|
|
bd1a936c7a | ||
|
|
0871d6e321 | ||
|
|
265d296db6 | ||
|
|
5ec136b57c | ||
|
|
f1e8f9c9d0 | ||
|
|
f03841c4e4 | ||
|
|
0ae58ff0ea | ||
|
|
390c1899ca | ||
|
|
89aa6d1e4e | ||
|
|
24638b02a9 | ||
|
|
4b0ab6ab37 | ||
|
|
1f02d9f141 | ||
|
|
0103c38c13 | ||
|
|
3cc76f91b4 | ||
|
|
10dc248f07 | ||
|
|
d1f89c6217 | ||
|
|
76ba4fc32d | ||
|
|
37f90fff44 | ||
|
|
9fa6da3bc7 | ||
|
|
aa8cf237ab | ||
|
|
c131728aa4 | ||
|
|
2abd137494 | ||
|
|
90815ace59 | ||
|
|
53d916d7f1 | ||
|
|
2b4035979b | ||
|
|
38cdc93010 | ||
|
|
346dad3898 | ||
|
|
42256bc262 | ||
|
|
70babb4067 | ||
|
|
3603e6373d | ||
|
|
bd1760682f | ||
|
|
52d0490f95 | ||
|
|
b3b0aa75ae | ||
|
|
e42d177920 | ||
|
|
c2156af7b0 | ||
|
|
86394eb56c | ||
|
|
2d7790f5e4 | ||
|
|
3df43529a6 | ||
|
|
a1c9b27057 | ||
|
|
089dd233a5 | ||
|
|
86a7a0bd6c | ||
|
|
7ebca36a9c | ||
|
|
d5aed5a88a | ||
|
|
dac1f270ce | ||
|
|
839be24053 | ||
|
|
91c4179f05 | ||
|
|
a4b427bfac | ||
|
|
250f5bec18 | ||
|
|
336e2a10e0 | ||
|
|
b5e9b7635e | ||
|
|
0df7fa1596 | ||
|
|
c10487d635 | ||
|
|
bc9b3bef74 | ||
|
|
e57462e7ca | ||
|
|
78bf27709f | ||
|
|
fc84cd9204 | ||
|
|
e361f7b246 | ||
|
|
b0497b7505 | ||
|
|
a894e31fa5 | ||
|
|
dd062adec2 | ||
|
|
d187575a2c | ||
|
|
85096c4ba2 | ||
|
|
e13fd693c3 | ||
|
|
cc35512472 | ||
|
|
4e548291ce | ||
|
|
3ba86f329f | ||
|
|
5b34af0f12 | ||
|
|
0a9eb1ff83 | ||
|
|
942dd0e5f7 | ||
|
|
400102f3d3 | ||
|
|
9ccdeb3d36 | ||
|
|
7e0f0dd2d2 | ||
|
|
d920cbb7f6 | ||
|
|
417677e6f4 | ||
|
|
33f3038316 | ||
|
|
429dbe3e0c | ||
|
|
471fc44751 | ||
|
|
1b8e346307 | ||
|
|
a456583a08 | ||
|
|
a63c17baab | ||
|
|
9c4faddc0f | ||
|
|
6828c4c1e9 | ||
|
|
80131e7ec0 | ||
|
|
ac381a6f58 | ||
|
|
521b516bf9 | ||
|
|
6a825f8e60 | ||
|
|
f0a243ca10 | ||
|
|
e5457a3390 | ||
|
|
47e21f8bef | ||
|
|
331b854e4e | ||
|
|
274bc9cbba | ||
|
|
827f8d95b4 | ||
|
|
2946dd35d5 | ||
|
|
74d884787d | ||
|
|
5516e16fb8 | ||
|
|
cadf4eb322 | ||
|
|
269249e86e | ||
|
|
30f87d2308 | ||
|
|
8b7e72b95e | ||
|
|
1e64048956 | ||
|
|
8512e696f5 | ||
|
|
6a03a9ec5f | ||
|
|
5e04c99d57 | ||
|
|
82f15491c0 | ||
|
|
101843fbb7 | ||
|
|
9e534f3804 | ||
|
|
ca3cfc0184 | ||
|
|
d62fb901e2 | ||
|
|
73a2b59c2c | ||
|
|
2c7a56625c | ||
|
|
e2572b214f | ||
|
|
d22cd3a74f | ||
|
|
0c8fcedfdf | ||
|
|
99009c37ce | ||
|
|
d9f5771778 | ||
|
|
d35b5070fb | ||
|
|
02f41f9afc | ||
|
|
c797aa1d5e | ||
|
|
b2f7f67756 | ||
|
|
09014ab6d5 | ||
|
|
8f8747d65e | ||
|
|
be19e137e2 | ||
|
|
3155291064 | ||
|
|
a5b22e6a36 | ||
|
|
9c251e8b6a | ||
|
|
6056568182 | ||
|
|
7b7c1da18d | ||
|
|
fb5b4b3547 | ||
|
|
19918d485e | ||
|
|
45c6c6ba10 | ||
|
|
0e436bc686 | ||
|
|
b0dfea0a09 | ||
|
|
2c4e6c4188 | ||
|
|
9f3254dbd1 | ||
|
|
88cdad4189 | ||
|
|
4ba50a4379 | ||
|
|
2b6c104011 | ||
|
|
4b446d100e | ||
|
|
f9ca2114a9 | ||
|
|
afc9c6ef85 | ||
|
|
ac200f6173 | ||
|
|
73a78eadb1 | ||
|
|
c5139fb6c2 | ||
|
|
9faf9aa1b9 | ||
|
|
3c2e9be248 | ||
|
|
5b59f6612f | ||
|
|
3d69363f35 | ||
|
|
b580b4fd33 | ||
|
|
74f86d70f8 | ||
|
|
3959104f9b | ||
|
|
1ff037a257 | ||
|
|
32700df117 | ||
|
|
b4edec215e | ||
|
|
dffa1e45f4 | ||
|
|
df3b6dde34 | ||
|
|
834bbf467e | ||
|
|
4d4f94208e | ||
|
|
416713de0c | ||
|
|
c523ca8efe | ||
|
|
9acea56108 | ||
|
|
44a7422375 | ||
|
|
0d0e7eb179 | ||
|
|
ea6c840343 | ||
|
|
d2db0913ac | ||
|
|
3721dae74d | ||
|
|
9050a2447b | ||
|
|
df4c363e9c | ||
|
|
a909a3e692 | ||
|
|
dd26253905 | ||
|
|
69b7ba2d22 | ||
|
|
d2f306e488 | ||
|
|
4a6fefa434 | ||
|
|
16e0ea611e | ||
|
|
05a2e77be2 | ||
|
|
dd2fa36261 | ||
|
|
5fa1dec6d8 | ||
|
|
f86d96794d | ||
|
|
304307df5a | ||
|
|
c8856749f3 | ||
|
|
ce91c5a76f | ||
|
|
e6314a944c | ||
|
|
93d006e14e | ||
|
|
d73ed7670a | ||
|
|
c0587cc568 | ||
|
|
ed415cb637 | ||
|
|
a10deddf53 | ||
|
|
a5bc841355 | ||
|
|
41657dd291 | ||
|
|
a7d5b7b8b0 | ||
|
|
eb007fc43c | ||
|
|
301d09ea8f | ||
|
|
ac46b4b791 | ||
|
|
fb7c781a66 | ||
|
|
e5309ee586 | ||
|
|
84df61c672 | ||
|
|
de4a7cecf0 | ||
|
|
c0ece00c4b | ||
|
|
6ba02800ab | ||
|
|
84419e4259 | ||
|
|
2016ab105e | ||
|
|
def65b10bd | ||
|
|
31d98cc802 | ||
|
|
ccbaa4f816 | ||
|
|
2049d1b26a | ||
|
|
15f8469272 | ||
|
|
43cac2f051 | ||
|
|
39b7db2703 | ||
|
|
d3291ea585 | ||
|
|
2e8740d6a8 | ||
|
|
1bcad55c1f | ||
|
|
7ffc689037 | ||
|
|
7d17916e3f | ||
|
|
e877bb1206 | ||
|
|
226159d220 | ||
|
|
da4ed24064 | ||
|
|
b524dc9b72 | ||
|
|
1e43a8f590 | ||
|
|
805a8fc556 | ||
|
|
885931ae74 | ||
|
|
4d4f1d6068 | ||
|
|
fafa219d11 | ||
|
|
eae287efc3 | ||
|
|
8903577227 | ||
|
|
103cb61020 | ||
|
|
1dd1786469 | ||
|
|
3e2022a3dd | ||
|
|
28f7604172 | ||
|
|
96463634c7 | ||
|
|
01f5ecb2f9 | ||
|
|
d835090d0b | ||
|
|
c687600d66 | ||
|
|
ed618c807d | ||
|
|
85faa5edf6 | ||
|
|
3f601e1ec1 | ||
|
|
9d68b815a1 | ||
|
|
cffe5e3d9d | ||
|
|
7e72cd262c | ||
|
|
9a6602656b | ||
|
|
57da756f2f | ||
|
|
40bb45a02b | ||
|
|
f73d23661f | ||
|
|
09a019f9c6 | ||
|
|
4ec258cbdd | ||
|
|
41d7a2d429 | ||
|
|
7cec1b771d | ||
|
|
b1b74c13c3 | ||
|
|
c4a5540bfd | ||
|
|
abcb2ea512 | ||
|
|
f45dd24781 | ||
|
|
8d23994a04 | ||
|
|
46e0b3b363 | ||
|
|
689049cc12 | ||
|
|
1485194ef3 | ||
|
|
4203a3191f | ||
|
|
7b4019577d | ||
|
|
52f4e24e22 | ||
|
|
ab196a23e6 | ||
|
|
3a688e03f0 | ||
|
|
a43335c7b0 | ||
|
|
585b8dc13c | ||
|
|
b2f0ac15e8 | ||
|
|
9dc4ea8541 | ||
|
|
817f75c808 | ||
|
|
7e3830e053 | ||
|
|
b81ede2d64 | ||
|
|
6ab0ace9e1 | ||
|
|
85a5b6cc17 | ||
|
|
62af58c2e6 | ||
|
|
aeacc86028 | ||
|
|
5c4ec1bf0e | ||
|
|
e3f545cae9 | ||
|
|
71cbfc5968 | ||
|
|
fdc3a6a32d | ||
|
|
e6c1356d59 | ||
|
|
e8df28c518 | ||
|
|
98aef70e88 | ||
|
|
de0bb9ee39 | ||
|
|
c18857f15d | ||
|
|
9a0916afbb | ||
|
|
6388a6bff4 | ||
|
|
ce445f993c | ||
|
|
0e39c98989 | ||
|
|
c2bc313bf0 | ||
|
|
5c76a6e7a7 | ||
|
|
1e8912b4e4 | ||
|
|
6cc27eaa76 | ||
|
|
1020a7bfab | ||
|
|
b80928bd95 | ||
|
|
a6371f6fa9 | ||
|
|
bf314fdc7b | ||
|
|
f0033c44c7 | ||
|
|
fd64ad7c89 | ||
|
|
809b1507a6 | ||
|
|
d91495a041 | ||
|
|
84daf890d3 | ||
|
|
1bc6fb0f46 | ||
|
|
1f849e9f7d | ||
|
|
bd4724842c | ||
|
|
25a7299c67 | ||
|
|
518450cd8a | ||
|
|
57a8cf7a59 | ||
|
|
83732f299b | ||
|
|
f730b55255 | ||
|
|
370f7a44fa | ||
|
|
f7e5111123 | ||
|
|
847db5e59b | ||
|
|
d7f43b33c7 | ||
|
|
3a6d88cfef | ||
|
|
70f899c8d1 | ||
|
|
e138afc328 | ||
|
|
a84c914317 | ||
|
|
46d59eef5e | ||
|
|
79d25b9eca | ||
|
|
cc530649c7 | ||
|
|
aeb623498f | ||
|
|
d4b08850f3 | ||
|
|
1fada0f2b4 | ||
|
|
b08f9886be | ||
|
|
46384d25f2 | ||
|
|
9dcba8710b | ||
|
|
b839204c7f | ||
|
|
595b6c8923 | ||
|
|
9474bdba5c | ||
|
|
f5aa304e09 | ||
|
|
93bec9e430 | ||
|
|
d33a5bf94e | ||
|
|
b00154e2bc | ||
|
|
6220d7e62e | ||
|
|
fdc0a6e2b9 | ||
|
|
f37d9a7010 | ||
|
|
9f66e3936d | ||
|
|
f1325e12d4 | ||
|
|
7611449d3d | ||
|
|
15b2d6b9e0 | ||
|
|
d0f44143c2 | ||
|
|
51870a471a | ||
|
|
6f52365f9d | ||
|
|
66b8689957 | ||
|
|
4fd475f7c2 | ||
|
|
522861e484 | ||
|
|
1111ce4754 | ||
|
|
cdbee49280 | ||
|
|
aa63696933 | ||
|
|
10540839f6 | ||
|
|
a9eca2cf54 | ||
|
|
d6436858a9 | ||
|
|
38cb818469 | ||
|
|
422cc2b0d0 | ||
|
|
d74a5065b9 | ||
|
|
6b2c019caa | ||
|
|
c430884c8b | ||
|
|
8dda6d8e3d | ||
|
|
c43d581e7f | ||
|
|
f0b69f8b8d | ||
|
|
0e93d85273 | ||
|
|
adb3c8e39c | ||
|
|
85bc843554 | ||
|
|
c95216cd19 | ||
|
|
cc05621c10 | ||
|
|
e74033bded | ||
|
|
fe25fdf0ff | ||
|
|
1282650274 | ||
|
|
d622015b59 | ||
|
|
524e8875d0 | ||
|
|
8c2a2d2cb8 | ||
|
|
a28992aa38 | ||
|
|
0db4085950 | ||
|
|
f162d90e9f | ||
|
|
abfb28a4f3 | ||
|
|
c15a555cff | ||
|
|
e1c07d32d5 | ||
|
|
fbfef903ac | ||
|
|
695d9a6cb1 | ||
|
|
0106ed3669 | ||
|
|
ac42dd79ca | ||
|
|
deffc2dd15 | ||
|
|
bb1d5f1d5c | ||
|
|
43eabdf54a | ||
|
|
0e32cbee5e | ||
|
|
308c64c7b1 | ||
|
|
16c2062d9d | ||
|
|
ac44367440 | ||
|
|
20beb4abe1 | ||
|
|
a369634ea8 | ||
|
|
7a095f30ec | ||
|
|
59bcac410f | ||
|
|
4499343ed2 | ||
|
|
4b006bc484 | ||
|
|
23b3c237b7 | ||
|
|
e33cf8a8ae | ||
|
|
7e9a3d3bc9 | ||
|
|
6ea85a1a62 | ||
|
|
535144b208 | ||
|
|
439cd4aded | ||
|
|
2094142b7d | ||
|
|
196d9670d3 | ||
|
|
434c46058f | ||
|
|
c360d8bcef | ||
|
|
76221471ff | ||
|
|
a9d7535915 | ||
|
|
20610d05a2 | ||
|
|
af5d8a3bbe | ||
|
|
ab348336f5 | ||
|
|
804d61a6a1 | ||
|
|
baa5b3d25e | ||
|
|
8f2bf27edf | ||
|
|
a9f37bc9f1 | ||
|
|
79136a520f | ||
|
|
adf1c0b616 | ||
|
|
e063e13ff4 | ||
|
|
9d7ecdbc2c | ||
|
|
7a57f0e6ef | ||
|
|
c55c65f6d7 | ||
|
|
037ce9ebf3 | ||
|
|
cef940fea9 | ||
|
|
a7ae93978a | ||
|
|
4052620f94 | ||
|
|
032c412e09 | ||
|
|
5a686b3289 | ||
|
|
2bf16a34d6 | ||
|
|
7ccc63b51c | ||
|
|
e7253fd643 | ||
|
|
f3d7bf403e | ||
|
|
0b8a367867 | ||
|
|
aabfd91001 | ||
|
|
3d1b4c2509 | ||
|
|
14bc7885b3 | ||
|
|
2204e807b8 | ||
|
|
baa50c9c53 | ||
|
|
7d887f0332 | ||
|
|
51eaa17b1e | ||
|
|
35a36b4cdf | ||
|
|
2231183fe0 | ||
|
|
656a260171 | ||
|
|
959c750851 | ||
|
|
4d407da3e6 | ||
|
|
bb1d1f8140 | ||
|
|
d2db707521 | ||
|
|
a107da0888 | ||
|
|
b1f7c5c4e3 | ||
|
|
04bad1ae66 | ||
|
|
5830d4de6c | ||
|
|
bb85146544 | ||
|
|
c4ab5d4561 | ||
|
|
54e2aad1cd | ||
|
|
b1f6c69fce | ||
|
|
dbe73a06ad | ||
|
|
f36d0cc214 | ||
|
|
1e138a9774 | ||
|
|
905e02b765 | ||
|
|
3487846636 | ||
|
|
203fff0ab7 | ||
|
|
3236499fb7 | ||
|
|
28dbda29e3 | ||
|
|
1dcb903580 | ||
|
|
860117daf9 | ||
|
|
cd368b43df | ||
|
|
ae4b2591bf | ||
|
|
b0c65c5eb9 | ||
|
|
010fafc6d3 | ||
|
|
79aac08a48 | ||
|
|
630ca0aefb | ||
|
|
2918ecadaa | ||
|
|
82be8d9990 | ||
|
|
4d92fde5f7 | ||
|
|
5afc1c1443 | ||
|
|
22374ed732 | ||
|
|
bc484a9858 | ||
|
|
9a57980952 | ||
|
|
0900ac2b2f | ||
|
|
4751b1a176 | ||
|
|
cd123830c3 | ||
|
|
93e42b0b27 | ||
|
|
088919fecc | ||
|
|
efc06a020b | ||
|
|
fe1d3b3821 | ||
|
|
948a9c9b19 | ||
|
|
2603a495e6 | ||
|
|
8c41e6a3f7 | ||
|
|
327866ffc3 | ||
|
|
0c8a47b5af | ||
|
|
e7de7b4c05 | ||
|
|
ebd1557523 | ||
|
|
9a5eaa7cb7 | ||
|
|
f037436536 | ||
|
|
b1571ad17a | ||
|
|
494a7870d6 | ||
|
|
dbe1d2d928 | ||
|
|
2146dd29bb | ||
|
|
5ac9d2c2f1 | ||
|
|
a74235bdbc | ||
|
|
a98a96b05d | ||
|
|
b4c116cb31 | ||
|
|
074ebefee1 | ||
|
|
6fb3dc050b | ||
|
|
00ce1d7ee6 | ||
|
|
b8e15fbe40 | ||
|
|
3f06541b2b | ||
|
|
65728bc032 | ||
|
|
c4597b5c6b | ||
|
|
142f823e6f | ||
|
|
25b7386f0d | ||
|
|
4717e98c48 | ||
|
|
980c1e1b6a | ||
|
|
a47f60d3a6 | ||
|
|
ee00954f2e | ||
|
|
c77aa4c8f9 | ||
|
|
8181a452cb | ||
|
|
f39b688c39 | ||
|
|
1fa90c0474 | ||
|
|
70b020205d | ||
|
|
e03abdc0da | ||
|
|
61c56dcb00 | ||
|
|
6b1e81a7b5 | ||
|
|
04912ea996 | ||
|
|
72eb4e1749 | ||
|
|
70b1df6ce7 | ||
|
|
a586f10875 | ||
|
|
980c0c9cd4 | ||
|
|
d20182f158 | ||
|
|
49d07e9d64 | ||
|
|
ba73842747 | ||
|
|
900e857bfa | ||
|
|
d2819dca77 | ||
|
|
780982dbe2 | ||
|
|
2de51ae73c | ||
|
|
27205b30e5 | ||
|
|
34f4c9bdaa | ||
|
|
023750db06 | ||
|
|
c94cf61069 | ||
|
|
5315f8603f | ||
|
|
0e4cb53ada | ||
|
|
3d6621f7ff | ||
|
|
9d4f683d80 | ||
|
|
0cfd6337ff | ||
|
|
230a0b330c | ||
|
|
3ee697a54d | ||
|
|
d2991247a3 | ||
|
|
38caadfdf0 | ||
|
|
c4d1468f62 | ||
|
|
7e5b1abc0e | ||
|
|
b98123d9f8 | ||
|
|
9a9bf441ba | ||
|
|
1599eac66c | ||
|
|
43e84c89ef | ||
|
|
8e280ef0a7 | ||
|
|
81d9b705a6 | ||
|
|
4ec0fa299d | ||
|
|
7937383bf4 | ||
|
|
e772adb0a9 | ||
|
|
8c1b0f1afe | ||
|
|
0ff078968d | ||
|
|
7d1ce0c83b | ||
|
|
1dc84f48de | ||
|
|
edaa7918fc | ||
|
|
060ea80ca4 | ||
|
|
c15a0a54bb | ||
|
|
252c833320 | ||
|
|
fdd3bffa1d | ||
|
|
988d6079e3 | ||
|
|
f14d3985a0 | ||
|
|
5f8fa7a35a | ||
|
|
69970d42f3 | ||
|
|
ae34410c80 | ||
|
|
9b4d149a06 | ||
|
|
f5f626cd89 | ||
|
|
763e6d8109 | ||
|
|
aa953ba5a1 | ||
|
|
33bba98773 | ||
|
|
8b1f1b21e7 | ||
|
|
3ec0aa55b9 | ||
|
|
33d089a9d6 | ||
|
|
1d90e08bd0 | ||
|
|
acf028581a | ||
|
|
9084295d7c | ||
|
|
3f89f74d8e | ||
|
|
88d930579b | ||
|
|
3d3814f336 | ||
|
|
3e244998cd | ||
|
|
17bec1435b | ||
|
|
fa1ca981ac | ||
|
|
e9020048fb | ||
|
|
76ad9962d7 | ||
|
|
55c3f313b1 | ||
|
|
c4f48ad521 | ||
|
|
ba2d2299d9 | ||
|
|
f9f1167b62 | ||
|
|
e2bbbde494 | ||
|
|
3672b4e674 | ||
|
|
ee839869fc | ||
|
|
4ca42f6e83 | ||
|
|
0ad8320bff | ||
|
|
feb58801e0 | ||
|
|
3860cd1b98 | ||
|
|
c08e290f44 | ||
|
|
966290a623 | ||
|
|
5eaa99827d | ||
|
|
eed00ded0d | ||
|
|
1557f4c134 | ||
|
|
acea19312d | ||
|
|
56726a0533 | ||
|
|
39f8d34494 | ||
|
|
ab87e78dff | ||
|
|
548ff411ef | ||
|
|
36c48e1785 | ||
|
|
460f5bbb30 | ||
|
|
d9ec3e0d88 | ||
|
|
e1e7691fb4 | ||
|
|
ac6431acf8 | ||
|
|
1e786b8e31 | ||
|
|
575541ff4e | ||
|
|
e94f20f7ca | ||
|
|
3bda890f7b | ||
|
|
bdd0f68a4a | ||
|
|
275365917c | ||
|
|
b9b7f435dd | ||
|
|
7f7341a369 | ||
|
|
c30bb28210 | ||
|
|
3ad3c39b21 | ||
|
|
b839796b89 | ||
|
|
ca92e13b24 | ||
|
|
294908485f | ||
|
|
d2a2c11326 | ||
|
|
716aeb1e8c | ||
|
|
321c891bc0 | ||
|
|
d34bce9eab | ||
|
|
9bfc324c04 | ||
|
|
ad02adff3e | ||
|
|
38f5d2c100 | ||
|
|
81eb939d4d | ||
|
|
865d8d77e0 | ||
|
|
cf427f8cb3 | ||
|
|
6f85711252 | ||
|
|
70c278dec5 | ||
|
|
9ca7eb6ab1 | ||
|
|
93704ccfcf | ||
|
|
411316726b | ||
|
|
4d2b70acd1 | ||
|
|
c0620bd186 | ||
|
|
0e6fa51bb0 | ||
|
|
76034c198e | ||
|
|
1f78b3a425 | ||
|
|
48059e8249 | ||
|
|
09cd56b367 | ||
|
|
145b6a05a3 | ||
|
|
5787f74af9 | ||
|
|
78d7f79817 | ||
|
|
9356c8afd0 | ||
|
|
72bff74ca5 | ||
|
|
2d304efb73 | ||
|
|
c7784cbc8e | ||
|
|
e1a5a725b0 | ||
|
|
3d4838b5bc | ||
|
|
b4270af170 | ||
|
|
3fb54ea6ea | ||
|
|
ed7667683b | ||
|
|
61aaac888b | ||
|
|
6c9fbd40dc | ||
|
|
4a609bbee8 | ||
|
|
32309bb8ea | ||
|
|
df2300bee0 | ||
|
|
6e18de4370 | ||
|
|
0462cfa507 | ||
|
|
aee9ee6187 | ||
|
|
31918e8712 | ||
|
|
0e6c37d765 | ||
|
|
f45423ed76 | ||
|
|
b3984c8db4 | ||
|
|
999ad0e18a | ||
|
|
ccd07b6cfe | ||
|
|
60e42c1ea1 | ||
|
|
c9ed749908 | ||
|
|
a9a43d54f7 | ||
|
|
7f6149713e | ||
|
|
eb39080248 | ||
|
|
97fe36af5a | ||
|
|
c15f66aa62 | ||
|
|
694680720e | ||
|
|
465ee97090 | ||
|
|
a8b1762464 | ||
|
|
46c0b4cf31 | ||
|
|
4f99982ef1 | ||
|
|
fe48eede0e | ||
|
|
afe3e24cda | ||
|
|
ce8112fb5a | ||
|
|
91c63034d3 | ||
|
|
d712bdea85 | ||
|
|
912a424596 | ||
|
|
1e6660ecb2 | ||
|
|
8f267ebcef | ||
|
|
c13fb80257 | ||
|
|
ca8ca2df5c | ||
|
|
aff3bf369b | ||
|
|
6fe31e44cb | ||
|
|
6795fb6967 | ||
|
|
9516ee511d | ||
|
|
1d106e71c4 | ||
|
|
c0ee346c1c | ||
|
|
d2298b6f04 | ||
|
|
09dd66fd5a | ||
|
|
b93c7cabb3 | ||
|
|
5d786f411f | ||
|
|
801f5ba525 | ||
|
|
ce29dcad87 | ||
|
|
8edd202b64 | ||
|
|
07de3ba5e0 | ||
|
|
2b4ad71151 | ||
|
|
815ea1e13b | ||
|
|
1d2b3ac917 | ||
|
|
c1be8d277e | ||
|
|
19d9541aad | ||
|
|
4dd8472d9b | ||
|
|
6702395357 | ||
|
|
946c9f420b | ||
|
|
8b0f2e1462 | ||
|
|
d5f42c0628 | ||
|
|
76d1447a91 | ||
|
|
4db3da61e1 | ||
|
|
be1f820674 | ||
|
|
ed8abe9861 | ||
|
|
e71001f4f8 | ||
|
|
8457dfdc39 | ||
|
|
a491bae39b | ||
|
|
f31cfe5b96 | ||
|
|
ab94ea9715 | ||
|
|
7e4da8ea2c | ||
|
|
7311ae889f | ||
|
|
9aec48aec3 | ||
|
|
3a51cf0ef8 | ||
|
|
7544d4b4e6 | ||
|
|
345905bf68 | ||
|
|
8cf6aa267c | ||
|
|
3fe78a8311 | ||
|
|
2d394f33b8 | ||
|
|
70459b311e | ||
|
|
e600848947 |
@@ -1,17 +1,106 @@
|
||||
; Top-most http://editorconfig.org/ file
|
||||
root = true
|
||||
charset=utf-8
|
||||
|
||||
; Unix-style newlines
|
||||
[*]
|
||||
end_of_line = LF
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
; 4-column tab indentation
|
||||
; 4-column tab indentation and .NET coding conventions
|
||||
[*.cs]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
dotnet_separate_import_directive_groups = false
|
||||
dotnet_sort_system_directives_first = true
|
||||
|
||||
csharp_style_var_elsewhere = true:suggestion
|
||||
csharp_style_var_for_built_in_types = true:suggestion
|
||||
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||
|
||||
csharp_prefer_braces = when_multiline:suggestion
|
||||
csharp_using_directive_placement = outside_namespace:suggestion
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
|
||||
#### Naming styles ####
|
||||
|
||||
dotnet_naming_style.camel_case.capitalization = camel_case
|
||||
|
||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||
|
||||
# Symbol specifications
|
||||
|
||||
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal
|
||||
|
||||
dotnet_naming_symbols.const_private_field.applicable_kinds = field
|
||||
dotnet_naming_symbols.const_private_field.required_modifiers = const
|
||||
dotnet_naming_symbols.const_private_field.applicable_accessibilities = private
|
||||
|
||||
dotnet_naming_symbols.internal_field.applicable_kinds = field
|
||||
dotnet_naming_symbols.internal_field.applicable_accessibilities = internal
|
||||
|
||||
dotnet_naming_symbols.static_private_or_internal_field.required_modifiers = static
|
||||
dotnet_naming_symbols.static_private_or_internal_field.applicable_accessibilities = internal, private
|
||||
|
||||
dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private
|
||||
|
||||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal
|
||||
|
||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal
|
||||
|
||||
# Naming rules
|
||||
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||
|
||||
|
||||
dotnet_naming_rule.static_private_or_internal_field_should_be_pascal_case.severity = none
|
||||
dotnet_naming_rule.static_private_or_internal_field_should_be_pascal_case.symbols = static_private_or_internal_field
|
||||
dotnet_naming_rule.static_private_or_internal_field_should_be_pascal_case.style = pascal_case
|
||||
|
||||
dotnet_naming_rule.const_private_field_should_be_pascal_case.severity = warning
|
||||
dotnet_naming_rule.const_private_field_should_be_pascal_case.symbols = const_private_field
|
||||
dotnet_naming_rule.const_private_field_should_be_pascal_case.style = pascal_case
|
||||
|
||||
dotnet_naming_rule.const_private_or_internal_field_should_be_pascal_case.severity = warning
|
||||
dotnet_naming_rule.const_private_or_internal_field_should_be_pascal_case.symbols = internal_field
|
||||
dotnet_naming_rule.const_private_or_internal_field_should_be_pascal_case.style = pascal_case
|
||||
|
||||
dotnet_naming_rule.private_or_internal_field_should_be_camel_case.severity = warning
|
||||
dotnet_naming_rule.private_or_internal_field_should_be_camel_case.symbols = private_or_internal_field
|
||||
dotnet_naming_rule.private_or_internal_field_should_be_camel_case.style = camel_case
|
||||
|
||||
# Naming rules
|
||||
|
||||
#require a space before the colon for bases or interfaces in a type declaration
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
#require a space after a keyword in a control flow statement such as a for loop
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
#require a space before the colon for bases or interfaces in a type declaration
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
|
||||
#Formatting - wrapping options
|
||||
|
||||
#leave code block on single line
|
||||
csharp_preserve_single_line_blocks = true
|
||||
#leave statements and member declarations on the same line
|
||||
csharp_preserve_single_line_statements = true
|
||||
|
||||
#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them
|
||||
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||
|
||||
#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
||||
|
||||
; 4-column tab indentation
|
||||
[*.yaml]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
indent_size = 4
|
||||
|
||||
39
.github/ISSUE_TEMPLATE.md
vendored
39
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,39 +0,0 @@
|
||||
<!--
|
||||
This is a guideline, which shall help to write enhancement requests or bug reports.
|
||||
Fill in the placeholders below. Delete any headings and placeholders that you do not use.
|
||||
|
||||
Before you start check if a similar request/bug report already exists in this Github issue tracker and comment there.
|
||||
|
||||
When submitting a feature or enhancement request:
|
||||
|
||||
1. Explain briefly what the enhancement is and why you think it would be useful.
|
||||
2. Provide any other necessary or useful information regarding your issue, such as (code) examples or related links.
|
||||
|
||||
|
||||
When submitting a bug report, please follow the template below:
|
||||
-->
|
||||
|
||||
### Issue Summary
|
||||
<!-- Explanation of the issue. Expectation vs. actual behavior. -->
|
||||
... ... ...
|
||||
|
||||
#### System Information
|
||||
- **Operating System:** [e.g. Windows 10, Mac OS 10.12, Ubuntu 16.04, ...]
|
||||
- **.NET / Mono Version:** [e.g. .NET 4.7.1, Mono 4.6.2, ...]
|
||||
- **OpenRA Version:** [e.g. release-20180218, playtest-20180208, ...]
|
||||
- **Mod:** [e.g. Red Alert, Tiberian Dawn, Dune2000, ...]
|
||||
|
||||
#### Additional Information:
|
||||
- Steps to reproduce
|
||||
1. Step
|
||||
2. Step
|
||||
3. ...
|
||||
|
||||
- Logs
|
||||
<!-- If you have a log (e.g. debug.log, exception.log), zip and attach it. -->
|
||||
|
||||
- OpenRA Replays
|
||||
<!-- You have to zip it before you can attach it. When does the issue appear [e.g. 10:33]? -->
|
||||
|
||||
- Screenshots & Videos
|
||||
<!-- You should be able to attach screenshots by drag&drop. Videos need to be uploaded to an external platform (e.g. https://www.youtube.com, https://www.dropbox.com) -->
|
||||
33
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
33
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Report unexpected behavior or any issues you experienced in OpenRA.
|
||||
title: ''
|
||||
labels: Bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- This is a guideline that shall help you to include information we need to understand and fix the issue you experienced. Please follow the instructions and replace any placeholders that are written in capital letters. Instructions like this comment will not be visible in your report. -->
|
||||
|
||||
<!-- Important: Help us to avoid duplicates and use the search function to find existing reports for your issue. Please do not submit duplicate reports! Try to help others to find your report by using a precise title. -->
|
||||
|
||||
## Issue Summary
|
||||
<!-- Please provide a a clear and concise description of what the issue is below. -->
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
|
||||
## Reproduction
|
||||
<!-- Please provide information about how the issue can be reproduced below. -->
|
||||
|
||||
STEPS TO REPRODUCE THE ISSUE
|
||||
|
||||
|
||||
## Expected behavior
|
||||
<!-- Please explain what you expected to happen below. -->
|
||||
|
||||
EXPECTED BEHAVIOR
|
||||
|
||||
|
||||
## Screenshots / Screen recordings / Replays
|
||||
<!-- If applicable, attach screenshots, screen recordings or replays to help explain your problem. -->
|
||||
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: OpenRA Forum
|
||||
url: https://forum.openra.net/
|
||||
about: "Please ask questions about modding here."
|
||||
- name: OpenRA Discord server
|
||||
url: https://discord.openra.net/
|
||||
about: "Join the community Discord server for community discussion and support."
|
||||
- name: OpenRA IRC Channel
|
||||
url: https://webchat.freenode.net/#openra
|
||||
about: "Join our development IRC channel on freenode for discussion of development topics."
|
||||
35
.github/ISSUE_TEMPLATE/crash-report.md
vendored
Normal file
35
.github/ISSUE_TEMPLATE/crash-report.md
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: Crash report
|
||||
about: Report a game crash.
|
||||
title: My game crashed
|
||||
labels: Crash
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- This is a guideline that shall help you to include all the required information we depend on to investigate and fix game-breaking bugs. Please follow the instructions and replace any placeholders that are written in capital letters. Instructions like this comment will not be visible in your report. -->
|
||||
|
||||
## System Information
|
||||
<!-- Information about the operating system, engine version, game mod and package source are mandatory for investigating and fixing crashes. -->
|
||||
|
||||
- Operating System: OPERATING SYSTEM
|
||||
- OpenRA Version: ENGINE VERSION
|
||||
- OpenRA Mod: GAME MOD
|
||||
- Source: Official download package OR self-compiled OR third-party package
|
||||
- For self-compiled or third-party packages: Mono version
|
||||
|
||||
|
||||
## Exception log
|
||||
<!-- Please replace the placeholder below with the content of the exception.log file. The three backticks before and after the placeholder are used for formatting, so don't remove them. If you don't find the log folder consult https://github.com/OpenRA/OpenRA/wiki/FAQ#my-game-just-crashed. -->
|
||||
|
||||
```
|
||||
PASTE LOG HERE
|
||||
```
|
||||
|
||||
|
||||
## Replay
|
||||
<!-- If you have a replay file for the game that crashed, and it crashes again when you play it back, it will be a great help for us to fix the issue. Please compress the replay into a zip file and drag it here to include it in the report. -->
|
||||
|
||||
|
||||
## Additional information
|
||||
<!-- Please tell us below everything that you think is important for us to know about the crash. Specifically, what you were doing in the moment before the crash or ideally steps to reproduce it are very valuable information. -->
|
||||
35
.github/ISSUE_TEMPLATE/feature---enhancement-request.md
vendored
Normal file
35
.github/ISSUE_TEMPLATE/feature---enhancement-request.md
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: Feature / Enhancement request
|
||||
about: Describe what you think is missing or could be improved in OpenRA.
|
||||
title: ''
|
||||
labels: Idea/Wishlist
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- This is a guideline that shall help you to describe your idea for a missing feature or enhancement. Please follow the instructions and replace any placeholders that are written in capital letters. Instructions like this comment will not be visible in your report. -->
|
||||
|
||||
<!-- Important: Help us to avoid duplicates and use the search function to find existing requests. Please do not submit duplicate requests! Try to help others to find your request by using a precise title. -->
|
||||
|
||||
## Motivation
|
||||
<!-- Please provide a clear and concise description of the motivation behind the request. If your request is related to a problem or limitation, describe it below. -->
|
||||
|
||||
REQUEST MOTIVATION
|
||||
|
||||
|
||||
## Proposed solution
|
||||
<!-- Please describe your idea and how it is intended to address the motivation of your request. Provide a clear and concise description of the proposed changes. -->
|
||||
|
||||
PROPOSED SOLUTION
|
||||
|
||||
|
||||
## Side effects
|
||||
<!-- Changes often have side effects or unintended consequences. If you expect that your solution has any side effects, please describe them below. -->
|
||||
|
||||
EXPECTED SIDE EFFECTS
|
||||
|
||||
|
||||
## Alternatives
|
||||
<!-- Please outline any alternative solutions you have considered. -->
|
||||
|
||||
ALTERNATIVES
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -26,7 +26,7 @@ mods/*/*.pdb
|
||||
/*.exe
|
||||
/*.exe.config
|
||||
thirdparty/download/*
|
||||
*.mmdb.gz
|
||||
IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP
|
||||
|
||||
# backup files by various editors
|
||||
*~
|
||||
|
||||
66
.travis.yml
66
.travis.yml
@@ -1,26 +1,26 @@
|
||||
# Travis-CI Build for OpenRA
|
||||
# see travis-ci.org for details
|
||||
|
||||
dist: xenial
|
||||
language: csharp
|
||||
mono: 5.20.1
|
||||
mono: 6.4.0
|
||||
os: linux
|
||||
dist: xenial
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- thirdparty/download
|
||||
jobs:
|
||||
include:
|
||||
- os: linux
|
||||
dist: xenial
|
||||
- os: osx
|
||||
if: tag IS present
|
||||
osx_image: xcode10
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- lua5.1
|
||||
- dpkg
|
||||
- markdown
|
||||
- zlib1g-dev
|
||||
- libbz2-dev
|
||||
- cmake
|
||||
- genisoimage
|
||||
- fakeroot
|
||||
- zsync
|
||||
- imagemagick
|
||||
|
||||
# Environment variables
|
||||
env:
|
||||
@@ -32,29 +32,28 @@ env:
|
||||
# call OpenRA to check for YAML errors
|
||||
# Run the NUnit tests
|
||||
script:
|
||||
- travis_retry make all-dependencies
|
||||
- make all
|
||||
- make check
|
||||
- make check-scripts
|
||||
- make test
|
||||
- make nunit
|
||||
|
||||
# Automatically update the trait documentation and Lua API
|
||||
after_success:
|
||||
- test $TRAVIS_PULL_REQUEST == "false" && cd packaging && ./update-wiki.sh $TRAVIS_BRANCH; cd ..
|
||||
- |
|
||||
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
|
||||
make check || travis_terminate 1;
|
||||
make check-scripts || travis_terminate 1;
|
||||
make test || travis_terminate 1;
|
||||
mono ~/.nuget/packages/nunit.consolerunner/3.11.1/tools/nunit3-console.exe --noresult OpenRA.Test.dll || travis_terminate 1;
|
||||
fi
|
||||
|
||||
# Only watch the development branch and tagged release.
|
||||
branches:
|
||||
only:
|
||||
- /^release-.*$/
|
||||
- /^playtest-.*$/
|
||||
- /^pkgtest-.*$/
|
||||
- /^devtest-.*$/
|
||||
- /^prep-.*$/
|
||||
- bleed
|
||||
|
||||
# Notify developers when build passed/failed.
|
||||
notifications:
|
||||
irc:
|
||||
if: repo = OpenRA/OpenRA
|
||||
template:
|
||||
- "%{repository}#%{build_number} %{commit} %{author}: %{message} %{build_url}"
|
||||
channels:
|
||||
@@ -63,24 +62,31 @@ notifications:
|
||||
skip_join: true
|
||||
|
||||
before_deploy:
|
||||
- wget http://mirrors.kernel.org/ubuntu/pool/universe/n/nsis/nsis-common_3.03-2_all.deb
|
||||
- wget http://mirrors.kernel.org/ubuntu/pool/universe/n/nsis/nsis_3.03-2_amd64.deb
|
||||
- sudo dpkg -i nsis-common_3.03-2_all.deb
|
||||
- sudo dpkg -i nsis_3.03-2_amd64.deb
|
||||
- makensis -VERSION
|
||||
- export PATH=$PATH:$HOME/usr/bin
|
||||
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
|
||||
wget https://mirrors.edge.kernel.org/ubuntu/pool/universe/n/nsis/nsis-common_3.04-1_all.deb;
|
||||
wget https://mirrors.edge.kernel.org/ubuntu/pool/universe/n/nsis/nsis_3.04-1_amd64.deb;
|
||||
sudo dpkg -i nsis-common_3.04-1_all.deb;
|
||||
sudo dpkg -i nsis_3.04-1_amd64.deb;
|
||||
echo ${TRAVIS_REPO_SLUG};
|
||||
if [[ "${TRAVIS_REPO_SLUG}" == "OpenRA/OpenRA" ]]; then
|
||||
cd packaging && ./update-wiki.sh ${TRAVIS_TAG} && cd ..;
|
||||
fi;
|
||||
fi
|
||||
- export PATH=${PATH}:${HOME}/usr/bin
|
||||
- DOTVERSION=`echo ${TRAVIS_TAG} | sed "s/-/\\./g"`
|
||||
- cd packaging
|
||||
- mkdir build
|
||||
- ./package-all.sh ${TRAVIS_TAG} ${PWD}/build/
|
||||
- if [[ "${TRAVIS_REPO_SLUG}" == "OpenRA/OpenRA" ]]; then
|
||||
./upload-itch.sh ${TRAVIS_TAG} ${PWD}/build/;
|
||||
fi
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: "g/LU11f+mjqv+lj0sR1UliHwogXL4ofJUwoG5Dbqlvdf5UTLWytw/OWSCv8RGyuh10miyWeaoqHh1cn2C1IFhUEqN1sSeKKKOWOTvJ2FR5mzi9uH3d/MOBzG5icQ7Qh0fZ1YPz5RaJJhYu6bmfvA/1gD49GoaX2kxQL4J5cEBgg="
|
||||
token: ${GH_DEPLOY_API_KEY}
|
||||
file_glob: true
|
||||
file: build/*
|
||||
skip_cleanup: true
|
||||
on:
|
||||
all_branches: true
|
||||
tags: true
|
||||
repo: OpenRA/OpenRA
|
||||
|
||||
7
.vscode/extensions.json
vendored
Normal file
7
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"ms-dotnettools.csharp",
|
||||
"EditorConfig.EditorConfig",
|
||||
"ms-vscode.mono-debug"
|
||||
]
|
||||
}
|
||||
61
.vscode/launch.json
vendored
Normal file
61
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch (TD)",
|
||||
"type": "clr",
|
||||
"linux": {
|
||||
"type": "mono"
|
||||
},
|
||||
"osx": {
|
||||
"type": "mono"
|
||||
},
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/OpenRA.Game.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": ["Game.Mod=cnc"]
|
||||
},
|
||||
{
|
||||
"name": "Launch (RA)",
|
||||
"type": "clr",
|
||||
"linux": {
|
||||
"type": "mono"
|
||||
},
|
||||
"osx": {
|
||||
"type": "mono"
|
||||
},
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/OpenRA.Game.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": ["Game.Mod=ra"]
|
||||
},
|
||||
{
|
||||
"name": "Launch (D2k)",
|
||||
"type": "clr",
|
||||
"linux": {
|
||||
"type": "mono"
|
||||
},
|
||||
"osx": {
|
||||
"type": "mono"
|
||||
},
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/OpenRA.Game.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": ["Game.Mod=d2k"]
|
||||
},
|
||||
{
|
||||
"name": "Launch (TS)",
|
||||
"type": "clr",
|
||||
"linux": {
|
||||
"type": "mono"
|
||||
},
|
||||
"osx": {
|
||||
"type": "mono"
|
||||
},
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/OpenRA.Game.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": ["Game.Mod=ts"]
|
||||
},
|
||||
]
|
||||
}
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"omnisharp.enableRoslynAnalyzers": true
|
||||
}
|
||||
13
.vscode/tasks.json
vendored
Normal file
13
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "make",
|
||||
"args": ["all"],
|
||||
"windows": {
|
||||
"command": "make.cmd"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
29
AUTHORS
29
AUTHORS
@@ -3,26 +3,27 @@ hard work of many contributors.
|
||||
|
||||
The OpenRA developers are:
|
||||
* Chris Forbes (chrisf)
|
||||
* Igor Popov (ihptru)
|
||||
* Lukas Franke (abcdefg30)
|
||||
* Oliver Brakmann (obrakmann)
|
||||
* Paul Chote (pchote)
|
||||
* Reaperrr
|
||||
* Tom Roostan (RoosterDragon)
|
||||
|
||||
Previous developers included:
|
||||
* Alli Witheford (alzeih)
|
||||
* Caleb Anderson (RobotCaleb)
|
||||
* Curtis Shmyr (hamb)
|
||||
* Daniel Hernandez (Mancano)
|
||||
* Igor Popov (ihptru)
|
||||
* Matthias Mailänder (Mailaender)
|
||||
* Megan Bowra-Dean (beedee)
|
||||
* Mike Bundy (kehaar)
|
||||
* Oliver Brakmann (obrakmann)
|
||||
* Pavel Penev (penev92)
|
||||
* Robert Pepperell (ytinasni)
|
||||
* ScottNZ
|
||||
* Tom Roostan (RoosterDragon)
|
||||
|
||||
Also thanks to:
|
||||
* abmyii
|
||||
* Adam Valy (Tschokky)
|
||||
* Akseli Virtanen (RAGEQUIT)
|
||||
* Alexander Fast (mizipzor)
|
||||
@@ -39,10 +40,12 @@ Also thanks to:
|
||||
* Biofreak
|
||||
* Braxton Williams (Buddytex)
|
||||
* Brendan Gluth (Mechanical_Man)
|
||||
* Brent Gardner (bggardner)
|
||||
* Bryan Wilbur
|
||||
* Bugra Cuhadaroglu (BugraC)
|
||||
* Christer Ulfsparre (Holloweye)
|
||||
* Chris Cameron (Vesuvian)
|
||||
* Chris Grant (Unit158)
|
||||
* Christer Ulfsparre (Holloweye)
|
||||
* clem
|
||||
* Cody Brittain (Generalcamo)
|
||||
* Constantin Helmig (CH4Code)
|
||||
@@ -135,15 +138,18 @@ Also thanks to:
|
||||
* Sebastien Kerguen (xanax)
|
||||
* Shawn Collins (UberWaffe)
|
||||
* Simon Verbeke (Saticmotion)
|
||||
* Stuart McHattie (SDJMcHattie)
|
||||
* Taryn Hill (Phrohdoh)
|
||||
* Teemu Nieminen (Temeez)
|
||||
* Tim Mylemans (gecko)
|
||||
* Tirili
|
||||
* Tomas Einarsson (Mesacer)
|
||||
* Tom van Leth (tovl)
|
||||
* Tristan Keating (Kilkakon)
|
||||
* Tristan Mühlbacher (MicroBit)
|
||||
* UnknownProgrammer
|
||||
* Vladimir Komarov (VrKomarov)
|
||||
* Wojciech Walaszek (Voidwalker)
|
||||
* Wuschel
|
||||
|
||||
Using GNU FreeFont distributed under the GNU GPL
|
||||
@@ -157,12 +163,6 @@ FreeType License.
|
||||
|
||||
Using OpenAL Soft distributed under the GNU LGPL.
|
||||
|
||||
Using MaxMind GeoIP2 .NET API distributed under
|
||||
the Apache 2.0 license.
|
||||
|
||||
Using GeoLite2 data created by MaxMind and
|
||||
distributed under the CC BY-SA 3.0 license.
|
||||
|
||||
Using SDL2-CS and OpenAL-CS created by Ethan
|
||||
Lee and released under the zlib license.
|
||||
|
||||
@@ -182,6 +182,15 @@ Krueger and distributed under the GNU GPL terms.
|
||||
Using rix0rrr.BeaconLib developed by Rico Huijbers
|
||||
distributed under MIT License.
|
||||
|
||||
Using DiscordRichPresence developed by Lachee
|
||||
distributed under MIT License.
|
||||
|
||||
Using Json.NET developed by James Newton-King
|
||||
distributed under MIT License.
|
||||
|
||||
This site or product includes IP2Location LITE data
|
||||
available from http://www.ip2location.com.
|
||||
|
||||
Finally, special thanks goes to the original teams
|
||||
at Westwood Studios and EA for creating the classic
|
||||
games which OpenRA aims to reimagine.
|
||||
|
||||
File diff suppressed because one or more lines are too long
41
INSTALL.md
41
INSTALL.md
@@ -8,15 +8,9 @@ Windows
|
||||
|
||||
Compiling OpenRA requires the following dependencies:
|
||||
* [Windows PowerShell >= 4.0](http://microsoft.com/powershell) (included by default in recent Windows 10 versions)
|
||||
* [.NET Framework 4.6.1 (Developer Pack)](https://dotnet.microsoft.com/download/dotnet-framework/net461) (or via Visual Studio 2017)
|
||||
* [.NET Framework 4.7.2 (Developer Pack)](https://dotnet.microsoft.com/download/dotnet-framework/net472) (or via Visual Studio 2017)
|
||||
* [.NET Core 2.2 SDK](https://dotnet.microsoft.com/download/dotnet-core/2.2) (or via Visual Studio 2017)
|
||||
|
||||
Type `make dependencies` in a command terminal to download pre-compiled native libraries for:
|
||||
* [SDL 2](http://www.libsdl.org/download-2.0.php)
|
||||
* [FreeType](http://gnuwin32.sourceforge.net/packages/freetype.htm)
|
||||
* [zlib](http://gnuwin32.sourceforge.net/packages/zlib.htm)
|
||||
* [OpenAL](http://kcat.strangesoft.net/openal.html)
|
||||
* [liblua 5.1](http://luabinaries.sourceforge.net/download.html)
|
||||
|
||||
To compile OpenRA, open the `OpenRA.sln` solution in the main folder, build it from the command-line with MSBuild or use the Makefile analogue command `make all` scripted in PowerShell syntax.
|
||||
|
||||
@@ -25,13 +19,19 @@ Run the game with `launch-game.cmd`. It can be handed arguments that specify the
|
||||
Linux
|
||||
=====
|
||||
|
||||
Mono, version 5.4 or later, is required to compile OpenRA. You can add the [upstream mono repository](https://www.mono-project.com/download/stable/#download-lin) for your distro to obtain the latest version if your system packages are not sufficient.
|
||||
Mono, version 5.18 or later, is required to compile OpenRA. You can add the [upstream mono repository](https://www.mono-project.com/download/stable/#download-lin) for your distro to obtain the latest version if your system packages are not sufficient.
|
||||
|
||||
Use `make dependencies` to map the native libraries to your system and fetch the remaining CLI dependencies to place them at the appropriate places.
|
||||
To compile OpenRA, run `make` from the command line. After this one can run the game with `./launch-game.sh`. It is also possible to specify the mod you wish to run from the command line, e.g. with `./launch-game.sh Game.Mod=ts` if you wish to try the experimental Tiberian Sun mod.
|
||||
|
||||
To compile OpenRA, run `make all` from the command line. After this one can run the game with `./launch-game.sh`. It is also possible to specify the mod you wish to run from the command line, e.g. with `./launch-game.sh Game.Mod=ts` if you wish to try the experimental Tiberian Sun mod.
|
||||
The default behaviour on the x86_64 architecture is to download several pre-compiled native libraries using the Nuget packaging manager. If you prefer to use system libraries, compile instead using `make TARGETPLATFORM=unix-generic`.
|
||||
|
||||
Type `sudo make install` for system-wide installation. Run `sudo make install-linux-shortcuts` to get startup scripts, icons and desktop files. You can then run the Red Alert by executing the `openra-ra` command, the Dune 2000 mod by running the `openra-d2k` command and Tiberian Dawn by the `openra-cnc` command. Alternatively, you can also run these mods by clicking on their desktop shortcuts if you ran `sudo make install-linux-shortcuts`.
|
||||
If you choose to use system libraries, or your system is not x86_64, you will need to install the following using your system package manager:
|
||||
* [SDL 2](http://www.libsdl.org/download-2.0.php)
|
||||
* [FreeType](http://gnuwin32.sourceforge.net/packages/freetype.htm)
|
||||
* [OpenAL](http://kcat.strangesoft.net/openal.html)
|
||||
* [liblua 5.1](http://luabinaries.sourceforge.net/download.html)
|
||||
|
||||
Type `sudo make install` for system-wide installation. Run `sudo make install-linux-shortcuts` to get startup scripts, icons and desktop files. You can then run the Red Alert by executing the `openra-ra` command, the Dune 2000 mod by running the `openra-d2k` command and Tiberian Dawn by the `openra-cnc` command. Alternatively, you can also run these mods by clicking on their desktop shortcuts if you ran `sudo make install-linux-shortcuts`.
|
||||
|
||||
Arch Linux
|
||||
----------
|
||||
@@ -89,24 +89,23 @@ sudo zypper in mono-devel openal-soft freetype2 SDL2 lua51 xdg-utils zenity
|
||||
Red Hat Enterprise Linux (and rebuilds, e.g. CentOS)
|
||||
----------------------------------------------------
|
||||
|
||||
The EPEL repository is required in order for the following command to run properly.
|
||||
The EPEL repository is required in order for the following command to run properly.
|
||||
|
||||
```
|
||||
sudo yum install "pkgconfig(mono)" SDL2 freetype "lua = 5.1" openal-soft xdg-utils zenity
|
||||
```
|
||||
|
||||
OSX
|
||||
macOS
|
||||
=====
|
||||
|
||||
Before compiling OpenRA you must install the following dependencies:
|
||||
* [Mono >= 5.4](https://www.mono-project.com/download/stable/#download-mac)
|
||||
* [Mono >= 5.18](https://www.mono-project.com/download/stable/#download-mac)
|
||||
|
||||
Use `make dependencies` to download pre-compiled native libraries for:
|
||||
* [SDL 2](http://www.libsdl.org/download-2.0.php)
|
||||
* [FreeType](http://gnuwin32.sourceforge.net/packages/freetype.htm)
|
||||
* [OpenAL](http://kcat.strangesoft.net/openal.html)
|
||||
* [liblua 5.1](http://luabinaries.sourceforge.net/download.html)
|
||||
To compile OpenRA, run `make` from the command line. Run with `./launch-game.sh`.
|
||||
|
||||
To compile OpenRA, run `make` from the command line.
|
||||
The default behaviour is to download several pre-compiled native libraries using the Nuget packaging manager. If you prefer to use system libraries, compile instead using `make TARGETPLATFORM=unix-generic`. If you choose to use system libraries you will need to install:
|
||||
* [SDL 2](http://www.libsdl.org/download-2.0.php) (`brew install sdl2`)
|
||||
* [FreeType](http://gnuwin32.sourceforge.net/packages/freetype.htm) (`brew install freetype`)
|
||||
* [OpenAL](http://kcat.strangesoft.net/openal.html) (`brew install openal-soft`)
|
||||
* [liblua 5.1](http://luabinaries.sourceforge.net/download.html) (`brew install lua@5.1`)
|
||||
|
||||
Run with `./launch-game.sh`.
|
||||
|
||||
179
Makefile
179
Makefile
@@ -3,15 +3,13 @@
|
||||
# to compile, run:
|
||||
# make [DEBUG=true]
|
||||
#
|
||||
# to check unit tests (requires NUnit version >= 2.6), run:
|
||||
# make nunit [NUNIT_CONSOLE=<path-to/nunit[2]-console>] [NUNIT_LIBS_PATH=<path-to-libs-dir>] [NUNIT_LIBS=<nunit-libs>]
|
||||
# Use NUNIT_CONSOLE if nunit[3|2]-console was not downloaded by `make dependencies` nor is it in bin search paths
|
||||
# Use NUNIT_LIBS_PATH if NUnit libs are not in search paths. Include trailing /
|
||||
# Use NUNIT_LIBS if NUnit libs have different names (such as including a prefix or suffix)
|
||||
# to compile using system libraries for native dependencies, run:
|
||||
# make [DEBUG=true] TARGETPLATFORM=unix-generic
|
||||
#
|
||||
# to check the official mods for erroneous yaml files, run:
|
||||
# make test
|
||||
#
|
||||
# to check the official mod dlls for StyleCop violations, run:
|
||||
# to check the engine and official mod dlls for code style violations, run:
|
||||
# make check
|
||||
#
|
||||
# to install, run:
|
||||
@@ -22,6 +20,7 @@
|
||||
#
|
||||
# to install the engine and common mod files (omitting the default mods):
|
||||
# make install-engine
|
||||
# make install-dependencies
|
||||
# make install-common-mod-files
|
||||
#
|
||||
# to uninstall, run:
|
||||
@@ -40,11 +39,11 @@
|
||||
WHITELISTED_OPENRA_ASSEMBLIES = OpenRA.Game.exe OpenRA.Utility.exe OpenRA.Platforms.Default.dll OpenRA.Mods.Common.dll OpenRA.Mods.Cnc.dll OpenRA.Mods.D2k.dll OpenRA.Game.dll
|
||||
|
||||
# These are explicitly shipped alongside our core files by the packaging script
|
||||
WHITELISTED_THIRDPARTY_ASSEMBLIES = ICSharpCode.SharpZipLib.dll FuzzyLogicLibrary.dll MaxMind.Db.dll Eluant.dll rix0rrr.BeaconLib.dll Open.Nat.dll SDL2-CS.dll OpenAL-CS.dll
|
||||
WHITELISTED_THIRDPARTY_ASSEMBLIES = ICSharpCode.SharpZipLib.dll FuzzyLogicLibrary.dll Eluant.dll BeaconLib.dll Open.Nat.dll SDL2-CS.dll OpenAL-CS.Core.dll DiscordRPC.dll Newtonsoft.Json.dll
|
||||
|
||||
# These are shipped in our custom minimal mono runtime and also available in the full system-installed .NET/mono stack
|
||||
# This list *must* be kept in sync with the files packaged by the AppImageSupport and OpenRALauncherOSX repositories
|
||||
WHITELISTED_CORE_ASSEMBLIES = mscorlib.dll System.dll System.Configuration.dll System.Core.dll System.Numerics.dll System.Security.dll System.Xml.dll Mono.Security.dll
|
||||
WHITELISTED_CORE_ASSEMBLIES = mscorlib.dll System.dll System.Configuration.dll System.Core.dll System.Numerics.dll System.Security.dll System.Xml.dll Mono.Security.dll netstandard.dll
|
||||
|
||||
NUNIT_LIBS_PATH :=
|
||||
NUNIT_LIBS := $(NUNIT_LIBS_PATH)nunit.framework.dll
|
||||
@@ -81,24 +80,31 @@ MSBUILD = msbuild -verbosity:m -nologo
|
||||
# Enable 32 bit builds while generating the windows installer
|
||||
WIN32 = false
|
||||
|
||||
# dependencies
|
||||
ifndef TARGETPLATFORM
|
||||
UNAME_S := $(shell uname -s)
|
||||
UNAME_M := $(shell uname -m)
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
TARGETPLATFORM = osx-x64
|
||||
else
|
||||
ifeq ($(UNAME_M),x86_64)
|
||||
TARGETPLATFORM = linux-x64
|
||||
else
|
||||
TARGETPLATFORM = unix-generic
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
# program targets
|
||||
VERSION = $(shell git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || echo git-`git rev-parse --short HEAD`)
|
||||
|
||||
# dependencies
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
os-dependencies = osx-dependencies
|
||||
else
|
||||
os-dependencies = linux-dependencies
|
||||
endif
|
||||
|
||||
check-scripts:
|
||||
@echo
|
||||
@echo "Checking for Lua syntax errors..."
|
||||
@luac -p $(shell find mods/*/maps/* -iname '*.lua')
|
||||
@luac -p $(shell find lua/* -iname '*.lua')
|
||||
|
||||
check: dependencies
|
||||
check:
|
||||
@echo
|
||||
@echo "Compiling in debug mode..."
|
||||
@$(MSBUILD) -t:build -p:Configuration=Debug
|
||||
@@ -112,26 +118,6 @@ check: dependencies
|
||||
@echo "Checking for incorrect conditional trait interface overrides..."
|
||||
@mono --debug OpenRA.Utility.exe all --check-conditional-trait-interface-overrides
|
||||
|
||||
|
||||
NUNIT_CONSOLE := $(shell test -f thirdparty/download/nunit3-console.exe && echo mono thirdparty/download/nunit3-console.exe || \
|
||||
which nunit3-console 2>/dev/null || which nunit2-console 2>/dev/null || which nunit-console 2>/dev/null)
|
||||
nunit: core
|
||||
@echo
|
||||
@echo "Checking unit tests..."
|
||||
@if [ "$(NUNIT_CONSOLE)" = "" ] ; then \
|
||||
echo 'nunit[3|2]-console not found!'; \
|
||||
echo 'Was "make dependencies" called or is NUnit installed?'>&2; \
|
||||
echo 'See "make help".'; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if $(NUNIT_CONSOLE) --help | head -n 1 | grep -E "NUnit version (1|2\.[0-5])";then \
|
||||
echo 'NUnit version >= 2.6 required'>&2; \
|
||||
echo 'Try "make dependencies" first to use NUnit from NuGet.'>&2; \
|
||||
echo 'See "make help".'; \
|
||||
exit 1; \
|
||||
fi
|
||||
@$(NUNIT_CONSOLE) --noresult OpenRA.Test.nunit
|
||||
|
||||
test: core
|
||||
@echo
|
||||
@echo "Testing Tiberian Sun mod MiniYAML..."
|
||||
@@ -148,55 +134,21 @@ test: core
|
||||
|
||||
########################## MAKE/INSTALL RULES ##########################
|
||||
#
|
||||
all: dependencies core
|
||||
all: core
|
||||
|
||||
core:
|
||||
@command -v $(MSBUILD) >/dev/null || (echo "OpenRA requires the '$(MSBUILD)' tool provided by Mono >= 5.4."; exit 1)
|
||||
ifeq ($(WIN32), $(filter $(WIN32),true yes y on 1))
|
||||
@$(MSBUILD) -t:build -p:Configuration="Release-x86"
|
||||
else
|
||||
@$(MSBUILD) -t:build -p:Configuration=Release
|
||||
@command -v $(firstword $(MSBUILD)) >/dev/null || (echo "OpenRA requires the '$(MSBUILD)' tool provided by Mono >= 5.18."; exit 1)
|
||||
@$(MSBUILD) -t:Build -restore -p:Configuration=Release -p:TargetPlatform=$(TARGETPLATFORM)
|
||||
ifeq ($(TARGETPLATFORM), unix-generic)
|
||||
@./configure-system-libraries.sh
|
||||
endif
|
||||
@./fetch-geoip.sh
|
||||
|
||||
clean:
|
||||
@ $(MSBUILD) -t:clean
|
||||
@-$(RM_F) *.config
|
||||
@-$(RM_F) *.exe *.dll *.dylib ./OpenRA*/*.dll *.pdb mods/**/*.dll mods/**/*.pdb *.resources
|
||||
@-$(RM_F) *.config IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP
|
||||
@-$(RM_F) *.exe *.dll *.dll.config *.so *.dylib ./OpenRA*/*.dll *.pdb mods/**/*.dll mods/**/*.pdb *.resources
|
||||
@-$(RM_RF) ./*/bin ./*/obj
|
||||
@-$(RM_RF) ./thirdparty/download
|
||||
|
||||
distclean: clean
|
||||
|
||||
cli-dependencies:
|
||||
@./thirdparty/fetch-thirdparty-deps.sh
|
||||
@ $(CP_R) thirdparty/download/*.dll .
|
||||
@ $(CP_R) thirdparty/download/*.dll.config .
|
||||
@ test -f OpenRA.Game/obj/project.assets.json || $(MSBUILD) -t:restore
|
||||
|
||||
linux-dependencies: cli-dependencies geoip-dependencies linux-native-dependencies
|
||||
|
||||
linux-native-dependencies:
|
||||
@./thirdparty/configure-native-deps.sh
|
||||
|
||||
windows-dependencies: cli-dependencies geoip-dependencies
|
||||
ifeq ($(WIN32), $(filter $(WIN32),true yes y on 1))
|
||||
@./thirdparty/fetch-thirdparty-deps-windows.sh x86
|
||||
else
|
||||
@./thirdparty/fetch-thirdparty-deps-windows.sh x64
|
||||
endif
|
||||
|
||||
osx-dependencies: cli-dependencies geoip-dependencies
|
||||
@./thirdparty/fetch-thirdparty-deps-osx.sh
|
||||
@ $(CP_R) thirdparty/download/osx/*.dylib .
|
||||
@ $(CP_R) thirdparty/download/osx/*.dll.config .
|
||||
|
||||
geoip-dependencies:
|
||||
@./thirdparty/fetch-geoip-db.sh
|
||||
@ $(CP) thirdparty/download/GeoLite2-Country.mmdb.gz .
|
||||
|
||||
dependencies: $(os-dependencies)
|
||||
|
||||
all-dependencies: cli-dependencies windows-dependencies osx-dependencies geoip-dependencies
|
||||
@ $(MSBUILD) -t:clean
|
||||
|
||||
version: VERSION mods/ra/mod.yaml mods/cnc/mod.yaml mods/d2k/mod.yaml mods/ts/mod.yaml mods/modcontent/mod.yaml mods/all/mod.yaml
|
||||
@echo "$(VERSION)" > VERSION
|
||||
@@ -206,10 +158,34 @@ version: VERSION mods/ra/mod.yaml mods/cnc/mod.yaml mods/d2k/mod.yaml mods/ts/mo
|
||||
rm $${i}.tmp; \
|
||||
done
|
||||
|
||||
install: dependencies core install-core
|
||||
install: core install-engine install-common-mod-files install-default-mods
|
||||
@$(CP) *.sh "$(DATA_INSTALL_DIR)"
|
||||
|
||||
install-linux-shortcuts: install-linux-scripts install-linux-icons install-linux-desktop
|
||||
|
||||
install-dependencies:
|
||||
ifeq ($(TARGETPLATFORM), $(filter $(TARGETPLATFORM),win-x86 win-x64))
|
||||
@-echo "Installing OpenRA dependencies to $(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) soft_oal.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) SDL2.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) freetype6.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) lua51.dll "$(DATA_INSTALL_DIR)"
|
||||
endif
|
||||
ifeq ($(TARGETPLATFORM), linux-x64)
|
||||
@-echo "Installing OpenRA dependencies to $(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) soft_oal.so "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) SDL2.so "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) freetype6.so "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) lua51.so "$(DATA_INSTALL_DIR)"
|
||||
endif
|
||||
ifeq ($(TARGETPLATFORM), osx-x64)
|
||||
@-echo "Installing OpenRA dependencies to $(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) soft_oal.dylib "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) SDL2.dylib "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) freetype6.dylib "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) lua51.dylib "$(DATA_INSTALL_DIR)"
|
||||
endif
|
||||
|
||||
install-engine:
|
||||
@-echo "Installing OpenRA engine to $(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_DIR) "$(DATA_INSTALL_DIR)"
|
||||
@@ -218,11 +194,13 @@ install-engine:
|
||||
@$(INSTALL_PROGRAM) OpenRA.Utility.exe "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) OpenRA.Platforms.Default.dll "$(DATA_INSTALL_DIR)"
|
||||
|
||||
ifneq ($(TARGETPLATFORM), $(filter $(TARGETPLATFORM),win-x86 win-x64))
|
||||
@$(INSTALL_DATA) OpenRA.Platforms.Default.dll.config "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_DATA) "GeoLite2-Country.mmdb.gz" "$(DATA_INSTALL_DIR)/GeoLite2-Country.mmdb.gz"
|
||||
endif
|
||||
@$(INSTALL_DATA) VERSION "$(DATA_INSTALL_DIR)/VERSION"
|
||||
@$(INSTALL_DATA) AUTHORS "$(DATA_INSTALL_DIR)/AUTHORS"
|
||||
@$(INSTALL_DATA) COPYING "$(DATA_INSTALL_DIR)/COPYING"
|
||||
@$(INSTALL_DATA) IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP "$(DATA_INSTALL_DIR)/IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP"
|
||||
|
||||
@$(CP_R) glsl "$(DATA_INSTALL_DIR)"
|
||||
@$(CP_R) lua "$(DATA_INSTALL_DIR)"
|
||||
@@ -232,8 +210,9 @@ install-engine:
|
||||
@$(INSTALL_PROGRAM) ICSharpCode.SharpZipLib.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) FuzzyLogicLibrary.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) Open.Nat.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) MaxMind.Db.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) rix0rrr.BeaconLib.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) BeaconLib.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) DiscordRPC.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) Newtonsoft.Json.dll "$(DATA_INSTALL_DIR)"
|
||||
|
||||
install-common-mod-files:
|
||||
@-echo "Installing OpenRA common mod files to $(DATA_INSTALL_DIR)"
|
||||
@@ -252,19 +231,16 @@ install-default-mods:
|
||||
@$(INSTALL_PROGRAM) mods/d2k/OpenRA.Mods.D2k.dll "$(DATA_INSTALL_DIR)/mods/d2k"
|
||||
@$(CP_R) mods/modcontent "$(DATA_INSTALL_DIR)/mods/"
|
||||
|
||||
install-core: install-engine install-common-mod-files install-default-mods
|
||||
@$(CP) *.sh "$(DATA_INSTALL_DIR)"
|
||||
|
||||
install-linux-icons:
|
||||
for SIZE in 16x16 32x32 48x48 64x64 128x128; do \
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps"; \
|
||||
$(INSTALL_DATA) packaging/linux/icons/ra_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-ra.png"; \
|
||||
$(INSTALL_DATA) packaging/linux/icons/cnc_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-cnc.png"; \
|
||||
$(INSTALL_DATA) packaging/linux/icons/d2k_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-d2k.png"; \
|
||||
$(INSTALL_DATA) packaging/artwork/ra_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-ra.png"; \
|
||||
$(INSTALL_DATA) packaging/artwork/cnc_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-cnc.png"; \
|
||||
$(INSTALL_DATA) packaging/artwork/d2k_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-d2k.png"; \
|
||||
done
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps"
|
||||
$(INSTALL_DATA) packaging/linux/icons/ra_scalable.svg "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/openra-ra.svg"
|
||||
$(INSTALL_DATA) packaging/linux/icons/cnc_scalable.svg "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/openra-cnc.svg"
|
||||
$(INSTALL_DATA) packaging/artwork/ra_scalable.svg "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/openra-ra.svg"
|
||||
$(INSTALL_DATA) packaging/artwork/cnc_scalable.svg "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/openra-cnc.svg"
|
||||
|
||||
install-linux-desktop:
|
||||
@$(INSTALL_DIR) "$(DESTDIR)$(datadir)/applications"
|
||||
@@ -358,23 +334,28 @@ uninstall:
|
||||
|
||||
help:
|
||||
@echo 'to compile, run:'
|
||||
@echo ' make [DEBUG=false]'
|
||||
@echo ' make [DEBUG=true]'
|
||||
@echo
|
||||
@echo 'to check unit tests (requires NUnit version >= 2.6), run:'
|
||||
@echo ' make nunit [NUNIT_CONSOLE=<path-to/nunit[3|2]-console>] [NUNIT_LIBS_PATH=<path-to-libs-dir>] [NUNIT_LIBS=<nunit-libs>]'
|
||||
@echo ' Use NUNIT_CONSOLE if nunit[3|2]-console was not downloaded by `make dependencies` nor is it in bin search paths'
|
||||
@echo ' Use NUNIT_LIBS_PATH if NUnit libs are not in search paths. Include trailing /'
|
||||
@echo ' Use NUNIT_LIBS if NUnit libs have different names (such as including a prefix or suffix)'
|
||||
@echo 'to compile using system libraries for native dependencies, run:'
|
||||
@echo ' make [DEBUG=true] TARGETPLATFORM=unix-generic'
|
||||
@echo
|
||||
@echo 'to check the official mods for erroneous yaml files, run:'
|
||||
@echo ' make test'
|
||||
@echo
|
||||
@echo 'to check the engine and official mod dlls for code style violations, run:'
|
||||
@echo ' make test'
|
||||
@echo
|
||||
@echo 'to install, run:'
|
||||
@echo ' make [prefix=/foo] [bindir=/bar/bin] install'
|
||||
@echo
|
||||
@echo 'to install Linux startup scripts, desktop files and icons'
|
||||
@echo ' make install-linux-shortcuts [DEBUG=false]'
|
||||
@echo
|
||||
@echo ' to install the engine and common mod files (omitting the default mods):'
|
||||
@echo ' make install-engine'
|
||||
@echo ' make install-dependencies'
|
||||
@echo ' make install-common-mod-files'
|
||||
@echo
|
||||
@echo 'to uninstall, run:'
|
||||
@echo ' make uninstall'
|
||||
@echo
|
||||
@@ -387,4 +368,4 @@ help:
|
||||
|
||||
.SUFFIXES:
|
||||
|
||||
.PHONY: check-scripts check nunit test all core clean distclean cli-dependencies linux-dependencies linux-native-dependencies windows-dependencies osx-dependencies geoip-dependencies dependencies all-dependencies version install install-linux-shortcuts install-engine install-common-mod-files install-default-mods install-core install-linux-icons install-linux-desktop install-linux-mime install-linux-appdata install-man-page install-linux-scripts uninstall help
|
||||
.PHONY: check-scripts check test all core clean version install install-linux-shortcuts install-dependencies install-engine install-common-mod-files install-default-mods install-linux-icons install-linux-desktop install-linux-mime install-linux-appdata install-man-page install-linux-scripts uninstall help
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -40,21 +40,50 @@ namespace OpenRA.Activities
|
||||
* Things to be aware of when writing activities:
|
||||
*
|
||||
* - Use "return true" at least once somewhere in the tick method.
|
||||
* - Do not "reuse" (with "SequenceActivities", for example) activity objects that have already started running.
|
||||
* - Do not "reuse" activity objects (by queuing them as next or child, for example) that have already started running.
|
||||
* Queue a new instance instead.
|
||||
* - Avoid calling actor.CancelActivity(). It is almost always a bug. Call activity.Cancel() instead.
|
||||
* - Do not evaluate dynamic state (an actor's location, health, conditions, etc.) in the activity's constructor,
|
||||
* as that might change before the activity gets to tick for the first time. Use the OnFirstRun() method instead.
|
||||
*/
|
||||
public abstract class Activity : IActivityInterface
|
||||
{
|
||||
public ActivityState State { get; private set; }
|
||||
|
||||
protected Activity ChildActivity { get; private set; }
|
||||
public Activity NextActivity { get; private set; }
|
||||
Activity childActivity;
|
||||
protected Activity ChildActivity
|
||||
{
|
||||
get { return SkipDoneActivities(childActivity); }
|
||||
private set { childActivity = value; }
|
||||
}
|
||||
|
||||
Activity nextActivity;
|
||||
public Activity NextActivity
|
||||
{
|
||||
get { return SkipDoneActivities(nextActivity); }
|
||||
private set { nextActivity = value; }
|
||||
}
|
||||
|
||||
internal static Activity SkipDoneActivities(Activity first)
|
||||
{
|
||||
// If first.Cancel() was called while it was queued (i.e. before it first ticked), its state will be Done
|
||||
// rather than Queued (the activity system guarantees that it cannot be Active or Canceling).
|
||||
// An unknown number of ticks may have elapsed between the Cancel() call and now,
|
||||
// so we cannot make any assumptions on the value of first.NextActivity.
|
||||
// We must not return first (ticking it would be bogus), but returning null would potentially
|
||||
// drop valid activities queued after it. Walk the queue until we find a valid activity or
|
||||
// (more likely) run out of activities.
|
||||
while (first != null && first.State == ActivityState.Done)
|
||||
first = first.NextActivity;
|
||||
|
||||
return first;
|
||||
}
|
||||
|
||||
public bool IsInterruptible { get; protected set; }
|
||||
public bool ChildHasPriority { get; protected set; }
|
||||
public bool IsCanceling { get { return State == ActivityState.Canceling; } }
|
||||
bool finishing;
|
||||
bool firstRunCompleted;
|
||||
bool lastRun;
|
||||
|
||||
public Activity()
|
||||
@@ -71,9 +100,13 @@ namespace OpenRA.Activities
|
||||
if (State == ActivityState.Queued)
|
||||
{
|
||||
OnFirstRun(self);
|
||||
firstRunCompleted = true;
|
||||
State = ActivityState.Active;
|
||||
}
|
||||
|
||||
if (!firstRunCompleted)
|
||||
throw new InvalidOperationException("Actor {0} attempted to tick activity {1} before running its OnFirstRun method.".F(self, GetType()));
|
||||
|
||||
// Only run the parent tick when the child is done.
|
||||
// We must always let the child finish on its own before continuing.
|
||||
if (ChildHasPriority)
|
||||
@@ -152,8 +185,7 @@ namespace OpenRA.Activities
|
||||
/// </summary>
|
||||
internal void OnActorDisposeOuter(Actor self)
|
||||
{
|
||||
if (ChildActivity != null)
|
||||
ChildActivity.OnActorDisposeOuter(self);
|
||||
ChildActivity?.OnActorDisposeOuter(self);
|
||||
|
||||
OnActorDispose(self);
|
||||
}
|
||||
@@ -166,10 +198,10 @@ namespace OpenRA.Activities
|
||||
if (!IsInterruptible)
|
||||
return;
|
||||
|
||||
if (ChildActivity != null)
|
||||
ChildActivity.Cancel(self);
|
||||
ChildActivity?.Cancel(self);
|
||||
|
||||
State = ActivityState.Canceling;
|
||||
// Directly mark activities that are queued and therefore didn't run yet as done
|
||||
State = State == ActivityState.Queued ? ActivityState.Done : ActivityState.Canceling;
|
||||
}
|
||||
|
||||
public void Queue(Activity activity)
|
||||
@@ -209,11 +241,9 @@ namespace OpenRA.Activities
|
||||
|
||||
Console.WriteLine(GetType().ToString().Split('.').Last());
|
||||
|
||||
if (ChildActivity != null)
|
||||
ChildActivity.PrintActivityTree(self, origin, level + 1);
|
||||
ChildActivity?.PrintActivityTree(self, origin, level + 1);
|
||||
|
||||
if (NextActivity != null)
|
||||
NextActivity.PrintActivityTree(self, origin, level);
|
||||
NextActivity?.PrintActivityTree(self, origin, level);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -26,7 +26,7 @@ namespace OpenRA.Activities
|
||||
|
||||
public override bool Tick(Actor self)
|
||||
{
|
||||
if (a != null) a();
|
||||
a?.Invoke();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
@@ -44,7 +45,12 @@ namespace OpenRA
|
||||
public bool WillDispose { get; private set; }
|
||||
public bool Disposed { get; private set; }
|
||||
|
||||
public Activity CurrentActivity { get; private set; }
|
||||
Activity currentActivity;
|
||||
public Activity CurrentActivity
|
||||
{
|
||||
get { return Activity.SkipDoneActivities(currentActivity); }
|
||||
private set { currentActivity = value; }
|
||||
}
|
||||
|
||||
public int Generation;
|
||||
public Actor ReplacedByActor;
|
||||
@@ -63,12 +69,35 @@ namespace OpenRA
|
||||
{
|
||||
get
|
||||
{
|
||||
// TODO: Support non-zero pitch/roll in IFacing (IOrientation?)
|
||||
var facingValue = facing != null ? facing.Facing : 0;
|
||||
return new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(facingValue));
|
||||
return facing != null ? facing.Orientation : WRot.None;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Value used to represent an invalid token.</summary>
|
||||
public static readonly int InvalidConditionToken = -1;
|
||||
|
||||
class ConditionState
|
||||
{
|
||||
/// <summary>Delegates that have registered to be notified when this condition changes.</summary>
|
||||
public readonly List<VariableObserverNotifier> Notifiers = new List<VariableObserverNotifier>();
|
||||
|
||||
/// <summary>Unique integers identifying granted instances of the condition.</summary>
|
||||
public readonly HashSet<int> Tokens = new HashSet<int>();
|
||||
}
|
||||
|
||||
readonly Dictionary<string, ConditionState> conditionStates = new Dictionary<string, ConditionState>();
|
||||
|
||||
/// <summary>Each granted condition receives a unique token that is used when revoking.</summary>
|
||||
readonly Dictionary<int, string> conditionTokens = new Dictionary<int, string>();
|
||||
|
||||
int nextConditionToken = 1;
|
||||
|
||||
/// <summary>Cache of condition -> enabled state for quick evaluation of token counter conditions.</summary>
|
||||
readonly Dictionary<string, int> conditionCache = new Dictionary<string, int>();
|
||||
|
||||
/// <summary>Read-only version of conditionCache that is passed to IConditionConsumers.</summary>
|
||||
readonly IReadOnlyDictionary<string, int> readOnlyConditionCache;
|
||||
|
||||
internal SyncHash[] SyncHashes { get; private set; }
|
||||
|
||||
readonly IFacing facing;
|
||||
@@ -82,15 +111,25 @@ namespace OpenRA
|
||||
readonly INotifyIdle[] tickIdles;
|
||||
readonly ITargetablePositions[] targetablePositions;
|
||||
WPos[] staticTargetablePositions;
|
||||
bool created;
|
||||
|
||||
internal Actor(World world, string name, TypeDictionary initDict)
|
||||
{
|
||||
var duplicateInit = initDict.WithInterface<ISingleInstanceInit>().GroupBy(i => i.GetType())
|
||||
.FirstOrDefault(i => i.Count() > 1);
|
||||
|
||||
if (duplicateInit != null)
|
||||
throw new InvalidDataException("Duplicate initializer '{0}'".F(duplicateInit.Key.Name));
|
||||
|
||||
var init = new ActorInitializer(this, initDict);
|
||||
|
||||
readOnlyConditionCache = new ReadOnlyDictionary<string, int>(conditionCache);
|
||||
|
||||
World = world;
|
||||
ActorID = world.NextAID();
|
||||
if (initDict.Contains<OwnerInit>())
|
||||
Owner = init.Get<OwnerInit, Player>();
|
||||
var ownerInit = init.GetOrDefault<OwnerInit>();
|
||||
if (ownerInit != null)
|
||||
Owner = ownerInit.Value(world);
|
||||
|
||||
if (name != null)
|
||||
{
|
||||
@@ -138,6 +177,65 @@ namespace OpenRA
|
||||
SyncHashes = TraitsImplementing<ISync>().Select(sync => new SyncHash(sync)).ToArray();
|
||||
}
|
||||
|
||||
internal void Initialize(bool addToWorld = true)
|
||||
{
|
||||
created = true;
|
||||
|
||||
// Make sure traits are usable for condition notifiers
|
||||
foreach (var t in TraitsImplementing<INotifyCreated>())
|
||||
t.Created(this);
|
||||
|
||||
var allObserverNotifiers = new HashSet<VariableObserverNotifier>();
|
||||
foreach (var provider in TraitsImplementing<IObservesVariables>())
|
||||
{
|
||||
foreach (var variableUser in provider.GetVariableObservers())
|
||||
{
|
||||
allObserverNotifiers.Add(variableUser.Notifier);
|
||||
foreach (var variable in variableUser.Variables)
|
||||
{
|
||||
var cs = conditionStates.GetOrAdd(variable);
|
||||
cs.Notifiers.Add(variableUser.Notifier);
|
||||
|
||||
// Initialize conditions that have not yet been granted to 0
|
||||
// NOTE: Some conditions may have already been granted by INotifyCreated calling GrantCondition,
|
||||
// and we choose to assign the token count to safely cover both cases instead of adding an if branch.
|
||||
conditionCache[variable] = cs.Tokens.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update all traits with their initial condition state
|
||||
foreach (var notify in allObserverNotifiers)
|
||||
notify(this, readOnlyConditionCache);
|
||||
|
||||
// TODO: Some traits may need initialization after being notified of initial condition state.
|
||||
|
||||
// TODO: A post condition initialization notification phase may allow queueing activities instead.
|
||||
// The initial activity should run before any activities queued by INotifyCreated.Created
|
||||
// However, we need to know which traits are enabled (via conditions), so wait for after the calls and insert the activity as the first
|
||||
ICreationActivity creationActivity = null;
|
||||
foreach (var ica in TraitsImplementing<ICreationActivity>())
|
||||
{
|
||||
if (!ica.IsTraitEnabled())
|
||||
continue;
|
||||
|
||||
if (creationActivity != null)
|
||||
throw new InvalidOperationException("More than one enabled ICreationActivity trait: {0} and {1}".F(creationActivity.GetType().Name, ica.GetType().Name));
|
||||
|
||||
var activity = ica.GetCreationActivity();
|
||||
if (activity == null)
|
||||
continue;
|
||||
|
||||
creationActivity = ica;
|
||||
|
||||
activity.Queue(CurrentActivity);
|
||||
CurrentActivity = activity;
|
||||
}
|
||||
|
||||
if (addToWorld)
|
||||
World.Add(this);
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
var wasIdle = IsIdle;
|
||||
@@ -198,7 +296,7 @@ namespace OpenRA
|
||||
yield return r;
|
||||
}
|
||||
|
||||
public Rectangle MouseBounds(WorldRenderer wr)
|
||||
public Polygon MouseBounds(WorldRenderer wr)
|
||||
{
|
||||
foreach (var mb in mouseBounds)
|
||||
{
|
||||
@@ -207,18 +305,22 @@ namespace OpenRA
|
||||
return bounds;
|
||||
}
|
||||
|
||||
return Rectangle.Empty;
|
||||
return Polygon.Empty;
|
||||
}
|
||||
|
||||
public void QueueActivity(bool queued, Activity nextActivity)
|
||||
{
|
||||
if (!queued)
|
||||
CancelActivity();
|
||||
|
||||
QueueActivity(nextActivity);
|
||||
}
|
||||
|
||||
public void QueueActivity(Activity nextActivity)
|
||||
{
|
||||
if (!created)
|
||||
throw new InvalidOperationException("An activity was queued before the actor was created. Queue it inside the INotifyCreated.Created callback instead.");
|
||||
|
||||
if (CurrentActivity == null)
|
||||
CurrentActivity = nextActivity;
|
||||
else
|
||||
@@ -227,8 +329,7 @@ namespace OpenRA
|
||||
|
||||
public void CancelActivity()
|
||||
{
|
||||
if (CurrentActivity != null)
|
||||
CurrentActivity.Cancel(this);
|
||||
CurrentActivity?.Cancel(this);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
@@ -280,8 +381,7 @@ namespace OpenRA
|
||||
{
|
||||
// If CurrentActivity isn't null, run OnActorDisposeOuter in case some cleanups are needed.
|
||||
// This should be done before the FrameEndTask to avoid dependency issues.
|
||||
if (CurrentActivity != null)
|
||||
CurrentActivity.OnActorDisposeOuter(this);
|
||||
CurrentActivity?.OnActorDisposeOuter(this);
|
||||
|
||||
// Allow traits/activities to prevent a race condition when they depend on disposing the actor (e.g. Transforms)
|
||||
WillDispose = true;
|
||||
@@ -300,8 +400,7 @@ namespace OpenRA
|
||||
World.TraitDict.RemoveActor(this);
|
||||
Disposed = true;
|
||||
|
||||
if (luaInterface != null)
|
||||
luaInterface.Value.OnActorDestroyed();
|
||||
luaInterface?.Value.OnActorDestroyed();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -415,6 +514,65 @@ namespace OpenRA
|
||||
return new[] { CenterPosition };
|
||||
}
|
||||
|
||||
#region Conditions
|
||||
|
||||
void UpdateConditionState(string condition, int token, bool isRevoke)
|
||||
{
|
||||
ConditionState conditionState = conditionStates.GetOrAdd(condition);
|
||||
|
||||
if (isRevoke)
|
||||
conditionState.Tokens.Remove(token);
|
||||
else
|
||||
conditionState.Tokens.Add(token);
|
||||
|
||||
conditionCache[condition] = conditionState.Tokens.Count;
|
||||
|
||||
// Conditions may be granted or revoked before the state is initialized.
|
||||
// These notifications will be processed after INotifyCreated.Created.
|
||||
if (created)
|
||||
foreach (var notify in conditionState.Notifiers)
|
||||
notify(this, readOnlyConditionCache);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Grants a specified condition if it is valid.
|
||||
/// Otherwise, just returns InvalidConditionToken.
|
||||
/// </summary>
|
||||
/// <returns>The token that is used to revoke this condition.</returns>
|
||||
public int GrantCondition(string condition)
|
||||
{
|
||||
if (string.IsNullOrEmpty(condition))
|
||||
return InvalidConditionToken;
|
||||
|
||||
var token = nextConditionToken++;
|
||||
conditionTokens.Add(token, condition);
|
||||
UpdateConditionState(condition, token, false);
|
||||
return token;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Revokes a previously granted condition.
|
||||
/// </summary>
|
||||
/// <param name="token">The token ID returned by GrantCondition.</param>
|
||||
/// <returns>The invalid token ID.</returns>
|
||||
public int RevokeCondition(int token)
|
||||
{
|
||||
if (!conditionTokens.TryGetValue(token, out var condition))
|
||||
throw new InvalidOperationException("Attempting to revoke condition with invalid token {0} for {1}.".F(token, this));
|
||||
|
||||
conditionTokens.Remove(token);
|
||||
UpdateConditionState(condition, token, true);
|
||||
return InvalidConditionToken;
|
||||
}
|
||||
|
||||
/// <summary>Returns whether the specified token is valid for RevokeCondition</summary>
|
||||
public bool TokenValid(int token)
|
||||
{
|
||||
return conditionTokens.ContainsKey(token);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Scripting interface
|
||||
|
||||
Lazy<ScriptActorInterface> luaInterface;
|
||||
@@ -432,8 +590,7 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
Actor a, b;
|
||||
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
|
||||
if (!left.TryGetClrValue(out Actor a) || !right.TryGetClrValue(out Actor b))
|
||||
return false;
|
||||
|
||||
return a == b;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -89,9 +89,7 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Add(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CPos a;
|
||||
CVec b;
|
||||
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
|
||||
if (!left.TryGetClrValue(out CPos a) || !right.TryGetClrValue(out CVec b))
|
||||
throw new LuaException("Attempted to call CPos.Add(CPos, CVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
|
||||
|
||||
return new LuaCustomClrObject(a + b);
|
||||
@@ -99,21 +97,18 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Subtract(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CPos a;
|
||||
var rightType = right.WrappedClrType();
|
||||
if (!left.TryGetClrValue(out a))
|
||||
if (!left.TryGetClrValue(out CPos a))
|
||||
throw new LuaException("Attempted to call CPos.Subtract(CPos, (CPos|CVec)) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, rightType.Name));
|
||||
|
||||
if (rightType == typeof(CPos))
|
||||
{
|
||||
CPos b;
|
||||
right.TryGetClrValue(out b);
|
||||
right.TryGetClrValue(out CPos b);
|
||||
return new LuaCustomClrObject(a - b);
|
||||
}
|
||||
else if (rightType == typeof(CVec))
|
||||
{
|
||||
CVec b;
|
||||
right.TryGetClrValue(out b);
|
||||
right.TryGetClrValue(out CVec b);
|
||||
return new LuaCustomClrObject(a - b);
|
||||
}
|
||||
|
||||
@@ -122,8 +117,7 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CPos a, b;
|
||||
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
|
||||
if (!left.TryGetClrValue(out CPos a) || !right.TryGetClrValue(out CPos b))
|
||||
return false;
|
||||
|
||||
return a == b;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -75,8 +75,7 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Add(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CVec a, b;
|
||||
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
|
||||
if (!left.TryGetClrValue(out CVec a) || !right.TryGetClrValue(out CVec b))
|
||||
throw new LuaException("Attempted to call CVec.Add(CVec, CVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
|
||||
|
||||
return new LuaCustomClrObject(a + b);
|
||||
@@ -84,8 +83,7 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Subtract(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CVec a, b;
|
||||
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
|
||||
if (!left.TryGetClrValue(out CVec a) || !right.TryGetClrValue(out CVec b))
|
||||
throw new LuaException("Attempted to call CVec.Subtract(CVec, CVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
|
||||
|
||||
return new LuaCustomClrObject(a - b);
|
||||
@@ -98,8 +96,7 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CVec a, b;
|
||||
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
|
||||
if (!left.TryGetClrValue(out CVec a) || !right.TryGetClrValue(out CVec b))
|
||||
return false;
|
||||
|
||||
return a == b;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
20
OpenRA.Game/DefaultPlayer.cs
Normal file
20
OpenRA.Game/DefaultPlayer.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public class DefaultPlayer : IGlobalModData
|
||||
{
|
||||
public readonly Color Color = Color.FromAhsl(0, 0, 238);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -90,8 +90,7 @@ namespace OpenRA
|
||||
public void CancelAsync()
|
||||
{
|
||||
lock (syncObject)
|
||||
if (wc != null)
|
||||
wc.CancelAsync();
|
||||
wc?.CancelAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -10,6 +10,7 @@
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
@@ -18,26 +19,23 @@ namespace OpenRA.Effects
|
||||
public class DelayedImpact : IEffect
|
||||
{
|
||||
readonly Target target;
|
||||
readonly Actor firedBy;
|
||||
readonly IEnumerable<int> damageModifiers;
|
||||
readonly IWarhead wh;
|
||||
readonly WarheadArgs args;
|
||||
|
||||
int delay;
|
||||
|
||||
public DelayedImpact(int delay, IWarhead wh, Target target, Actor firedBy, IEnumerable<int> damageModifiers)
|
||||
public DelayedImpact(int delay, IWarhead wh, Target target, WarheadArgs args)
|
||||
{
|
||||
this.wh = wh;
|
||||
this.delay = delay;
|
||||
|
||||
this.target = target;
|
||||
this.firedBy = firedBy;
|
||||
this.damageModifiers = damageModifiers;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public void Tick(World world)
|
||||
{
|
||||
if (--delay <= 0)
|
||||
world.AddFrameEndTask(w => { w.Remove(this); wh.DoImpact(target, firedBy, damageModifiers); });
|
||||
world.AddFrameEndTask(w => { w.Remove(this); wh.DoImpact(target, args); });
|
||||
}
|
||||
|
||||
public IEnumerable<IRenderable> Render(WorldRenderer wr) { yield break; }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -24,4 +24,5 @@ namespace OpenRA.Effects
|
||||
public interface ISpatiallyPartitionable { }
|
||||
|
||||
public interface IEffectAboveShroud { IEnumerable<IRenderable> RenderAboveShroud(WorldRenderer wr); }
|
||||
public interface IEffectAnnotation { IEnumerable<IRenderable> RenderAnnotation(WorldRenderer wr); }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -16,6 +16,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
@@ -30,6 +31,8 @@ namespace OpenRA
|
||||
public readonly string LaunchPath;
|
||||
public readonly string[] LaunchArgs;
|
||||
public Sprite Icon { get; internal set; }
|
||||
public Sprite Icon2x { get; internal set; }
|
||||
public Sprite Icon3x { get; internal set; }
|
||||
|
||||
public static string MakeKey(Manifest mod) { return MakeKey(mod.Id, mod.Metadata.Version); }
|
||||
public static string MakeKey(ExternalMod mod) { return MakeKey(mod.Id, mod.Version); }
|
||||
@@ -41,14 +44,34 @@ namespace OpenRA
|
||||
readonly Dictionary<string, ExternalMod> mods = new Dictionary<string, ExternalMod>();
|
||||
readonly SheetBuilder sheetBuilder;
|
||||
|
||||
Sheet CreateSheet()
|
||||
{
|
||||
var sheet = new Sheet(SheetType.BGRA, new Size(512, 512));
|
||||
|
||||
// We must manually force the buffer creation to avoid a crash
|
||||
// that is indirectly triggered by rendering from a Sheet that
|
||||
// has not yet been written to.
|
||||
sheet.CreateBuffer();
|
||||
sheet.GetTexture().ScaleFilter = TextureScaleFilter.Linear;
|
||||
|
||||
return sheet;
|
||||
}
|
||||
|
||||
public ExternalMods()
|
||||
{
|
||||
sheetBuilder = new SheetBuilder(SheetType.BGRA, 256);
|
||||
// Don't try to load mod icons if we don't have a texture to put them in
|
||||
if (Game.Renderer != null)
|
||||
sheetBuilder = new SheetBuilder(SheetType.BGRA, CreateSheet);
|
||||
|
||||
// If the player has defined a local support directory (in the game directory)
|
||||
// then this will override both the regular and system support dirs
|
||||
var sources = new[] { Platform.SystemSupportDir, Platform.SupportDir };
|
||||
foreach (var source in sources.Distinct())
|
||||
// Several types of support directory types are available, depending on
|
||||
// how the player has installed and launched the game.
|
||||
// Read registration metadata from all of them
|
||||
var sources = Enum.GetValues(typeof(SupportDirType))
|
||||
.Cast<SupportDirType>()
|
||||
.Select(t => Platform.GetSupportDir(t))
|
||||
.Distinct();
|
||||
|
||||
foreach (var source in sources)
|
||||
{
|
||||
var metadataPath = Path.Combine(source, "ModMetadata");
|
||||
if (!Directory.Exists(metadataPath))
|
||||
@@ -73,11 +96,23 @@ namespace OpenRA
|
||||
void LoadMod(MiniYaml yaml, string path = null, bool forceRegistration = false)
|
||||
{
|
||||
var mod = FieldLoader.Load<ExternalMod>(yaml);
|
||||
var iconNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon");
|
||||
if (iconNode != null && !string.IsNullOrEmpty(iconNode.Value.Value))
|
||||
|
||||
if (sheetBuilder != null)
|
||||
{
|
||||
using (var stream = new MemoryStream(Convert.FromBase64String(iconNode.Value.Value)))
|
||||
mod.Icon = sheetBuilder.Add(new Png(stream));
|
||||
var iconNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon");
|
||||
if (iconNode != null && !string.IsNullOrEmpty(iconNode.Value.Value))
|
||||
using (var stream = new MemoryStream(Convert.FromBase64String(iconNode.Value.Value)))
|
||||
mod.Icon = sheetBuilder.Add(new Png(stream));
|
||||
|
||||
var icon2xNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon2x");
|
||||
if (icon2xNode != null && !string.IsNullOrEmpty(icon2xNode.Value.Value))
|
||||
using (var stream = new MemoryStream(Convert.FromBase64String(icon2xNode.Value.Value)))
|
||||
mod.Icon2x = sheetBuilder.Add(new Png(stream), 1f / 2);
|
||||
|
||||
var icon3xNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon3x");
|
||||
if (icon3xNode != null && !string.IsNullOrEmpty(icon3xNode.Value.Value))
|
||||
using (var stream = new MemoryStream(Convert.FromBase64String(icon3xNode.Value.Value)))
|
||||
mod.Icon3x = sheetBuilder.Add(new Png(stream), 1f / 3);
|
||||
}
|
||||
|
||||
// Avoid possibly overwriting a valid mod with an obviously bogus one
|
||||
@@ -91,35 +126,47 @@ namespace OpenRA
|
||||
if (mod.Metadata.Hidden)
|
||||
return;
|
||||
|
||||
var iconData = "";
|
||||
var key = ExternalMod.MakeKey(mod);
|
||||
var yaml = new MiniYamlNode("Registration", new MiniYaml("", new List<MiniYamlNode>()
|
||||
{
|
||||
new MiniYamlNode("Id", mod.Id),
|
||||
new MiniYamlNode("Version", mod.Metadata.Version),
|
||||
new MiniYamlNode("Title", mod.Metadata.Title),
|
||||
new MiniYamlNode("LaunchPath", launchPath),
|
||||
new MiniYamlNode("LaunchArgs", "Game.Mod=" + mod.Id)
|
||||
}));
|
||||
|
||||
using (var stream = mod.Package.GetStream("icon.png"))
|
||||
if (stream != null)
|
||||
iconData = Convert.ToBase64String(stream.ReadAllBytes());
|
||||
yaml.Value.Nodes.Add(new MiniYamlNode("Icon", Convert.ToBase64String(stream.ReadAllBytes())));
|
||||
|
||||
var key = ExternalMod.MakeKey(mod);
|
||||
var yaml = new List<MiniYamlNode>()
|
||||
{
|
||||
new MiniYamlNode("Registration", new MiniYaml("", new List<MiniYamlNode>()
|
||||
{
|
||||
new MiniYamlNode("Id", mod.Id),
|
||||
new MiniYamlNode("Version", mod.Metadata.Version),
|
||||
new MiniYamlNode("Title", mod.Metadata.Title),
|
||||
new MiniYamlNode("Icon", iconData),
|
||||
new MiniYamlNode("LaunchPath", launchPath),
|
||||
new MiniYamlNode("LaunchArgs", "Game.Mod=" + mod.Id)
|
||||
}))
|
||||
};
|
||||
using (var stream = mod.Package.GetStream("icon-2x.png"))
|
||||
if (stream != null)
|
||||
yaml.Value.Nodes.Add(new MiniYamlNode("Icon2x", Convert.ToBase64String(stream.ReadAllBytes())));
|
||||
|
||||
using (var stream = mod.Package.GetStream("icon-3x.png"))
|
||||
if (stream != null)
|
||||
yaml.Value.Nodes.Add(new MiniYamlNode("Icon3x", Convert.ToBase64String(stream.ReadAllBytes())));
|
||||
|
||||
var sources = new List<string>();
|
||||
if (registration.HasFlag(ModRegistration.System))
|
||||
sources.Add(Platform.SystemSupportDir);
|
||||
sources.Add(Platform.GetSupportDir(SupportDirType.System));
|
||||
|
||||
if (registration.HasFlag(ModRegistration.User))
|
||||
sources.Add(Platform.SupportDir);
|
||||
{
|
||||
sources.Add(Platform.GetSupportDir(SupportDirType.User));
|
||||
|
||||
// If using the modern support dir we must also write the registration
|
||||
// to the legacy support dir for older engine versions, but ONLY if it exists
|
||||
var legacyPath = Platform.GetSupportDir(SupportDirType.LegacyUser);
|
||||
if (Directory.Exists(legacyPath))
|
||||
sources.Add(legacyPath);
|
||||
}
|
||||
|
||||
// Make sure the mod is available for this session, even if saving it fails
|
||||
LoadMod(yaml.First().Value, forceRegistration: true);
|
||||
LoadMod(yaml.Value, forceRegistration: true);
|
||||
|
||||
var lines = new List<MiniYamlNode> { yaml }.ToLines().ToArray();
|
||||
foreach (var source in sources.Distinct())
|
||||
{
|
||||
var metadataPath = Path.Combine(source, "ModMetadata");
|
||||
@@ -127,7 +174,7 @@ namespace OpenRA
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(metadataPath);
|
||||
File.WriteAllLines(Path.Combine(metadataPath, key + ".yaml"), yaml.ToLines().ToArray());
|
||||
File.WriteAllLines(Path.Combine(metadataPath, key + ".yaml"), lines);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -148,10 +195,16 @@ namespace OpenRA
|
||||
{
|
||||
var sources = new List<string>();
|
||||
if (registration.HasFlag(ModRegistration.System))
|
||||
sources.Add(Platform.SystemSupportDir);
|
||||
sources.Add(Platform.GetSupportDir(SupportDirType.System));
|
||||
|
||||
if (registration.HasFlag(ModRegistration.User))
|
||||
sources.Add(Platform.SupportDir);
|
||||
{
|
||||
// User support dir may be using the modern or legacy value, or overridden by the user
|
||||
// Add all the possibilities and let the .Distinct() below ignore the duplicates
|
||||
sources.Add(Platform.GetSupportDir(SupportDirType.User));
|
||||
sources.Add(Platform.GetSupportDir(SupportDirType.ModernUser));
|
||||
sources.Add(Platform.GetSupportDir(SupportDirType.LegacyUser));
|
||||
}
|
||||
|
||||
var activeModKey = ExternalMod.MakeKey(activeMod);
|
||||
foreach (var source in sources.Distinct())
|
||||
@@ -207,10 +260,16 @@ namespace OpenRA
|
||||
{
|
||||
var sources = new List<string>();
|
||||
if (registration.HasFlag(ModRegistration.System))
|
||||
sources.Add(Platform.SystemSupportDir);
|
||||
sources.Add(Platform.GetSupportDir(SupportDirType.System));
|
||||
|
||||
if (registration.HasFlag(ModRegistration.User))
|
||||
sources.Add(Platform.SupportDir);
|
||||
{
|
||||
// User support dir may be using the modern or legacy value, or overridden by the user
|
||||
// Add all the possibilities and let the .Distinct() below ignore the duplicates
|
||||
sources.Add(Platform.GetSupportDir(SupportDirType.User));
|
||||
sources.Add(Platform.GetSupportDir(SupportDirType.ModernUser));
|
||||
sources.Add(Platform.GetSupportDir(SupportDirType.LegacyUser));
|
||||
}
|
||||
|
||||
var key = ExternalMod.MakeKey(mod);
|
||||
mods.Remove(key);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -80,7 +80,7 @@ namespace OpenRA
|
||||
|
||||
static int WindingDirectionTest(int2 v0, int2 v1, int2 p)
|
||||
{
|
||||
return (v1.X - v0.X) * (p.Y - v0.Y) - (p.X - v0.X) * (v1.Y - v0.Y);
|
||||
return Math.Sign((v1.X - v0.X) * (p.Y - v0.Y) - (p.X - v0.X) * (v1.Y - v0.Y));
|
||||
}
|
||||
|
||||
public static bool PolygonContains(this int2[] polygon, int2 p)
|
||||
@@ -101,6 +101,16 @@ namespace OpenRA
|
||||
return windingNumber != 0;
|
||||
}
|
||||
|
||||
public static bool LinesIntersect(int2 a, int2 b, int2 c, int2 d)
|
||||
{
|
||||
// If line segments AB and CD intersect:
|
||||
// - the triangles ACD and BCD must have opposite sense (clockwise or anticlockwise)
|
||||
// - the triangles CAB and DAB must have opposite sense
|
||||
// Segments intersect if the orientation (clockwise or anticlockwise) of the two points in each line segment are opposite with respect to the other
|
||||
// Assumes that lines are not colinear
|
||||
return WindingDirectionTest(c, d, a) != WindingDirectionTest(c, d, b) && WindingDirectionTest(a, b, c) != WindingDirectionTest(a, b, d);
|
||||
}
|
||||
|
||||
public static bool HasModifier(this Modifiers k, Modifiers mod)
|
||||
{
|
||||
// PERF: Enum.HasFlag is slower and requires allocations.
|
||||
@@ -115,8 +125,7 @@ namespace OpenRA
|
||||
|
||||
public static V GetOrAdd<K, V>(this Dictionary<K, V> d, K k, Func<K, V> createFn)
|
||||
{
|
||||
V ret;
|
||||
if (!d.TryGetValue(k, out ret))
|
||||
if (!d.TryGetValue(k, out var ret))
|
||||
d.Add(k, ret = createFn(k));
|
||||
return ret;
|
||||
}
|
||||
@@ -151,6 +160,26 @@ namespace OpenRA
|
||||
return xs.ElementAt(r.Next(xs.Count));
|
||||
}
|
||||
|
||||
public static Rectangle Union(this IEnumerable<Rectangle> rects)
|
||||
{
|
||||
// PERF: Avoid LINQ.
|
||||
var first = true;
|
||||
var result = Rectangle.Empty;
|
||||
foreach (var rect in rects)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
result = rect;
|
||||
continue;
|
||||
}
|
||||
|
||||
result = Rectangle.Union(rect, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static float Product(this IEnumerable<float> xs)
|
||||
{
|
||||
return xs.Aggregate(1f, (a, x) => a * x);
|
||||
@@ -323,8 +352,7 @@ namespace OpenRA
|
||||
|
||||
public static int IntegerDivisionRoundingAwayFromZero(int dividend, int divisor)
|
||||
{
|
||||
int remainder;
|
||||
var quotient = Math.DivRem(dividend, divisor, out remainder);
|
||||
var quotient = Math.DivRem(dividend, divisor, out var remainder);
|
||||
if (remainder == 0)
|
||||
return quotient;
|
||||
return quotient + (Math.Sign(dividend) == Math.Sign(divisor) ? 1 : -1);
|
||||
@@ -375,8 +403,7 @@ namespace OpenRA
|
||||
// Check for a key conflict:
|
||||
if (d.ContainsKey(key))
|
||||
{
|
||||
List<string> dupKeyMessages;
|
||||
if (!dupKeys.TryGetValue(key, out dupKeyMessages))
|
||||
if (!dupKeys.TryGetValue(key, out var dupKeyMessages))
|
||||
{
|
||||
// Log the initial conflicting value already inserted:
|
||||
dupKeyMessages = new List<string>();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -18,7 +18,6 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Support;
|
||||
|
||||
@@ -122,8 +121,7 @@ namespace OpenRA
|
||||
{
|
||||
ret = null;
|
||||
|
||||
MiniYaml yaml;
|
||||
if (!md.TryGetValue(yamlName, out yaml))
|
||||
if (!md.TryGetValue(yamlName, out var yaml))
|
||||
return false;
|
||||
|
||||
ret = GetValue(field.Name, field.FieldType, yaml, field);
|
||||
@@ -181,42 +179,36 @@ namespace OpenRA
|
||||
|
||||
public static object GetValue(string fieldName, Type fieldType, MiniYaml yaml, MemberInfo field)
|
||||
{
|
||||
var value = yaml.Value;
|
||||
if (value != null) value = value.Trim();
|
||||
var value = yaml.Value?.Trim();
|
||||
|
||||
if (fieldType == typeof(int))
|
||||
{
|
||||
int res;
|
||||
if (Exts.TryParseIntegerInvariant(value, out res))
|
||||
if (Exts.TryParseIntegerInvariant(value, out var res))
|
||||
return res;
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
else if (fieldType == typeof(ushort))
|
||||
{
|
||||
ushort res;
|
||||
if (ushort.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out res))
|
||||
if (ushort.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out var res))
|
||||
return res;
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
|
||||
if (fieldType == typeof(long))
|
||||
{
|
||||
long res;
|
||||
if (long.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out res))
|
||||
if (long.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out var res))
|
||||
return res;
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
else if (fieldType == typeof(float))
|
||||
{
|
||||
float res;
|
||||
if (value != null && float.TryParse(value.Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
|
||||
if (value != null && float.TryParse(value.Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var res))
|
||||
return res * (value.Contains('%') ? 0.01f : 1f);
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
else if (fieldType == typeof(decimal))
|
||||
{
|
||||
decimal res;
|
||||
if (value != null && decimal.TryParse(value.Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
|
||||
if (value != null && decimal.TryParse(value.Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var res))
|
||||
return res * (value.Contains('%') ? 0.01m : 1m);
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
@@ -228,16 +220,14 @@ namespace OpenRA
|
||||
}
|
||||
else if (fieldType == typeof(Color))
|
||||
{
|
||||
Color color;
|
||||
if (value != null && Color.TryParse(value, out color))
|
||||
if (value != null && Color.TryParse(value, out var color))
|
||||
return color;
|
||||
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
else if (fieldType == typeof(Hotkey))
|
||||
{
|
||||
Hotkey res;
|
||||
if (Hotkey.TryParse(value, out res))
|
||||
if (Hotkey.TryParse(value, out var res))
|
||||
return res;
|
||||
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
@@ -248,8 +238,7 @@ namespace OpenRA
|
||||
}
|
||||
else if (fieldType == typeof(WDist))
|
||||
{
|
||||
WDist res;
|
||||
if (WDist.TryParse(value, out res))
|
||||
if (WDist.TryParse(value, out var res))
|
||||
return res;
|
||||
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
@@ -261,8 +250,7 @@ namespace OpenRA
|
||||
var parts = value.Split(',');
|
||||
if (parts.Length == 3)
|
||||
{
|
||||
WDist rx, ry, rz;
|
||||
if (WDist.TryParse(parts[0], out rx) && WDist.TryParse(parts[1], out ry) && WDist.TryParse(parts[2], out rz))
|
||||
if (WDist.TryParse(parts[0], out var rx) && WDist.TryParse(parts[1], out var ry) && WDist.TryParse(parts[2], out var rz))
|
||||
return new WVec(rx, ry, rz);
|
||||
}
|
||||
}
|
||||
@@ -282,8 +270,7 @@ namespace OpenRA
|
||||
|
||||
for (var i = 0; i < vecs.Length; ++i)
|
||||
{
|
||||
WDist rx, ry, rz;
|
||||
if (WDist.TryParse(parts[3 * i], out rx) && WDist.TryParse(parts[3 * i + 1], out ry) && WDist.TryParse(parts[3 * i + 2], out rz))
|
||||
if (WDist.TryParse(parts[3 * i], out var rx) && WDist.TryParse(parts[3 * i + 1], out var ry) && WDist.TryParse(parts[3 * i + 2], out var rz))
|
||||
vecs[i] = new WVec(rx, ry, rz);
|
||||
}
|
||||
|
||||
@@ -299,8 +286,7 @@ namespace OpenRA
|
||||
var parts = value.Split(',');
|
||||
if (parts.Length == 3)
|
||||
{
|
||||
WDist rx, ry, rz;
|
||||
if (WDist.TryParse(parts[0], out rx) && WDist.TryParse(parts[1], out ry) && WDist.TryParse(parts[2], out rz))
|
||||
if (WDist.TryParse(parts[0], out var rx) && WDist.TryParse(parts[1], out var ry) && WDist.TryParse(parts[2], out var rz))
|
||||
return new WPos(rx, ry, rz);
|
||||
}
|
||||
}
|
||||
@@ -309,8 +295,7 @@ namespace OpenRA
|
||||
}
|
||||
else if (fieldType == typeof(WAngle))
|
||||
{
|
||||
int res;
|
||||
if (Exts.TryParseIntegerInvariant(value, out res))
|
||||
if (Exts.TryParseIntegerInvariant(value, out var res))
|
||||
return new WAngle(res);
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
@@ -321,8 +306,7 @@ namespace OpenRA
|
||||
var parts = value.Split(',');
|
||||
if (parts.Length == 3)
|
||||
{
|
||||
int rr, rp, ry;
|
||||
if (Exts.TryParseIntegerInvariant(parts[0], out rr) && Exts.TryParseIntegerInvariant(parts[1], out rp) && Exts.TryParseIntegerInvariant(parts[2], out ry))
|
||||
if (Exts.TryParseIntegerInvariant(parts[0], out var rr) && Exts.TryParseIntegerInvariant(parts[1], out var rp) && Exts.TryParseIntegerInvariant(parts[2], out var ry))
|
||||
return new WRot(new WAngle(rr), new WAngle(rp), new WAngle(ry));
|
||||
}
|
||||
}
|
||||
@@ -361,8 +345,7 @@ namespace OpenRA
|
||||
var vecs = new CVec[parts.Length / 2];
|
||||
for (var i = 0; i < vecs.Length; i++)
|
||||
{
|
||||
int rx, ry;
|
||||
if (int.TryParse(parts[2 * i], out rx) && int.TryParse(parts[2 * i + 1], out ry))
|
||||
if (int.TryParse(parts[2 * i], out var rx) && int.TryParse(parts[2 * i + 1], out var ry))
|
||||
vecs[i] = new CVec(rx, ry);
|
||||
}
|
||||
|
||||
@@ -415,7 +398,12 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
else if (fieldType == typeof(bool))
|
||||
return ParseYesNo(value, fieldType, fieldName);
|
||||
{
|
||||
if (bool.TryParse(value.ToLowerInvariant(), out var result))
|
||||
return result;
|
||||
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
else if (fieldType == typeof(int2[]))
|
||||
{
|
||||
if (value != null)
|
||||
@@ -504,8 +492,7 @@ namespace OpenRA
|
||||
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
float xx = 0;
|
||||
float yy = 0;
|
||||
float res;
|
||||
if (float.TryParse(parts[0].Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
|
||||
if (float.TryParse(parts[0].Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var res))
|
||||
xx = res * (parts[0].Contains('%') ? 0.01f : 1f);
|
||||
if (float.TryParse(parts[1].Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
|
||||
yy = res * (parts[1].Contains('%') ? 0.01f : 1f);
|
||||
@@ -519,13 +506,11 @@ namespace OpenRA
|
||||
if (value != null)
|
||||
{
|
||||
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
float z = 0;
|
||||
float.TryParse(parts[0], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out x);
|
||||
float.TryParse(parts[1], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out y);
|
||||
float.TryParse(parts[0], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var x);
|
||||
float.TryParse(parts[1], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var y);
|
||||
|
||||
// z component is optional for compatibility with older float2 definitions
|
||||
float z = 0;
|
||||
if (parts.Length > 2)
|
||||
float.TryParse(parts[2], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out z);
|
||||
|
||||
@@ -561,14 +546,16 @@ namespace OpenRA
|
||||
}
|
||||
else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return null;
|
||||
|
||||
var innerType = fieldType.GetGenericArguments().First();
|
||||
var innerValue = GetValue("Nullable<T>", innerType, value, field);
|
||||
return fieldType.GetConstructor(new[] { innerType }).Invoke(new[] { innerValue });
|
||||
}
|
||||
else if (fieldType == typeof(DateTime))
|
||||
{
|
||||
DateTime dt;
|
||||
if (DateTime.TryParseExact(value, "yyyy-MM-dd HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out dt))
|
||||
if (DateTime.TryParseExact(value, "yyyy-MM-dd HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var dt))
|
||||
return dt;
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
@@ -592,20 +579,6 @@ namespace OpenRA
|
||||
return null;
|
||||
}
|
||||
|
||||
static object ParseYesNo(string p, Type fieldType, string field)
|
||||
{
|
||||
if (string.IsNullOrEmpty(p))
|
||||
return InvalidValueAction(p, fieldType, field);
|
||||
|
||||
p = p.ToLowerInvariant();
|
||||
if (p == "yes") return true;
|
||||
if (p == "true") return true;
|
||||
if (p == "no") return false;
|
||||
if (p == "false") return false;
|
||||
|
||||
return InvalidValueAction(p, fieldType, field);
|
||||
}
|
||||
|
||||
public sealed class FieldLoadInfo
|
||||
{
|
||||
public readonly FieldInfo Field;
|
||||
@@ -732,8 +705,7 @@ namespace OpenRA
|
||||
if (translations == null)
|
||||
return key;
|
||||
|
||||
string value;
|
||||
if (!translations.TryGetValue(key, out value))
|
||||
if (!translations.TryGetValue(key, out var value))
|
||||
return key;
|
||||
|
||||
return value;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -15,7 +15,6 @@ using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -67,15 +67,12 @@ namespace OpenRA.FileSystem
|
||||
return new Folder(resolvedPath);
|
||||
|
||||
// Children of another package require special handling
|
||||
IReadOnlyPackage parent;
|
||||
string subPath = null;
|
||||
if (TryGetPackageContaining(filename, out parent, out subPath))
|
||||
if (TryGetPackageContaining(filename, out var parent, out var subPath))
|
||||
return parent.OpenPackage(subPath, this);
|
||||
|
||||
// Try and open it normally
|
||||
IReadOnlyPackage package;
|
||||
var stream = Open(filename);
|
||||
if (TryParsePackage(stream, filename, out package))
|
||||
if (TryParsePackage(stream, filename, out var package))
|
||||
return package;
|
||||
|
||||
// No package loaders took ownership of the stream, so clean it up
|
||||
@@ -97,8 +94,7 @@ namespace OpenRA.FileSystem
|
||||
{
|
||||
name = name.Substring(1);
|
||||
|
||||
Manifest mod;
|
||||
if (!installedMods.TryGetValue(name, out mod))
|
||||
if (!installedMods.TryGetValue(name, out var mod))
|
||||
throw new InvalidOperationException("Could not load mod '{0}'. Available mods: {1}".F(name, installedMods.Keys.JoinWith(", ")));
|
||||
|
||||
package = mod.Package;
|
||||
@@ -122,8 +118,7 @@ namespace OpenRA.FileSystem
|
||||
|
||||
public void Mount(IReadOnlyPackage package, string explicitName = null)
|
||||
{
|
||||
var mountCount = 0;
|
||||
if (mountedPackages.TryGetValue(package, out mountCount))
|
||||
if (mountedPackages.TryGetValue(package, out var mountCount))
|
||||
{
|
||||
// Package is already mounted
|
||||
// Increment the mount count and bump up the file loading priority
|
||||
@@ -149,8 +144,7 @@ namespace OpenRA.FileSystem
|
||||
|
||||
public bool Unmount(IReadOnlyPackage package)
|
||||
{
|
||||
var mountCount = 0;
|
||||
if (!mountedPackages.TryGetValue(package, out mountCount))
|
||||
if (!mountedPackages.TryGetValue(package, out var mountCount))
|
||||
return false;
|
||||
|
||||
if (--mountCount <= 0)
|
||||
@@ -203,16 +197,12 @@ namespace OpenRA.FileSystem
|
||||
var package = fileIndex[filename]
|
||||
.LastOrDefault(x => x.Contains(filename));
|
||||
|
||||
if (package != null)
|
||||
return package.GetStream(filename);
|
||||
|
||||
return null;
|
||||
return package?.GetStream(filename);
|
||||
}
|
||||
|
||||
public Stream Open(string filename)
|
||||
{
|
||||
Stream s;
|
||||
if (!TryOpen(filename, out s))
|
||||
if (!TryOpen(filename, out var s))
|
||||
throw new FileNotFoundException("File not found: {0}".F(filename), filename);
|
||||
|
||||
return s;
|
||||
@@ -238,8 +228,7 @@ namespace OpenRA.FileSystem
|
||||
var explicitSplit = filename.IndexOf('|');
|
||||
if (explicitSplit > 0)
|
||||
{
|
||||
IReadOnlyPackage explicitPackage;
|
||||
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out explicitPackage))
|
||||
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out var explicitPackage))
|
||||
{
|
||||
s = explicitPackage.GetStream(filename.Substring(explicitSplit + 1));
|
||||
if (s != null)
|
||||
@@ -274,12 +263,9 @@ namespace OpenRA.FileSystem
|
||||
{
|
||||
var explicitSplit = filename.IndexOf('|');
|
||||
if (explicitSplit > 0)
|
||||
{
|
||||
IReadOnlyPackage explicitPackage;
|
||||
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out explicitPackage))
|
||||
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out var explicitPackage))
|
||||
if (explicitPackage.Contains(filename.Substring(explicitSplit + 1)))
|
||||
return true;
|
||||
}
|
||||
|
||||
return fileIndex.ContainsKey(filename);
|
||||
}
|
||||
@@ -293,8 +279,7 @@ namespace OpenRA.FileSystem
|
||||
if (explicitSplit < 0)
|
||||
return false;
|
||||
|
||||
IReadOnlyPackage explicitPackage;
|
||||
if (!explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out explicitPackage))
|
||||
if (!explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out var explicitPackage))
|
||||
return false;
|
||||
|
||||
if (installedMods[modID].Package == explicitPackage)
|
||||
@@ -321,8 +306,7 @@ namespace OpenRA.FileSystem
|
||||
|
||||
if (parentPath.StartsWith("$", StringComparison.Ordinal))
|
||||
{
|
||||
Manifest mod;
|
||||
if (!installedMods.TryGetValue(parentPath.Substring(1), out mod))
|
||||
if (!installedMods.TryGetValue(parentPath.Substring(1), out var mod))
|
||||
return null;
|
||||
|
||||
if (!(mod.Package is Folder))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -58,17 +58,15 @@ namespace OpenRA.FileSystem
|
||||
return new Folder(resolvedPath);
|
||||
|
||||
// Zip files loaded from Folders (and *only* from Folders) can be read-write
|
||||
IReadWritePackage readWritePackage;
|
||||
if (ZipFileLoader.TryParseReadWritePackage(resolvedPath, out readWritePackage))
|
||||
if (ZipFileLoader.TryParseReadWritePackage(resolvedPath, out var readWritePackage))
|
||||
return readWritePackage;
|
||||
|
||||
// Other package types can be loaded normally
|
||||
IReadOnlyPackage package;
|
||||
var s = GetStream(filename);
|
||||
if (s == null)
|
||||
return null;
|
||||
|
||||
if (context.TryParsePackage(s, filename, out package))
|
||||
if (context.TryParsePackage(s, filename, out var package))
|
||||
return package;
|
||||
|
||||
s.Dispose();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -14,7 +14,6 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.FileSystem
|
||||
{
|
||||
@@ -67,8 +66,7 @@ namespace OpenRA.FileSystem
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (pkg != null)
|
||||
pkg.Close();
|
||||
pkg?.Close();
|
||||
}
|
||||
|
||||
public IReadOnlyPackage OpenPackage(string filename, FileSystem context)
|
||||
@@ -82,12 +80,11 @@ namespace OpenRA.FileSystem
|
||||
return new ZipFolder(this, filename);
|
||||
|
||||
// Other package types can be loaded normally
|
||||
IReadOnlyPackage package;
|
||||
var s = GetStream(filename);
|
||||
if (s == null)
|
||||
return null;
|
||||
|
||||
if (context.TryParsePackage(s, filename, out package))
|
||||
if (context.TryParsePackage(s, filename, out var package))
|
||||
return package;
|
||||
|
||||
s.Dispose();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -22,6 +22,7 @@ using System.Threading.Tasks;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Server;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
@@ -38,9 +39,10 @@ namespace OpenRA
|
||||
|
||||
public static ModData ModData;
|
||||
public static Settings Settings;
|
||||
public static ICursor Cursor;
|
||||
public static CursorManager Cursor;
|
||||
public static bool HideCursor;
|
||||
static WorldRenderer worldRenderer;
|
||||
static string modLaunchWrapper;
|
||||
|
||||
internal static OrderManager OrderManager;
|
||||
static Server.Server server;
|
||||
@@ -49,7 +51,6 @@ namespace OpenRA
|
||||
|
||||
public static Renderer Renderer;
|
||||
public static Sound Sound;
|
||||
public static bool HasInputFocus = false;
|
||||
|
||||
public static string EngineVersion { get; private set; }
|
||||
public static LocalPlayerProfile LocalPlayerProfile;
|
||||
@@ -60,13 +61,13 @@ namespace OpenRA
|
||||
|
||||
public static event Action OnShellmapLoaded = () => { };
|
||||
|
||||
public static OrderManager JoinServer(string host, int port, string password, bool recordReplay = true)
|
||||
public static OrderManager JoinServer(ConnectionTarget endpoint, string password, bool recordReplay = true)
|
||||
{
|
||||
var connection = new NetworkConnection(host, port);
|
||||
var connection = new NetworkConnection(endpoint);
|
||||
if (recordReplay)
|
||||
connection.StartRecording(() => { return TimestampedFilename(); });
|
||||
|
||||
var om = new OrderManager(host, port, password, connection);
|
||||
var om = new OrderManager(endpoint, password, connection);
|
||||
JoinInner(om);
|
||||
return om;
|
||||
}
|
||||
@@ -74,12 +75,12 @@ namespace OpenRA
|
||||
static string TimestampedFilename(bool includemilliseconds = false)
|
||||
{
|
||||
var format = includemilliseconds ? "yyyy-MM-ddTHHmmssfffZ" : "yyyy-MM-ddTHHmmssZ";
|
||||
return "OpenRA-" + DateTime.UtcNow.ToString(format, CultureInfo.InvariantCulture);
|
||||
return ModData.Manifest.Id + "-" + DateTime.UtcNow.ToString(format, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
static void JoinInner(OrderManager om)
|
||||
{
|
||||
if (OrderManager != null) OrderManager.Dispose();
|
||||
OrderManager?.Dispose();
|
||||
OrderManager = om;
|
||||
lastConnectionState = ConnectionState.PreConnecting;
|
||||
ConnectionStateChanged(OrderManager);
|
||||
@@ -87,12 +88,12 @@ namespace OpenRA
|
||||
|
||||
public static void JoinReplay(string replayFile)
|
||||
{
|
||||
JoinInner(new OrderManager("<no server>", -1, "", new ReplayConnection(replayFile)));
|
||||
JoinInner(new OrderManager(new ConnectionTarget(), "", new ReplayConnection(replayFile)));
|
||||
}
|
||||
|
||||
static void JoinLocal()
|
||||
{
|
||||
JoinInner(new OrderManager("<no server>", -1, "", new EchoConnection()));
|
||||
JoinInner(new OrderManager(new ConnectionTarget(), "", new EchoConnection()));
|
||||
}
|
||||
|
||||
// More accurate replacement for Environment.TickCount
|
||||
@@ -103,14 +104,14 @@ namespace OpenRA
|
||||
public static int NetFrameNumber { get { return OrderManager.NetFrameNumber; } }
|
||||
public static int LocalTick { get { return OrderManager.LocalFrameNumber; } }
|
||||
|
||||
public static event Action<string, int> OnRemoteDirectConnect = (a, b) => { };
|
||||
public static event Action<ConnectionTarget> OnRemoteDirectConnect = _ => { };
|
||||
public static event Action<OrderManager> ConnectionStateChanged = _ => { };
|
||||
static ConnectionState lastConnectionState = ConnectionState.PreConnecting;
|
||||
public static int LocalClientId { get { return OrderManager.Connection.LocalClientId; } }
|
||||
|
||||
public static void RemoteDirectConnect(string host, int port)
|
||||
public static void RemoteDirectConnect(ConnectionTarget endpoint)
|
||||
{
|
||||
OnRemoteDirectConnect(host, port);
|
||||
OnRemoteDirectConnect(endpoint);
|
||||
}
|
||||
|
||||
// Hacky workaround for orderManager visibility
|
||||
@@ -153,8 +154,7 @@ namespace OpenRA
|
||||
internal static void StartGame(string mapUID, WorldType type)
|
||||
{
|
||||
// Dispose of the old world before creating a new one.
|
||||
if (worldRenderer != null)
|
||||
worldRenderer.Dispose();
|
||||
worldRenderer?.Dispose();
|
||||
|
||||
Cursor.SetCursor(null);
|
||||
BeforeGameStart();
|
||||
@@ -232,7 +232,7 @@ namespace OpenRA
|
||||
|
||||
LobbyInfoChanged += lobbyReady;
|
||||
|
||||
om = JoinServer(IPAddress.Loopback.ToString(), CreateLocalServer(mapUID), "");
|
||||
om = JoinServer(CreateLocalServer(mapUID), "");
|
||||
}
|
||||
|
||||
public static bool IsHost
|
||||
@@ -264,7 +264,7 @@ namespace OpenRA
|
||||
static void Initialize(Arguments args)
|
||||
{
|
||||
var supportDirArg = args.GetValue("Engine.SupportDir", null);
|
||||
if (supportDirArg != null)
|
||||
if (!string.IsNullOrEmpty(supportDirArg))
|
||||
Platform.OverrideSupportDir(supportDirArg);
|
||||
|
||||
Console.WriteLine("Platform is {0}", Platform.CurrentPlatform);
|
||||
@@ -300,6 +300,7 @@ namespace OpenRA
|
||||
Log.AddChannel("graphics", "graphics.log");
|
||||
Log.AddChannel("geoip", "geoip.log");
|
||||
Log.AddChannel("nat", "nat.log");
|
||||
Log.AddChannel("client", "client.log");
|
||||
|
||||
var platforms = new[] { Settings.Game.Platform, "Default", null };
|
||||
foreach (var p in platforms)
|
||||
@@ -328,16 +329,12 @@ namespace OpenRA
|
||||
Log.Write("graphics", "{0}", e);
|
||||
Console.WriteLine("Renderer initialization failed. Check graphics.log for details.");
|
||||
|
||||
if (Renderer != null)
|
||||
Renderer.Dispose();
|
||||
Renderer?.Dispose();
|
||||
|
||||
if (Sound != null)
|
||||
Sound.Dispose();
|
||||
Sound?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
GeoIP.Initialize();
|
||||
|
||||
if (Settings.Server.DiscoverNatDevices)
|
||||
discoverNat = UPnP.DiscoverNatDevices(Settings.Server.NatDiscoveryTimeout);
|
||||
|
||||
@@ -351,10 +348,11 @@ namespace OpenRA
|
||||
foreach (var mod in Mods)
|
||||
Console.WriteLine("\t{0}: {1} ({2})", mod.Key, mod.Value.Metadata.Title, mod.Value.Metadata.Version);
|
||||
|
||||
modLaunchWrapper = args.GetValue("Engine.LaunchWrapper", null);
|
||||
|
||||
ExternalMods = new ExternalMods();
|
||||
|
||||
Manifest currentMod;
|
||||
if (modID != null && Mods.TryGetValue(modID, out currentMod))
|
||||
if (modID != null && Mods.TryGetValue(modID, out _))
|
||||
{
|
||||
var launchPath = args.GetValue("Engine.LaunchPath", Assembly.GetEntryAssembly().Location);
|
||||
|
||||
@@ -365,8 +363,7 @@ namespace OpenRA
|
||||
|
||||
ExternalMods.Register(Mods[modID], launchPath, ModRegistration.User);
|
||||
|
||||
ExternalMod activeMod;
|
||||
if (ExternalMods.TryGetValue(ExternalMod.MakeKey(Mods[modID]), out activeMod))
|
||||
if (ExternalMods.TryGetValue(ExternalMod.MakeKey(Mods[modID]), out var activeMod))
|
||||
ExternalMods.ClearInvalidRegistrations(activeMod, ModRegistration.User);
|
||||
}
|
||||
|
||||
@@ -383,18 +380,15 @@ namespace OpenRA
|
||||
LobbyInfoChanged = () => { };
|
||||
ConnectionStateChanged = om => { };
|
||||
BeforeGameStart = () => { };
|
||||
OnRemoteDirectConnect = (a, b) => { };
|
||||
OnRemoteDirectConnect = endpoint => { };
|
||||
delayedActions = new ActionQueue();
|
||||
|
||||
Ui.ResetAll();
|
||||
|
||||
if (worldRenderer != null)
|
||||
worldRenderer.Dispose();
|
||||
worldRenderer?.Dispose();
|
||||
worldRenderer = null;
|
||||
if (server != null)
|
||||
server.Shutdown();
|
||||
if (OrderManager != null)
|
||||
OrderManager.Dispose();
|
||||
server?.Shutdown();
|
||||
OrderManager?.Dispose();
|
||||
|
||||
if (ModData != null)
|
||||
{
|
||||
@@ -430,40 +424,22 @@ namespace OpenRA
|
||||
var grid = ModData.Manifest.Contains<MapGrid>() ? ModData.Manifest.Get<MapGrid>() : null;
|
||||
Renderer.InitializeDepthBuffer(grid);
|
||||
|
||||
if (Cursor != null)
|
||||
Cursor.Dispose();
|
||||
Cursor?.Dispose();
|
||||
|
||||
if (Settings.Graphics.HardwareCursors)
|
||||
{
|
||||
try
|
||||
{
|
||||
Cursor = new HardwareCursor(ModData.CursorProvider);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Write("debug", "Failed to initialize hardware cursors. Falling back to software cursors.");
|
||||
Log.Write("debug", "Error was: " + e.Message);
|
||||
|
||||
Console.WriteLine("Failed to initialize hardware cursors. Falling back to software cursors.");
|
||||
Console.WriteLine("Error was: " + e.Message);
|
||||
|
||||
Cursor = new SoftwareCursor(ModData.CursorProvider);
|
||||
}
|
||||
}
|
||||
else
|
||||
Cursor = new SoftwareCursor(ModData.CursorProvider);
|
||||
Cursor = new CursorManager(ModData.CursorProvider);
|
||||
|
||||
PerfHistory.Items["render"].HasNormalTick = false;
|
||||
PerfHistory.Items["batches"].HasNormalTick = false;
|
||||
PerfHistory.Items["render_world"].HasNormalTick = false;
|
||||
PerfHistory.Items["render_widgets"].HasNormalTick = false;
|
||||
PerfHistory.Items["render_flip"].HasNormalTick = false;
|
||||
PerfHistory.Items["terrain_lighting"].HasNormalTick = false;
|
||||
|
||||
JoinLocal();
|
||||
|
||||
try
|
||||
{
|
||||
if (discoverNat != null)
|
||||
discoverNat.Wait();
|
||||
discoverNat?.Wait();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -509,8 +485,15 @@ namespace OpenRA
|
||||
{
|
||||
try
|
||||
{
|
||||
var path = mod.LaunchPath;
|
||||
var args = launchArguments != null ? mod.LaunchArgs.Append(launchArguments) : mod.LaunchArgs;
|
||||
var p = Process.Start(mod.LaunchPath, args.Select(a => "\"" + a + "\"").JoinWith(" "));
|
||||
if (modLaunchWrapper != null)
|
||||
{
|
||||
path = modLaunchWrapper;
|
||||
args = new[] { mod.LaunchPath }.Concat(args);
|
||||
}
|
||||
|
||||
var p = Process.Start(path, args.Select(a => "\"" + a + "\"").JoinWith(" "));
|
||||
if (p == null || p.HasExited)
|
||||
onFailed();
|
||||
else
|
||||
@@ -550,7 +533,7 @@ namespace OpenRA
|
||||
var path = Path.Combine(directory, string.Concat(filename, ".png"));
|
||||
Log.Write("debug", "Taking screenshot " + path);
|
||||
|
||||
Renderer.Context.SaveScreenshot(path);
|
||||
Renderer.SaveScreenshot(path);
|
||||
Debug("Saved screenshot " + filename);
|
||||
}
|
||||
}
|
||||
@@ -619,14 +602,13 @@ namespace OpenRA
|
||||
Sync.RunUnsynced(Settings.Debug.SyncCheckUnsyncedCode, world, () => world.TickRender(worldRenderer));
|
||||
}
|
||||
|
||||
if (benchmark != null)
|
||||
benchmark.Tick(LocalTick);
|
||||
benchmark?.Tick(LocalTick);
|
||||
}
|
||||
}
|
||||
|
||||
static void LogicTick()
|
||||
{
|
||||
delayedActions.PerformActions(RunTime);
|
||||
PerformDelayedActions();
|
||||
|
||||
if (OrderManager.Connection.ConnectionState != lastConnectionState)
|
||||
{
|
||||
@@ -639,6 +621,11 @@ namespace OpenRA
|
||||
InnerLogicTick(worldRenderer.World.OrderManager);
|
||||
}
|
||||
|
||||
public static void PerformDelayedActions()
|
||||
{
|
||||
delayedActions.PerformActions(RunTime);
|
||||
}
|
||||
|
||||
public static void TakeScreenshot()
|
||||
{
|
||||
takeScreenshot = true;
|
||||
@@ -650,24 +637,39 @@ namespace OpenRA
|
||||
{
|
||||
++RenderFrame;
|
||||
|
||||
// worldRenderer is null during the initial install/download screen
|
||||
if (worldRenderer != null)
|
||||
// Prepare renderables (i.e. render voxels) before calling BeginFrame
|
||||
using (new PerfSample("render_prepare"))
|
||||
{
|
||||
Renderer.BeginFrame(worldRenderer.Viewport.TopLeft, worldRenderer.Viewport.Zoom);
|
||||
Sound.SetListenerPosition(worldRenderer.Viewport.CenterPosition);
|
||||
Renderer.WorldModelRenderer.BeginFrame();
|
||||
|
||||
// Use worldRenderer.World instead of OrderManager.World to avoid a rendering mismatch while processing orders
|
||||
if (!worldRenderer.World.IsLoadingGameSave)
|
||||
// World rendering is disabled while the loading screen is displayed
|
||||
if (worldRenderer != null && !worldRenderer.World.IsLoadingGameSave)
|
||||
{
|
||||
worldRenderer.Viewport.Tick();
|
||||
worldRenderer.PrepareRenderables();
|
||||
}
|
||||
|
||||
Ui.PrepareRenderables();
|
||||
Renderer.WorldModelRenderer.EndFrame();
|
||||
}
|
||||
|
||||
// worldRenderer is null during the initial install/download screen
|
||||
// World rendering is disabled while the loading screen is displayed
|
||||
// Use worldRenderer.World instead of OrderManager.World to avoid a rendering mismatch while processing orders
|
||||
if (worldRenderer != null && !worldRenderer.World.IsLoadingGameSave)
|
||||
{
|
||||
Renderer.BeginWorld(worldRenderer.Viewport.Rectangle);
|
||||
Sound.SetListenerPosition(worldRenderer.Viewport.CenterPosition);
|
||||
using (new PerfSample("render_world"))
|
||||
worldRenderer.Draw();
|
||||
}
|
||||
else
|
||||
Renderer.BeginFrame(int2.Zero, 1f);
|
||||
|
||||
using (new PerfSample("render_widgets"))
|
||||
{
|
||||
Renderer.WorldModelRenderer.BeginFrame();
|
||||
Ui.PrepareRenderables();
|
||||
Renderer.WorldModelRenderer.EndFrame();
|
||||
Renderer.BeginUI();
|
||||
|
||||
if (worldRenderer != null && !worldRenderer.World.IsLoadingGameSave)
|
||||
worldRenderer.DrawAnnotations();
|
||||
|
||||
Ui.Draw();
|
||||
|
||||
@@ -695,8 +697,10 @@ namespace OpenRA
|
||||
|
||||
PerfHistory.Items["render"].Tick();
|
||||
PerfHistory.Items["batches"].Tick();
|
||||
PerfHistory.Items["render_world"].Tick();
|
||||
PerfHistory.Items["render_widgets"].Tick();
|
||||
PerfHistory.Items["render_flip"].Tick();
|
||||
PerfHistory.Items["terrain_lighting"].Tick();
|
||||
}
|
||||
|
||||
static void Loop()
|
||||
@@ -742,6 +746,7 @@ namespace OpenRA
|
||||
var nextLogic = RunTime;
|
||||
var nextRender = RunTime;
|
||||
var forcedNextRender = RunTime;
|
||||
var renderBeforeNextTick = false;
|
||||
|
||||
while (state == RunStatus.Running)
|
||||
{
|
||||
@@ -770,9 +775,9 @@ namespace OpenRA
|
||||
var nextUpdate = Math.Min(nextLogic, nextRender);
|
||||
if (now >= nextUpdate)
|
||||
{
|
||||
var forceRender = now >= forcedNextRender;
|
||||
var forceRender = renderBeforeNextTick || now >= forcedNextRender;
|
||||
|
||||
if (now >= nextLogic)
|
||||
if (now >= nextLogic && !renderBeforeNextTick)
|
||||
{
|
||||
nextLogic += logicInterval;
|
||||
|
||||
@@ -780,7 +785,7 @@ namespace OpenRA
|
||||
|
||||
// Force at least one render per tick during regular gameplay
|
||||
if (OrderManager.World != null && !OrderManager.World.IsLoadingGameSave && !OrderManager.World.IsReplay)
|
||||
forceRender = true;
|
||||
renderBeforeNextTick = true;
|
||||
}
|
||||
|
||||
var haveSomeTimeUntilNextLogic = now < nextLogic;
|
||||
@@ -799,6 +804,7 @@ namespace OpenRA
|
||||
forcedNextRender = now + maxRenderInterval;
|
||||
|
||||
RenderTick();
|
||||
renderBeforeNextTick = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -821,12 +827,10 @@ namespace OpenRA
|
||||
finally
|
||||
{
|
||||
// Ensure that the active replay is properly saved
|
||||
if (OrderManager != null)
|
||||
OrderManager.Dispose();
|
||||
OrderManager?.Dispose();
|
||||
}
|
||||
|
||||
if (worldRenderer != null)
|
||||
worldRenderer.Dispose();
|
||||
worldRenderer?.Dispose();
|
||||
ModData.Dispose();
|
||||
ChromeProvider.Deinitialize();
|
||||
|
||||
@@ -843,6 +847,11 @@ namespace OpenRA
|
||||
state = RunStatus.Success;
|
||||
}
|
||||
|
||||
public static void AddSystemLine(string text)
|
||||
{
|
||||
AddSystemLine("Battlefield Control", text);
|
||||
}
|
||||
|
||||
public static void AddSystemLine(string name, string text)
|
||||
{
|
||||
OrderManager.AddChatLine(name, systemMessageColor, text, systemMessageColor);
|
||||
@@ -860,8 +869,7 @@ namespace OpenRA
|
||||
|
||||
public static void Disconnect()
|
||||
{
|
||||
if (OrderManager.World != null)
|
||||
OrderManager.World.TraitDict.PrintReport();
|
||||
OrderManager.World?.TraitDict.PrintReport();
|
||||
|
||||
OrderManager.Dispose();
|
||||
CloseServer();
|
||||
@@ -870,8 +878,7 @@ namespace OpenRA
|
||||
|
||||
public static void CloseServer()
|
||||
{
|
||||
if (server != null)
|
||||
server.Shutdown();
|
||||
server?.Shutdown();
|
||||
}
|
||||
|
||||
public static T CreateObject<T>(string name)
|
||||
@@ -879,12 +886,19 @@ namespace OpenRA
|
||||
return ModData.ObjectCreator.CreateObject<T>(name);
|
||||
}
|
||||
|
||||
public static void CreateServer(ServerSettings settings)
|
||||
public static ConnectionTarget CreateServer(ServerSettings settings)
|
||||
{
|
||||
server = new Server.Server(new IPEndPoint(IPAddress.Any, settings.ListenPort), settings, ModData, false);
|
||||
var endpoints = new List<IPEndPoint>
|
||||
{
|
||||
new IPEndPoint(IPAddress.IPv6Any, settings.ListenPort),
|
||||
new IPEndPoint(IPAddress.Any, settings.ListenPort)
|
||||
};
|
||||
server = new Server.Server(endpoints, settings, ModData, ServerType.Multiplayer);
|
||||
|
||||
return server.GetEndpointForLocalConnection();
|
||||
}
|
||||
|
||||
public static int CreateLocalServer(string map)
|
||||
public static ConnectionTarget CreateLocalServer(string map)
|
||||
{
|
||||
var settings = new ServerSettings()
|
||||
{
|
||||
@@ -893,9 +907,14 @@ namespace OpenRA
|
||||
AdvertiseOnline = false
|
||||
};
|
||||
|
||||
server = new Server.Server(new IPEndPoint(IPAddress.Loopback, 0), settings, ModData, false);
|
||||
var endpoints = new List<IPEndPoint>
|
||||
{
|
||||
new IPEndPoint(IPAddress.IPv6Loopback, 0),
|
||||
new IPEndPoint(IPAddress.Loopback, 0)
|
||||
};
|
||||
server = new Server.Server(endpoints, settings, ModData, ServerType.Local);
|
||||
|
||||
return server.Port;
|
||||
return server.GetEndpointForLocalConnection();
|
||||
}
|
||||
|
||||
public static bool IsCurrentWorld(World world)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -133,9 +133,7 @@ namespace OpenRA
|
||||
/// <summary>Gets the player information for the specified runtime player instance.</summary>
|
||||
public Player GetPlayer(OpenRA.Player runtimePlayer)
|
||||
{
|
||||
Player player;
|
||||
|
||||
playersByRuntime.TryGetValue(runtimePlayer, out player);
|
||||
playersByRuntime.TryGetValue(runtimePlayer, out var player);
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -23,6 +23,7 @@ namespace OpenRA
|
||||
public class ActorInfo
|
||||
{
|
||||
public const string AbstractActorPrefix = "^";
|
||||
public const char TraitInstanceSeparator = '@';
|
||||
|
||||
/// <summary>
|
||||
/// The actor name can be anything, but the sprites used in the Render*: traits default to this one.
|
||||
@@ -32,7 +33,7 @@ namespace OpenRA
|
||||
/// </summary>
|
||||
public readonly string Name;
|
||||
readonly TypeDictionary traits = new TypeDictionary();
|
||||
List<ITraitInfo> constructOrderCache = null;
|
||||
List<TraitInfo> constructOrderCache = null;
|
||||
|
||||
public ActorInfo(ObjectCreator creator, string name, MiniYaml node)
|
||||
{
|
||||
@@ -44,7 +45,11 @@ namespace OpenRA
|
||||
{
|
||||
try
|
||||
{
|
||||
traits.Add(LoadTraitInfo(creator, t.Key.Split('@')[0], t.Value));
|
||||
// HACK: The linter does not want to crash when a trait doesn't exist but only print an error instead
|
||||
// LoadTraitInfo will only return null to signal us to abort here if the linter is running
|
||||
var trait = LoadTraitInfo(creator, t.Key, t.Value);
|
||||
if (trait != null)
|
||||
traits.Add(trait);
|
||||
}
|
||||
catch (FieldLoader.MissingFieldsException e)
|
||||
{
|
||||
@@ -60,7 +65,7 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
public ActorInfo(string name, params ITraitInfo[] traitInfos)
|
||||
public ActorInfo(string name, params TraitInfo[] traitInfos)
|
||||
{
|
||||
Name = name;
|
||||
foreach (var t in traitInfos)
|
||||
@@ -68,14 +73,24 @@ namespace OpenRA
|
||||
traits.TrimExcess();
|
||||
}
|
||||
|
||||
static ITraitInfo LoadTraitInfo(ObjectCreator creator, string traitName, MiniYaml my)
|
||||
static TraitInfo LoadTraitInfo(ObjectCreator creator, string traitName, MiniYaml my)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(my.Value))
|
||||
throw new YamlException("Junk value `{0}` on trait node {1}"
|
||||
.F(my.Value, traitName));
|
||||
var info = creator.CreateObject<ITraitInfo>(traitName + "Info");
|
||||
|
||||
// HACK: The linter does not want to crash when a trait doesn't exist but only print an error instead
|
||||
// ObjectCreator will only return null to signal us to abort here if the linter is running
|
||||
var traitInstance = traitName.Split(TraitInstanceSeparator);
|
||||
var info = creator.CreateObject<TraitInfo>(traitInstance[0] + "Info");
|
||||
if (info == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
if (traitInstance.Length > 1)
|
||||
info.GetType().GetField("InstanceName").SetValue(info, traitInstance[1]);
|
||||
|
||||
FieldLoader.Load(info, my);
|
||||
}
|
||||
catch (FieldLoader.MissingFieldsException e)
|
||||
@@ -87,12 +102,12 @@ namespace OpenRA
|
||||
return info;
|
||||
}
|
||||
|
||||
public IEnumerable<ITraitInfo> TraitsInConstructOrder()
|
||||
public IEnumerable<TraitInfo> TraitsInConstructOrder()
|
||||
{
|
||||
if (constructOrderCache != null)
|
||||
return constructOrderCache;
|
||||
|
||||
var source = traits.WithInterface<ITraitInfo>().Select(i => new
|
||||
var source = traits.WithInterface<TraitInfo>().Select(i => new
|
||||
{
|
||||
Trait = i,
|
||||
Type = i.GetType(),
|
||||
@@ -138,7 +153,7 @@ namespace OpenRA
|
||||
return constructOrderCache;
|
||||
}
|
||||
|
||||
public static IEnumerable<Type> PrerequisitesOf(ITraitInfo info)
|
||||
public static IEnumerable<Type> PrerequisitesOf(TraitInfo info)
|
||||
{
|
||||
return info
|
||||
.GetType()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -41,8 +41,7 @@ namespace OpenRA.GameRules
|
||||
|
||||
public void Load(IReadOnlyFileSystem fileSystem)
|
||||
{
|
||||
Stream stream;
|
||||
if (!fileSystem.TryOpen(Filename, out stream))
|
||||
if (!fileSystem.TryOpen(Filename, out var stream))
|
||||
return;
|
||||
|
||||
try
|
||||
@@ -50,8 +49,7 @@ namespace OpenRA.GameRules
|
||||
Exists = true;
|
||||
foreach (var loader in Game.ModData.SoundLoaders)
|
||||
{
|
||||
ISoundFormat soundFormat;
|
||||
if (loader.TryParseSound(stream, out soundFormat))
|
||||
if (loader.TryParseSound(stream, out var soundFormat))
|
||||
{
|
||||
Length = (int)soundFormat.LengthInSeconds;
|
||||
soundFormat.Dispose();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -24,8 +24,8 @@ namespace OpenRA.GameRules
|
||||
public int[] DamageModifiers;
|
||||
public int[] InaccuracyModifiers;
|
||||
public int[] RangeModifiers;
|
||||
public int Facing;
|
||||
public Func<int> CurrentMuzzleFacing;
|
||||
public WAngle Facing;
|
||||
public Func<WAngle> CurrentMuzzleFacing;
|
||||
public WPos Source;
|
||||
public Func<WPos> CurrentSource;
|
||||
public Actor SourceActor;
|
||||
@@ -33,6 +33,40 @@ namespace OpenRA.GameRules
|
||||
public Target GuidedTarget;
|
||||
}
|
||||
|
||||
public class WarheadArgs
|
||||
{
|
||||
public WeaponInfo Weapon;
|
||||
public int[] DamageModifiers = { };
|
||||
public WPos? Source;
|
||||
public WRot ImpactOrientation;
|
||||
public WPos ImpactPosition;
|
||||
public Actor SourceActor;
|
||||
public Target WeaponTarget;
|
||||
|
||||
public WarheadArgs(ProjectileArgs args)
|
||||
{
|
||||
Weapon = args.Weapon;
|
||||
DamageModifiers = args.DamageModifiers;
|
||||
ImpactPosition = args.PassiveTarget;
|
||||
Source = args.Source;
|
||||
SourceActor = args.SourceActor;
|
||||
WeaponTarget = args.GuidedTarget;
|
||||
}
|
||||
|
||||
// For places that only want to update some of the fields (usually DamageModifiers)
|
||||
public WarheadArgs(WarheadArgs args)
|
||||
{
|
||||
Weapon = args.Weapon;
|
||||
DamageModifiers = args.DamageModifiers;
|
||||
Source = args.Source;
|
||||
SourceActor = args.SourceActor;
|
||||
WeaponTarget = args.WeaponTarget;
|
||||
}
|
||||
|
||||
// Default empty constructor for callers that want to initialize fields themselves
|
||||
public WarheadArgs() { }
|
||||
}
|
||||
|
||||
public interface IProjectile : IEffect { }
|
||||
public interface IProjectileInfo { IProjectile Create(ProjectileArgs args); }
|
||||
|
||||
@@ -71,6 +105,12 @@ namespace OpenRA.GameRules
|
||||
[Desc("What types of targets are unaffected.", "Overrules ValidTargets.")]
|
||||
public readonly BitSet<TargetableType> InvalidTargets;
|
||||
|
||||
static readonly BitSet<TargetableType> TargetTypeAir = new BitSet<TargetableType>("Air");
|
||||
|
||||
[Desc("If weapon is not directly targeting an actor and targeted position is above this altitude,",
|
||||
"the weapon will ignore terrain target types and only check TargetTypeAir for validity.")]
|
||||
public readonly WDist AirThreshold = new WDist(128);
|
||||
|
||||
[Desc("Delay in ticks between firing shots from the same ammo magazine. If one entry, it will be used for all bursts.",
|
||||
"If multiple entries, their number needs to match Burst - 1.")]
|
||||
public readonly int[] BurstDelays = { 5 };
|
||||
@@ -97,8 +137,7 @@ namespace OpenRA.GameRules
|
||||
|
||||
static object LoadProjectile(MiniYaml yaml)
|
||||
{
|
||||
MiniYaml proj;
|
||||
if (!yaml.ToDictionary().TryGetValue("Projectile", out proj))
|
||||
if (!yaml.ToDictionary().TryGetValue("Projectile", out var proj))
|
||||
return null;
|
||||
var ret = Game.CreateObject<IProjectileInfo>(proj.Value + "Info");
|
||||
FieldLoader.Load(ret, proj);
|
||||
@@ -134,6 +173,10 @@ namespace OpenRA.GameRules
|
||||
|
||||
if (target.Type == TargetType.Terrain)
|
||||
{
|
||||
var dat = world.Map.DistanceAboveTerrain(target.CenterPosition);
|
||||
if (dat > AirThreshold)
|
||||
return IsValidTarget(TargetTypeAir);
|
||||
|
||||
var cell = world.Map.CellContaining(target.CenterPosition);
|
||||
if (!world.Map.Contains(cell))
|
||||
return false;
|
||||
@@ -177,17 +220,33 @@ namespace OpenRA.GameRules
|
||||
}
|
||||
|
||||
/// <summary>Applies all the weapon's warheads to the target.</summary>
|
||||
public void Impact(Target target, Actor firedBy, IEnumerable<int> damageModifiers)
|
||||
public void Impact(Target target, WarheadArgs args)
|
||||
{
|
||||
var world = args.SourceActor.World;
|
||||
foreach (var warhead in Warheads)
|
||||
{
|
||||
var wh = warhead; // force the closure to bind to the current warhead
|
||||
|
||||
if (wh.Delay > 0)
|
||||
firedBy.World.AddFrameEndTask(w => w.Add(new DelayedImpact(wh.Delay, wh, target, firedBy, damageModifiers)));
|
||||
if (warhead.Delay > 0)
|
||||
world.AddFrameEndTask(w => w.Add(new DelayedImpact(warhead.Delay, warhead, target, args)));
|
||||
else
|
||||
wh.DoImpact(target, firedBy, damageModifiers);
|
||||
warhead.DoImpact(target, args);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Applies all the weapon's warheads to the target. Only use for projectile-less, special-case impacts.</summary>
|
||||
public void Impact(Target target, Actor firedBy)
|
||||
{
|
||||
// The impact will happen immediately at target.CenterPosition.
|
||||
var args = new WarheadArgs
|
||||
{
|
||||
Weapon = this,
|
||||
SourceActor = firedBy,
|
||||
WeaponTarget = target
|
||||
};
|
||||
|
||||
if (firedBy.OccupiesSpace != null)
|
||||
args.Source = firedBy.CenterPosition;
|
||||
|
||||
Impact(target, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -23,7 +23,7 @@ namespace OpenRA.Graphics
|
||||
public bool IsDecoration { get; set; }
|
||||
|
||||
readonly SequenceProvider sequenceProvider;
|
||||
readonly Func<int> facingFunc;
|
||||
readonly Func<WAngle> facingFunc;
|
||||
readonly Func<bool> paused;
|
||||
|
||||
int frame;
|
||||
@@ -33,15 +33,15 @@ namespace OpenRA.Graphics
|
||||
Action tickFunc = () => { };
|
||||
|
||||
public Animation(World world, string name)
|
||||
: this(world, name, () => 0) { }
|
||||
: this(world, name, () => WAngle.Zero) { }
|
||||
|
||||
public Animation(World world, string name, Func<int> facingFunc)
|
||||
public Animation(World world, string name, Func<WAngle> facingFunc)
|
||||
: this(world, name, facingFunc, null) { }
|
||||
|
||||
public Animation(World world, string name, Func<bool> paused)
|
||||
: this(world, name, () => 0, paused) { }
|
||||
: this(world, name, () => WAngle.Zero, paused) { }
|
||||
|
||||
public Animation(World world, string name, Func<int> facingFunc, Func<bool> paused)
|
||||
public Animation(World world, string name, Func<WAngle> facingFunc, Func<bool> paused)
|
||||
{
|
||||
sequenceProvider = world.Map.Rules.Sequences;
|
||||
Name = name.ToLowerInvariant();
|
||||
@@ -54,12 +54,29 @@ namespace OpenRA.Graphics
|
||||
|
||||
public IRenderable[] Render(WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale)
|
||||
{
|
||||
var imageRenderable = new SpriteRenderable(Image, pos, offset, CurrentSequence.ZOffset + zOffset, palette, scale, IsDecoration);
|
||||
var imageRenderable = new SpriteRenderable(Image, pos, offset, CurrentSequence.ZOffset + zOffset, palette, scale, IsDecoration, CurrentSequence.IgnoreWorldTint);
|
||||
|
||||
if (CurrentSequence.ShadowStart >= 0)
|
||||
{
|
||||
var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc());
|
||||
var shadowRenderable = new SpriteRenderable(shadow, pos, offset, CurrentSequence.ShadowZOffset + zOffset, palette, scale, true);
|
||||
var shadowRenderable = new SpriteRenderable(shadow, pos, offset, CurrentSequence.ShadowZOffset + zOffset, palette, scale, true, CurrentSequence.IgnoreWorldTint);
|
||||
return new IRenderable[] { shadowRenderable, imageRenderable };
|
||||
}
|
||||
|
||||
return new IRenderable[] { imageRenderable };
|
||||
}
|
||||
|
||||
public IRenderable[] RenderUI(WorldRenderer wr, int2 pos, WVec offset, int zOffset, PaletteReference palette, float scale)
|
||||
{
|
||||
var screenOffset = (scale * wr.ScreenVectorComponents(offset)).XY.ToInt2();
|
||||
var imagePos = pos + screenOffset - new int2((int)(scale * Image.Size.X / 2), (int)(scale * Image.Size.Y / 2));
|
||||
var imageRenderable = new UISpriteRenderable(Image, WPos.Zero + offset, imagePos, CurrentSequence.ZOffset + zOffset, palette, scale);
|
||||
|
||||
if (CurrentSequence.ShadowStart >= 0)
|
||||
{
|
||||
var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc());
|
||||
var shadowPos = pos - new int2((int)(scale * shadow.Size.X / 2), (int)(scale * shadow.Size.Y / 2));
|
||||
var shadowRenderable = new UISpriteRenderable(shadow, WPos.Zero + offset, shadowPos, CurrentSequence.ShadowZOffset + zOffset, palette, scale);
|
||||
return new IRenderable[] { shadowRenderable, imageRenderable };
|
||||
}
|
||||
|
||||
@@ -139,7 +156,7 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
frame = CurrentSequence.Length - 1;
|
||||
tickFunc = () => { };
|
||||
if (after != null) after();
|
||||
after?.Invoke();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -9,81 +9,140 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.FileSystem;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
[Flags]
|
||||
public enum PanelSides
|
||||
{
|
||||
Left = 1,
|
||||
Top = 2,
|
||||
Right = 4,
|
||||
Bottom = 8,
|
||||
Center = 16,
|
||||
|
||||
Edges = Left | Top | Right | Bottom,
|
||||
All = Edges | Center,
|
||||
}
|
||||
|
||||
public static class PanelSidesExts
|
||||
{
|
||||
public static bool HasSide(this PanelSides self, PanelSides m)
|
||||
{
|
||||
// PERF: Enum.HasFlag is slower and requires allocations.
|
||||
return (self & m) == m;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ChromeProvider
|
||||
{
|
||||
struct Collection
|
||||
public class Collection
|
||||
{
|
||||
public string Src;
|
||||
public Dictionary<string, MappedImage> Regions;
|
||||
public readonly string Image = null;
|
||||
public readonly string Image2x = null;
|
||||
public readonly string Image3x = null;
|
||||
|
||||
public readonly int[] PanelRegion = null;
|
||||
public readonly PanelSides PanelSides = PanelSides.All;
|
||||
public readonly Dictionary<string, Rectangle> Regions = new Dictionary<string, Rectangle>();
|
||||
}
|
||||
|
||||
public static IReadOnlyDictionary<string, Collection> Collections { get; private set; }
|
||||
static Dictionary<string, Collection> collections;
|
||||
static Dictionary<string, Sheet> cachedSheets;
|
||||
static Dictionary<string, (Sheet Sheet, int Density)> cachedSheets;
|
||||
static Dictionary<string, Dictionary<string, Sprite>> cachedSprites;
|
||||
static Dictionary<string, Sprite[]> cachedPanelSprites;
|
||||
static Dictionary<Collection, (Sheet Sheet, int)> cachedCollectionSheets;
|
||||
|
||||
static IReadOnlyFileSystem fileSystem;
|
||||
static float dpiScale = 1;
|
||||
|
||||
public static void Initialize(ModData modData)
|
||||
{
|
||||
Deinitialize();
|
||||
|
||||
// Load higher resolution images if available on HiDPI displays
|
||||
if (Game.Renderer != null)
|
||||
dpiScale = Game.Renderer.WindowScale;
|
||||
|
||||
fileSystem = modData.DefaultFileSystem;
|
||||
collections = new Dictionary<string, Collection>();
|
||||
cachedSheets = new Dictionary<string, Sheet>();
|
||||
cachedSheets = new Dictionary<string, (Sheet, int)>();
|
||||
cachedSprites = new Dictionary<string, Dictionary<string, Sprite>>();
|
||||
cachedPanelSprites = new Dictionary<string, Sprite[]>();
|
||||
cachedCollectionSheets = new Dictionary<Collection, (Sheet, int)>();
|
||||
|
||||
Collections = new ReadOnlyDictionary<string, Collection>(collections);
|
||||
|
||||
var chrome = MiniYaml.Merge(modData.Manifest.Chrome
|
||||
.Select(s => MiniYaml.FromStream(fileSystem.Open(s), s)));
|
||||
|
||||
foreach (var c in chrome)
|
||||
LoadCollection(c.Key, c.Value);
|
||||
if (!c.Key.StartsWith("^", StringComparison.Ordinal))
|
||||
LoadCollection(c.Key, c.Value);
|
||||
}
|
||||
|
||||
public static void Deinitialize()
|
||||
{
|
||||
if (cachedSheets != null)
|
||||
foreach (var sheet in cachedSheets.Values)
|
||||
sheet.Dispose();
|
||||
sheet.Sheet.Dispose();
|
||||
|
||||
collections = null;
|
||||
cachedSheets = null;
|
||||
cachedSprites = null;
|
||||
}
|
||||
|
||||
public static void Save(string file)
|
||||
{
|
||||
var root = new List<MiniYamlNode>();
|
||||
foreach (var kv in collections)
|
||||
root.Add(new MiniYamlNode(kv.Key, SaveCollection(kv.Value)));
|
||||
|
||||
root.WriteToFile(file);
|
||||
}
|
||||
|
||||
static MiniYaml SaveCollection(Collection collection)
|
||||
{
|
||||
var root = new List<MiniYamlNode>();
|
||||
foreach (var kv in collection.Regions)
|
||||
root.Add(new MiniYamlNode(kv.Key, kv.Value.Save(collection.Src)));
|
||||
|
||||
return new MiniYaml(collection.Src, root);
|
||||
cachedPanelSprites = null;
|
||||
cachedCollectionSheets = null;
|
||||
}
|
||||
|
||||
static void LoadCollection(string name, MiniYaml yaml)
|
||||
{
|
||||
if (Game.ModData.LoadScreen != null)
|
||||
Game.ModData.LoadScreen.Display();
|
||||
var collection = new Collection()
|
||||
{
|
||||
Src = yaml.Value,
|
||||
Regions = yaml.Nodes.ToDictionary(n => n.Key, n => new MappedImage(yaml.Value, n.Value))
|
||||
};
|
||||
|
||||
collections.Add(name, collection);
|
||||
collections.Add(name, FieldLoader.Load<Collection>(yaml));
|
||||
}
|
||||
|
||||
static (Sheet Sheet, int Density) SheetForCollection(Collection c)
|
||||
{
|
||||
// Outer cache avoids recalculating image names
|
||||
if (!cachedCollectionSheets.TryGetValue(c, out (Sheet, int) sheetDensity))
|
||||
{
|
||||
var image = c.Image;
|
||||
var density = 1;
|
||||
if (dpiScale > 2 && !string.IsNullOrEmpty(c.Image3x))
|
||||
{
|
||||
image = c.Image3x;
|
||||
density = 3;
|
||||
}
|
||||
else if (dpiScale > 1 && !string.IsNullOrEmpty(c.Image2x))
|
||||
{
|
||||
image = c.Image2x;
|
||||
density = 2;
|
||||
}
|
||||
|
||||
// Inner cache makes sure we share sheets between collections
|
||||
if (!cachedSheets.TryGetValue(image, out sheetDensity))
|
||||
{
|
||||
Sheet sheet;
|
||||
using (var stream = fileSystem.Open(image))
|
||||
sheet = new Sheet(SheetType.BGRA, stream);
|
||||
|
||||
sheet.GetTexture().ScaleFilter = TextureScaleFilter.Linear;
|
||||
|
||||
sheetDensity = (sheet, density);
|
||||
cachedSheets.Add(image, sheetDensity);
|
||||
}
|
||||
|
||||
cachedCollectionSheets.Add(c, sheetDensity);
|
||||
}
|
||||
|
||||
return sheetDensity;
|
||||
}
|
||||
|
||||
public static Sprite GetImage(string collectionName, string imageName)
|
||||
@@ -92,45 +151,135 @@ namespace OpenRA.Graphics
|
||||
return null;
|
||||
|
||||
// Cached sprite
|
||||
Dictionary<string, Sprite> cachedCollection;
|
||||
Sprite sprite;
|
||||
if (cachedSprites.TryGetValue(collectionName, out cachedCollection) && cachedCollection.TryGetValue(imageName, out sprite))
|
||||
if (cachedSprites.TryGetValue(collectionName, out var cachedCollection) && cachedCollection.TryGetValue(imageName, out var sprite))
|
||||
return sprite;
|
||||
|
||||
Collection collection;
|
||||
if (!collections.TryGetValue(collectionName, out collection))
|
||||
if (!collections.TryGetValue(collectionName, out var collection))
|
||||
{
|
||||
Log.Write("debug", "Could not find collection '{0}'", collectionName);
|
||||
return null;
|
||||
}
|
||||
|
||||
MappedImage mi;
|
||||
if (!collection.Regions.TryGetValue(imageName, out mi))
|
||||
if (!collection.Regions.TryGetValue(imageName, out var mi))
|
||||
return null;
|
||||
|
||||
// Cached sheet
|
||||
Sheet sheet;
|
||||
if (cachedSheets.ContainsKey(mi.Src))
|
||||
sheet = cachedSheets[mi.Src];
|
||||
else
|
||||
{
|
||||
using (var stream = fileSystem.Open(mi.Src))
|
||||
sheet = new Sheet(SheetType.BGRA, stream);
|
||||
|
||||
cachedSheets.Add(mi.Src, sheet);
|
||||
}
|
||||
|
||||
// Cache the sprite
|
||||
var sheetDensity = SheetForCollection(collection);
|
||||
if (cachedCollection == null)
|
||||
{
|
||||
cachedCollection = new Dictionary<string, Sprite>();
|
||||
cachedSprites.Add(collectionName, cachedCollection);
|
||||
}
|
||||
|
||||
var image = mi.GetImage(sheet);
|
||||
var image = new Sprite(sheetDensity.Sheet, sheetDensity.Density * mi, TextureChannel.RGBA, 1f / sheetDensity.Density);
|
||||
cachedCollection.Add(imageName, image);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
public static Sprite[] GetPanelImages(string collectionName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(collectionName))
|
||||
return null;
|
||||
|
||||
// Cached sprite
|
||||
if (cachedPanelSprites.TryGetValue(collectionName, out var cachedSprites))
|
||||
return cachedSprites;
|
||||
|
||||
if (!collections.TryGetValue(collectionName, out var collection))
|
||||
{
|
||||
Log.Write("debug", "Could not find collection '{0}'", collectionName);
|
||||
return null;
|
||||
}
|
||||
|
||||
Sprite[] sprites;
|
||||
if (collection.PanelRegion != null)
|
||||
{
|
||||
if (collection.PanelRegion.Length != 8)
|
||||
{
|
||||
Log.Write("debug", "Collection '{0}' does not define a valid PanelRegion", collectionName);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Cache the sprites
|
||||
var sheetDensity = SheetForCollection(collection);
|
||||
var pr = collection.PanelRegion;
|
||||
var ps = collection.PanelSides;
|
||||
|
||||
var sides = new (PanelSides PanelSides, Rectangle Bounds)[]
|
||||
{
|
||||
(PanelSides.Top | PanelSides.Left, new Rectangle(pr[0], pr[1], pr[2], pr[3])),
|
||||
(PanelSides.Top, new Rectangle(pr[0] + pr[2], pr[1], pr[4], pr[3])),
|
||||
(PanelSides.Top | PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1], pr[6], pr[3])),
|
||||
(PanelSides.Left, new Rectangle(pr[0], pr[1] + pr[3], pr[2], pr[5])),
|
||||
(PanelSides.Center, new Rectangle(pr[0] + pr[2], pr[1] + pr[3], pr[4], pr[5])),
|
||||
(PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1] + pr[3], pr[6], pr[5])),
|
||||
(PanelSides.Bottom | PanelSides.Left, new Rectangle(pr[0], pr[1] + pr[3] + pr[5], pr[2], pr[7])),
|
||||
(PanelSides.Bottom, new Rectangle(pr[0] + pr[2], pr[1] + pr[3] + pr[5], pr[4], pr[7])),
|
||||
(PanelSides.Bottom | PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1] + pr[3] + pr[5], pr[6], pr[7]))
|
||||
};
|
||||
|
||||
sprites = sides.Select(x => ps.HasSide(x.PanelSides) ? new Sprite(sheetDensity.Sheet, sheetDensity.Density * x.Bounds, TextureChannel.RGBA, 1f / sheetDensity.Density) : null)
|
||||
.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Support manual definitions for unusual dialog layouts
|
||||
sprites = new[]
|
||||
{
|
||||
GetImage(collectionName, "corner-tl"),
|
||||
GetImage(collectionName, "border-t"),
|
||||
GetImage(collectionName, "corner-tr"),
|
||||
GetImage(collectionName, "border-l"),
|
||||
GetImage(collectionName, "background"),
|
||||
GetImage(collectionName, "border-r"),
|
||||
GetImage(collectionName, "corner-bl"),
|
||||
GetImage(collectionName, "border-b"),
|
||||
GetImage(collectionName, "corner-br")
|
||||
};
|
||||
}
|
||||
|
||||
cachedPanelSprites.Add(collectionName, sprites);
|
||||
return sprites;
|
||||
}
|
||||
|
||||
public static Size GetMinimumPanelSize(string collectionName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(collectionName))
|
||||
return new Size(0, 0);
|
||||
|
||||
if (!collections.TryGetValue(collectionName, out var collection))
|
||||
{
|
||||
Log.Write("debug", "Could not find collection '{0}'", collectionName);
|
||||
return new Size(0, 0);
|
||||
}
|
||||
|
||||
if (collection.PanelRegion == null || collection.PanelRegion.Length != 8)
|
||||
{
|
||||
Log.Write("debug", "Collection '{0}' does not define a valid PanelRegion", collectionName);
|
||||
return new Size(0, 0);
|
||||
}
|
||||
|
||||
var pr = collection.PanelRegion;
|
||||
return new Size(pr[2] + pr[6], pr[3] + pr[7]);
|
||||
}
|
||||
|
||||
public static void SetDPIScale(float scale)
|
||||
{
|
||||
if (dpiScale == scale)
|
||||
return;
|
||||
|
||||
dpiScale = scale;
|
||||
|
||||
// Clear the sprite caches so the new artwork can be loaded
|
||||
// Sheets are not cleared: we assume that the extra memory overhead
|
||||
// of having the same sheet in memory in multiple DPIs is better than
|
||||
// the overhead of having to dispose and reload everything.
|
||||
// Changing the DPI scale is rare, but if it does happen then there
|
||||
// is a reasonable chance that it may happen again this session.
|
||||
cachedSprites.Clear();
|
||||
cachedPanelSprites.Clear();
|
||||
cachedCollectionSheets.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
299
OpenRA.Game/Graphics/CursorManager.cs
Normal file
299
OpenRA.Game/Graphics/CursorManager.cs
Normal file
@@ -0,0 +1,299 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
public sealed class CursorManager
|
||||
{
|
||||
class Cursor
|
||||
{
|
||||
public string Name;
|
||||
public int2 PaddedSize;
|
||||
public Rectangle Bounds;
|
||||
|
||||
public int Length;
|
||||
public Sprite[] Sprites;
|
||||
public IHardwareCursor[] Cursors;
|
||||
}
|
||||
|
||||
readonly Dictionary<string, Cursor> cursors = new Dictionary<string, Cursor>();
|
||||
readonly SheetBuilder sheetBuilder;
|
||||
readonly GraphicSettings graphicSettings;
|
||||
|
||||
Cursor cursor;
|
||||
bool isLocked = false;
|
||||
int2 lockedPosition;
|
||||
bool hardwareCursorsDisabled = false;
|
||||
bool hardwareCursorsDoubled = false;
|
||||
|
||||
public CursorManager(CursorProvider cursorProvider)
|
||||
{
|
||||
hardwareCursorsDisabled = Game.Settings.Graphics.DisableHardwareCursors;
|
||||
|
||||
graphicSettings = Game.Settings.Graphics;
|
||||
sheetBuilder = new SheetBuilder(SheetType.BGRA);
|
||||
foreach (var kv in cursorProvider.Cursors)
|
||||
{
|
||||
var frames = kv.Value.Frames;
|
||||
var palette = !string.IsNullOrEmpty(kv.Value.Palette) ? cursorProvider.Palettes[kv.Value.Palette] : null;
|
||||
|
||||
var c = new Cursor
|
||||
{
|
||||
Name = kv.Key,
|
||||
Bounds = Rectangle.FromLTRB(0, 0, 1, 1),
|
||||
|
||||
Length = 0,
|
||||
Sprites = new Sprite[frames.Length],
|
||||
Cursors = new IHardwareCursor[frames.Length]
|
||||
};
|
||||
|
||||
// Hardware cursors have a number of odd platform-specific bugs/limitations.
|
||||
// Reduce the number of edge cases by padding the individual frames such that:
|
||||
// - the hotspot is inside the frame bounds (enforced by SDL)
|
||||
// - all frames within a sequence have the same size (needed for macOS 10.15)
|
||||
// - the frame size is a multiple of 8 (needed for Windows)
|
||||
foreach (var f in frames)
|
||||
{
|
||||
// Hotspot is specified relative to the center of the frame
|
||||
var hotspot = f.Offset.ToInt2() - kv.Value.Hotspot - new int2(f.Size) / 2;
|
||||
|
||||
// SheetBuilder expects data in BGRA
|
||||
var data = FrameToBGRA(kv.Key, f, palette);
|
||||
c.Sprites[c.Length++] = sheetBuilder.Add(data, f.Size, 0, hotspot);
|
||||
|
||||
// Bounds relative to the hotspot
|
||||
c.Bounds = Rectangle.Union(c.Bounds, new Rectangle(hotspot, f.Size));
|
||||
}
|
||||
|
||||
// Pad bottom-right edge to make the frame size a multiple of 8
|
||||
c.PaddedSize = 8 * new int2((c.Bounds.Width + 7) / 8, (c.Bounds.Height + 7) / 8);
|
||||
|
||||
cursors.Add(kv.Key, c);
|
||||
}
|
||||
|
||||
CreateOrUpdateHardwareCursors();
|
||||
|
||||
foreach (var s in sheetBuilder.AllSheets)
|
||||
s.ReleaseBuffer();
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
void CreateOrUpdateHardwareCursors()
|
||||
{
|
||||
if (hardwareCursorsDisabled)
|
||||
return;
|
||||
|
||||
// Dispose any existing cursors to avoid leaking native resources
|
||||
ClearHardwareCursors();
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var kv in cursors)
|
||||
{
|
||||
var template = kv.Value;
|
||||
for (var i = 0; i < template.Sprites.Length; i++)
|
||||
{
|
||||
if (template.Cursors[i] != null)
|
||||
template.Cursors[i].Dispose();
|
||||
|
||||
// Calculate the padding to position the frame within sequenceBounds
|
||||
var paddingTL = -(template.Bounds.Location - template.Sprites[i].Offset.XY.ToInt2());
|
||||
var paddingBR = template.PaddedSize - new int2(template.Sprites[i].Bounds.Size) - paddingTL;
|
||||
|
||||
template.Cursors[i] = CreateHardwareCursor(kv.Key, template.Sprites[i], paddingTL, paddingBR, -template.Bounds.Location);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Write("debug", "Failed to initialize hardware cursors. Falling back to software cursors.");
|
||||
Log.Write("debug", "Error was: " + e.Message);
|
||||
|
||||
Console.WriteLine("Failed to initialize hardware cursors. Falling back to software cursors.");
|
||||
Console.WriteLine("Error was: " + e.Message);
|
||||
|
||||
ClearHardwareCursors();
|
||||
}
|
||||
|
||||
hardwareCursorsDoubled = graphicSettings.CursorDouble;
|
||||
}
|
||||
|
||||
public void SetCursor(string cursorName)
|
||||
{
|
||||
if ((cursorName == null && cursor == null) || (cursor != null && cursorName == cursor.Name))
|
||||
return;
|
||||
|
||||
if (cursorName == null || !cursors.TryGetValue(cursorName, out cursor))
|
||||
cursor = null;
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
int frame;
|
||||
int ticks;
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
if (hardwareCursorsDoubled != graphicSettings.CursorDouble)
|
||||
{
|
||||
CreateOrUpdateHardwareCursors();
|
||||
Update();
|
||||
}
|
||||
|
||||
if (cursor == null || cursor.Cursors.Length == 1)
|
||||
return;
|
||||
|
||||
if (++ticks > 2)
|
||||
{
|
||||
ticks -= 2;
|
||||
frame++;
|
||||
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (cursor != null && frame >= cursor.Cursors.Length)
|
||||
frame %= cursor.Cursors.Length;
|
||||
|
||||
if (cursor == null || isLocked)
|
||||
Game.Renderer.Window.SetHardwareCursor(null);
|
||||
else
|
||||
Game.Renderer.Window.SetHardwareCursor(cursor.Cursors[frame]);
|
||||
}
|
||||
|
||||
public void Render(Renderer renderer)
|
||||
{
|
||||
// Cursor is hidden
|
||||
if (cursor == null)
|
||||
return;
|
||||
|
||||
// Hardware cursor is enabled
|
||||
if (!isLocked && cursor.Cursors[frame % cursor.Length] != null)
|
||||
return;
|
||||
|
||||
// Render cursor in software
|
||||
var doubleCursor = graphicSettings.CursorDouble;
|
||||
var cursorSprite = cursor.Sprites[frame % cursor.Length];
|
||||
var cursorSize = doubleCursor ? 2.0f * cursorSprite.Size : cursorSprite.Size;
|
||||
|
||||
// Cursor is rendered in native window coordinates
|
||||
// Apply same scaling rules as hardware cursors
|
||||
if (Game.Renderer.NativeWindowScale > 1.5f)
|
||||
cursorSize = 2 * cursorSize;
|
||||
|
||||
var mousePos = isLocked ? lockedPosition : Viewport.LastMousePos;
|
||||
renderer.RgbaSpriteRenderer.DrawSprite(cursorSprite,
|
||||
mousePos,
|
||||
cursorSize / Game.Renderer.WindowScale);
|
||||
}
|
||||
|
||||
public void Lock()
|
||||
{
|
||||
lockedPosition = Viewport.LastMousePos;
|
||||
Game.Renderer.Window.SetRelativeMouseMode(true);
|
||||
isLocked = true;
|
||||
Update();
|
||||
}
|
||||
|
||||
public void Unlock()
|
||||
{
|
||||
Game.Renderer.Window.SetRelativeMouseMode(false);
|
||||
isLocked = false;
|
||||
Update();
|
||||
}
|
||||
|
||||
public static byte[] FrameToBGRA(string name, ISpriteFrame frame, ImmutablePalette palette)
|
||||
{
|
||||
// Data is already in BGRA format
|
||||
if (frame.Type == SpriteFrameType.BGRA)
|
||||
return frame.Data;
|
||||
|
||||
// Cursors may be either native BGRA or Indexed.
|
||||
// Indexed sprites are converted to BGRA using the referenced palette.
|
||||
// All palettes must be explicitly referenced, even if they are embedded in the sprite.
|
||||
if (frame.Type == SpriteFrameType.Indexed && palette == null)
|
||||
throw new InvalidOperationException("Cursor sequence `{0}` attempted to load an indexed sprite but does not define Palette".F(name));
|
||||
|
||||
var width = frame.Size.Width;
|
||||
var height = frame.Size.Height;
|
||||
var data = new byte[4 * width * height];
|
||||
for (var j = 0; j < height; j++)
|
||||
{
|
||||
for (var i = 0; i < width; i++)
|
||||
{
|
||||
var bytes = BitConverter.GetBytes(palette[frame.Data[j * width + i]]);
|
||||
var c = palette[frame.Data[j * width + i]];
|
||||
var k = 4 * (j * width + i);
|
||||
|
||||
// Convert RGBA to BGRA
|
||||
data[k] = bytes[2];
|
||||
data[k + 1] = bytes[1];
|
||||
data[k + 2] = bytes[0];
|
||||
data[k + 3] = bytes[3];
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
IHardwareCursor CreateHardwareCursor(string name, Sprite data, int2 paddingTL, int2 paddingBR, int2 hotspot)
|
||||
{
|
||||
var size = data.Bounds.Size;
|
||||
var srcStride = data.Sheet.Size.Width;
|
||||
var srcData = data.Sheet.GetData();
|
||||
var newWidth = paddingTL.X + size.Width + paddingBR.X;
|
||||
var newHeight = paddingTL.Y + size.Height + paddingBR.Y;
|
||||
var rgbaData = new byte[4 * newWidth * newHeight];
|
||||
|
||||
for (var j = 0; j < size.Height; j++)
|
||||
{
|
||||
for (var i = 0; i < size.Width; i++)
|
||||
{
|
||||
var src = 4 * ((j + data.Bounds.Top) * srcStride + data.Bounds.Left + i);
|
||||
var dest = 4 * ((j + paddingTL.Y) * newWidth + i + paddingTL.X);
|
||||
Array.Copy(srcData, src, rgbaData, dest, 4);
|
||||
}
|
||||
}
|
||||
|
||||
return Game.Renderer.Window.CreateHardwareCursor(name, new Size(newWidth, newHeight), rgbaData, hotspot, graphicSettings.CursorDouble);
|
||||
}
|
||||
|
||||
void ClearHardwareCursors()
|
||||
{
|
||||
foreach (var c in cursors.Values)
|
||||
{
|
||||
for (var i = 0; i < c.Cursors.Length; i++)
|
||||
{
|
||||
if (c.Cursors[i] != null)
|
||||
{
|
||||
c.Cursors[i].Dispose();
|
||||
c.Cursors[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ClearHardwareCursors();
|
||||
|
||||
cursors.Clear();
|
||||
sheetBuilder.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -36,6 +36,7 @@ namespace OpenRA.Graphics
|
||||
pals[p.Palette] = p;
|
||||
|
||||
Palettes = nodesDict["Cursors"].Nodes.Select(n => n.Value.Value)
|
||||
.Where(p => p != null)
|
||||
.Distinct()
|
||||
.ToDictionary(p => p, p => pals[p].ReadPalette(modData.DefaultFileSystem))
|
||||
.AsReadOnly();
|
||||
@@ -49,8 +50,6 @@ namespace OpenRA.Graphics
|
||||
Cursors = cursors.AsReadOnly();
|
||||
}
|
||||
|
||||
public static bool CursorViewportZoomed { get { return Game.Settings.Graphics.CursorDouble && Game.Settings.Graphics.PixelDouble; } }
|
||||
|
||||
public bool HasCursorSequence(string cursor)
|
||||
{
|
||||
return Cursors.ContainsKey(cursor);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -47,15 +47,13 @@ namespace OpenRA.Graphics
|
||||
|
||||
if (d.ContainsKey("X"))
|
||||
{
|
||||
int x;
|
||||
Exts.TryParseIntegerInvariant(d["X"].Value, out x);
|
||||
Exts.TryParseIntegerInvariant(d["X"].Value, out var x);
|
||||
Hotspot = Hotspot.WithX(x);
|
||||
}
|
||||
|
||||
if (d.ContainsKey("Y"))
|
||||
{
|
||||
int y;
|
||||
Exts.TryParseIntegerInvariant(d["Y"].Value, out y);
|
||||
Exts.TryParseIntegerInvariant(d["Y"].Value, out var y);
|
||||
Hotspot = Hotspot.WithY(y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,192 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
public sealed class HardwareCursor : ICursor
|
||||
{
|
||||
readonly Dictionary<string, IHardwareCursor[]> hardwareCursors = new Dictionary<string, IHardwareCursor[]>();
|
||||
readonly CursorProvider cursorProvider;
|
||||
readonly Dictionary<string, Sprite[]> sprites = new Dictionary<string, Sprite[]>();
|
||||
readonly SheetBuilder sheetBuilder;
|
||||
readonly HardwarePalette hardwarePalette = new HardwarePalette();
|
||||
readonly Cache<string, PaletteReference> paletteReferences;
|
||||
|
||||
CursorSequence cursor;
|
||||
bool isLocked = false;
|
||||
int2 lockedPosition;
|
||||
|
||||
public HardwareCursor(CursorProvider cursorProvider)
|
||||
{
|
||||
this.cursorProvider = cursorProvider;
|
||||
|
||||
paletteReferences = new Cache<string, PaletteReference>(CreatePaletteReference);
|
||||
foreach (var p in cursorProvider.Palettes)
|
||||
hardwarePalette.AddPalette(p.Key, p.Value, false);
|
||||
|
||||
hardwarePalette.Initialize();
|
||||
|
||||
sheetBuilder = new SheetBuilder(SheetType.Indexed);
|
||||
foreach (var kv in cursorProvider.Cursors)
|
||||
{
|
||||
var palette = cursorProvider.Palettes[kv.Value.Palette];
|
||||
var hc = kv.Value.Frames
|
||||
.Select(f => CreateCursor(f, palette, kv.Key, kv.Value))
|
||||
.ToArray();
|
||||
|
||||
hardwareCursors.Add(kv.Key, hc);
|
||||
|
||||
var s = kv.Value.Frames.Select(a => sheetBuilder.Add(a)).ToArray();
|
||||
sprites.Add(kv.Key, s);
|
||||
}
|
||||
|
||||
sheetBuilder.Current.ReleaseBuffer();
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
PaletteReference CreatePaletteReference(string name)
|
||||
{
|
||||
var pal = hardwarePalette.GetPalette(name);
|
||||
return new PaletteReference(name, hardwarePalette.GetPaletteIndex(name), pal, hardwarePalette);
|
||||
}
|
||||
|
||||
IHardwareCursor CreateCursor(ISpriteFrame f, ImmutablePalette palette, string name, CursorSequence sequence)
|
||||
{
|
||||
var hotspot = sequence.Hotspot - f.Offset.ToInt2() + new int2(f.Size) / 2;
|
||||
|
||||
// Expand the frame if required to include the hotspot
|
||||
var frameWidth = f.Size.Width;
|
||||
var dataWidth = f.Size.Width;
|
||||
var dataX = 0;
|
||||
if (hotspot.X < 0)
|
||||
{
|
||||
dataX = -hotspot.X;
|
||||
dataWidth += dataX;
|
||||
hotspot = hotspot.WithX(0);
|
||||
}
|
||||
else if (hotspot.X >= frameWidth)
|
||||
dataWidth = hotspot.X + 1;
|
||||
|
||||
var frameHeight = f.Size.Height;
|
||||
var dataHeight = f.Size.Height;
|
||||
var dataY = 0;
|
||||
if (hotspot.Y < 0)
|
||||
{
|
||||
dataY = -hotspot.Y;
|
||||
dataHeight += dataY;
|
||||
hotspot = hotspot.WithY(0);
|
||||
}
|
||||
else if (hotspot.Y >= frameHeight)
|
||||
dataHeight = hotspot.Y + 1;
|
||||
|
||||
var data = new byte[4 * dataWidth * dataHeight];
|
||||
for (var j = 0; j < frameHeight; j++)
|
||||
{
|
||||
for (var i = 0; i < frameWidth; i++)
|
||||
{
|
||||
var bytes = BitConverter.GetBytes(palette[f.Data[j * frameWidth + i]]);
|
||||
var start = 4 * ((j + dataY) * dataWidth + dataX + i);
|
||||
for (var k = 0; k < 4; k++)
|
||||
data[start + k] = bytes[k];
|
||||
}
|
||||
}
|
||||
|
||||
return Game.Renderer.Window.CreateHardwareCursor(name, new Size(dataWidth, dataHeight), data, hotspot);
|
||||
}
|
||||
|
||||
public void SetCursor(string cursorName)
|
||||
{
|
||||
if ((cursorName == null && cursor == null) || (cursor != null && cursorName == cursor.Name))
|
||||
return;
|
||||
|
||||
if (cursorName == null || !cursorProvider.Cursors.TryGetValue(cursorName, out cursor))
|
||||
cursor = null;
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
int frame;
|
||||
int ticks;
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
if (cursor == null || cursor.Length == 1)
|
||||
return;
|
||||
|
||||
if (++ticks > 2)
|
||||
{
|
||||
ticks -= 2;
|
||||
frame++;
|
||||
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (cursor != null && frame >= cursor.Length)
|
||||
frame %= cursor.Length;
|
||||
|
||||
if (cursor == null || isLocked)
|
||||
Game.Renderer.Window.SetHardwareCursor(null);
|
||||
else
|
||||
Game.Renderer.Window.SetHardwareCursor(hardwareCursors[cursor.Name][frame]);
|
||||
}
|
||||
|
||||
public void Render(Renderer renderer)
|
||||
{
|
||||
if (cursor.Name == null || !isLocked)
|
||||
return;
|
||||
|
||||
var cursorSequence = cursorProvider.GetCursorSequence(cursor.Name);
|
||||
var cursorSprite = sprites[cursor.Name][frame];
|
||||
|
||||
var cursorOffset = cursorSequence.Hotspot + (0.5f * cursorSprite.Size.XY).ToInt2();
|
||||
|
||||
renderer.SetPalette(hardwarePalette);
|
||||
renderer.SpriteRenderer.DrawSprite(cursorSprite,
|
||||
lockedPosition - cursorOffset,
|
||||
paletteReferences[cursorSequence.Palette],
|
||||
cursorSprite.Size);
|
||||
}
|
||||
|
||||
public void Lock()
|
||||
{
|
||||
lockedPosition = Viewport.LastMousePos;
|
||||
Game.Renderer.Window.SetRelativeMouseMode(true);
|
||||
isLocked = true;
|
||||
Update();
|
||||
}
|
||||
|
||||
public void Unlock()
|
||||
{
|
||||
Game.Renderer.Window.SetRelativeMouseMode(false);
|
||||
isLocked = false;
|
||||
Update();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var cursors in hardwareCursors)
|
||||
foreach (var cursor in cursors.Value)
|
||||
cursor.Dispose();
|
||||
|
||||
sheetBuilder.Dispose();
|
||||
hardwareCursors.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -38,19 +38,16 @@ namespace OpenRA.Graphics
|
||||
|
||||
public IPalette GetPalette(string name)
|
||||
{
|
||||
MutablePalette mutable;
|
||||
if (modifiablePalettes.TryGetValue(name, out mutable))
|
||||
if (modifiablePalettes.TryGetValue(name, out var mutable))
|
||||
return mutable.AsReadOnly();
|
||||
ImmutablePalette immutable;
|
||||
if (palettes.TryGetValue(name, out immutable))
|
||||
if (palettes.TryGetValue(name, out var immutable))
|
||||
return immutable;
|
||||
throw new InvalidOperationException("Palette `{0}` does not exist".F(name));
|
||||
}
|
||||
|
||||
public int GetPaletteIndex(string name)
|
||||
{
|
||||
int ret;
|
||||
if (!indices.TryGetValue(name, out ret))
|
||||
if (!indices.TryGetValue(name, out var ret))
|
||||
throw new InvalidOperationException("Palette `{0}` does not exist".F(name));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
class MappedImage
|
||||
{
|
||||
readonly Rectangle rect = Rectangle.Empty;
|
||||
public readonly string Src;
|
||||
|
||||
public MappedImage(string defaultSrc, MiniYaml info)
|
||||
{
|
||||
FieldLoader.LoadField(this, "rect", info.Value);
|
||||
FieldLoader.Load(this, info);
|
||||
if (Src == null)
|
||||
Src = defaultSrc;
|
||||
}
|
||||
|
||||
public Sprite GetImage(Sheet s)
|
||||
{
|
||||
return new Sprite(s, rect, TextureChannel.RGBA);
|
||||
}
|
||||
|
||||
public MiniYaml Save(string defaultSrc)
|
||||
{
|
||||
var root = new List<MiniYamlNode>();
|
||||
if (defaultSrc != Src)
|
||||
root.Add(new MiniYamlNode("Src", Src));
|
||||
|
||||
return new MiniYaml(FieldSaver.FormatValue(this, GetType().GetField("rect")), root);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -10,7 +10,6 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
@@ -19,12 +18,12 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
public readonly IModel Model;
|
||||
public readonly Func<WVec> OffsetFunc;
|
||||
public readonly Func<IEnumerable<WRot>> RotationFunc;
|
||||
public readonly Func<WRot> RotationFunc;
|
||||
public readonly Func<bool> DisableFunc;
|
||||
public readonly Func<uint> FrameFunc;
|
||||
public readonly bool ShowShadow;
|
||||
|
||||
public ModelAnimation(IModel model, Func<WVec> offset, Func<IEnumerable<WRot>> rotation, Func<bool> disable, Func<uint> frame, bool showshadow)
|
||||
public ModelAnimation(IModel model, Func<WVec> offset, Func<WRot> rotation, Func<bool> disable, Func<uint> frame, bool showshadow)
|
||||
{
|
||||
Model = model;
|
||||
OffsetFunc = offset;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -48,7 +48,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
readonly Dictionary<Sheet, IFrameBuffer> mappedBuffers = new Dictionary<Sheet, IFrameBuffer>();
|
||||
readonly Stack<KeyValuePair<Sheet, IFrameBuffer>> unmappedBuffers = new Stack<KeyValuePair<Sheet, IFrameBuffer>>();
|
||||
readonly List<Pair<Sheet, Action>> doRender = new List<Pair<Sheet, Action>>();
|
||||
readonly List<(Sheet Sheet, Action Func)> doRender = new List<(Sheet, Action)>();
|
||||
|
||||
SheetBuilder sheetBuilderForFrame;
|
||||
bool isInFrame;
|
||||
@@ -64,10 +64,10 @@ namespace OpenRA.Graphics
|
||||
shader.SetTexture("Palette", palette);
|
||||
}
|
||||
|
||||
public void SetViewportParams(Size screen, float zoom, int2 scroll)
|
||||
public void SetViewportParams(Size screen, int2 scroll)
|
||||
{
|
||||
var a = 2f / renderer.SheetSize;
|
||||
var view = new float[]
|
||||
var view = new[]
|
||||
{
|
||||
a, 0, 0, 0,
|
||||
0, -a, 0, 0,
|
||||
@@ -114,8 +114,7 @@ namespace OpenRA.Graphics
|
||||
var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(m.OffsetFunc()));
|
||||
var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]);
|
||||
|
||||
var worldTransform = m.RotationFunc().Aggregate(Util.IdentityMatrix(),
|
||||
(x, y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x));
|
||||
var worldTransform = Util.MakeFloatMatrix(m.RotationFunc().AsMatrix());
|
||||
worldTransform = Util.MatrixMultiply(scaleTransform, worldTransform);
|
||||
worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform);
|
||||
|
||||
@@ -161,10 +160,8 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
|
||||
// Shadows are rendered at twice the resolution to reduce artifacts
|
||||
Size spriteSize, shadowSpriteSize;
|
||||
int2 spriteOffset, shadowSpriteOffset;
|
||||
CalculateSpriteGeometry(tl, br, 1, out spriteSize, out spriteOffset);
|
||||
CalculateSpriteGeometry(stl, sbr, 2, out shadowSpriteSize, out shadowSpriteOffset);
|
||||
CalculateSpriteGeometry(tl, br, 1, out var spriteSize, out var spriteOffset);
|
||||
CalculateSpriteGeometry(stl, sbr, 2, out var shadowSpriteSize, out var shadowSpriteOffset);
|
||||
|
||||
if (sheetBuilderForFrame == null)
|
||||
sheetBuilderForFrame = new SheetBuilder(SheetType.BGRA, AllocateSheet);
|
||||
@@ -181,7 +178,7 @@ namespace OpenRA.Graphics
|
||||
var correctionTransform = Util.MatrixMultiply(translateMtx, FlipMtx);
|
||||
var shadowCorrectionTransform = Util.MatrixMultiply(shadowTranslateMtx, ShadowScaleFlipMtx);
|
||||
|
||||
doRender.Add(Pair.New<Sheet, Action>(sprite.Sheet, () =>
|
||||
doRender.Add((sprite.Sheet, () =>
|
||||
{
|
||||
foreach (var m in models)
|
||||
{
|
||||
@@ -189,8 +186,7 @@ namespace OpenRA.Graphics
|
||||
var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(m.OffsetFunc()));
|
||||
var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]);
|
||||
|
||||
var rotations = m.RotationFunc().Aggregate(Util.IdentityMatrix(),
|
||||
(x, y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x));
|
||||
var rotations = Util.MakeFloatMatrix(m.RotationFunc().AsMatrix());
|
||||
var worldTransform = Util.MatrixMultiply(scaleTransform, rotations);
|
||||
worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform);
|
||||
|
||||
@@ -326,16 +322,16 @@ namespace OpenRA.Graphics
|
||||
foreach (var v in doRender)
|
||||
{
|
||||
// Change sheet
|
||||
if (v.First != currentSheet)
|
||||
if (v.Sheet != currentSheet)
|
||||
{
|
||||
if (fbo != null)
|
||||
DisableFrameBuffer(fbo);
|
||||
|
||||
currentSheet = v.First;
|
||||
currentSheet = v.Sheet;
|
||||
fbo = EnableFrameBuffer(currentSheet);
|
||||
}
|
||||
|
||||
v.Second();
|
||||
v.Func();
|
||||
}
|
||||
|
||||
if (fbo != null)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -15,9 +15,16 @@ using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public enum GLProfile
|
||||
{
|
||||
Modern,
|
||||
Embedded,
|
||||
Legacy
|
||||
}
|
||||
|
||||
public interface IPlatform
|
||||
{
|
||||
IPlatformWindow CreateWindow(Size size, WindowMode windowMode, int batchSize);
|
||||
IPlatformWindow CreateWindow(Size size, WindowMode windowMode, float scaleModifier, int batchSize, int videoDisplay, GLProfile profile);
|
||||
ISoundEngine CreateSound(string device);
|
||||
IFont CreateFont(byte[] data);
|
||||
}
|
||||
@@ -32,16 +39,26 @@ namespace OpenRA
|
||||
Subtractive,
|
||||
Multiply,
|
||||
Multiplicative,
|
||||
DoubleMultiplicative
|
||||
DoubleMultiplicative,
|
||||
LowAdditive,
|
||||
Screen,
|
||||
Translucent
|
||||
}
|
||||
|
||||
public interface IPlatformWindow : IDisposable
|
||||
{
|
||||
IGraphicsContext Context { get; }
|
||||
|
||||
Size WindowSize { get; }
|
||||
float WindowScale { get; }
|
||||
event Action<float, float> OnWindowScaleChanged;
|
||||
Size NativeWindowSize { get; }
|
||||
Size EffectiveWindowSize { get; }
|
||||
float NativeWindowScale { get; }
|
||||
float EffectiveWindowScale { get; }
|
||||
Size SurfaceSize { get; }
|
||||
int DisplayCount { get; }
|
||||
int CurrentDisplay { get; }
|
||||
bool HasInputFocus { get; }
|
||||
|
||||
event Action<float, float, float, float> OnWindowScaleChanged;
|
||||
|
||||
void PumpInput(IInputHandler inputHandler);
|
||||
string GetClipboardText();
|
||||
@@ -50,9 +67,14 @@ namespace OpenRA
|
||||
void GrabWindowMouseFocus();
|
||||
void ReleaseWindowMouseFocus();
|
||||
|
||||
IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot);
|
||||
IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot, bool pixelDouble);
|
||||
void SetHardwareCursor(IHardwareCursor cursor);
|
||||
void SetRelativeMouseMode(bool mode);
|
||||
void SetScaleModifier(float scale);
|
||||
|
||||
GLProfile GLProfile { get; }
|
||||
|
||||
GLProfile[] SupportedGLProfiles { get; }
|
||||
}
|
||||
|
||||
public interface IGraphicsContext : IDisposable
|
||||
@@ -60,10 +82,10 @@ namespace OpenRA
|
||||
IVertexBuffer<Vertex> CreateVertexBuffer(int size);
|
||||
ITexture CreateTexture();
|
||||
IFrameBuffer CreateFrameBuffer(Size s);
|
||||
IFrameBuffer CreateFrameBuffer(Size s, Color clearColor);
|
||||
IShader CreateShader(string name);
|
||||
void EnableScissor(int left, int top, int width, int height);
|
||||
void EnableScissor(int x, int y, int width, int height);
|
||||
void DisableScissor();
|
||||
void SaveScreenshot(string path);
|
||||
void Present();
|
||||
void DrawPrimitives(PrimitiveType pt, int firstVertex, int numVertices);
|
||||
void Clear();
|
||||
@@ -71,6 +93,7 @@ namespace OpenRA
|
||||
void DisableDepthBuffer();
|
||||
void ClearDepthBuffer();
|
||||
void SetBlendMode(BlendMode mode);
|
||||
void SetVSyncEnabled(bool enabled);
|
||||
string GLVersion { get; }
|
||||
}
|
||||
|
||||
@@ -109,6 +132,8 @@ namespace OpenRA
|
||||
{
|
||||
void Bind();
|
||||
void Unbind();
|
||||
void EnableScissor(Rectangle rect);
|
||||
void DisableScissor();
|
||||
ITexture Texture { get; }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -47,14 +47,13 @@ namespace OpenRA.Graphics
|
||||
remapRamp = ramp.Select(r => r - ramp[rampMaxIndex]);
|
||||
}
|
||||
|
||||
remapColors = remapRamp.Select((x, i) => Pair.New(baseIndex + i, Exts.ColorLerp(x / (float)ramp.Length, c1, c2)))
|
||||
.ToDictionary(u => u.First, u => u.Second);
|
||||
remapColors = remapRamp.Select((x, i) => (baseIndex + i, Exts.ColorLerp(x / (float)ramp.Length, c1, c2)))
|
||||
.ToDictionary(u => u.Item1, u => u.Item2);
|
||||
}
|
||||
|
||||
public Color GetRemappedColor(Color original, int index)
|
||||
{
|
||||
Color c;
|
||||
return remapColors.TryGetValue(index, out c)
|
||||
return remapColors.TryGetValue(index, out var c)
|
||||
? c : original;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -28,6 +28,11 @@ namespace OpenRA.Graphics
|
||||
IFinalizedRenderable PrepareRender(WorldRenderer wr);
|
||||
}
|
||||
|
||||
public interface ITintableRenderable
|
||||
{
|
||||
IRenderable WithTint(float3 newTint);
|
||||
}
|
||||
|
||||
public interface IFinalizedRenderable
|
||||
{
|
||||
void Render(WorldRenderer wr);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -153,8 +153,8 @@ namespace OpenRA.Graphics
|
||||
var nextCorner = width / 2 * new float3(-nextDir.Y, nextDir.X, nextDir.Z);
|
||||
|
||||
// Vertices for the corners joining start-end to end-next
|
||||
var cc = closed || i < limit ? IntersectionOf(end + corner, dir, end + nextCorner, nextDir) : end + corner;
|
||||
var cd = closed || i < limit ? IntersectionOf(end - corner, dir, end - nextCorner, nextDir) : end - corner;
|
||||
var cc = closed || i < limit - 1 ? IntersectionOf(end + corner, dir, end + nextCorner, nextDir) : end + corner;
|
||||
var cd = closed || i < limit - 1 ? IntersectionOf(end - corner, dir, end - nextCorner, nextDir) : end - corner;
|
||||
|
||||
// Fill segment
|
||||
vertices[0] = new Vertex(ca + Offset, r, g, b, a, 0, 0);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -45,5 +45,21 @@ namespace OpenRA.Graphics
|
||||
|
||||
parent.DrawSprite(s, a, b, c, d);
|
||||
}
|
||||
|
||||
public void DrawSpriteWithTint(Sprite s, float3 location, float3 size, float3 tint)
|
||||
{
|
||||
if (s.Channel != TextureChannel.RGBA)
|
||||
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");
|
||||
|
||||
parent.DrawSpriteWithTint(s, location, 0, size, tint);
|
||||
}
|
||||
|
||||
public void DrawSpriteWithTint(Sprite s, float3 a, float3 b, float3 c, float3 d, float3 tint)
|
||||
{
|
||||
if (s.Channel != TextureChannel.RGBA)
|
||||
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");
|
||||
|
||||
parent.DrawSpriteWithTint(s, a, b, c, d, tint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -32,10 +32,11 @@ namespace OpenRA.Graphics
|
||||
int ShadowZOffset { get; }
|
||||
int[] Frames { get; }
|
||||
Rectangle Bounds { get; }
|
||||
bool IgnoreWorldTint { get; }
|
||||
|
||||
Sprite GetSprite(int frame);
|
||||
Sprite GetSprite(int frame, int facing);
|
||||
Sprite GetShadow(int frame, int facing);
|
||||
Sprite GetSprite(int frame, WAngle facing);
|
||||
Sprite GetShadow(int frame, WAngle facing);
|
||||
}
|
||||
|
||||
public interface ISpriteSequenceLoader
|
||||
@@ -64,17 +65,15 @@ namespace OpenRA.Graphics
|
||||
return Load(fileSystem, additionalSequences);
|
||||
});
|
||||
|
||||
spriteCache = Exts.Lazy(() => new SpriteCache(fileSystem, modData.SpriteLoaders, new SheetBuilder(SheetType.Indexed)));
|
||||
spriteCache = Exts.Lazy(() => new SpriteCache(fileSystem, modData.SpriteLoaders));
|
||||
}
|
||||
|
||||
public ISpriteSequence GetSequence(string unitName, string sequenceName)
|
||||
{
|
||||
UnitSequences unitSeq;
|
||||
if (!sequences.Value.TryGetValue(unitName, out unitSeq))
|
||||
if (!sequences.Value.TryGetValue(unitName, out var unitSeq))
|
||||
throw new InvalidOperationException("Unit `{0}` does not have any sequences defined.".F(unitName));
|
||||
|
||||
ISpriteSequence seq;
|
||||
if (!unitSeq.Value.TryGetValue(sequenceName, out seq))
|
||||
if (!unitSeq.Value.TryGetValue(sequenceName, out var seq))
|
||||
throw new InvalidOperationException("Unit `{0}` does not have a sequence named `{1}`".F(unitName, sequenceName));
|
||||
|
||||
return seq;
|
||||
@@ -87,8 +86,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public bool HasSequence(string unitName, string sequenceName)
|
||||
{
|
||||
UnitSequences unitSeq;
|
||||
if (!sequences.Value.TryGetValue(unitName, out unitSeq))
|
||||
if (!sequences.Value.TryGetValue(unitName, out var unitSeq))
|
||||
throw new InvalidOperationException("Unit `{0}` does not have any sequences defined.".F(unitName));
|
||||
|
||||
return unitSeq.Value.ContainsKey(sequenceName);
|
||||
@@ -96,8 +94,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public IEnumerable<string> Sequences(string unitName)
|
||||
{
|
||||
UnitSequences unitSeq;
|
||||
if (!sequences.Value.TryGetValue(unitName, out unitSeq))
|
||||
if (!sequences.Value.TryGetValue(unitName, out var unitSeq))
|
||||
throw new InvalidOperationException("Unit `{0}` does not have any sequences defined.".F(unitName));
|
||||
|
||||
return unitSeq.Value.Keys;
|
||||
@@ -114,8 +111,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
var key = node.Value.ToLines(node.Key).JoinWith("|");
|
||||
|
||||
UnitSequences t;
|
||||
if (sequenceCache.TryGetValue(key, out t))
|
||||
if (sequenceCache.TryGetValue(key, out var t))
|
||||
items.Add(node.Key, t);
|
||||
else
|
||||
{
|
||||
@@ -130,16 +126,21 @@ namespace OpenRA.Graphics
|
||||
|
||||
public void Preload()
|
||||
{
|
||||
SpriteCache.SheetBuilder.Current.CreateBuffer();
|
||||
foreach (var sb in SpriteCache.SheetBuilders.Values)
|
||||
sb.Current.CreateBuffer();
|
||||
|
||||
foreach (var unitSeq in sequences.Value.Values)
|
||||
foreach (var seq in unitSeq.Value.Values) { }
|
||||
SpriteCache.SheetBuilder.Current.ReleaseBuffer();
|
||||
|
||||
foreach (var sb in SpriteCache.SheetBuilders.Values)
|
||||
sb.Current.ReleaseBuffer();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (spriteCache.IsValueCreated)
|
||||
spriteCache.Value.SheetBuilder.Dispose();
|
||||
foreach (var sb in SpriteCache.SheetBuilders.Values)
|
||||
sb.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -79,7 +79,17 @@ namespace OpenRA.Graphics
|
||||
|
||||
public Png AsPng()
|
||||
{
|
||||
return new Png(GetData(), Size.Width, Size.Height);
|
||||
var data = GetData();
|
||||
|
||||
// Convert BGRA to RGBA
|
||||
for (var i = 0; i < Size.Width * Size.Height; i++)
|
||||
{
|
||||
var temp = data[i * 4];
|
||||
data[i * 4] = data[i * 4 + 2];
|
||||
data[i * 4 + 2] = temp;
|
||||
}
|
||||
|
||||
return new Png(data, Size.Width, Size.Height);
|
||||
}
|
||||
|
||||
public Png AsPng(TextureChannel channel, IPalette pal)
|
||||
@@ -136,8 +146,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (texture != null)
|
||||
texture.Dispose();
|
||||
texture?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -36,6 +36,7 @@ namespace OpenRA.Graphics
|
||||
public readonly SheetType Type;
|
||||
readonly List<Sheet> sheets = new List<Sheet>();
|
||||
readonly Func<Sheet> allocateSheet;
|
||||
readonly int margin;
|
||||
|
||||
Sheet current;
|
||||
TextureChannel channel;
|
||||
@@ -47,19 +48,30 @@ namespace OpenRA.Graphics
|
||||
return new Sheet(type, new Size(sheetSize, sheetSize));
|
||||
}
|
||||
|
||||
public static SheetType FrameTypeToSheetType(SpriteFrameType t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case SpriteFrameType.Indexed: return SheetType.Indexed;
|
||||
case SpriteFrameType.BGRA: return SheetType.BGRA;
|
||||
default: throw new NotImplementedException("Unknown SpriteFrameType {0}".F(t));
|
||||
}
|
||||
}
|
||||
|
||||
public SheetBuilder(SheetType t)
|
||||
: this(t, Game.Settings.Graphics.SheetSize) { }
|
||||
|
||||
public SheetBuilder(SheetType t, int sheetSize)
|
||||
: this(t, () => AllocateSheet(t, sheetSize)) { }
|
||||
public SheetBuilder(SheetType t, int sheetSize, int margin = 1)
|
||||
: this(t, () => AllocateSheet(t, sheetSize), margin) { }
|
||||
|
||||
public SheetBuilder(SheetType t, Func<Sheet> allocateSheet)
|
||||
public SheetBuilder(SheetType t, Func<Sheet> allocateSheet, int margin = 1)
|
||||
{
|
||||
channel = t == SheetType.Indexed ? TextureChannel.Red : TextureChannel.RGBA;
|
||||
Type = t;
|
||||
current = allocateSheet();
|
||||
sheets.Add(current);
|
||||
this.allocateSheet = allocateSheet;
|
||||
this.margin = margin;
|
||||
}
|
||||
|
||||
public Sprite Add(ISpriteFrame frame) { return Add(frame.Data, frame.Size, 0, frame.Offset); }
|
||||
@@ -76,9 +88,9 @@ namespace OpenRA.Graphics
|
||||
return rect;
|
||||
}
|
||||
|
||||
public Sprite Add(Png src)
|
||||
public Sprite Add(Png src, float scale = 1f)
|
||||
{
|
||||
var rect = Allocate(new Size(src.Width, src.Height));
|
||||
var rect = Allocate(new Size(src.Width, src.Height), scale);
|
||||
Util.FastCopyIntoSprite(rect, src);
|
||||
current.CommitBufferedData();
|
||||
return rect;
|
||||
@@ -102,19 +114,19 @@ namespace OpenRA.Graphics
|
||||
return (TextureChannel)nextChannel;
|
||||
}
|
||||
|
||||
public Sprite Allocate(Size imageSize) { return Allocate(imageSize, 0, float3.Zero); }
|
||||
public Sprite Allocate(Size imageSize, float zRamp, float3 spriteOffset)
|
||||
public Sprite Allocate(Size imageSize, float scale = 1f) { return Allocate(imageSize, 0, float3.Zero, scale); }
|
||||
public Sprite Allocate(Size imageSize, float zRamp, float3 spriteOffset, float scale = 1f)
|
||||
{
|
||||
if (imageSize.Width + p.X > current.Size.Width)
|
||||
if (imageSize.Width + p.X + margin > current.Size.Width)
|
||||
{
|
||||
p = new int2(0, p.Y + rowHeight);
|
||||
p = new int2(0, p.Y + rowHeight + margin);
|
||||
rowHeight = imageSize.Height;
|
||||
}
|
||||
|
||||
if (imageSize.Height > rowHeight)
|
||||
rowHeight = imageSize.Height;
|
||||
|
||||
if (p.Y + imageSize.Height > current.Size.Height)
|
||||
if (p.Y + imageSize.Height + margin > current.Size.Height)
|
||||
{
|
||||
var next = NextChannel(channel);
|
||||
if (next == null)
|
||||
@@ -131,8 +143,8 @@ namespace OpenRA.Graphics
|
||||
p = int2.Zero;
|
||||
}
|
||||
|
||||
var rect = new Sprite(current, new Rectangle(p.X, p.Y, imageSize.Width, imageSize.Height), zRamp, spriteOffset, channel, BlendMode.Alpha);
|
||||
p += new int2(imageSize.Width, 0);
|
||||
var rect = new Sprite(current, new Rectangle(p.X + margin, p.Y + margin, imageSize.Width, imageSize.Height), zRamp, spriteOffset, channel, BlendMode.Alpha, scale);
|
||||
p += new int2(imageSize.Width + margin, 0);
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
public interface ICursor : IDisposable
|
||||
{
|
||||
void Render(Renderer renderer);
|
||||
void SetCursor(string cursor);
|
||||
void Tick();
|
||||
void Lock();
|
||||
void Unlock();
|
||||
}
|
||||
|
||||
public sealed class SoftwareCursor : ICursor
|
||||
{
|
||||
readonly HardwarePalette palette = new HardwarePalette();
|
||||
readonly Cache<string, PaletteReference> paletteReferences;
|
||||
readonly Dictionary<string, Sprite[]> sprites = new Dictionary<string, Sprite[]>();
|
||||
readonly CursorProvider cursorProvider;
|
||||
readonly SheetBuilder sheetBuilder;
|
||||
|
||||
bool isLocked = false;
|
||||
int2 lockedPosition;
|
||||
|
||||
public SoftwareCursor(CursorProvider cursorProvider)
|
||||
{
|
||||
this.cursorProvider = cursorProvider;
|
||||
|
||||
paletteReferences = new Cache<string, PaletteReference>(CreatePaletteReference);
|
||||
foreach (var p in cursorProvider.Palettes)
|
||||
palette.AddPalette(p.Key, p.Value, false);
|
||||
|
||||
palette.Initialize();
|
||||
|
||||
sheetBuilder = new SheetBuilder(SheetType.Indexed);
|
||||
foreach (var kv in cursorProvider.Cursors)
|
||||
{
|
||||
var s = kv.Value.Frames.Select(a => sheetBuilder.Add(a)).ToArray();
|
||||
sprites.Add(kv.Key, s);
|
||||
}
|
||||
|
||||
sheetBuilder.Current.ReleaseBuffer();
|
||||
|
||||
Game.Renderer.Window.SetHardwareCursor(null);
|
||||
}
|
||||
|
||||
PaletteReference CreatePaletteReference(string name)
|
||||
{
|
||||
var pal = palette.GetPalette(name);
|
||||
return new PaletteReference(name, palette.GetPaletteIndex(name), pal, palette);
|
||||
}
|
||||
|
||||
string cursorName;
|
||||
public void SetCursor(string cursor)
|
||||
{
|
||||
cursorName = cursor;
|
||||
}
|
||||
|
||||
float cursorFrame;
|
||||
public void Tick()
|
||||
{
|
||||
cursorFrame += 0.5f;
|
||||
}
|
||||
|
||||
public void Render(Renderer renderer)
|
||||
{
|
||||
if (cursorName == null)
|
||||
return;
|
||||
|
||||
var cursorSequence = cursorProvider.GetCursorSequence(cursorName);
|
||||
var cursorSprite = sprites[cursorName][(int)cursorFrame % cursorSequence.Length];
|
||||
var cursorSize = CursorProvider.CursorViewportZoomed ? 2.0f * cursorSprite.Size : cursorSprite.Size;
|
||||
|
||||
var cursorOffset = CursorProvider.CursorViewportZoomed ?
|
||||
(2 * cursorSequence.Hotspot) + cursorSprite.Size.XY.ToInt2() :
|
||||
cursorSequence.Hotspot + (0.5f * cursorSprite.Size.XY).ToInt2();
|
||||
|
||||
renderer.SetPalette(palette);
|
||||
var mousePos = isLocked ? lockedPosition : Viewport.LastMousePos;
|
||||
renderer.SpriteRenderer.DrawSprite(cursorSprite,
|
||||
mousePos - cursorOffset,
|
||||
paletteReferences[cursorSequence.Palette],
|
||||
cursorSize);
|
||||
}
|
||||
|
||||
public void Lock()
|
||||
{
|
||||
Game.Renderer.Window.SetRelativeMouseMode(true);
|
||||
lockedPosition = Viewport.LastMousePos;
|
||||
isLocked = true;
|
||||
}
|
||||
|
||||
public void Unlock()
|
||||
{
|
||||
Game.Renderer.Window.SetRelativeMouseMode(false);
|
||||
isLocked = false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
palette.Dispose();
|
||||
sheetBuilder.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -26,17 +26,17 @@ namespace OpenRA.Graphics
|
||||
public readonly float3 FractionalOffset;
|
||||
public readonly float Top, Left, Bottom, Right;
|
||||
|
||||
public Sprite(Sheet sheet, Rectangle bounds, TextureChannel channel)
|
||||
: this(sheet, bounds, 0, float2.Zero, channel) { }
|
||||
public Sprite(Sheet sheet, Rectangle bounds, TextureChannel channel, float scale = 1)
|
||||
: this(sheet, bounds, 0, float2.Zero, channel, BlendMode.Alpha, scale) { }
|
||||
|
||||
public Sprite(Sheet sheet, Rectangle bounds, float zRamp, float3 offset, TextureChannel channel, BlendMode blendMode = BlendMode.Alpha)
|
||||
public Sprite(Sheet sheet, Rectangle bounds, float zRamp, float3 offset, TextureChannel channel, BlendMode blendMode = BlendMode.Alpha, float scale = 1f)
|
||||
{
|
||||
Sheet = sheet;
|
||||
Bounds = bounds;
|
||||
Offset = offset;
|
||||
ZRamp = zRamp;
|
||||
Channel = channel;
|
||||
Size = new float3(bounds.Size.Width, bounds.Size.Height, bounds.Size.Height * zRamp);
|
||||
Size = scale * new float3(bounds.Size.Width, bounds.Size.Height, bounds.Size.Height * zRamp);
|
||||
BlendMode = blendMode;
|
||||
FractionalOffset = Size.Z != 0 ? offset / Size :
|
||||
new float3(offset.X / Size.X, offset.Y / Size.Y, 0);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -13,7 +13,6 @@ using System;
|
||||
using System.Linq;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
@@ -24,7 +23,9 @@ namespace OpenRA.Graphics
|
||||
readonly SheetBuilder builder;
|
||||
readonly Func<string, float> lineWidth;
|
||||
readonly IFont font;
|
||||
readonly Cache<Pair<char, Color>, GlyphInfo> glyphs;
|
||||
readonly Cache<char, GlyphInfo> glyphs;
|
||||
readonly Cache<(char C, int Radius), Sprite> contrastGlyphs;
|
||||
readonly Cache<int, float[]> dilationElements;
|
||||
|
||||
float deviceScale;
|
||||
|
||||
@@ -38,15 +39,20 @@ namespace OpenRA.Graphics
|
||||
this.builder = builder;
|
||||
|
||||
font = Game.Renderer.CreateFont(data);
|
||||
|
||||
glyphs = new Cache<Pair<char, Color>, GlyphInfo>(CreateGlyph, Pair<char, Color>.EqualityComparer);
|
||||
glyphs = new Cache<char, GlyphInfo>(CreateGlyph);
|
||||
contrastGlyphs = new Cache<(char, int), Sprite>(CreateContrastGlyph);
|
||||
dilationElements = new Cache<int, float[]>(CreateCircularWeightMap);
|
||||
|
||||
// PERF: Cache these delegates for Measure calls.
|
||||
Func<char, float> characterWidth = character => glyphs[Pair.New(character, Color.White)].Advance;
|
||||
Func<char, float> characterWidth = character => glyphs[character].Advance;
|
||||
lineWidth = line => line.Sum(characterWidth) / deviceScale;
|
||||
|
||||
// Pre-cache small font sizes so glyphs are immediately available when we need them
|
||||
if (size <= 24)
|
||||
PrecacheColor(Color.White, name);
|
||||
using (new PerfTimer("Precache {0} {1}px".F(name, size)))
|
||||
for (var n = (char)0x20; n < (char)0x7f; n++)
|
||||
if (glyphs[n] == null)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
TopOffset = size - ascender;
|
||||
}
|
||||
@@ -55,14 +61,42 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
deviceScale = scale;
|
||||
glyphs.Clear();
|
||||
contrastGlyphs.Clear();
|
||||
}
|
||||
|
||||
void PrecacheColor(Color c, string name)
|
||||
void DrawTextContrast(string text, float2 location, Color contrastColor, int contrastOffset)
|
||||
{
|
||||
using (new PerfTimer("PrecacheColor {0} {1}px {2}".F(name, size, c)))
|
||||
for (var n = (char)0x20; n < (char)0x7f; n++)
|
||||
if (glyphs[Pair.New(n, c)] == null)
|
||||
throw new InvalidOperationException();
|
||||
// Offset from the baseline position to the top-left of the glyph for rendering
|
||||
location += new float2(0, size);
|
||||
|
||||
// Calculate positions in screen pixel coordinates
|
||||
var screenContrast = (int)(contrastOffset * deviceScale);
|
||||
var screen = new int2((int)(location.X * deviceScale + 0.5f), (int)(location.Y * deviceScale + 0.5f));
|
||||
var contrastVector = new float2(screenContrast, screenContrast);
|
||||
var tint = new float3(contrastColor.R / 255f, contrastColor.G / 255f, contrastColor.B / 255f);
|
||||
foreach (var s in text)
|
||||
{
|
||||
if (s == '\n')
|
||||
{
|
||||
location += new float2(0, size);
|
||||
screen = new int2((int)(location.X * deviceScale + 0.5f), (int)(location.Y * deviceScale + 0.5f));
|
||||
continue;
|
||||
}
|
||||
|
||||
var g = glyphs[s];
|
||||
|
||||
// Convert screen coordinates back to UI coordinates for drawing
|
||||
if (g.Sprite != null)
|
||||
{
|
||||
var contrastSprite = contrastGlyphs[(s, screenContrast)];
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSpriteWithTint(contrastSprite,
|
||||
(screen + g.Offset - contrastVector) / deviceScale,
|
||||
contrastSprite.Size / deviceScale,
|
||||
tint);
|
||||
}
|
||||
|
||||
screen += new int2((int)(g.Advance + 0.5f), 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawText(string text, float2 location, Color c)
|
||||
@@ -70,42 +104,46 @@ namespace OpenRA.Graphics
|
||||
// Offset from the baseline position to the top-left of the glyph for rendering
|
||||
location += new float2(0, size);
|
||||
|
||||
var p = location;
|
||||
// Calculate positions in screen pixel coordinates
|
||||
var screen = new int2((int)(location.X * deviceScale + 0.5f), (int)(location.Y * deviceScale + 0.5f));
|
||||
var tint = new float3(c.R / 255f, c.G / 255f, c.B / 255f);
|
||||
foreach (var s in text)
|
||||
{
|
||||
if (s == '\n')
|
||||
{
|
||||
location += new float2(0, size);
|
||||
p = location;
|
||||
screen = new int2((int)(location.X * deviceScale + 0.5f), (int)(location.Y * deviceScale + 0.5f));
|
||||
continue;
|
||||
}
|
||||
|
||||
var g = glyphs[Pair.New(s, c)];
|
||||
if (g.Sprite != null)
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite,
|
||||
new float2(
|
||||
(int)Math.Round(p.X * deviceScale + g.Offset.X, 0) / deviceScale,
|
||||
p.Y + g.Offset.Y / deviceScale),
|
||||
g.Sprite.Size / deviceScale);
|
||||
var g = glyphs[s];
|
||||
|
||||
p += new float2(g.Advance / deviceScale, 0);
|
||||
// Convert screen coordinates back to UI coordinates for drawing
|
||||
if (g.Sprite != null)
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSpriteWithTint(g.Sprite,
|
||||
(screen + g.Offset).ToFloat2() / deviceScale,
|
||||
g.Sprite.Size / deviceScale,
|
||||
tint);
|
||||
|
||||
screen += new int2((int)(g.Advance + 0.5f), 0);
|
||||
}
|
||||
}
|
||||
|
||||
float3 Rotate(float3 v, float sina, float cosa, float2 offset)
|
||||
float2 Rotate(float2 v, float sina, float cosa, float2 offset)
|
||||
{
|
||||
return new float3(
|
||||
return new float2(
|
||||
v.X * cosa - v.Y * sina + offset.X,
|
||||
v.X * sina + v.Y * cosa + offset.Y,
|
||||
0);
|
||||
v.X * sina + v.Y * cosa + offset.Y);
|
||||
}
|
||||
|
||||
public void DrawText(string text, float2 location, Color c, float angle)
|
||||
{
|
||||
// Offset from the baseline position to the top-left of the glyph for rendering
|
||||
// All positions are calculated in UI coordinates
|
||||
var offset = new float2(0, size);
|
||||
var cosa = (float)Math.Cos(-angle);
|
||||
var sina = (float)Math.Sin(-angle);
|
||||
var tint = new float3(c.R / 255f, c.G / 255f, c.B / 255f);
|
||||
|
||||
var p = offset;
|
||||
foreach (var s in text)
|
||||
@@ -117,21 +155,29 @@ namespace OpenRA.Graphics
|
||||
continue;
|
||||
}
|
||||
|
||||
var g = glyphs[Pair.New(s, c)];
|
||||
var g = glyphs[s];
|
||||
if (g.Sprite != null)
|
||||
{
|
||||
var tl = new float3(
|
||||
(int)Math.Round(p.X * deviceScale + g.Offset.X, 0) / deviceScale,
|
||||
p.Y + g.Offset.Y / deviceScale, 0);
|
||||
var br = tl + g.Sprite.Size / deviceScale;
|
||||
var tr = new float3(br.X, tl.Y, 0);
|
||||
var bl = new float3(tl.X, br.Y, 0);
|
||||
var tl = new float2(
|
||||
p.X + g.Offset.X / deviceScale,
|
||||
p.Y + g.Offset.Y / deviceScale);
|
||||
var br = tl + g.Sprite.Size.XY / deviceScale;
|
||||
var tr = new float2(br.X, tl.Y);
|
||||
var bl = new float2(tl.X, br.Y);
|
||||
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite,
|
||||
Rotate(tl, sina, cosa, location),
|
||||
Rotate(tr, sina, cosa, location),
|
||||
Rotate(br, sina, cosa, location),
|
||||
Rotate(bl, sina, cosa, location));
|
||||
var ra = Rotate(tl, sina, cosa, location);
|
||||
var rb = Rotate(tr, sina, cosa, location);
|
||||
var rc = Rotate(br, sina, cosa, location);
|
||||
var rd = Rotate(bl, sina, cosa, location);
|
||||
|
||||
// Offset rotated glyph to align the top-left corner with the screen pixel grid
|
||||
var screenOffset = new float2((int)(ra.X * deviceScale + 0.5f), (int)(ra.Y * deviceScale + 0.5f)) / deviceScale - ra;
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSpriteWithTint(g.Sprite,
|
||||
ra + screenOffset,
|
||||
rb + screenOffset,
|
||||
rc + screenOffset,
|
||||
rd + screenOffset,
|
||||
tint);
|
||||
}
|
||||
|
||||
p += new float2(g.Advance / deviceScale, 0);
|
||||
@@ -141,12 +187,7 @@ namespace OpenRA.Graphics
|
||||
public void DrawTextWithContrast(string text, float2 location, Color fg, Color bg, int offset)
|
||||
{
|
||||
if (offset > 0)
|
||||
{
|
||||
DrawText(text, location + new float2(-offset / deviceScale, 0), bg);
|
||||
DrawText(text, location + new float2(offset / deviceScale, 0), bg);
|
||||
DrawText(text, location + new float2(0, -offset / deviceScale), bg);
|
||||
DrawText(text, location + new float2(0, offset / deviceScale), bg);
|
||||
}
|
||||
DrawTextContrast(text, location, bg, offset);
|
||||
|
||||
DrawText(text, location, fg);
|
||||
}
|
||||
@@ -159,7 +200,12 @@ namespace OpenRA.Graphics
|
||||
public void DrawTextWithShadow(string text, float2 location, Color fg, Color bg, int offset)
|
||||
{
|
||||
if (offset != 0)
|
||||
DrawText(text, location + new float2(offset, offset), bg);
|
||||
{
|
||||
// Shadow offsets are rounded to an integer number of screen pixels.
|
||||
// This makes sure the shadow will be positioned consistently everywhere on the screen.
|
||||
var screenOffset = (int)(offset * deviceScale) / deviceScale;
|
||||
DrawText(text, location + new float2(screenOffset, screenOffset), bg);
|
||||
}
|
||||
|
||||
DrawText(text, location, fg);
|
||||
}
|
||||
@@ -172,7 +218,12 @@ namespace OpenRA.Graphics
|
||||
public void DrawTextWithShadow(string text, float2 location, Color fg, Color bg, int offset, float angle)
|
||||
{
|
||||
if (offset != 0)
|
||||
DrawText(text, location + new float2(offset, offset), bg, angle);
|
||||
{
|
||||
// Shadow offsets are rounded to an integer number of screen pixels.
|
||||
// This makes sure the shadow will be positioned consistently everywhere on the screen.
|
||||
var screenOffset = (int)(offset * deviceScale) / deviceScale;
|
||||
DrawText(text, location + new float2(screenOffset, screenOffset), bg, angle);
|
||||
}
|
||||
|
||||
DrawText(text, location, fg, angle);
|
||||
}
|
||||
@@ -191,10 +242,9 @@ namespace OpenRA.Graphics
|
||||
return new int2((int)Math.Ceiling(lines.Max(lineWidth)), lines.Length * size);
|
||||
}
|
||||
|
||||
GlyphInfo CreateGlyph(Pair<char, Color> c)
|
||||
GlyphInfo CreateGlyph(char c)
|
||||
{
|
||||
var glyph = font.CreateGlyph(c.First, size, deviceScale);
|
||||
|
||||
var glyph = font.CreateGlyph(c, size, deviceScale);
|
||||
if (glyph.Data == null)
|
||||
{
|
||||
return new GlyphInfo
|
||||
@@ -224,12 +274,10 @@ namespace OpenRA.Graphics
|
||||
if (p != 0)
|
||||
{
|
||||
var q = destStride * (j + s.Bounds.Top) + 4 * (i + s.Bounds.Left);
|
||||
var pmc = Util.PremultiplyAlpha(Color.FromArgb(p, c.Second));
|
||||
|
||||
dest[q] = pmc.B;
|
||||
dest[q + 1] = pmc.G;
|
||||
dest[q + 2] = pmc.R;
|
||||
dest[q + 3] = pmc.A;
|
||||
dest[q] = p;
|
||||
dest[q + 1] = p;
|
||||
dest[q + 2] = p;
|
||||
dest[q + 3] = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -239,6 +287,123 @@ namespace OpenRA.Graphics
|
||||
return g;
|
||||
}
|
||||
|
||||
float[] CreateCircularWeightMap(int r)
|
||||
{
|
||||
// Create circular weight maps that are used by CreateContrastGlyph for
|
||||
// both the structuring element and to weight the resulting pixel value.
|
||||
// The output is a 2 * r + 1 square array giving the pixel intersection
|
||||
// with a circle of radius (r + 0.5).
|
||||
//
|
||||
// Example output for r=1:
|
||||
// 0.60 1.00 0.60
|
||||
// 1.00 1.00 1.00
|
||||
// 0.60 1.00 0.60
|
||||
//
|
||||
// Example output for r=3:
|
||||
// 0.00 0.44 0.80 1.00 0.80 0.44 0.00
|
||||
// 0.44 1.00 1.00 1.00 1.00 1.00 0.44
|
||||
// 0.80 1.00 1.00 1.00 1.00 1.00 0.80
|
||||
// 1.00 1.00 1.00 1.00 1.00 1.00 1.00
|
||||
// 0.80 1.00 1.00 1.00 1.00 1.00 0.80
|
||||
// 0.44 1.00 1.00 1.00 1.00 1.00 0.44
|
||||
// 0.00 0.44 0.80 1.00 0.80 0.44 0.00
|
||||
var stride = 2 * r + 1;
|
||||
var elem = new float[stride * stride];
|
||||
|
||||
for (var j = 0; j <= 2 * r; j++)
|
||||
{
|
||||
for (var i = 0; i <= 2 * r; i++)
|
||||
{
|
||||
var di = i - r;
|
||||
var dj = j - r;
|
||||
|
||||
// No intersection with circle
|
||||
if (di * di + dj * dj > (r + 1) * (r + 1))
|
||||
continue;
|
||||
|
||||
// Fully contained within circle
|
||||
if (di * di + dj * dj < (r - 1) * (r - 1))
|
||||
{
|
||||
elem[j * stride + i] = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Approximate sub-pixel intersection using a 5x5 grid
|
||||
for (var jj = 0; jj < 5; jj++)
|
||||
{
|
||||
for (var ii = 0; ii < 5; ii++)
|
||||
{
|
||||
var si = di - (float)Math.Sign(di) * ii / 5;
|
||||
var sj = dj - (float)Math.Sign(dj) * jj / 5;
|
||||
if (si * si + sj * sj <= r * r)
|
||||
elem[j * stride + i] += 0.04f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
Sprite CreateContrastGlyph((char C, int Radius) c)
|
||||
{
|
||||
var glyph = glyphs[c.C];
|
||||
var r = c.Radius;
|
||||
|
||||
var s = builder.Allocate(new Size(glyph.Sprite.Bounds.Width + 2 * r, glyph.Sprite.Bounds.Height + 2 * r));
|
||||
var dest = s.Sheet.GetData();
|
||||
var destStride = s.Sheet.Size.Width * 4;
|
||||
|
||||
var glyphData = glyph.Sprite.Sheet.GetData();
|
||||
var glyphStride = glyph.Sprite.Sheet.Size.Width * 4;
|
||||
var glyphBounds = glyph.Sprite.Bounds;
|
||||
|
||||
var elem = dilationElements[r];
|
||||
var elemStride = 2 * r + 1;
|
||||
|
||||
// Expand the glyph by applying the greyscale dilation operator to the source glyph's alpha channel
|
||||
for (var j = 0; j < s.Size.Y; j++)
|
||||
{
|
||||
for (var i = 0; i < s.Size.X; i++)
|
||||
{
|
||||
// Apply the weight map to the source glyph and find the largest weighted alpha
|
||||
var first = true;
|
||||
var alpha = (byte)0;
|
||||
for (var wj = 0; wj <= 2 * r; wj++)
|
||||
{
|
||||
for (var wi = 0; wi <= 2 * r; wi++)
|
||||
{
|
||||
// Ignore pixels that are outside the source glyph bounds
|
||||
var ii = i + wi - 2 * r;
|
||||
var jj = j + wj - 2 * r;
|
||||
if (ii < 0 || ii >= glyphBounds.Width || jj < 0 || jj >= glyphBounds.Height)
|
||||
continue;
|
||||
|
||||
// Weighted alpha for this pixel
|
||||
var weighted = (byte)(elem[wj * elemStride + wi] * glyphData[glyphStride * (jj + glyphBounds.Top) + 4 * (ii + glyphBounds.Left) + 3]);
|
||||
if (first || weighted > alpha)
|
||||
{
|
||||
alpha = weighted;
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (alpha > 0)
|
||||
{
|
||||
var q = destStride * (j + s.Bounds.Top) + 4 * (i + s.Bounds.Left);
|
||||
dest[q] = alpha;
|
||||
dest[q + 1] = alpha;
|
||||
dest[q + 2] = alpha;
|
||||
dest[q + 3] = alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s.Sheet.CommitBufferedData();
|
||||
return s;
|
||||
}
|
||||
|
||||
static Color GetContrastColor(Color fgColor, Color bgDark, Color bgLight)
|
||||
{
|
||||
return fgColor == Color.White || fgColor.GetBrightness() > 0.33 ? bgDark : bgLight;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -18,6 +18,8 @@ using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
public enum SpriteFrameType { Indexed, BGRA }
|
||||
|
||||
public interface ISpriteLoader
|
||||
{
|
||||
bool TryParseSprite(Stream s, out ISpriteFrame[] frames, out TypeDictionary metadata);
|
||||
@@ -25,6 +27,8 @@ namespace OpenRA.Graphics
|
||||
|
||||
public interface ISpriteFrame
|
||||
{
|
||||
SpriteFrameType Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Size of the frame's `Data`.
|
||||
/// </summary>
|
||||
@@ -43,7 +47,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public class SpriteCache
|
||||
{
|
||||
public readonly SheetBuilder SheetBuilder;
|
||||
public readonly Cache<SpriteFrameType, SheetBuilder> SheetBuilders;
|
||||
readonly ISpriteLoader[] loaders;
|
||||
readonly IReadOnlyFileSystem fileSystem;
|
||||
|
||||
@@ -51,9 +55,10 @@ namespace OpenRA.Graphics
|
||||
readonly Dictionary<string, ISpriteFrame[]> unloadedFrames = new Dictionary<string, ISpriteFrame[]>();
|
||||
readonly Dictionary<string, TypeDictionary> metadata = new Dictionary<string, TypeDictionary>();
|
||||
|
||||
public SpriteCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders, SheetBuilder sheetBuilder)
|
||||
public SpriteCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders)
|
||||
{
|
||||
SheetBuilder = sheetBuilder;
|
||||
SheetBuilders = new Cache<SpriteFrameType, SheetBuilder>(t => new SheetBuilder(SheetBuilder.FrameTypeToSheetType(t)));
|
||||
|
||||
this.fileSystem = fileSystem;
|
||||
this.loaders = loaders;
|
||||
}
|
||||
@@ -71,8 +76,7 @@ namespace OpenRA.Graphics
|
||||
var allSprites = sprites.GetOrAdd(filename);
|
||||
var sprite = allSprites.FirstOrDefault();
|
||||
|
||||
ISpriteFrame[] unloaded;
|
||||
if (!unloadedFrames.TryGetValue(filename, out unloaded))
|
||||
if (!unloadedFrames.TryGetValue(filename, out var unloaded))
|
||||
unloaded = null;
|
||||
|
||||
// This is the first time that the file has been requested
|
||||
@@ -80,8 +84,7 @@ namespace OpenRA.Graphics
|
||||
// the loaded cache (initially empty)
|
||||
if (sprite == null)
|
||||
{
|
||||
TypeDictionary fileMetadata = null;
|
||||
unloaded = FrameLoader.GetFrames(fileSystem, filename, loaders, out fileMetadata);
|
||||
unloaded = FrameLoader.GetFrames(fileSystem, filename, loaders, out var fileMetadata);
|
||||
unloadedFrames[filename] = unloaded;
|
||||
metadata[filename] = fileMetadata;
|
||||
|
||||
@@ -89,7 +92,7 @@ namespace OpenRA.Graphics
|
||||
allSprites.Add(sprite);
|
||||
}
|
||||
|
||||
// HACK: The sequency code relies on side-effects from getUsedFrames
|
||||
// HACK: The sequence code relies on side-effects from getUsedFrames
|
||||
var indices = getUsedFrames != null ? getUsedFrames(sprite.Length) :
|
||||
Enumerable.Range(0, sprite.Length);
|
||||
|
||||
@@ -100,7 +103,7 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
if (unloaded[i] != null)
|
||||
{
|
||||
sprite[i] = SheetBuilder.Add(unloaded[i]);
|
||||
sprite[i] = SheetBuilders[unloaded[i].Type].Add(unloaded[i]);
|
||||
unloaded[i] = null;
|
||||
}
|
||||
}
|
||||
@@ -120,8 +123,7 @@ namespace OpenRA.Graphics
|
||||
/// </summary>
|
||||
public TypeDictionary FrameMetadata(string filename)
|
||||
{
|
||||
TypeDictionary fileMetadata;
|
||||
if (!metadata.TryGetValue(filename, out fileMetadata))
|
||||
if (!metadata.TryGetValue(filename, out var fileMetadata))
|
||||
{
|
||||
FrameLoader.GetFrames(fileSystem, filename, loaders, out fileMetadata);
|
||||
metadata[filename] = fileMetadata;
|
||||
@@ -137,8 +139,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public FrameCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders)
|
||||
{
|
||||
TypeDictionary metadata;
|
||||
frames = new Cache<string, ISpriteFrame[]>(filename => FrameLoader.GetFrames(fileSystem, filename, loaders, out metadata));
|
||||
frames = new Cache<string, ISpriteFrame[]>(filename => FrameLoader.GetFrames(fileSystem, filename, loaders, out _));
|
||||
}
|
||||
|
||||
public ISpriteFrame[] this[string filename] { get { return frames[filename]; } }
|
||||
@@ -160,11 +161,10 @@ namespace OpenRA.Graphics
|
||||
|
||||
public static ISpriteFrame[] GetFrames(Stream stream, ISpriteLoader[] loaders, out TypeDictionary metadata)
|
||||
{
|
||||
ISpriteFrame[] frames;
|
||||
metadata = null;
|
||||
|
||||
foreach (var loader in loaders)
|
||||
if (loader.TryParseSprite(stream, out frames, out metadata))
|
||||
if (loader.TryParseSprite(stream, out var frames, out metadata))
|
||||
return frames;
|
||||
|
||||
return null;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -14,7 +14,7 @@ using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
public struct SpriteRenderable : IRenderable, IFinalizedRenderable
|
||||
public struct SpriteRenderable : IRenderable, ITintableRenderable, IFinalizedRenderable
|
||||
{
|
||||
public static readonly IEnumerable<IRenderable> None = new IRenderable[0];
|
||||
|
||||
@@ -24,9 +24,17 @@ namespace OpenRA.Graphics
|
||||
readonly int zOffset;
|
||||
readonly PaletteReference palette;
|
||||
readonly float scale;
|
||||
readonly float3 tint;
|
||||
readonly bool isDecoration;
|
||||
readonly bool ignoreWorldTint;
|
||||
|
||||
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, bool isDecoration)
|
||||
: this(sprite, pos, offset, zOffset, palette, scale, float3.Ones, isDecoration, false) { }
|
||||
|
||||
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, bool isDecoration, bool ignoreWorldTint)
|
||||
: this(sprite, pos, offset, zOffset, palette, scale, float3.Ones, isDecoration, ignoreWorldTint) { }
|
||||
|
||||
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, float3 tint, bool isDecoration, bool ignoreWorldTint)
|
||||
{
|
||||
this.sprite = sprite;
|
||||
this.pos = pos;
|
||||
@@ -34,7 +42,9 @@ namespace OpenRA.Graphics
|
||||
this.zOffset = zOffset;
|
||||
this.palette = palette;
|
||||
this.scale = scale;
|
||||
this.tint = tint;
|
||||
this.isDecoration = isDecoration;
|
||||
this.ignoreWorldTint = ignoreWorldTint;
|
||||
}
|
||||
|
||||
public WPos Pos { get { return pos + offset; } }
|
||||
@@ -43,10 +53,12 @@ namespace OpenRA.Graphics
|
||||
public int ZOffset { get { return zOffset; } }
|
||||
public bool IsDecoration { get { return isDecoration; } }
|
||||
|
||||
public IRenderable WithPalette(PaletteReference newPalette) { return new SpriteRenderable(sprite, pos, offset, zOffset, newPalette, scale, isDecoration); }
|
||||
public IRenderable WithZOffset(int newOffset) { return new SpriteRenderable(sprite, pos, offset, newOffset, palette, scale, isDecoration); }
|
||||
public IRenderable OffsetBy(WVec vec) { return new SpriteRenderable(sprite, pos + vec, offset, zOffset, palette, scale, isDecoration); }
|
||||
public IRenderable AsDecoration() { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, true); }
|
||||
public IRenderable WithPalette(PaletteReference newPalette) { return new SpriteRenderable(sprite, pos, offset, zOffset, newPalette, scale, tint, isDecoration, ignoreWorldTint); }
|
||||
public IRenderable WithZOffset(int newOffset) { return new SpriteRenderable(sprite, pos, offset, newOffset, palette, scale, tint, isDecoration, ignoreWorldTint); }
|
||||
public IRenderable OffsetBy(WVec vec) { return new SpriteRenderable(sprite, pos + vec, offset, zOffset, palette, scale, tint, isDecoration, ignoreWorldTint); }
|
||||
public IRenderable AsDecoration() { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, tint, true, ignoreWorldTint); }
|
||||
|
||||
public IRenderable WithTint(float3 newTint) { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, newTint, isDecoration, ignoreWorldTint); }
|
||||
|
||||
float3 ScreenPosition(WorldRenderer wr)
|
||||
{
|
||||
@@ -59,13 +71,25 @@ namespace OpenRA.Graphics
|
||||
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
|
||||
public void Render(WorldRenderer wr)
|
||||
{
|
||||
Game.Renderer.WorldSpriteRenderer.DrawSprite(sprite, ScreenPosition(wr), palette, scale * sprite.Size);
|
||||
var wsr = Game.Renderer.WorldSpriteRenderer;
|
||||
if (ignoreWorldTint)
|
||||
wsr.DrawSprite(sprite, ScreenPosition(wr), palette, scale * sprite.Size);
|
||||
else
|
||||
{
|
||||
var t = tint;
|
||||
if (wr.TerrainLighting != null)
|
||||
t *= wr.TerrainLighting.TintAt(pos);
|
||||
|
||||
wsr.DrawSpriteWithTint(sprite, ScreenPosition(wr), palette, scale * sprite.Size, t);
|
||||
}
|
||||
}
|
||||
|
||||
public void RenderDebugGeometry(WorldRenderer wr)
|
||||
{
|
||||
var screenOffset = ScreenPosition(wr) + sprite.Offset;
|
||||
Game.Renderer.WorldRgbaColorRenderer.DrawRect(screenOffset, screenOffset + sprite.Size, 1 / wr.Viewport.Zoom, Color.Red);
|
||||
var pos = ScreenPosition(wr) + sprite.Offset;
|
||||
var tl = wr.Viewport.WorldToViewPx(pos);
|
||||
var br = wr.Viewport.WorldToViewPx(pos + sprite.Size);
|
||||
Game.Renderer.RgbaColorRenderer.DrawRect(tl, br, 1, Color.Red);
|
||||
}
|
||||
|
||||
public Rectangle ScreenBounds(WorldRenderer wr)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -107,7 +107,7 @@ namespace OpenRA.Graphics
|
||||
internal void DrawSprite(Sprite s, float3 location, float paletteTextureIndex, float3 size)
|
||||
{
|
||||
var samplers = SetRenderStateForSprite(s);
|
||||
Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, samplers, paletteTextureIndex, nv, size);
|
||||
Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, samplers, paletteTextureIndex, nv, size, float3.Ones);
|
||||
nv += 6;
|
||||
}
|
||||
|
||||
@@ -124,7 +124,26 @@ namespace OpenRA.Graphics
|
||||
public void DrawSprite(Sprite s, float3 a, float3 b, float3 c, float3 d)
|
||||
{
|
||||
var samplers = SetRenderStateForSprite(s);
|
||||
Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, 0, nv);
|
||||
Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, 0, float3.Ones, nv);
|
||||
nv += 6;
|
||||
}
|
||||
|
||||
internal void DrawSpriteWithTint(Sprite s, float3 location, float paletteTextureIndex, float3 size, float3 tint)
|
||||
{
|
||||
var samplers = SetRenderStateForSprite(s);
|
||||
Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, samplers, paletteTextureIndex, nv, size, tint);
|
||||
nv += 6;
|
||||
}
|
||||
|
||||
public void DrawSpriteWithTint(Sprite s, float3 location, PaletteReference pal, float3 size, float3 tint)
|
||||
{
|
||||
DrawSpriteWithTint(s, location, pal.TextureIndex, size, tint);
|
||||
}
|
||||
|
||||
public void DrawSpriteWithTint(Sprite s, float3 a, float3 b, float3 c, float3 d, float3 tint)
|
||||
{
|
||||
var samplers = SetRenderStateForSprite(s);
|
||||
Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, 0, tint, nv);
|
||||
nv += 6;
|
||||
}
|
||||
|
||||
@@ -155,22 +174,27 @@ namespace OpenRA.Graphics
|
||||
shader.SetTexture("Palette", palette);
|
||||
}
|
||||
|
||||
public void SetViewportParams(Size screen, float depthScale, float depthOffset, float zoom, int2 scroll)
|
||||
public void SetViewportParams(Size screen, float depthScale, float depthOffset, int2 scroll)
|
||||
{
|
||||
shader.SetVec("Scroll", scroll.X, scroll.Y, scroll.Y);
|
||||
shader.SetVec("r1",
|
||||
zoom * 2f / screen.Width,
|
||||
-zoom * 2f / screen.Height,
|
||||
-depthScale * zoom / screen.Height);
|
||||
shader.SetVec("r2", -1, 1, 1 - depthOffset);
|
||||
2f / screen.Width,
|
||||
2f / screen.Height,
|
||||
-depthScale / screen.Height);
|
||||
shader.SetVec("r2", -1, -1, 1 - depthOffset);
|
||||
|
||||
// Texture index is sampled as a float, so convert to pixels then scale
|
||||
shader.SetVec("DepthTextureScale", 128 * depthScale * zoom / screen.Height);
|
||||
shader.SetVec("DepthTextureScale", 128 * depthScale / screen.Height);
|
||||
}
|
||||
|
||||
public void SetDepthPreviewEnabled(bool enabled)
|
||||
{
|
||||
shader.SetBool("EnableDepthPreview", enabled);
|
||||
}
|
||||
|
||||
public void SetAntialiasingPixelsPerTexel(float pxPerTx)
|
||||
{
|
||||
shader.SetVec("AntialiasPixelsPerTexel", pxPerTx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -46,12 +46,11 @@ namespace OpenRA.Graphics
|
||||
if (!waypoints.Any())
|
||||
return;
|
||||
|
||||
var sw = width / wr.Viewport.Zoom;
|
||||
var first = wr.Screen3DPosition(waypoints.First());
|
||||
var first = wr.Viewport.WorldToViewPx(wr.Screen3DPosition(waypoints.First()));
|
||||
var a = first;
|
||||
foreach (var b in waypoints.Skip(1).Select(pos => wr.Screen3DPosition(pos)))
|
||||
foreach (var b in waypoints.Skip(1).Select(pos => wr.Viewport.WorldToViewPx(wr.Screen3DPosition(pos))))
|
||||
{
|
||||
Game.Renderer.WorldRgbaColorRenderer.DrawLine(a, b, sw, color);
|
||||
Game.Renderer.RgbaColorRenderer.DrawLine(a, b, width, color);
|
||||
DrawTargetMarker(wr, color, b, markerSize);
|
||||
a = b;
|
||||
}
|
||||
@@ -59,13 +58,12 @@ namespace OpenRA.Graphics
|
||||
DrawTargetMarker(wr, color, first);
|
||||
}
|
||||
|
||||
public static void DrawTargetMarker(WorldRenderer wr, Color color, float3 location, int size = 1)
|
||||
public static void DrawTargetMarker(WorldRenderer wr, Color color, int2 screenPos, int size = 1)
|
||||
{
|
||||
var sw = size / wr.Viewport.Zoom;
|
||||
var offset = new float2(sw, sw);
|
||||
var tl = location - offset;
|
||||
var br = location + offset;
|
||||
Game.Renderer.WorldRgbaColorRenderer.FillRect(tl, br, color);
|
||||
var offset = new int2(size, size);
|
||||
var tl = screenPos - offset;
|
||||
var br = screenPos + offset;
|
||||
Game.Renderer.RgbaColorRenderer.FillRect(tl, br, color);
|
||||
}
|
||||
|
||||
public void RenderDebugGeometry(WorldRenderer wr) { }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -18,6 +18,8 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
public sealed class TerrainSpriteLayer : IDisposable
|
||||
{
|
||||
static readonly int[] CornerVertexMap = { 0, 1, 2, 2, 3, 0 };
|
||||
|
||||
public readonly Sheet Sheet;
|
||||
public readonly BlendMode BlendMode;
|
||||
|
||||
@@ -25,6 +27,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
readonly IVertexBuffer<Vertex> vertexBuffer;
|
||||
readonly Vertex[] vertices;
|
||||
readonly bool[] ignoreTint;
|
||||
readonly HashSet<int> dirtyRows = new HashSet<int>();
|
||||
readonly int rowStride;
|
||||
readonly bool restrictToBounds;
|
||||
@@ -50,6 +53,12 @@ namespace OpenRA.Graphics
|
||||
emptySprite = new Sprite(sheet, Rectangle.Empty, TextureChannel.Alpha);
|
||||
|
||||
wr.PaletteInvalidated += UpdatePaletteIndices;
|
||||
|
||||
if (wr.TerrainLighting != null)
|
||||
{
|
||||
ignoreTint = new bool[rowStride * map.MapSize.Y];
|
||||
wr.TerrainLighting.CellChanged += UpdateTint;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdatePaletteIndices()
|
||||
@@ -59,22 +68,76 @@ namespace OpenRA.Graphics
|
||||
for (var i = 0; i < vertices.Length; i++)
|
||||
{
|
||||
var v = vertices[i];
|
||||
vertices[i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C);
|
||||
vertices[i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C, v.R, v.G, v.B);
|
||||
}
|
||||
|
||||
for (var row = 0; row < map.MapSize.Y; row++)
|
||||
dirtyRows.Add(row);
|
||||
}
|
||||
|
||||
public void Update(CPos cell, Sprite sprite)
|
||||
public void Clear(CPos cell)
|
||||
{
|
||||
var xyz = sprite == null ? float3.Zero :
|
||||
worldRenderer.Screen3DPosition(map.CenterOfCell(cell)) + sprite.Offset - 0.5f * sprite.Size;
|
||||
|
||||
Update(cell.ToMPos(map.Grid.Type), sprite, xyz);
|
||||
Update(cell, null, true);
|
||||
}
|
||||
|
||||
public void Update(MPos uv, Sprite sprite, float3 pos)
|
||||
public void Update(CPos cell, ISpriteSequence sequence, int frame)
|
||||
{
|
||||
Update(cell, sequence.GetSprite(frame), sequence.IgnoreWorldTint);
|
||||
}
|
||||
|
||||
public void Update(CPos cell, Sprite sprite, bool ignoreTint)
|
||||
{
|
||||
var xyz = float3.Zero;
|
||||
if (sprite != null)
|
||||
{
|
||||
var cellOrigin = map.CenterOfCell(cell) - new WVec(0, 0, map.Grid.Ramps[map.Ramp[cell]].CenterHeightOffset);
|
||||
xyz = worldRenderer.Screen3DPosition(cellOrigin) + sprite.Offset - 0.5f * sprite.Size;
|
||||
}
|
||||
|
||||
Update(cell.ToMPos(map.Grid.Type), sprite, xyz, ignoreTint);
|
||||
}
|
||||
|
||||
void UpdateTint(MPos uv)
|
||||
{
|
||||
var offset = rowStride * uv.V + 6 * uv.U;
|
||||
if (ignoreTint[offset])
|
||||
{
|
||||
var noTint = float3.Ones;
|
||||
for (var i = 0; i < 6; i++)
|
||||
{
|
||||
var v = vertices[offset + i];
|
||||
vertices[offset + i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C, noTint);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow the terrain tint to vary linearly across the cell to smooth out the staircase effect
|
||||
// This is done by sampling the lighting the corners of the sprite, even though those pixels are
|
||||
// transparent for isometric tiles
|
||||
var tl = worldRenderer.TerrainLighting;
|
||||
var pos = map.CenterOfCell(uv.ToCPos(map));
|
||||
var step = map.Grid.Type == MapGridType.RectangularIsometric ? 724 : 512;
|
||||
var weights = new[]
|
||||
{
|
||||
tl.TintAt(pos + new WVec(-step, -step, 0)),
|
||||
tl.TintAt(pos + new WVec(step, -step, 0)),
|
||||
tl.TintAt(pos + new WVec(step, step, 0)),
|
||||
tl.TintAt(pos + new WVec(-step, step, 0))
|
||||
};
|
||||
|
||||
// Apply tint directly to the underlying vertices
|
||||
// This saves us from having to re-query the sprite information, which has not changed
|
||||
for (var i = 0; i < 6; i++)
|
||||
{
|
||||
var v = vertices[offset + i];
|
||||
vertices[offset + i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C, weights[CornerVertexMap[i]]);
|
||||
}
|
||||
|
||||
dirtyRows.Add(uv.V);
|
||||
}
|
||||
|
||||
public void Update(MPos uv, Sprite sprite, float3 pos, bool ignoreTint)
|
||||
{
|
||||
if (sprite != null)
|
||||
{
|
||||
@@ -92,7 +155,13 @@ namespace OpenRA.Graphics
|
||||
return;
|
||||
|
||||
var offset = rowStride * uv.V + 6 * uv.U;
|
||||
Util.FastCreateQuad(vertices, pos, sprite, int2.Zero, palette.TextureIndex, offset, sprite.Size);
|
||||
Util.FastCreateQuad(vertices, pos, sprite, int2.Zero, palette.TextureIndex, offset, sprite.Size, float3.Ones);
|
||||
|
||||
if (worldRenderer.TerrainLighting != null)
|
||||
{
|
||||
this.ignoreTint[offset] = ignoreTint;
|
||||
UpdateTint(uv);
|
||||
}
|
||||
|
||||
dirtyRows.Add(uv.V);
|
||||
}
|
||||
@@ -135,6 +204,9 @@ namespace OpenRA.Graphics
|
||||
public void Dispose()
|
||||
{
|
||||
worldRenderer.PaletteInvalidated -= UpdatePaletteIndices;
|
||||
if (worldRenderer.TerrainLighting != null)
|
||||
worldRenderer.TerrainLighting.CellChanged -= UpdateTint;
|
||||
|
||||
vertexBuffer.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Support;
|
||||
@@ -34,7 +35,7 @@ namespace OpenRA.Graphics
|
||||
public sealed class Theater : IDisposable
|
||||
{
|
||||
readonly Dictionary<ushort, TheaterTemplate> templates = new Dictionary<ushort, TheaterTemplate>();
|
||||
readonly SheetBuilder sheetBuilder;
|
||||
SheetBuilder sheetBuilder;
|
||||
readonly Sprite missingTile;
|
||||
readonly MersenneTwister random;
|
||||
TileSet tileset;
|
||||
@@ -53,7 +54,6 @@ namespace OpenRA.Graphics
|
||||
return new Sheet(SheetType.Indexed, new Size(tileset.SheetSize, tileset.SheetSize));
|
||||
};
|
||||
|
||||
sheetBuilder = new SheetBuilder(SheetType.Indexed, allocate);
|
||||
random = new MersenneTwister();
|
||||
|
||||
var frameCache = new FrameCache(Game.ModData.DefaultFileSystem, Game.ModData.SpriteLoaders);
|
||||
@@ -75,6 +75,15 @@ namespace OpenRA.Graphics
|
||||
var zOffset = tile != null ? -tile.ZOffset : 0;
|
||||
var zRamp = tile != null ? tile.ZRamp : 1f;
|
||||
var offset = new float3(f.Offset, zOffset);
|
||||
var type = SheetBuilder.FrameTypeToSheetType(f.Type);
|
||||
|
||||
// Defer SheetBuilder creation until we know what type of frames we are loading!
|
||||
// TODO: Support mixed indexed and BGRA frames
|
||||
if (sheetBuilder == null)
|
||||
sheetBuilder = new SheetBuilder(SheetBuilder.FrameTypeToSheetType(f.Type), allocate);
|
||||
else if (type != sheetBuilder.Type)
|
||||
throw new InvalidDataException("Sprite type mismatch. Terrain sprites must all be either Indexed or RGBA.");
|
||||
|
||||
var s = sheetBuilder.Allocate(f.Size, zRamp, offset);
|
||||
Util.FastCopyIntoChannel(s, f.Data);
|
||||
|
||||
@@ -102,15 +111,14 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
|
||||
// 1x1px transparent tile
|
||||
missingTile = sheetBuilder.Add(new byte[1], new Size(1, 1));
|
||||
missingTile = sheetBuilder.Add(new byte[sheetBuilder.Type == SheetType.BGRA ? 4 : 1], new Size(1, 1));
|
||||
|
||||
Sheet.ReleaseBuffer();
|
||||
}
|
||||
|
||||
public Sprite TileSprite(TerrainTile r, int? variant = null)
|
||||
{
|
||||
TheaterTemplate template;
|
||||
if (!templates.TryGetValue(r.Type, out template))
|
||||
if (!templates.TryGetValue(r.Type, out var template))
|
||||
return missingTile;
|
||||
|
||||
if (r.Index >= template.Stride)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -20,15 +20,18 @@ namespace OpenRA.Graphics
|
||||
// yes, our channel order is nuts.
|
||||
static readonly int[] ChannelMasks = { 2, 1, 0, 3 };
|
||||
|
||||
public static void FastCreateQuad(Vertex[] vertices, float3 o, Sprite r, int2 samplers, float paletteTextureIndex, int nv, float3 size)
|
||||
public static void FastCreateQuad(Vertex[] vertices, float3 o, Sprite r, int2 samplers, float paletteTextureIndex, int nv, float3 size, float3 tint)
|
||||
{
|
||||
var b = new float3(o.X + size.X, o.Y, o.Z);
|
||||
var c = new float3(o.X + size.X, o.Y + size.Y, o.Z + size.Z);
|
||||
var d = new float3(o.X, o.Y + size.Y, o.Z + size.Z);
|
||||
FastCreateQuad(vertices, o, b, c, d, r, samplers, paletteTextureIndex, nv);
|
||||
FastCreateQuad(vertices, o, b, c, d, r, samplers, paletteTextureIndex, tint, nv);
|
||||
}
|
||||
|
||||
public static void FastCreateQuad(Vertex[] vertices, float3 a, float3 b, float3 c, float3 d, Sprite r, int2 samplers, float paletteTextureIndex, int nv)
|
||||
public static void FastCreateQuad(Vertex[] vertices,
|
||||
float3 a, float3 b, float3 c, float3 d,
|
||||
Sprite r, int2 samplers, float paletteTextureIndex,
|
||||
float3 tint, int nv)
|
||||
{
|
||||
float sl = 0;
|
||||
float st = 0;
|
||||
@@ -51,33 +54,66 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
|
||||
var fAttribC = (float)attribC;
|
||||
vertices[nv] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC);
|
||||
vertices[nv + 1] = new Vertex(b, r.Right, r.Top, sr, st, paletteTextureIndex, fAttribC);
|
||||
vertices[nv + 2] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC);
|
||||
vertices[nv + 3] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC);
|
||||
vertices[nv + 4] = new Vertex(d, r.Left, r.Bottom, sl, sb, paletteTextureIndex, fAttribC);
|
||||
vertices[nv + 5] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC);
|
||||
vertices[nv] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC, tint);
|
||||
vertices[nv + 1] = new Vertex(b, r.Right, r.Top, sr, st, paletteTextureIndex, fAttribC, tint);
|
||||
vertices[nv + 2] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC, tint);
|
||||
vertices[nv + 3] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC, tint);
|
||||
vertices[nv + 4] = new Vertex(d, r.Left, r.Bottom, sl, sb, paletteTextureIndex, fAttribC, tint);
|
||||
vertices[nv + 5] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC, tint);
|
||||
}
|
||||
|
||||
public static void FastCopyIntoChannel(Sprite dest, byte[] src)
|
||||
{
|
||||
var data = dest.Sheet.GetData();
|
||||
var srcStride = dest.Bounds.Width;
|
||||
var destStride = dest.Sheet.Size.Width * 4;
|
||||
var destOffset = destStride * dest.Bounds.Top + dest.Bounds.Left * 4 + ChannelMasks[(int)dest.Channel];
|
||||
var destSkip = destStride - 4 * srcStride;
|
||||
var destData = dest.Sheet.GetData();
|
||||
var width = dest.Bounds.Width;
|
||||
var height = dest.Bounds.Height;
|
||||
|
||||
var srcOffset = 0;
|
||||
for (var j = 0; j < height; j++)
|
||||
if (dest.Channel == TextureChannel.RGBA)
|
||||
{
|
||||
for (var i = 0; i < srcStride; i++, srcOffset++)
|
||||
var destStride = dest.Sheet.Size.Width;
|
||||
unsafe
|
||||
{
|
||||
data[destOffset] = src[srcOffset];
|
||||
destOffset += 4;
|
||||
}
|
||||
// Cast the data to an int array so we can copy the src data directly
|
||||
fixed (byte* bd = &destData[0])
|
||||
{
|
||||
var data = (int*)bd;
|
||||
var x = dest.Bounds.Left;
|
||||
var y = dest.Bounds.Top;
|
||||
|
||||
destOffset += destSkip;
|
||||
var k = 0;
|
||||
for (var j = 0; j < height; j++)
|
||||
{
|
||||
for (var i = 0; i < width; i++)
|
||||
{
|
||||
var r = src[k++];
|
||||
var g = src[k++];
|
||||
var b = src[k++];
|
||||
var a = src[k++];
|
||||
var cc = Color.FromArgb(a, r, g, b);
|
||||
|
||||
data[(y + j) * destStride + x + i] = PremultiplyAlpha(cc).ToArgb();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var destStride = dest.Sheet.Size.Width * 4;
|
||||
var destOffset = destStride * dest.Bounds.Top + dest.Bounds.Left * 4 + ChannelMasks[(int)dest.Channel];
|
||||
var destSkip = destStride - 4 * width;
|
||||
|
||||
var srcOffset = 0;
|
||||
for (var j = 0; j < height; j++)
|
||||
{
|
||||
for (var i = 0; i < width; i++, srcOffset++)
|
||||
{
|
||||
destData[destOffset] = src[srcOffset];
|
||||
destOffset += 4;
|
||||
}
|
||||
|
||||
destOffset += destSkip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -16,17 +16,34 @@ namespace OpenRA.Graphics
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Vertex
|
||||
{
|
||||
public readonly float X, Y, Z, S, T, U, V, P, C;
|
||||
// 3d position
|
||||
public readonly float X, Y, Z;
|
||||
|
||||
// Primary and secondary texture coordinates or RGBA color
|
||||
public readonly float S, T, U, V;
|
||||
|
||||
// Palette and channel flags
|
||||
public readonly float P, C;
|
||||
|
||||
// Color tint
|
||||
public readonly float R, G, B;
|
||||
|
||||
public Vertex(float3 xyz, float s, float t, float u, float v, float p, float c)
|
||||
: this(xyz.X, xyz.Y, xyz.Z, s, t, u, v, p, c) { }
|
||||
: this(xyz.X, xyz.Y, xyz.Z, s, t, u, v, p, c, float3.Ones) { }
|
||||
|
||||
public Vertex(float x, float y, float z, float s, float t, float u, float v, float p, float c)
|
||||
public Vertex(float3 xyz, float s, float t, float u, float v, float p, float c, float3 tint)
|
||||
: this(xyz.X, xyz.Y, xyz.Z, s, t, u, v, p, c, tint.X, tint.Y, tint.Z) { }
|
||||
|
||||
public Vertex(float x, float y, float z, float s, float t, float u, float v, float p, float c, float3 tint)
|
||||
: this(x, y, z, s, t, u, v, p, c, tint.X, tint.Y, tint.Z) { }
|
||||
|
||||
public Vertex(float x, float y, float z, float s, float t, float u, float v, float p, float c, float r, float g, float b)
|
||||
{
|
||||
X = x; Y = y; Z = z;
|
||||
S = s; T = t;
|
||||
U = u; V = v;
|
||||
P = p; C = c;
|
||||
R = r; G = g; B = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -19,6 +19,11 @@ namespace OpenRA.Graphics
|
||||
[Flags]
|
||||
public enum ScrollDirection { None = 0, Up = 1, Left = 2, Down = 4, Right = 8 }
|
||||
|
||||
public interface INotifyViewportZoomExtentsChanged
|
||||
{
|
||||
void ViewportZoomExtentsChanged(float minZoom, float maxZoom);
|
||||
}
|
||||
|
||||
public static class ViewportExts
|
||||
{
|
||||
public static bool Includes(this ScrollDirection d, ScrollDirection s)
|
||||
@@ -36,6 +41,8 @@ namespace OpenRA.Graphics
|
||||
public class Viewport
|
||||
{
|
||||
readonly WorldRenderer worldRenderer;
|
||||
readonly WorldViewportSizes viewportSizes;
|
||||
readonly GraphicSettings graphicSettings;
|
||||
|
||||
// Map bounds (world-px)
|
||||
readonly Rectangle mapBounds;
|
||||
@@ -46,6 +53,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public WPos CenterPosition { get { return worldRenderer.ProjectedPosition(CenterLocation); } }
|
||||
|
||||
public Rectangle Rectangle { get { return new Rectangle(TopLeft, new Size(viewportSize.X, viewportSize.Y)); } }
|
||||
public int2 TopLeft { get { return CenterLocation - viewportSize / 2; } }
|
||||
public int2 BottomRight { get { return CenterLocation + viewportSize / 2; } }
|
||||
int2 viewportSize;
|
||||
@@ -54,14 +62,16 @@ namespace OpenRA.Graphics
|
||||
|
||||
ProjectedCellRegion allCells;
|
||||
bool allCellsDirty = true;
|
||||
readonly float[] availableZoomSteps = new[] { 2f, 1f, 0.5f, 0.25f };
|
||||
|
||||
WorldViewport lastViewportDistance;
|
||||
|
||||
float zoom = 1f;
|
||||
float minZoom = 1f;
|
||||
float maxZoom = 2f;
|
||||
|
||||
public float[] AvailableZoomSteps
|
||||
{
|
||||
get { return availableZoomSteps; }
|
||||
}
|
||||
bool unlockMinZoom;
|
||||
float unlockedMinZoomScale;
|
||||
float unlockedMinZoom = 1f;
|
||||
|
||||
public float Zoom
|
||||
{
|
||||
@@ -70,16 +80,47 @@ namespace OpenRA.Graphics
|
||||
return zoom;
|
||||
}
|
||||
|
||||
set
|
||||
private set
|
||||
{
|
||||
var newValue = ClosestTo(AvailableZoomSteps, value);
|
||||
zoom = newValue;
|
||||
viewportSize = (1f / zoom * new float2(Game.Renderer.Resolution)).ToInt2();
|
||||
zoom = value;
|
||||
viewportSize = (1f / zoom * new float2(Game.Renderer.NativeResolution)).ToInt2();
|
||||
cellsDirty = true;
|
||||
allCellsDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
public float MinZoom { get { return minZoom; } }
|
||||
|
||||
public void AdjustZoom(float dz)
|
||||
{
|
||||
// Exponential ensures that equal positive and negative steps have the same effect
|
||||
Zoom = (zoom * (float)Math.Exp(dz)).Clamp(unlockMinZoom ? unlockedMinZoom : minZoom, maxZoom);
|
||||
}
|
||||
|
||||
public void AdjustZoom(float dz, int2 center)
|
||||
{
|
||||
var oldCenter = worldRenderer.Viewport.ViewToWorldPx(center);
|
||||
AdjustZoom(dz);
|
||||
var newCenter = worldRenderer.Viewport.ViewToWorldPx(center);
|
||||
CenterLocation += oldCenter - newCenter;
|
||||
}
|
||||
|
||||
public void ToggleZoom()
|
||||
{
|
||||
// Unlocked zooms always reset to the default zoom
|
||||
if (zoom < minZoom)
|
||||
Zoom = minZoom;
|
||||
else
|
||||
Zoom = zoom > minZoom ? minZoom : maxZoom;
|
||||
}
|
||||
|
||||
public void UnlockMinimumZoom(float scale)
|
||||
{
|
||||
unlockMinZoom = true;
|
||||
unlockedMinZoomScale = scale;
|
||||
UpdateViewportZooms(false);
|
||||
}
|
||||
|
||||
public static long LastMoveRunTime = 0;
|
||||
public static int2 LastMousePos;
|
||||
|
||||
@@ -119,6 +160,8 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
worldRenderer = wr;
|
||||
var grid = Game.ModData.Manifest.Get<MapGrid>();
|
||||
viewportSizes = Game.ModData.Manifest.Get<WorldViewportSizes>();
|
||||
graphicSettings = Game.Settings.Graphics;
|
||||
|
||||
// Calculate map bounds in world-px
|
||||
if (wr.World.Type == WorldType.Editor)
|
||||
@@ -140,8 +183,78 @@ namespace OpenRA.Graphics
|
||||
CenterLocation = (tl + br) / 2;
|
||||
}
|
||||
|
||||
Zoom = Game.Settings.Graphics.PixelDouble ? 2 : 1;
|
||||
tileSize = grid.TileSize;
|
||||
|
||||
UpdateViewportZooms();
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
if (lastViewportDistance != graphicSettings.ViewportDistance)
|
||||
UpdateViewportZooms();
|
||||
}
|
||||
|
||||
float CalculateMinimumZoom(float minHeight, float maxHeight)
|
||||
{
|
||||
var h = Game.Renderer.NativeResolution.Height;
|
||||
|
||||
// Check the easy case: the native resolution is within the maximum limit
|
||||
// Also catches the case where the user may force a resolution smaller than the minimum window size
|
||||
if (h <= maxHeight)
|
||||
return 1;
|
||||
|
||||
// Find a clean fraction that brings us within the desired range to reduce aliasing
|
||||
var step = 1f;
|
||||
while (true)
|
||||
{
|
||||
var testZoom = 1f;
|
||||
while (true)
|
||||
{
|
||||
var nextZoom = testZoom + step;
|
||||
if (h < minHeight * nextZoom)
|
||||
break;
|
||||
|
||||
testZoom = nextZoom;
|
||||
}
|
||||
|
||||
if (h < maxHeight * testZoom)
|
||||
return testZoom;
|
||||
|
||||
step /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateViewportZooms(bool resetCurrentZoom = true)
|
||||
{
|
||||
lastViewportDistance = graphicSettings.ViewportDistance;
|
||||
|
||||
var vd = graphicSettings.ViewportDistance;
|
||||
if (viewportSizes.AllowNativeZoom && vd == WorldViewport.Native)
|
||||
minZoom = 1;
|
||||
else
|
||||
{
|
||||
var range = viewportSizes.GetSizeRange(vd);
|
||||
minZoom = CalculateMinimumZoom(range.X, range.Y);
|
||||
}
|
||||
|
||||
maxZoom = Math.Min(minZoom * viewportSizes.MaxZoomScale, Game.Renderer.NativeResolution.Height * 1f / viewportSizes.MaxZoomWindowHeight);
|
||||
|
||||
if (unlockMinZoom)
|
||||
{
|
||||
// Specators and the map editor support zooming out by an extra factor of two.
|
||||
// TODO: Allow zooming out until the full map is visible
|
||||
// We need to improve our viewport scroll handling to center the map as we zoom out
|
||||
// before this will work well enough to enable
|
||||
unlockedMinZoom = minZoom * unlockedMinZoomScale;
|
||||
}
|
||||
|
||||
if (resetCurrentZoom)
|
||||
Zoom = minZoom;
|
||||
else
|
||||
Zoom = Zoom.Clamp(minZoom, maxZoom);
|
||||
|
||||
foreach (var t in worldRenderer.World.WorldActor.TraitsImplementing<INotifyViewportZoomExtentsChanged>())
|
||||
t.ViewportZoomExtentsChanged(minZoom, maxZoom);
|
||||
}
|
||||
|
||||
public CPos ViewToWorld(int2 view)
|
||||
@@ -149,7 +262,6 @@ namespace OpenRA.Graphics
|
||||
var world = worldRenderer.Viewport.ViewToWorldPx(view);
|
||||
var map = worldRenderer.World.Map;
|
||||
var candidates = CandidateMouseoverCells(world).ToList();
|
||||
var tileSet = worldRenderer.World.Map.Rules.TileSet;
|
||||
|
||||
foreach (var uv in candidates)
|
||||
{
|
||||
@@ -158,18 +270,9 @@ namespace OpenRA.Graphics
|
||||
var s = worldRenderer.ScreenPxPosition(p);
|
||||
if (Math.Abs(s.X - world.X) <= tileSize.Width && Math.Abs(s.Y - world.Y) <= tileSize.Height)
|
||||
{
|
||||
var ramp = 0;
|
||||
if (map.Contains(uv))
|
||||
{
|
||||
var ti = tileSet.GetTileInfo(map.Tiles[uv]);
|
||||
if (ti != null)
|
||||
ramp = ti.RampType;
|
||||
}
|
||||
|
||||
var corners = map.Grid.CellCorners[ramp];
|
||||
var pos = map.CenterOfCell(uv.ToCPos(map));
|
||||
var screen = corners.Select(c => worldRenderer.ScreenPxPosition(pos + c)).ToArray();
|
||||
|
||||
var ramp = map.Grid.Ramps[map.Ramp.Contains(uv) ? map.Ramp[uv] : 0];
|
||||
var pos = map.CenterOfCell(uv.ToCPos(map)) - new WVec(0, 0, ramp.CenterHeightOffset);
|
||||
var screen = ramp.Corners.Select(c => worldRenderer.ScreenPxPosition(pos + c)).ToArray();
|
||||
if (screen.PolygonContains(world))
|
||||
return uv.ToCPos(map);
|
||||
}
|
||||
@@ -209,8 +312,9 @@ namespace OpenRA.Graphics
|
||||
yield return new MPos(u, v);
|
||||
}
|
||||
|
||||
public int2 ViewToWorldPx(int2 view) { return (1f / Zoom * view.ToFloat2()).ToInt2() + TopLeft; }
|
||||
public int2 WorldToViewPx(int2 world) { return (Zoom * (world - TopLeft).ToFloat2()).ToInt2(); }
|
||||
public int2 ViewToWorldPx(int2 view) { return (graphicSettings.UIScale / Zoom * view.ToFloat2()).ToInt2() + TopLeft; }
|
||||
public int2 WorldToViewPx(int2 world) { return ((Zoom / graphicSettings.UIScale) * (world - TopLeft).ToFloat2()).ToInt2(); }
|
||||
public int2 WorldToViewPx(float3 world) { return ((Zoom / graphicSettings.UIScale) * (world - TopLeft).XY).ToInt2(); }
|
||||
|
||||
public void Center(IEnumerable<Actor> actors)
|
||||
{
|
||||
@@ -239,7 +343,6 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
|
||||
// Rectangle (in viewport coords) that contains things to be drawn
|
||||
static readonly Rectangle ScreenClip = Rectangle.FromLTRB(0, 0, Game.Renderer.Resolution.Width, Game.Renderer.Resolution.Height);
|
||||
public Rectangle GetScissorBounds(bool insideBounds)
|
||||
{
|
||||
// Visible rectangle in world coordinates (expanded to the corners of the cells)
|
||||
@@ -249,12 +352,12 @@ namespace OpenRA.Graphics
|
||||
var cbr = map.CenterOfCell(((MPos)bounds.BottomRight).ToCPos(map)) + new WVec(512, 512, 0);
|
||||
|
||||
// Convert to screen coordinates
|
||||
var tl = WorldToViewPx(worldRenderer.ScreenPxPosition(ctl - new WVec(0, 0, ctl.Z))).Clamp(ScreenClip);
|
||||
var br = WorldToViewPx(worldRenderer.ScreenPxPosition(cbr - new WVec(0, 0, cbr.Z))).Clamp(ScreenClip);
|
||||
var tl = worldRenderer.ScreenPxPosition(ctl - new WVec(0, 0, ctl.Z)) - TopLeft;
|
||||
var br = worldRenderer.ScreenPxPosition(cbr - new WVec(0, 0, cbr.Z)) - TopLeft;
|
||||
|
||||
// Add an extra one cell fudge in each direction for safety
|
||||
return Rectangle.FromLTRB(tl.X - tileSize.Width, tl.Y - tileSize.Height,
|
||||
br.X + tileSize.Width, br.Y + tileSize.Height);
|
||||
// Add an extra half-cell fudge to avoid clipping isometric tiles
|
||||
return Rectangle.FromLTRB(tl.X - tileSize.Width / 2, tl.Y - tileSize.Height / 2,
|
||||
br.X + tileSize.Width / 2, br.Y + tileSize.Height / 2);
|
||||
}
|
||||
|
||||
ProjectedCellRegion CalculateVisibleCells(bool insideBounds)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -28,6 +28,7 @@ namespace OpenRA.Graphics
|
||||
public readonly World World;
|
||||
public readonly Theater Theater;
|
||||
public Viewport Viewport { get; private set; }
|
||||
public readonly ITerrainLighting TerrainLighting;
|
||||
|
||||
public event Action PaletteInvalidated = null;
|
||||
|
||||
@@ -39,6 +40,12 @@ namespace OpenRA.Graphics
|
||||
readonly Func<string, PaletteReference> createPaletteReference;
|
||||
readonly bool enableDepthBuffer;
|
||||
|
||||
readonly List<IFinalizedRenderable> preparedRenderables = new List<IFinalizedRenderable>();
|
||||
readonly List<IFinalizedRenderable> preparedOverlayRenderables = new List<IFinalizedRenderable>();
|
||||
readonly List<IFinalizedRenderable> preparedAnnotationRenderables = new List<IFinalizedRenderable>();
|
||||
|
||||
readonly List<IRenderable> renderablesBuffer = new List<IRenderable>();
|
||||
|
||||
bool lastDepthPreviewEnabled;
|
||||
|
||||
internal WorldRenderer(ModData modData, World world)
|
||||
@@ -62,6 +69,7 @@ namespace OpenRA.Graphics
|
||||
palette.Initialize();
|
||||
|
||||
Theater = new Theater(world.Map.Rules.TileSet);
|
||||
TerrainLighting = world.WorldActor.TraitOrDefault<ITerrainLighting>();
|
||||
terrainRenderer = world.WorldActor.TraitOrDefault<IRenderTerrain>();
|
||||
|
||||
debugVis = Exts.Lazy(() => world.WorldActor.TraitOrDefault<DebugVisualizations>());
|
||||
@@ -89,8 +97,8 @@ namespace OpenRA.Graphics
|
||||
var oldHeight = palette.Height;
|
||||
palette.AddPalette(name, pal, allowModifiers);
|
||||
|
||||
if (oldHeight != palette.Height && PaletteInvalidated != null)
|
||||
PaletteInvalidated();
|
||||
if (oldHeight != palette.Height)
|
||||
PaletteInvalidated?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,61 +111,134 @@ namespace OpenRA.Graphics
|
||||
palettes[name].Palette = pal;
|
||||
}
|
||||
|
||||
List<IFinalizedRenderable> GenerateRenderables()
|
||||
// PERF: Avoid LINQ.
|
||||
void GenerateRenderables()
|
||||
{
|
||||
var actors = onScreenActors.Append(World.WorldActor);
|
||||
if (World.RenderPlayer != null)
|
||||
actors = actors.Append(World.RenderPlayer.PlayerActor);
|
||||
foreach (var actor in onScreenActors)
|
||||
renderablesBuffer.AddRange(actor.Render(this));
|
||||
|
||||
renderablesBuffer.AddRange(World.WorldActor.Render(this));
|
||||
|
||||
if (World.RenderPlayer != null)
|
||||
renderablesBuffer.AddRange(World.RenderPlayer.PlayerActor.Render(this));
|
||||
|
||||
var worldRenderables = actors.SelectMany(a => a.Render(this));
|
||||
if (World.OrderGenerator != null)
|
||||
worldRenderables = worldRenderables.Concat(World.OrderGenerator.Render(this, World));
|
||||
renderablesBuffer.AddRange(World.OrderGenerator.Render(this, World));
|
||||
|
||||
// Unpartitioned effects
|
||||
worldRenderables = worldRenderables.Concat(World.UnpartitionedEffects.SelectMany(e => e.Render(this)));
|
||||
foreach (var e in World.UnpartitionedEffects)
|
||||
renderablesBuffer.AddRange(e.Render(this));
|
||||
|
||||
// Partitioned, currently on-screen effects
|
||||
var effectRenderables = World.ScreenMap.RenderableEffectsInBox(Viewport.TopLeft, Viewport.BottomRight);
|
||||
worldRenderables = worldRenderables.Concat(effectRenderables.SelectMany(e => e.Render(this)));
|
||||
foreach (var e in World.ScreenMap.RenderableEffectsInBox(Viewport.TopLeft, Viewport.BottomRight))
|
||||
renderablesBuffer.AddRange(e.Render(this));
|
||||
|
||||
worldRenderables = worldRenderables.OrderBy(RenderableScreenZPositionComparisonKey);
|
||||
foreach (var renderable in renderablesBuffer.OrderBy(RenderableScreenZPositionComparisonKey))
|
||||
preparedRenderables.Add(renderable.PrepareRender(this));
|
||||
|
||||
Game.Renderer.WorldModelRenderer.BeginFrame();
|
||||
var renderables = worldRenderables.Select(r => r.PrepareRender(this)).ToList();
|
||||
Game.Renderer.WorldModelRenderer.EndFrame();
|
||||
|
||||
return renderables;
|
||||
// PERF: Reuse collection to avoid allocations.
|
||||
renderablesBuffer.Clear();
|
||||
}
|
||||
|
||||
List<IFinalizedRenderable> GenerateOverlayRenderables()
|
||||
// PERF: Avoid LINQ.
|
||||
void GenerateOverlayRenderables()
|
||||
{
|
||||
var aboveShroud = World.ActorsWithTrait<IRenderAboveShroud>()
|
||||
.Where(a => a.Actor.IsInWorld && !a.Actor.Disposed && (!a.Trait.SpatiallyPartitionable || onScreenActors.Contains(a.Actor)))
|
||||
.SelectMany(a => a.Trait.RenderAboveShroud(a.Actor, this));
|
||||
foreach (var a in World.ActorsWithTrait<IRenderAboveShroud>())
|
||||
{
|
||||
if (!a.Actor.IsInWorld || a.Actor.Disposed || (a.Trait.SpatiallyPartitionable && !onScreenActors.Contains(a.Actor)))
|
||||
continue;
|
||||
|
||||
var aboveShroudSelected = World.Selection.Actors.Where(a => a.IsInWorld && !a.Disposed)
|
||||
.SelectMany(a => a.TraitsImplementing<IRenderAboveShroudWhenSelected>()
|
||||
.Where(t => !t.SpatiallyPartitionable || onScreenActors.Contains(a))
|
||||
.SelectMany(t => t.RenderAboveShroud(a, this)));
|
||||
foreach (var renderable in a.Trait.RenderAboveShroud(a.Actor, this))
|
||||
preparedOverlayRenderables.Add(renderable.PrepareRender(this));
|
||||
}
|
||||
|
||||
var aboveShroudEffects = World.Effects.Select(e => e as IEffectAboveShroud)
|
||||
.Where(e => e != null)
|
||||
.SelectMany(e => e.RenderAboveShroud(this));
|
||||
foreach (var a in World.Selection.Actors)
|
||||
{
|
||||
if (!a.IsInWorld || a.Disposed)
|
||||
continue;
|
||||
|
||||
foreach (var t in a.TraitsImplementing<IRenderAboveShroudWhenSelected>())
|
||||
{
|
||||
if (t.SpatiallyPartitionable && !onScreenActors.Contains(a))
|
||||
continue;
|
||||
|
||||
foreach (var renderable in t.RenderAboveShroud(a, this))
|
||||
preparedOverlayRenderables.Add(renderable.PrepareRender(this));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var e in World.Effects)
|
||||
{
|
||||
var ea = e as IEffectAboveShroud;
|
||||
if (ea == null)
|
||||
continue;
|
||||
|
||||
foreach (var renderable in ea.RenderAboveShroud(this))
|
||||
preparedOverlayRenderables.Add(renderable.PrepareRender(this));
|
||||
}
|
||||
|
||||
var aboveShroudOrderGenerator = SpriteRenderable.None;
|
||||
if (World.OrderGenerator != null)
|
||||
aboveShroudOrderGenerator = World.OrderGenerator.RenderAboveShroud(this, World);
|
||||
foreach (var renderable in World.OrderGenerator.RenderAboveShroud(this, World))
|
||||
preparedOverlayRenderables.Add(renderable.PrepareRender(this));
|
||||
}
|
||||
|
||||
var overlayRenderables = aboveShroud
|
||||
.Concat(aboveShroudSelected)
|
||||
.Concat(aboveShroudEffects)
|
||||
.Concat(aboveShroudOrderGenerator);
|
||||
// PERF: Avoid LINQ.
|
||||
void GenerateAnnotationRenderables()
|
||||
{
|
||||
foreach (var a in World.ActorsWithTrait<IRenderAnnotations>())
|
||||
{
|
||||
if (!a.Actor.IsInWorld || a.Actor.Disposed || (a.Trait.SpatiallyPartitionable && !onScreenActors.Contains(a.Actor)))
|
||||
continue;
|
||||
|
||||
Game.Renderer.WorldModelRenderer.BeginFrame();
|
||||
var finalOverlayRenderables = overlayRenderables.Select(r => r.PrepareRender(this)).ToList();
|
||||
Game.Renderer.WorldModelRenderer.EndFrame();
|
||||
foreach (var renderAnnotation in a.Trait.RenderAnnotations(a.Actor, this))
|
||||
preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this));
|
||||
}
|
||||
|
||||
return finalOverlayRenderables;
|
||||
foreach (var a in World.Selection.Actors)
|
||||
{
|
||||
if (!a.IsInWorld || a.Disposed)
|
||||
continue;
|
||||
|
||||
foreach (var t in a.TraitsImplementing<IRenderAnnotationsWhenSelected>())
|
||||
{
|
||||
if (t.SpatiallyPartitionable && !onScreenActors.Contains(a))
|
||||
continue;
|
||||
|
||||
foreach (var renderAnnotation in t.RenderAnnotations(a, this))
|
||||
preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var e in World.Effects)
|
||||
{
|
||||
var ea = e as IEffectAnnotation;
|
||||
if (ea == null)
|
||||
continue;
|
||||
|
||||
foreach (var renderAnnotation in ea.RenderAnnotation(this))
|
||||
preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this));
|
||||
}
|
||||
|
||||
if (World.OrderGenerator != null)
|
||||
foreach (var renderAnnotation in World.OrderGenerator.RenderAnnotations(this, World))
|
||||
preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this));
|
||||
}
|
||||
|
||||
public void PrepareRenderables()
|
||||
{
|
||||
if (World.WorldActor.Disposed)
|
||||
return;
|
||||
|
||||
RefreshPalette();
|
||||
|
||||
// PERF: Reuse collection to avoid allocations.
|
||||
onScreenActors.UnionWith(World.ScreenMap.RenderableActorsInBox(Viewport.TopLeft, Viewport.BottomRight));
|
||||
|
||||
GenerateRenderables();
|
||||
GenerateOverlayRenderables();
|
||||
GenerateAnnotationRenderables();
|
||||
|
||||
onScreenActors.Clear();
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
@@ -171,23 +252,18 @@ namespace OpenRA.Graphics
|
||||
Game.Renderer.WorldSpriteRenderer.SetDepthPreviewEnabled(lastDepthPreviewEnabled);
|
||||
}
|
||||
|
||||
RefreshPalette();
|
||||
|
||||
onScreenActors.UnionWith(World.ScreenMap.RenderableActorsInBox(Viewport.TopLeft, Viewport.BottomRight));
|
||||
var renderables = GenerateRenderables();
|
||||
var bounds = Viewport.GetScissorBounds(World.Type != WorldType.Editor);
|
||||
Game.Renderer.EnableScissor(bounds);
|
||||
|
||||
if (enableDepthBuffer)
|
||||
Game.Renderer.Context.EnableDepthBuffer();
|
||||
|
||||
if (terrainRenderer != null)
|
||||
terrainRenderer.RenderTerrain(this, Viewport);
|
||||
terrainRenderer?.RenderTerrain(this, Viewport);
|
||||
|
||||
Game.Renderer.Flush();
|
||||
|
||||
for (var i = 0; i < renderables.Count; i++)
|
||||
renderables[i].Render(this);
|
||||
for (var i = 0; i < preparedRenderables.Count; i++)
|
||||
preparedRenderables[i].Render(this);
|
||||
|
||||
if (enableDepthBuffer)
|
||||
Game.Renderer.ClearDepthBuffer();
|
||||
@@ -196,56 +272,73 @@ namespace OpenRA.Graphics
|
||||
if (a.Actor.IsInWorld && !a.Actor.Disposed)
|
||||
a.Trait.RenderAboveWorld(a.Actor, this);
|
||||
|
||||
var renderShroud = World.RenderPlayer != null ? World.RenderPlayer.Shroud : null;
|
||||
|
||||
if (enableDepthBuffer)
|
||||
Game.Renderer.ClearDepthBuffer();
|
||||
|
||||
foreach (var a in World.ActorsWithTrait<IRenderShroud>())
|
||||
a.Trait.RenderShroud(renderShroud, this);
|
||||
a.Trait.RenderShroud(this);
|
||||
|
||||
if (enableDepthBuffer)
|
||||
Game.Renderer.Context.DisableDepthBuffer();
|
||||
|
||||
Game.Renderer.DisableScissor();
|
||||
|
||||
var finalOverlayRenderables = GenerateOverlayRenderables();
|
||||
|
||||
// HACK: Keep old grouping behaviour
|
||||
var groupedOverlayRenderables = finalOverlayRenderables.GroupBy(prs => prs.GetType());
|
||||
var groupedOverlayRenderables = preparedOverlayRenderables.GroupBy(prs => prs.GetType());
|
||||
foreach (var g in groupedOverlayRenderables)
|
||||
foreach (var r in g)
|
||||
r.Render(this);
|
||||
|
||||
Game.Renderer.Flush();
|
||||
}
|
||||
|
||||
public void DrawAnnotations()
|
||||
{
|
||||
Game.Renderer.EnableAntialiasingFilter();
|
||||
for (var i = 0; i < preparedAnnotationRenderables.Count; i++)
|
||||
preparedAnnotationRenderables[i].Render(this);
|
||||
Game.Renderer.DisableAntialiasingFilter();
|
||||
|
||||
// Engine debugging overlays
|
||||
if (debugVis.Value != null && debugVis.Value.RenderGeometry)
|
||||
{
|
||||
for (var i = 0; i < renderables.Count; i++)
|
||||
renderables[i].RenderDebugGeometry(this);
|
||||
for (var i = 0; i < preparedRenderables.Count; i++)
|
||||
preparedRenderables[i].RenderDebugGeometry(this);
|
||||
|
||||
foreach (var g in groupedOverlayRenderables)
|
||||
foreach (var r in g)
|
||||
r.RenderDebugGeometry(this);
|
||||
for (var i = 0; i < preparedOverlayRenderables.Count; i++)
|
||||
preparedOverlayRenderables[i].RenderDebugGeometry(this);
|
||||
|
||||
for (var i = 0; i < preparedAnnotationRenderables.Count; i++)
|
||||
preparedAnnotationRenderables[i].RenderDebugGeometry(this);
|
||||
}
|
||||
|
||||
if (debugVis.Value != null && debugVis.Value.ScreenMap)
|
||||
{
|
||||
foreach (var r in World.ScreenMap.RenderBounds(World.RenderPlayer))
|
||||
Game.Renderer.WorldRgbaColorRenderer.DrawRect(
|
||||
new float3(r.Left, r.Top, r.Bottom),
|
||||
new float3(r.Right, r.Bottom, r.Bottom),
|
||||
1 / Viewport.Zoom, Color.MediumSpringGreen);
|
||||
{
|
||||
var tl = Viewport.WorldToViewPx(new float2(r.Left, r.Top));
|
||||
var br = Viewport.WorldToViewPx(new float2(r.Right, r.Bottom));
|
||||
Game.Renderer.RgbaColorRenderer.DrawRect(tl, br, 1, Color.MediumSpringGreen);
|
||||
}
|
||||
|
||||
foreach (var r in World.ScreenMap.MouseBounds(World.RenderPlayer))
|
||||
Game.Renderer.WorldRgbaColorRenderer.DrawRect(
|
||||
new float3(r.Left, r.Top, r.Bottom),
|
||||
new float3(r.Right, r.Bottom, r.Bottom),
|
||||
1 / Viewport.Zoom, Color.OrangeRed);
|
||||
foreach (var b in World.ScreenMap.MouseBounds(World.RenderPlayer))
|
||||
{
|
||||
var points = new float2[b.Vertices.Length];
|
||||
for (var index = 0; index < b.Vertices.Length; index++)
|
||||
{
|
||||
var vertex = b.Vertices[index];
|
||||
points[index] = Viewport.WorldToViewPx(vertex).ToFloat2();
|
||||
}
|
||||
|
||||
Game.Renderer.RgbaColorRenderer.DrawPolygon(points, 1, Color.OrangeRed);
|
||||
}
|
||||
}
|
||||
|
||||
Game.Renderer.Flush();
|
||||
|
||||
// PERF: Reuse collection to avoid allocations.
|
||||
onScreenActors.Clear();
|
||||
preparedRenderables.Clear();
|
||||
preparedOverlayRenderables.Clear();
|
||||
preparedAnnotationRenderables.Clear();
|
||||
}
|
||||
|
||||
public void RefreshPalette()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -20,6 +20,7 @@ namespace OpenRA
|
||||
public readonly Hotkey Default = Hotkey.Invalid;
|
||||
public readonly string Description = "";
|
||||
public readonly HashSet<string> Types = new HashSet<string>();
|
||||
public bool HasDuplicates { get; internal set; }
|
||||
|
||||
public HotkeyDefinition(string name, MiniYaml node)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -38,6 +38,9 @@ namespace OpenRA
|
||||
if (definitions.ContainsKey(kv.Key))
|
||||
keys[kv.Key] = kv.Value;
|
||||
}
|
||||
|
||||
foreach (var hd in definitions)
|
||||
hd.Value.HasDuplicates = GetFirstDuplicate(hd.Value.Name, this[hd.Value.Name].GetValue(), hd.Value) != null;
|
||||
}
|
||||
|
||||
internal Func<Hotkey> GetHotkeyReference(string name)
|
||||
@@ -47,8 +50,7 @@ namespace OpenRA
|
||||
return () => keys[name];
|
||||
|
||||
// Try and parse as a hardcoded definition
|
||||
Hotkey key;
|
||||
if (!Hotkey.TryParse(name, out key))
|
||||
if (!Hotkey.TryParse(name, out var key))
|
||||
key = Hotkey.Invalid;
|
||||
|
||||
return () => key;
|
||||
@@ -56,8 +58,7 @@ namespace OpenRA
|
||||
|
||||
public void Set(string name, Hotkey value)
|
||||
{
|
||||
HotkeyDefinition definition;
|
||||
if (!definitions.TryGetValue(name, out definition))
|
||||
if (!definitions.TryGetValue(name, out var definition))
|
||||
return;
|
||||
|
||||
keys[name] = value;
|
||||
@@ -65,6 +66,20 @@ namespace OpenRA
|
||||
settings[name] = value;
|
||||
else
|
||||
settings.Remove(name);
|
||||
|
||||
var hadDuplicates = definition.HasDuplicates;
|
||||
definition.HasDuplicates = GetFirstDuplicate(definition.Name, this[definition.Name].GetValue(), definition) != null;
|
||||
|
||||
if (hadDuplicates || definition.HasDuplicates)
|
||||
{
|
||||
foreach (var hd in definitions)
|
||||
{
|
||||
if (hd.Value == definition)
|
||||
continue;
|
||||
|
||||
hd.Value.HasDuplicates = GetFirstDuplicate(hd.Value.Name, this[hd.Value.Name].GetValue(), hd.Value) != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public HotkeyDefinition GetFirstDuplicate(string name, Hotkey value, HotkeyDefinition definition)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -9,9 +9,11 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public struct Hotkey
|
||||
public struct Hotkey : IEquatable<Hotkey>
|
||||
{
|
||||
public static Hotkey Invalid = new Hotkey(Keycode.UNKNOWN, Modifiers.None);
|
||||
public bool IsValid()
|
||||
@@ -30,11 +32,9 @@ namespace OpenRA
|
||||
|
||||
var parts = s.Split(' ');
|
||||
|
||||
Keycode key;
|
||||
if (!Enum<Keycode>.TryParse(parts[0], true, out key))
|
||||
if (!Enum<Keycode>.TryParse(parts[0], true, out var key))
|
||||
{
|
||||
int c;
|
||||
if (!int.TryParse(parts[0], out c))
|
||||
if (!int.TryParse(parts[0], out var c))
|
||||
return false;
|
||||
key = (Keycode)c;
|
||||
}
|
||||
@@ -74,6 +74,11 @@ namespace OpenRA
|
||||
|
||||
public override int GetHashCode() { return Key.GetHashCode() ^ Modifiers.GetHashCode(); }
|
||||
|
||||
public bool Equals(Hotkey other)
|
||||
{
|
||||
return other == this;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var o = obj as Hotkey?;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -498,8 +498,7 @@ namespace OpenRA
|
||||
|
||||
public static string DisplayString(Keycode k)
|
||||
{
|
||||
string ret;
|
||||
if (!KeyNames.TryGetValue(k, out ret))
|
||||
if (!KeyNames.TryGetValue(k, out var ret))
|
||||
return k.ToString();
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -14,7 +14,6 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.FileSystem;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
@@ -26,22 +25,18 @@ namespace OpenRA
|
||||
readonly Dictionary<string, Manifest> mods;
|
||||
readonly SheetBuilder sheetBuilder;
|
||||
|
||||
readonly Dictionary<string, Sprite> icons = new Dictionary<string, Sprite>();
|
||||
public readonly IReadOnlyDictionary<string, Sprite> Icons;
|
||||
|
||||
/// <summary>Initializes the collection of locally installed mods.</summary>
|
||||
/// <param name="searchPaths">Filesystem paths to search for mod packages.</param>
|
||||
/// <param name="explicitPaths">Filesystem paths to additional mod packages.</param>
|
||||
public InstalledMods(IEnumerable<string> searchPaths, IEnumerable<string> explicitPaths)
|
||||
{
|
||||
sheetBuilder = new SheetBuilder(SheetType.BGRA, 256);
|
||||
Icons = new ReadOnlyDictionary<string, Sprite>(icons);
|
||||
mods = GetInstalledMods(searchPaths, explicitPaths);
|
||||
}
|
||||
|
||||
static IEnumerable<Pair<string, string>> GetCandidateMods(IEnumerable<string> searchPaths)
|
||||
static IEnumerable<(string Id, string Path)> GetCandidateMods(IEnumerable<string> searchPaths)
|
||||
{
|
||||
var mods = new List<Pair<string, string>>();
|
||||
var mods = new List<(string, string)>();
|
||||
foreach (var path in searchPaths)
|
||||
{
|
||||
try
|
||||
@@ -52,7 +47,7 @@ namespace OpenRA
|
||||
|
||||
var directory = new DirectoryInfo(resolved);
|
||||
foreach (var subdir in directory.EnumerateDirectories())
|
||||
mods.Add(Pair.New(subdir.Name, subdir.FullName));
|
||||
mods.Add((subdir.Name, subdir.FullName));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -76,28 +71,14 @@ namespace OpenRA
|
||||
|
||||
package = new Folder(path);
|
||||
if (package.Contains("mod.yaml"))
|
||||
{
|
||||
var manifest = new Manifest(id, package);
|
||||
|
||||
if (package.Contains("icon.png"))
|
||||
{
|
||||
using (var stream = package.GetStream("icon.png"))
|
||||
if (stream != null)
|
||||
icons[id] = sheetBuilder.Add(new Png(stream));
|
||||
}
|
||||
else if (!manifest.Metadata.Hidden)
|
||||
Log.Write("debug", "Mod '{0}' is missing 'icon.png'.".F(path));
|
||||
|
||||
return manifest;
|
||||
}
|
||||
return new Manifest(id, package);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Write("debug", "Load mod '{0}': {1}".F(path, e));
|
||||
}
|
||||
|
||||
if (package != null)
|
||||
package.Dispose();
|
||||
package?.Dispose();
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -106,13 +87,13 @@ namespace OpenRA
|
||||
{
|
||||
var ret = new Dictionary<string, Manifest>();
|
||||
var candidates = GetCandidateMods(searchPaths)
|
||||
.Concat(explicitPaths.Select(p => Pair.New(Path.GetFileNameWithoutExtension(p), p)));
|
||||
.Concat(explicitPaths.Select(p => (Id: Path.GetFileNameWithoutExtension(p), Path: p)));
|
||||
|
||||
foreach (var pair in candidates)
|
||||
{
|
||||
var mod = LoadMod(pair.First, pair.Second);
|
||||
var mod = LoadMod(pair.Id, pair.Path);
|
||||
if (mod != null)
|
||||
ret[pair.First] = mod;
|
||||
ret[pair.Id] = mod;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -80,8 +80,6 @@ namespace OpenRA
|
||||
{
|
||||
try
|
||||
{
|
||||
innerState = LinkState.Unlinked;
|
||||
|
||||
if (i.Error != null)
|
||||
{
|
||||
innerState = LinkState.ConnectionFailed;
|
||||
@@ -100,6 +98,8 @@ namespace OpenRA
|
||||
else
|
||||
innerState = LinkState.Linked;
|
||||
}
|
||||
else
|
||||
innerState = LinkState.Unlinked;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -108,8 +108,7 @@ namespace OpenRA
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (onComplete != null)
|
||||
onComplete();
|
||||
onComplete?.Invoke();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -52,7 +52,7 @@ namespace OpenRA
|
||||
}
|
||||
|
||||
/// <summary> Describes what is to be loaded in order to run a mod. </summary>
|
||||
public class Manifest
|
||||
public class Manifest : IDisposable
|
||||
{
|
||||
public readonly string Id;
|
||||
public readonly IReadOnlyPackage Package;
|
||||
@@ -96,8 +96,7 @@ namespace OpenRA
|
||||
// TODO: Use fieldloader
|
||||
MapFolders = YamlDictionary(yaml, "MapFolders");
|
||||
|
||||
MiniYaml packages;
|
||||
if (yaml.TryGetValue("Packages", out packages))
|
||||
if (yaml.TryGetValue("Packages", out var packages))
|
||||
Packages = packages.ToDictionary(x => x.Value).AsReadOnly();
|
||||
|
||||
Rules = YamlList(yaml, "Rules");
|
||||
@@ -217,9 +216,8 @@ namespace OpenRA
|
||||
/// </summary>
|
||||
public T Get<T>(ObjectCreator oc) where T : IGlobalModData
|
||||
{
|
||||
MiniYaml data;
|
||||
var t = typeof(T);
|
||||
if (!yaml.TryGetValue(t.Name, out data))
|
||||
if (!yaml.TryGetValue(t.Name, out var data))
|
||||
{
|
||||
// Lazily create the default values if not explicitly defined.
|
||||
return (T)oc.CreateBasic(t);
|
||||
@@ -241,5 +239,14 @@ namespace OpenRA
|
||||
|
||||
return (T)module;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var module in modules)
|
||||
{
|
||||
var disposableModule = module as IDisposable;
|
||||
disposableModule?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -9,17 +9,29 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public interface IActorInitializer
|
||||
{
|
||||
World World { get; }
|
||||
T Get<T>() where T : IActorInit;
|
||||
U Get<T, U>() where T : IActorInit<U>;
|
||||
bool Contains<T>() where T : IActorInit;
|
||||
T GetOrDefault<T>(TraitInfo info) where T : ActorInit;
|
||||
T Get<T>(TraitInfo info) where T : ActorInit;
|
||||
U GetValue<T, U>(TraitInfo info) where T : ValueActorInit<U>;
|
||||
U GetValue<T, U>(TraitInfo info, U fallback) where T : ValueActorInit<U>;
|
||||
bool Contains<T>(TraitInfo info) where T : ActorInit;
|
||||
|
||||
T GetOrDefault<T>() where T : ActorInit, ISingleInstanceInit;
|
||||
T Get<T>() where T : ActorInit, ISingleInstanceInit;
|
||||
U GetValue<T, U>() where T : ValueActorInit<U>, ISingleInstanceInit;
|
||||
U GetValue<T, U>(U fallback) where T : ValueActorInit<U>, ISingleInstanceInit;
|
||||
bool Contains<T>() where T : ActorInit, ISingleInstanceInit;
|
||||
}
|
||||
|
||||
public class ActorInitializer : IActorInitializer
|
||||
@@ -35,50 +47,228 @@ namespace OpenRA
|
||||
Dict = dict;
|
||||
}
|
||||
|
||||
public T Get<T>() where T : IActorInit { return Dict.Get<T>(); }
|
||||
public U Get<T, U>() where T : IActorInit<U> { return Dict.Get<T>().Value(World); }
|
||||
public bool Contains<T>() where T : IActorInit { return Dict.Contains<T>(); }
|
||||
}
|
||||
|
||||
public interface IActorInit { }
|
||||
|
||||
public interface IActorInit<T> : IActorInit
|
||||
{
|
||||
T Value(World world);
|
||||
}
|
||||
|
||||
public class LocationInit : IActorInit<CPos>
|
||||
{
|
||||
[FieldFromYamlKey]
|
||||
readonly CPos value = CPos.Zero;
|
||||
|
||||
public LocationInit() { }
|
||||
public LocationInit(CPos init) { value = init; }
|
||||
public CPos Value(World world) { return value; }
|
||||
}
|
||||
|
||||
public class OwnerInit : IActorInit<Player>
|
||||
{
|
||||
[FieldFromYamlKey]
|
||||
public readonly string PlayerName = "Neutral";
|
||||
|
||||
Player player;
|
||||
|
||||
public OwnerInit() { }
|
||||
public OwnerInit(string playerName) { PlayerName = playerName; }
|
||||
|
||||
public OwnerInit(Player player)
|
||||
public T GetOrDefault<T>(TraitInfo info) where T : ActorInit
|
||||
{
|
||||
this.player = player;
|
||||
PlayerName = player.InternalName;
|
||||
var inits = Dict.WithInterface<T>();
|
||||
|
||||
// Traits tagged with an instance name prefer inits with the same name.
|
||||
// If a more specific init is not available, fall back to an unnamed init.
|
||||
// If duplicate inits are defined, take the last to match standard yaml override expectations
|
||||
if (info != null && !string.IsNullOrEmpty(info.InstanceName))
|
||||
return inits.LastOrDefault(i => i.InstanceName == info.InstanceName) ??
|
||||
inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
|
||||
|
||||
// Untagged traits will only use untagged inits
|
||||
return inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
|
||||
}
|
||||
|
||||
public T Get<T>(TraitInfo info) where T : ActorInit
|
||||
{
|
||||
var init = GetOrDefault<T>(info);
|
||||
if (init == null)
|
||||
throw new InvalidOperationException("TypeDictionary does not contain instance of type `{0}`".F(typeof(T)));
|
||||
|
||||
return init;
|
||||
}
|
||||
|
||||
public U GetValue<T, U>(TraitInfo info) where T : ValueActorInit<U>
|
||||
{
|
||||
return Get<T>(info).Value;
|
||||
}
|
||||
|
||||
public U GetValue<T, U>(TraitInfo info, U fallback) where T : ValueActorInit<U>
|
||||
{
|
||||
var init = GetOrDefault<T>(info);
|
||||
return init != null ? init.Value : fallback;
|
||||
}
|
||||
|
||||
public bool Contains<T>(TraitInfo info) where T : ActorInit { return GetOrDefault<T>(info) != null; }
|
||||
|
||||
public T GetOrDefault<T>() where T : ActorInit, ISingleInstanceInit
|
||||
{
|
||||
return Dict.GetOrDefault<T>();
|
||||
}
|
||||
|
||||
public T Get<T>() where T : ActorInit, ISingleInstanceInit
|
||||
{
|
||||
return Dict.Get<T>();
|
||||
}
|
||||
|
||||
public U GetValue<T, U>() where T : ValueActorInit<U>, ISingleInstanceInit
|
||||
{
|
||||
return Get<T>().Value;
|
||||
}
|
||||
|
||||
public U GetValue<T, U>(U fallback) where T : ValueActorInit<U>, ISingleInstanceInit
|
||||
{
|
||||
var init = GetOrDefault<T>();
|
||||
return init != null ? init.Value : fallback;
|
||||
}
|
||||
|
||||
public bool Contains<T>() where T : ActorInit, ISingleInstanceInit { return GetOrDefault<T>() != null; }
|
||||
}
|
||||
|
||||
/*
|
||||
* Things to be aware of when writing ActorInits:
|
||||
*
|
||||
* - ActorReference and ActorGlobal can dynamically create objects without calling a constructor.
|
||||
* The object will be allocated directly then the best matching Initialize() method will be called to set valid state.
|
||||
* - ActorReference will always attempt to call Initialize(MiniYaml). ActorGlobal will use whichever one it first
|
||||
* finds with an argument type that matches the given LuaValue.
|
||||
* - Most ActorInits will want to inherit either ValueActorInit<T> or CompositeActorInit which hide the low-level plumbing.
|
||||
* - Inits that reference actors should use ActorInitActorReference which allows actors to be referenced by name in map.yaml
|
||||
* - Inits that should only have a single instance defined on an actor should implement ISingleInstanceInit to allow
|
||||
* direct queries and runtime enforcement.
|
||||
* - Inits that aren't ISingleInstanceInit should expose a ctor that accepts a TraitInfo to allow per-trait targeting.
|
||||
*/
|
||||
public abstract class ActorInit
|
||||
{
|
||||
[FieldLoader.Ignore]
|
||||
public readonly string InstanceName;
|
||||
|
||||
protected ActorInit(string instanceName)
|
||||
{
|
||||
InstanceName = instanceName;
|
||||
}
|
||||
|
||||
protected ActorInit() { }
|
||||
|
||||
public abstract MiniYaml Save();
|
||||
}
|
||||
|
||||
public interface ISingleInstanceInit { }
|
||||
|
||||
public abstract class ValueActorInit<T> : ActorInit
|
||||
{
|
||||
protected readonly T value;
|
||||
|
||||
protected ValueActorInit(TraitInfo info, T value)
|
||||
: base(info.InstanceName) { this.value = value; }
|
||||
|
||||
protected ValueActorInit(string instanceName, T value)
|
||||
: base(instanceName) { this.value = value; }
|
||||
|
||||
protected ValueActorInit(T value) { this.value = value; }
|
||||
|
||||
public virtual T Value { get { return value; } }
|
||||
|
||||
public virtual void Initialize(MiniYaml yaml)
|
||||
{
|
||||
Initialize((T)FieldLoader.GetValue("value", typeof(T), yaml.Value));
|
||||
}
|
||||
|
||||
public virtual void Initialize(T value)
|
||||
{
|
||||
var field = GetType().GetField("value", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (field != null)
|
||||
field.SetValue(this, value);
|
||||
}
|
||||
|
||||
public override MiniYaml Save()
|
||||
{
|
||||
return new MiniYaml(FieldSaver.FormatValue(value));
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class CompositeActorInit : ActorInit
|
||||
{
|
||||
protected CompositeActorInit(TraitInfo info)
|
||||
: base(info.InstanceName) { }
|
||||
|
||||
protected CompositeActorInit()
|
||||
: base() { }
|
||||
|
||||
public virtual void Initialize(MiniYaml yaml)
|
||||
{
|
||||
FieldLoader.Load(this, yaml);
|
||||
}
|
||||
|
||||
public virtual void Initialize(Dictionary<string, object> values)
|
||||
{
|
||||
foreach (var field in GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
|
||||
{
|
||||
var sa = field.GetCustomAttributes<FieldLoader.SerializeAttribute>(false).DefaultIfEmpty(FieldLoader.SerializeAttribute.Default).First();
|
||||
if (!sa.Serialize)
|
||||
continue;
|
||||
|
||||
if (values.TryGetValue(field.Name, out var value))
|
||||
field.SetValue(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Dictionary<string, Type> InitializeArgs()
|
||||
{
|
||||
var dict = new Dictionary<string, Type>();
|
||||
foreach (var field in GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
|
||||
{
|
||||
var sa = field.GetCustomAttributes<FieldLoader.SerializeAttribute>(false).DefaultIfEmpty(FieldLoader.SerializeAttribute.Default).First();
|
||||
if (!sa.Serialize)
|
||||
continue;
|
||||
|
||||
dict[field.Name] = field.FieldType;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
public override MiniYaml Save()
|
||||
{
|
||||
return FieldSaver.Save(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class LocationInit : ValueActorInit<CPos>, ISingleInstanceInit
|
||||
{
|
||||
public LocationInit(CPos value)
|
||||
: base(value) { }
|
||||
}
|
||||
|
||||
public class OwnerInit : ActorInit, ISingleInstanceInit
|
||||
{
|
||||
public readonly string InternalName;
|
||||
protected readonly Player value;
|
||||
|
||||
public OwnerInit(Player value)
|
||||
{
|
||||
this.value = value;
|
||||
InternalName = value.InternalName;
|
||||
}
|
||||
|
||||
public OwnerInit(string value)
|
||||
{
|
||||
InternalName = value;
|
||||
}
|
||||
|
||||
public Player Value(World world)
|
||||
{
|
||||
if (player != null)
|
||||
return player;
|
||||
return value ?? world.Players.First(x => x.InternalName == InternalName);
|
||||
}
|
||||
|
||||
return world.Players.First(x => x.InternalName == PlayerName);
|
||||
public void Initialize(MiniYaml yaml)
|
||||
{
|
||||
var field = GetType().GetField("InternalName", BindingFlags.Public | BindingFlags.Instance);
|
||||
if (field != null)
|
||||
field.SetValue(this, yaml.Value);
|
||||
}
|
||||
|
||||
public void Initialize(Player player)
|
||||
{
|
||||
var field = GetType().GetField("value", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (field != null)
|
||||
field.SetValue(this, player);
|
||||
}
|
||||
|
||||
public override MiniYaml Save()
|
||||
{
|
||||
return new MiniYaml(InternalName);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class RuntimeFlagInit : ActorInit, ISuppressInitExport
|
||||
{
|
||||
public override MiniYaml Save()
|
||||
{
|
||||
throw new NotImplementedException("RuntimeFlagInit cannot be saved");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -12,7 +12,11 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
@@ -21,13 +25,10 @@ namespace OpenRA
|
||||
public class ActorReference : IEnumerable
|
||||
{
|
||||
public string Type;
|
||||
public TypeDictionary InitDict
|
||||
{
|
||||
get { return initDict.Value; }
|
||||
}
|
||||
|
||||
Lazy<TypeDictionary> initDict;
|
||||
|
||||
internal TypeDictionary InitDict { get { return initDict.Value; } }
|
||||
|
||||
public ActorReference(string type)
|
||||
: this(type, new Dictionary<string, MiniYaml>()) { }
|
||||
|
||||
@@ -38,38 +39,168 @@ namespace OpenRA
|
||||
{
|
||||
var dict = new TypeDictionary();
|
||||
foreach (var i in inits)
|
||||
dict.Add(LoadInit(i.Key, i.Value));
|
||||
{
|
||||
var init = LoadInit(i.Key, i.Value);
|
||||
if (init is ISingleInstanceInit && dict.Contains(init.GetType()))
|
||||
throw new InvalidDataException("Duplicate initializer '{0}'".F(init.GetType().Name));
|
||||
|
||||
dict.Add(init);
|
||||
}
|
||||
|
||||
return dict;
|
||||
});
|
||||
}
|
||||
|
||||
static IActorInit LoadInit(string traitName, MiniYaml my)
|
||||
public ActorReference(string type, TypeDictionary inits)
|
||||
{
|
||||
var info = Game.CreateObject<IActorInit>(traitName + "Init");
|
||||
FieldLoader.Load(info, my);
|
||||
return info;
|
||||
Type = type;
|
||||
initDict = new Lazy<TypeDictionary>(() =>
|
||||
{
|
||||
var dict = new TypeDictionary();
|
||||
foreach (var i in inits)
|
||||
dict.Add(i);
|
||||
return dict;
|
||||
});
|
||||
}
|
||||
|
||||
public MiniYaml Save(Func<object, bool> initFilter = null)
|
||||
static ActorInit LoadInit(string initName, MiniYaml initYaml)
|
||||
{
|
||||
var initInstance = initName.Split(ActorInfo.TraitInstanceSeparator);
|
||||
var type = Game.ModData.ObjectCreator.FindType(initInstance[0] + "Init");
|
||||
if (type == null)
|
||||
throw new InvalidDataException("Unknown initializer type '{0}Init'".F(initInstance[0]));
|
||||
|
||||
var init = (ActorInit)FormatterServices.GetUninitializedObject(type);
|
||||
if (initInstance.Length > 1)
|
||||
type.GetField("InstanceName").SetValue(init, initInstance[1]);
|
||||
|
||||
var loader = type.GetMethod("Initialize", new[] { typeof(MiniYaml) });
|
||||
if (loader == null)
|
||||
throw new InvalidDataException("{0}Init does not define a yaml-assignable type.".F(initInstance[0]));
|
||||
|
||||
loader.Invoke(init, new[] { initYaml });
|
||||
return init;
|
||||
}
|
||||
|
||||
public MiniYaml Save(Func<ActorInit, bool> initFilter = null)
|
||||
{
|
||||
var ret = new MiniYaml(Type);
|
||||
foreach (var init in InitDict)
|
||||
foreach (var o in initDict.Value)
|
||||
{
|
||||
if (init is ISuppressInitExport)
|
||||
var init = o as ActorInit;
|
||||
if (init == null || o is ISuppressInitExport)
|
||||
continue;
|
||||
|
||||
if (initFilter != null && !initFilter(init))
|
||||
continue;
|
||||
|
||||
var initName = init.GetType().Name;
|
||||
ret.Nodes.Add(new MiniYamlNode(initName.Substring(0, initName.Length - 4), FieldSaver.Save(init)));
|
||||
var initTypeName = init.GetType().Name;
|
||||
var initName = initTypeName.Substring(0, initTypeName.Length - 4);
|
||||
if (!string.IsNullOrEmpty(init.InstanceName))
|
||||
initName += ActorInfo.TraitInstanceSeparator + init.InstanceName;
|
||||
|
||||
ret.Nodes.Add(new MiniYamlNode(initName, init.Save()));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// for initialization syntax
|
||||
public void Add(object o) { InitDict.Add(o); }
|
||||
public IEnumerator GetEnumerator() { return InitDict.GetEnumerator(); }
|
||||
public IEnumerator GetEnumerator() { return initDict.Value.GetEnumerator(); }
|
||||
|
||||
public ActorReference Clone()
|
||||
{
|
||||
var clone = new ActorReference(Type);
|
||||
foreach (var init in initDict.Value)
|
||||
clone.initDict.Value.Add(init);
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
public void Add(ActorInit init)
|
||||
{
|
||||
if (init is ISingleInstanceInit && InitDict.Contains(init.GetType()))
|
||||
throw new InvalidDataException("Duplicate initializer '{0}'".F(init.GetType().Name));
|
||||
|
||||
InitDict.Add(init);
|
||||
}
|
||||
|
||||
public void Remove(ActorInit o) { initDict.Value.Remove(o); }
|
||||
|
||||
public int RemoveAll<T>() where T : ActorInit
|
||||
{
|
||||
var removed = 0;
|
||||
foreach (var o in initDict.Value.WithInterface<T>().ToList())
|
||||
{
|
||||
removed++;
|
||||
initDict.Value.Remove(o);
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetAll<T>() where T : ActorInit
|
||||
{
|
||||
return initDict.Value.WithInterface<T>();
|
||||
}
|
||||
|
||||
public T GetOrDefault<T>(TraitInfo info) where T : ActorInit
|
||||
{
|
||||
var inits = initDict.Value.WithInterface<T>();
|
||||
|
||||
// Traits tagged with an instance name prefer inits with the same name.
|
||||
// If a more specific init is not available, fall back to an unnamed init.
|
||||
// If duplicate inits are defined, take the last to match standard yaml override expectations
|
||||
if (info != null && !string.IsNullOrEmpty(info.InstanceName))
|
||||
return inits.LastOrDefault(i => i.InstanceName == info.InstanceName) ??
|
||||
inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
|
||||
|
||||
// Untagged traits will only use untagged inits
|
||||
return inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
|
||||
}
|
||||
|
||||
public T Get<T>(TraitInfo info) where T : ActorInit
|
||||
{
|
||||
var init = GetOrDefault<T>(info);
|
||||
if (init == null)
|
||||
throw new InvalidOperationException("TypeDictionary does not contain instance of type `{0}`".F(typeof(T)));
|
||||
|
||||
return init;
|
||||
}
|
||||
|
||||
public U GetValue<T, U>(TraitInfo info) where T : ValueActorInit<U>
|
||||
{
|
||||
return Get<T>(info).Value;
|
||||
}
|
||||
|
||||
public U GetValue<T, U>(TraitInfo info, U fallback) where T : ValueActorInit<U>
|
||||
{
|
||||
var init = GetOrDefault<T>(info);
|
||||
return init != null ? init.Value : fallback;
|
||||
}
|
||||
|
||||
public bool Contains<T>(TraitInfo info) where T : ActorInit { return GetOrDefault<T>(info) != null; }
|
||||
|
||||
public T GetOrDefault<T>() where T : ActorInit, ISingleInstanceInit
|
||||
{
|
||||
return initDict.Value.GetOrDefault<T>();
|
||||
}
|
||||
|
||||
public T Get<T>() where T : ActorInit, ISingleInstanceInit
|
||||
{
|
||||
return initDict.Value.Get<T>();
|
||||
}
|
||||
|
||||
public U GetValue<T, U>() where T : ValueActorInit<U>, ISingleInstanceInit
|
||||
{
|
||||
return Get<T>().Value;
|
||||
}
|
||||
|
||||
public U GetValue<T, U>(U fallback) where T : ValueActorInit<U>, ISingleInstanceInit
|
||||
{
|
||||
var init = GetOrDefault<T>();
|
||||
return init != null ? init.Value : fallback;
|
||||
}
|
||||
|
||||
public bool Contains<T>() where T : ActorInit, ISingleInstanceInit { return GetOrDefault<T>() != null; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
@@ -10,42 +10,28 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
// Represents a layer of "something" that covers the map
|
||||
public class CellLayer<T> : IEnumerable<T>
|
||||
public sealed class CellLayer<T> : CellLayerBase<T>
|
||||
{
|
||||
public readonly Size Size;
|
||||
readonly Rectangle bounds;
|
||||
public readonly MapGridType GridType;
|
||||
public event Action<CPos> CellEntryChanged = null;
|
||||
|
||||
readonly T[] entries;
|
||||
|
||||
public CellLayer(Map map)
|
||||
: this(map.Grid.Type, new Size(map.MapSize.X, map.MapSize.Y)) { }
|
||||
: base(map) { }
|
||||
|
||||
public CellLayer(MapGridType gridType, Size size)
|
||||
{
|
||||
Size = size;
|
||||
bounds = new Rectangle(0, 0, Size.Width, Size.Height);
|
||||
GridType = gridType;
|
||||
entries = new T[size.Width * size.Height];
|
||||
}
|
||||
: base(gridType, size) { }
|
||||
|
||||
public void CopyValuesFrom(CellLayer<T> anotherLayer)
|
||||
public override void CopyValuesFrom(CellLayerBase<T> anotherLayer)
|
||||
{
|
||||
if (Size != anotherLayer.Size || GridType != anotherLayer.GridType)
|
||||
throw new ArgumentException(
|
||||
"layers must have a matching size and shape (grid type).", "anotherLayer");
|
||||
if (CellEntryChanged != null)
|
||||
throw new InvalidOperationException(
|
||||
"Cannot copy values when there are listeners attached to the CellEntryChanged event.");
|
||||
Array.Copy(anotherLayer.entries, entries, entries.Length);
|
||||
|
||||
base.CopyValuesFrom(anotherLayer);
|
||||
}
|
||||
|
||||
public static CellLayer<T> CreateInstance(Func<MPos, T> initialCellValueFactory, Size size, MapGridType mapGridType)
|
||||
@@ -87,8 +73,7 @@ namespace OpenRA
|
||||
{
|
||||
entries[Index(cell)] = value;
|
||||
|
||||
if (CellEntryChanged != null)
|
||||
CellEntryChanged(cell);
|
||||
CellEntryChanged?.Invoke(cell);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,28 +89,10 @@ namespace OpenRA
|
||||
{
|
||||
entries[Index(uv)] = value;
|
||||
|
||||
if (CellEntryChanged != null)
|
||||
CellEntryChanged(uv.ToCPos(GridType));
|
||||
CellEntryChanged?.Invoke(uv.ToCPos(GridType));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Clears the layer contents with a known value</summary>
|
||||
public void Clear(T clearValue)
|
||||
{
|
||||
for (var i = 0; i < entries.Length; i++)
|
||||
entries[i] = clearValue;
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<T>)entries).GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public bool Contains(CPos cell)
|
||||
{
|
||||
// .ToMPos() returns the same result if the X and Y coordinates
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user