Compare commits
630 Commits
devtest-20
...
release-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5943ecc943 | ||
|
|
c13e078ebd | ||
|
|
426c3d68a1 | ||
|
|
5b6b4ab4ee | ||
|
|
835a9e605a | ||
|
|
390fbe6718 | ||
|
|
1bf3d090d5 | ||
|
|
9e279b9987 | ||
|
|
4b6780bc67 | ||
|
|
0a9aa488db | ||
|
|
97ecfa548e | ||
|
|
82a98ae6e6 | ||
|
|
32b241eae7 | ||
|
|
e8084ade68 | ||
|
|
16c2cbf577 | ||
|
|
b51980a9ea | ||
|
|
631a61e219 | ||
|
|
5ff2546a5d | ||
|
|
bf2640308a | ||
|
|
63cee6a0e3 | ||
|
|
cab47c4975 | ||
|
|
6385c24875 | ||
|
|
1d1bafd836 | ||
|
|
5cacce3c93 | ||
|
|
798c92abeb | ||
|
|
9236a05d53 | ||
|
|
fc7b3d83e5 | ||
|
|
00ddf82e85 | ||
|
|
c10ebb455b | ||
|
|
d999a0534b | ||
|
|
02454fb764 | ||
|
|
df81857abe | ||
|
|
ae0745b542 | ||
|
|
89b5e34320 | ||
|
|
47cc6eda7e | ||
|
|
6da23ed3d7 | ||
|
|
3ee4613a98 | ||
|
|
616bfbe553 | ||
|
|
ecbcc9c8bb | ||
|
|
068ce9b215 | ||
|
|
170bcd500c | ||
|
|
6efb40c9a6 | ||
|
|
3c92c2d789 | ||
|
|
f823d20a86 | ||
|
|
ca4b97cd03 | ||
|
|
8b5e322891 | ||
|
|
70e1c4bb0c | ||
|
|
8690ad4e19 | ||
|
|
a01233c3df | ||
|
|
d0d55b3fc1 | ||
|
|
2a26a07ce6 | ||
|
|
65e088204f | ||
|
|
77e38ceea9 | ||
|
|
989d1475b9 | ||
|
|
580a96cda4 | ||
|
|
5a8964aabe | ||
|
|
c9dcf305c9 | ||
|
|
5489d6909c | ||
|
|
85412a6d98 | ||
|
|
b69806123a | ||
|
|
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,5 +1,6 @@
|
||||
; Top-most http://editorconfig.org/ file
|
||||
root = true
|
||||
charset=utf-8
|
||||
|
||||
; Unix-style newlines
|
||||
[*]
|
||||
|
||||
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
|
||||
*~
|
||||
|
||||
42
.travis.yml
42
.travis.yml
@@ -1,9 +1,16 @@
|
||||
# Travis-CI Build for OpenRA
|
||||
# see travis-ci.org for details
|
||||
|
||||
dist: xenial
|
||||
language: csharp
|
||||
mono: 5.20.1
|
||||
mono: 6.4.0
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- os: linux
|
||||
dist: xenial
|
||||
- os: osx
|
||||
if: tag IS present
|
||||
osx_image: xcode10
|
||||
|
||||
cache:
|
||||
directories:
|
||||
@@ -14,13 +21,8 @@ addons:
|
||||
packages:
|
||||
- lua5.1
|
||||
- dpkg
|
||||
- markdown
|
||||
- zlib1g-dev
|
||||
- libbz2-dev
|
||||
- cmake
|
||||
- genisoimage
|
||||
- fakeroot
|
||||
- zsync
|
||||
- markdown
|
||||
|
||||
# Environment variables
|
||||
env:
|
||||
@@ -34,14 +36,10 @@ env:
|
||||
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 ..
|
||||
- test "$TRAVIS_OS_NAME" == "linux" && make check || echo "Skipping check"
|
||||
- test "$TRAVIS_OS_NAME" == "linux" && make check-scripts || echo "Skipping scripts check"
|
||||
- test "$TRAVIS_OS_NAME" == "linux" && make test || echo "Skipping tests"
|
||||
- test "$TRAVIS_OS_NAME" == "linux" && make nunit || echo "Skipping nunit tests"
|
||||
|
||||
# Only watch the development branch and tagged release.
|
||||
branches:
|
||||
@@ -63,11 +61,13 @@ 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
|
||||
- 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;
|
||||
cd packaging && ./update-wiki.sh ${TRAVIS_TAG} && cd ..;
|
||||
fi;
|
||||
- export PATH=$PATH:$HOME/usr/bin
|
||||
- DOTVERSION=`echo ${TRAVIS_TAG} | sed "s/-/\\./g"`
|
||||
- cd packaging
|
||||
|
||||
20
AUTHORS
20
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)
|
||||
@@ -41,8 +42,9 @@ Also thanks to:
|
||||
* Brendan Gluth (Mechanical_Man)
|
||||
* Bryan Wilbur
|
||||
* Bugra Cuhadaroglu (BugraC)
|
||||
* Christer Ulfsparre (Holloweye)
|
||||
* Chris Cameron (Vesuvian)
|
||||
* Chris Grant (Unit158)
|
||||
* Christer Ulfsparre (Holloweye)
|
||||
* clem
|
||||
* Cody Brittain (Generalcamo)
|
||||
* Constantin Helmig (CH4Code)
|
||||
@@ -140,6 +142,7 @@ Also thanks to:
|
||||
* Tim Mylemans (gecko)
|
||||
* Tirili
|
||||
* Tomas Einarsson (Mesacer)
|
||||
* Tom van Leth (tovl)
|
||||
* Tristan Keating (Kilkakon)
|
||||
* Tristan Mühlbacher (MicroBit)
|
||||
* UnknownProgrammer
|
||||
@@ -157,12 +160,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 +179,9 @@ Krueger and distributed under the GNU GPL terms.
|
||||
Using rix0rrr.BeaconLib developed by Rico Huijbers
|
||||
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.
|
||||
|
||||
24
Makefile
24
Makefile
@@ -40,7 +40,7 @@
|
||||
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 rix0rrr.BeaconLib.dll Open.Nat.dll SDL2-CS.dll OpenAL-CS.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
|
||||
@@ -151,16 +151,17 @@ test: core
|
||||
all: dependencies core
|
||||
|
||||
core:
|
||||
@command -v $(MSBUILD) >/dev/null || (echo "OpenRA requires the '$(MSBUILD)' tool provided by Mono >= 5.4."; exit 1)
|
||||
@command -v $(firstword $(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
|
||||
endif
|
||||
@./fetch-geoip.sh
|
||||
|
||||
clean:
|
||||
@ $(MSBUILD) -t:clean
|
||||
@-$(RM_F) *.config
|
||||
@-$(RM_F) *.config IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP
|
||||
@-$(RM_F) *.exe *.dll *.dylib ./OpenRA*/*.dll *.pdb mods/**/*.dll mods/**/*.pdb *.resources
|
||||
@-$(RM_RF) ./*/bin ./*/obj
|
||||
@-$(RM_RF) ./thirdparty/download
|
||||
@@ -173,30 +174,26 @@ cli-dependencies:
|
||||
@ $(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-dependencies: cli-dependencies linux-native-dependencies
|
||||
|
||||
linux-native-dependencies:
|
||||
@./thirdparty/configure-native-deps.sh
|
||||
|
||||
windows-dependencies: cli-dependencies geoip-dependencies
|
||||
windows-dependencies: cli-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
|
||||
osx-dependencies: cli-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
|
||||
all-dependencies: cli-dependencies windows-dependencies osx-dependencies
|
||||
|
||||
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
|
||||
@@ -219,10 +216,10 @@ install-engine:
|
||||
@$(INSTALL_PROGRAM) OpenRA.Platforms.Default.dll "$(DATA_INSTALL_DIR)"
|
||||
|
||||
@$(INSTALL_DATA) OpenRA.Platforms.Default.dll.config "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_DATA) "GeoLite2-Country.mmdb.gz" "$(DATA_INSTALL_DIR)/GeoLite2-Country.mmdb.gz"
|
||||
@$(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,7 +229,6 @@ 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-common-mod-files:
|
||||
@@ -387,4 +383,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 nunit test all core clean distclean cli-dependencies linux-dependencies linux-native-dependencies windows-dependencies osx-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
|
||||
|
||||
@@ -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)
|
||||
@@ -169,7 +202,8 @@ namespace OpenRA.Activities
|
||||
if (ChildActivity != null)
|
||||
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)
|
||||
|
||||
@@ -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
|
||||
@@ -44,7 +44,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;
|
||||
@@ -82,6 +87,7 @@ namespace OpenRA
|
||||
readonly INotifyIdle[] tickIdles;
|
||||
readonly ITargetablePositions[] targetablePositions;
|
||||
WPos[] staticTargetablePositions;
|
||||
bool created;
|
||||
|
||||
internal Actor(World world, string name, TypeDictionary initDict)
|
||||
{
|
||||
@@ -138,6 +144,35 @@ namespace OpenRA
|
||||
SyncHashes = TraitsImplementing<ISync>().Select(sync => new SyncHash(sync)).ToArray();
|
||||
}
|
||||
|
||||
internal void Created()
|
||||
{
|
||||
created = true;
|
||||
|
||||
foreach (var t in TraitsImplementing<INotifyCreated>())
|
||||
t.Created(this);
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
var wasIdle = IsIdle;
|
||||
@@ -214,11 +249,15 @@ namespace OpenRA
|
||||
{
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -151,6 +151,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);
|
||||
|
||||
@@ -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
|
||||
@@ -415,7 +415,13 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
else if (fieldType == typeof(bool))
|
||||
return ParseYesNo(value, fieldType, fieldName);
|
||||
{
|
||||
bool result;
|
||||
if (bool.TryParse(value.ToLowerInvariant(), out result))
|
||||
return result;
|
||||
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
else if (fieldType == typeof(int2[]))
|
||||
{
|
||||
if (value != null)
|
||||
@@ -592,20 +598,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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -264,7 +266,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);
|
||||
@@ -336,8 +338,6 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
GeoIP.Initialize();
|
||||
|
||||
if (Settings.Server.DiscoverNatDevices)
|
||||
discoverNat = UPnP.DiscoverNatDevices(Settings.Server.NatDiscoveryTimeout);
|
||||
|
||||
@@ -351,6 +351,8 @@ 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;
|
||||
@@ -433,28 +435,11 @@ namespace OpenRA
|
||||
if (Cursor != null)
|
||||
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;
|
||||
|
||||
@@ -509,8 +494,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 +542,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);
|
||||
}
|
||||
}
|
||||
@@ -626,7 +618,7 @@ namespace OpenRA
|
||||
|
||||
static void LogicTick()
|
||||
{
|
||||
delayedActions.PerformActions(RunTime);
|
||||
PerformDelayedActions();
|
||||
|
||||
if (OrderManager.Connection.ConnectionState != lastConnectionState)
|
||||
{
|
||||
@@ -639,6 +631,11 @@ namespace OpenRA
|
||||
InnerLogicTick(worldRenderer.World.OrderManager);
|
||||
}
|
||||
|
||||
public static void PerformDelayedActions()
|
||||
{
|
||||
delayedActions.PerformActions(RunTime);
|
||||
}
|
||||
|
||||
public static void TakeScreenshot()
|
||||
{
|
||||
takeScreenshot = true;
|
||||
@@ -650,24 +647,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,6 +707,7 @@ 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();
|
||||
}
|
||||
@@ -742,6 +755,7 @@ namespace OpenRA
|
||||
var nextLogic = RunTime;
|
||||
var nextRender = RunTime;
|
||||
var forcedNextRender = RunTime;
|
||||
var renderBeforeNextTick = false;
|
||||
|
||||
while (state == RunStatus.Running)
|
||||
{
|
||||
@@ -770,9 +784,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 +794,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 +813,7 @@ namespace OpenRA
|
||||
forcedNextRender = now + maxRenderInterval;
|
||||
|
||||
RenderTick();
|
||||
renderBeforeNextTick = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -843,6 +858,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);
|
||||
@@ -881,7 +901,7 @@ namespace OpenRA
|
||||
|
||||
public static void CreateServer(ServerSettings settings)
|
||||
{
|
||||
server = new Server.Server(new IPEndPoint(IPAddress.Any, settings.ListenPort), settings, ModData, false);
|
||||
server = new Server.Server(new IPEndPoint(IPAddress.Any, settings.ListenPort), settings, ModData, ServerType.Multiplayer);
|
||||
}
|
||||
|
||||
public static int CreateLocalServer(string map)
|
||||
@@ -893,7 +913,7 @@ namespace OpenRA
|
||||
AdvertiseOnline = false
|
||||
};
|
||||
|
||||
server = new Server.Server(new IPEndPoint(IPAddress.Loopback, 0), settings, ModData, false);
|
||||
server = new Server.Server(new IPEndPoint(IPAddress.Loopback, 0), settings, ModData, ServerType.Local);
|
||||
|
||||
return server.Port;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -44,7 +44,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.Split('@')[0], t.Value);
|
||||
if (trait != null)
|
||||
traits.Add(trait);
|
||||
}
|
||||
catch (FieldLoader.MissingFieldsException e)
|
||||
{
|
||||
@@ -73,7 +77,13 @@ namespace OpenRA
|
||||
if (!string.IsNullOrEmpty(my.Value))
|
||||
throw new YamlException("Junk value `{0}` on trait node {1}"
|
||||
.F(my.Value, traitName));
|
||||
|
||||
// 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 info = creator.CreateObject<ITraitInfo>(traitName + "Info");
|
||||
if (info == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
FieldLoader.Load(info, my);
|
||||
|
||||
@@ -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
|
||||
@@ -33,6 +33,27 @@ namespace OpenRA.GameRules
|
||||
public Target GuidedTarget;
|
||||
}
|
||||
|
||||
public class WarheadArgs
|
||||
{
|
||||
public WeaponInfo Weapon;
|
||||
public int[] DamageModifiers = { };
|
||||
public WPos? Source;
|
||||
public Actor SourceActor;
|
||||
public Target WeaponTarget;
|
||||
|
||||
public WarheadArgs(ProjectileArgs args)
|
||||
{
|
||||
Weapon = args.Weapon;
|
||||
DamageModifiers = args.DamageModifiers;
|
||||
Source = args.Source;
|
||||
SourceActor = args.SourceActor;
|
||||
WeaponTarget = args.GuidedTarget;
|
||||
}
|
||||
|
||||
// Default empty constructor for callers that want to initialize fields themselves
|
||||
public WarheadArgs() { }
|
||||
}
|
||||
|
||||
public interface IProjectile : IEffect { }
|
||||
public interface IProjectileInfo { IProjectile Create(ProjectileArgs args); }
|
||||
|
||||
@@ -177,17 +198,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
|
||||
@@ -66,6 +66,23 @@ namespace OpenRA.Graphics
|
||||
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 };
|
||||
}
|
||||
|
||||
return new IRenderable[] { imageRenderable };
|
||||
}
|
||||
|
||||
public Rectangle ScreenBounds(WorldRenderer wr, WPos pos, WVec offset, float scale)
|
||||
{
|
||||
var xy = wr.ScreenPxPosition(pos) + wr.ScreenPxOffset(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
|
||||
|
||||
@@ -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,142 @@
|
||||
*/
|
||||
#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, Pair<Sheet, int>> cachedSheets;
|
||||
static Dictionary<string, Dictionary<string, Sprite>> cachedSprites;
|
||||
static Dictionary<string, Sprite[]> cachedPanelSprites;
|
||||
static Dictionary<Collection, Pair<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, Pair<Sheet, int>>();
|
||||
cachedSprites = new Dictionary<string, Dictionary<string, Sprite>>();
|
||||
cachedPanelSprites = new Dictionary<string, Sprite[]>();
|
||||
cachedCollectionSheets = new Dictionary<Collection, Pair<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.First.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 Pair<Sheet, int> SheetForCollection(Collection c)
|
||||
{
|
||||
Pair<Sheet, int> sheetDensity;
|
||||
|
||||
// Outer cache avoids recalculating image names
|
||||
if (!cachedCollectionSheets.TryGetValue(c, out 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 = Pair.New(sheet, density);
|
||||
cachedSheets.Add(image, sheetDensity);
|
||||
}
|
||||
|
||||
cachedCollectionSheets.Add(c, sheetDensity);
|
||||
}
|
||||
|
||||
return sheetDensity;
|
||||
}
|
||||
|
||||
public static Sprite GetImage(string collectionName, string imageName)
|
||||
@@ -104,33 +165,130 @@ namespace OpenRA.Graphics
|
||||
return null;
|
||||
}
|
||||
|
||||
MappedImage mi;
|
||||
Rectangle mi;
|
||||
if (!collection.Regions.TryGetValue(imageName, out 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.First, sheetDensity.Second * mi, TextureChannel.RGBA, 1f / sheetDensity.Second);
|
||||
cachedCollection.Add(imageName, image);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
public static Sprite[] GetPanelImages(string collectionName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(collectionName))
|
||||
return null;
|
||||
|
||||
// Cached sprite
|
||||
Sprite[] cachedSprites;
|
||||
if (cachedPanelSprites.TryGetValue(collectionName, out cachedSprites))
|
||||
return cachedSprites;
|
||||
|
||||
Collection collection;
|
||||
if (!collections.TryGetValue(collectionName, out 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[]
|
||||
{
|
||||
Pair.New(PanelSides.Top | PanelSides.Left, new Rectangle(pr[0], pr[1], pr[2], pr[3])),
|
||||
Pair.New(PanelSides.Top, new Rectangle(pr[0] + pr[2], pr[1], pr[4], pr[3])),
|
||||
Pair.New(PanelSides.Top | PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1], pr[6], pr[3])),
|
||||
Pair.New(PanelSides.Left, new Rectangle(pr[0], pr[1] + pr[3], pr[2], pr[5])),
|
||||
Pair.New(PanelSides.Center, new Rectangle(pr[0] + pr[2], pr[1] + pr[3], pr[4], pr[5])),
|
||||
Pair.New(PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1] + pr[3], pr[6], pr[5])),
|
||||
Pair.New(PanelSides.Bottom | PanelSides.Left, new Rectangle(pr[0], pr[1] + pr[3] + pr[5], pr[2], pr[7])),
|
||||
Pair.New(PanelSides.Bottom, new Rectangle(pr[0] + pr[2], pr[1] + pr[3] + pr[5], pr[4], pr[7])),
|
||||
Pair.New(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.First) ? new Sprite(sheetDensity.First, sheetDensity.Second * x.Second, TextureChannel.RGBA, 1f / sheetDensity.Second) : 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);
|
||||
|
||||
Collection collection;
|
||||
if (!collections.TryGetValue(collectionName, out 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -39,9 +46,15 @@ namespace OpenRA
|
||||
{
|
||||
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; }
|
||||
|
||||
event Action<float, float, float, float> OnWindowScaleChanged;
|
||||
|
||||
void PumpInput(IInputHandler inputHandler);
|
||||
string GetClipboardText();
|
||||
@@ -50,9 +63,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 +78,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 +89,7 @@ namespace OpenRA
|
||||
void DisableDepthBuffer();
|
||||
void ClearDepthBuffer();
|
||||
void SetBlendMode(BlendMode mode);
|
||||
void SetVSyncEnabled(bool enabled);
|
||||
string GLVersion { get; }
|
||||
}
|
||||
|
||||
@@ -109,6 +128,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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -64,7 +64,7 @@ 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)
|
||||
@@ -130,16 +130,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)
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -25,6 +24,8 @@ namespace OpenRA.Graphics
|
||||
readonly Func<string, float> lineWidth;
|
||||
readonly IFont font;
|
||||
readonly Cache<Pair<char, Color>, GlyphInfo> glyphs;
|
||||
readonly Cache<Tuple<char, Color, int>, Sprite> contrastGlyphs;
|
||||
readonly Cache<int, float[]> dilationElements;
|
||||
|
||||
float deviceScale;
|
||||
|
||||
@@ -40,6 +41,8 @@ namespace OpenRA.Graphics
|
||||
font = Game.Renderer.CreateFont(data);
|
||||
|
||||
glyphs = new Cache<Pair<char, Color>, GlyphInfo>(CreateGlyph, Pair<char, Color>.EqualityComparer);
|
||||
contrastGlyphs = new Cache<Tuple<char, Color, 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;
|
||||
@@ -55,6 +58,7 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
deviceScale = scale;
|
||||
glyphs.Clear();
|
||||
contrastGlyphs.Clear();
|
||||
}
|
||||
|
||||
void PrecacheColor(Color c, string name)
|
||||
@@ -65,44 +69,78 @@ namespace OpenRA.Graphics
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
public void DrawText(string text, float2 location, Color c)
|
||||
void DrawTextContrast(string text, float2 location, Color contrastColor, int contrastOffset)
|
||||
{
|
||||
// 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 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);
|
||||
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, Color.Black)];
|
||||
|
||||
// Convert screen coordinates back to UI coordinates for drawing
|
||||
if (g.Sprite != null)
|
||||
{
|
||||
var contrastSprite = contrastGlyphs[Tuple.Create(s, contrastColor, screenContrast)];
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(contrastSprite,
|
||||
(screen + g.Offset - contrastVector) / deviceScale,
|
||||
contrastSprite.Size / deviceScale);
|
||||
}
|
||||
|
||||
screen += new int2((int)(g.Advance + 0.5f), 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawText(string text, float2 location, Color c)
|
||||
{
|
||||
// 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 screen = new int2((int)(location.X * deviceScale + 0.5f), (int)(location.Y * deviceScale + 0.5f));
|
||||
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[Pair.New(s, c)];
|
||||
|
||||
// Convert screen coordinates back to UI coordinates for drawing
|
||||
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);
|
||||
(screen + g.Offset).ToFloat2() / deviceScale,
|
||||
g.Sprite.Size / deviceScale);
|
||||
|
||||
p += new float2(g.Advance / deviceScale, 0);
|
||||
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);
|
||||
@@ -120,18 +158,25 @@ namespace OpenRA.Graphics
|
||||
var g = glyphs[Pair.New(s, c)];
|
||||
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);
|
||||
|
||||
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.DrawSprite(g.Sprite,
|
||||
Rotate(tl, sina, cosa, location),
|
||||
Rotate(tr, sina, cosa, location),
|
||||
Rotate(br, sina, cosa, location),
|
||||
Rotate(bl, sina, cosa, location));
|
||||
ra + screenOffset,
|
||||
rb + screenOffset,
|
||||
rc + screenOffset,
|
||||
rd + screenOffset);
|
||||
}
|
||||
|
||||
p += new float2(g.Advance / deviceScale, 0);
|
||||
@@ -141,12 +186,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 +199,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 +217,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);
|
||||
}
|
||||
@@ -239,6 +289,128 @@ 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(Tuple<char, Color, int> c)
|
||||
{
|
||||
// Source glyph color doesn't matter, so use black
|
||||
var glyph = glyphs[Pair.New(c.Item1, Color.Black)];
|
||||
var color = c.Item2;
|
||||
var r = c.Item3;
|
||||
|
||||
var size = new Size(glyph.Sprite.Bounds.Width + 2 * r, glyph.Sprite.Bounds.Height + 2 * r);
|
||||
|
||||
var s = builder.Allocate(size);
|
||||
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);
|
||||
var pmc = Util.PremultiplyAlpha(Color.FromArgb(alpha, color));
|
||||
dest[q] = pmc.B;
|
||||
dest[q + 1] = pmc.G;
|
||||
dest[q + 2] = pmc.R;
|
||||
dest[q + 3] = pmc.A;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -89,7 +94,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 +105,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -64,8 +64,10 @@ namespace OpenRA.Graphics
|
||||
|
||||
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
|
||||
@@ -155,22 +155,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
|
||||
|
||||
@@ -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,7 +111,7 @@ 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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -61,23 +61,56 @@ namespace OpenRA.Graphics
|
||||
|
||||
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
|
||||
|
||||
@@ -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,37 @@ 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 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 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 +150,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 +173,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)
|
||||
@@ -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
|
||||
@@ -39,6 +39,10 @@ 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>();
|
||||
|
||||
bool lastDepthPreviewEnabled;
|
||||
|
||||
internal WorldRenderer(ModData modData, World world)
|
||||
@@ -103,7 +107,7 @@ namespace OpenRA.Graphics
|
||||
palettes[name].Palette = pal;
|
||||
}
|
||||
|
||||
List<IFinalizedRenderable> GenerateRenderables()
|
||||
IEnumerable<IFinalizedRenderable> GenerateRenderables()
|
||||
{
|
||||
var actors = onScreenActors.Append(World.WorldActor);
|
||||
if (World.RenderPlayer != null)
|
||||
@@ -122,42 +126,74 @@ namespace OpenRA.Graphics
|
||||
|
||||
worldRenderables = worldRenderables.OrderBy(RenderableScreenZPositionComparisonKey);
|
||||
|
||||
Game.Renderer.WorldModelRenderer.BeginFrame();
|
||||
var renderables = worldRenderables.Select(r => r.PrepareRender(this)).ToList();
|
||||
Game.Renderer.WorldModelRenderer.EndFrame();
|
||||
|
||||
return renderables;
|
||||
return worldRenderables.Select(r => r.PrepareRender(this));
|
||||
}
|
||||
|
||||
List<IFinalizedRenderable> GenerateOverlayRenderables()
|
||||
IEnumerable<IFinalizedRenderable> GenerateOverlayRenderables()
|
||||
{
|
||||
var aboveShroud = World.ActorsWithTrait<IRenderAboveShroud>()
|
||||
var actors = 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));
|
||||
|
||||
var aboveShroudSelected = World.Selection.Actors.Where(a => a.IsInWorld && !a.Disposed)
|
||||
var selected = 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)));
|
||||
|
||||
var aboveShroudEffects = World.Effects.Select(e => e as IEffectAboveShroud)
|
||||
var effects = World.Effects.Select(e => e as IEffectAboveShroud)
|
||||
.Where(e => e != null)
|
||||
.SelectMany(e => e.RenderAboveShroud(this));
|
||||
|
||||
var aboveShroudOrderGenerator = SpriteRenderable.None;
|
||||
var orderGenerator = SpriteRenderable.None;
|
||||
if (World.OrderGenerator != null)
|
||||
aboveShroudOrderGenerator = World.OrderGenerator.RenderAboveShroud(this, World);
|
||||
orderGenerator = World.OrderGenerator.RenderAboveShroud(this, World);
|
||||
|
||||
var overlayRenderables = aboveShroud
|
||||
.Concat(aboveShroudSelected)
|
||||
.Concat(aboveShroudEffects)
|
||||
.Concat(aboveShroudOrderGenerator);
|
||||
return actors
|
||||
.Concat(selected)
|
||||
.Concat(effects)
|
||||
.Concat(orderGenerator)
|
||||
.Select(r => r.PrepareRender(this));
|
||||
}
|
||||
|
||||
Game.Renderer.WorldModelRenderer.BeginFrame();
|
||||
var finalOverlayRenderables = overlayRenderables.Select(r => r.PrepareRender(this)).ToList();
|
||||
Game.Renderer.WorldModelRenderer.EndFrame();
|
||||
IEnumerable<IFinalizedRenderable> GenerateAnnotationRenderables()
|
||||
{
|
||||
var actors = World.ActorsWithTrait<IRenderAnnotations>()
|
||||
.Where(a => a.Actor.IsInWorld && !a.Actor.Disposed && (!a.Trait.SpatiallyPartitionable || onScreenActors.Contains(a.Actor)))
|
||||
.SelectMany(a => a.Trait.RenderAnnotations(a.Actor, this));
|
||||
|
||||
return finalOverlayRenderables;
|
||||
var selected = World.Selection.Actors.Where(a => a.IsInWorld && !a.Disposed)
|
||||
.SelectMany(a => a.TraitsImplementing<IRenderAnnotationsWhenSelected>()
|
||||
.Where(t => !t.SpatiallyPartitionable || onScreenActors.Contains(a))
|
||||
.SelectMany(t => t.RenderAnnotations(a, this)));
|
||||
|
||||
var effects = World.Effects.Select(e => e as IEffectAnnotation)
|
||||
.Where(e => e != null)
|
||||
.SelectMany(e => e.RenderAnnotation(this));
|
||||
|
||||
var orderGenerator = SpriteRenderable.None;
|
||||
if (World.OrderGenerator != null)
|
||||
orderGenerator = World.OrderGenerator.RenderAnnotations(this, World);
|
||||
|
||||
return actors
|
||||
.Concat(selected)
|
||||
.Concat(effects)
|
||||
.Concat(orderGenerator)
|
||||
.Select(r => r.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));
|
||||
preparedRenderables.AddRange(GenerateRenderables());
|
||||
preparedOverlayRenderables.AddRange(GenerateOverlayRenderables());
|
||||
preparedAnnotationRenderables.AddRange(GenerateAnnotationRenderables());
|
||||
onScreenActors.Clear();
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
@@ -171,10 +207,6 @@ 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);
|
||||
|
||||
@@ -186,8 +218,8 @@ namespace OpenRA.Graphics
|
||||
|
||||
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 +228,68 @@ 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);
|
||||
{
|
||||
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.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)
|
||||
@@ -65,6 +68,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()
|
||||
@@ -74,6 +76,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
|
||||
|
||||
@@ -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,16 +26,12 @@ 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);
|
||||
}
|
||||
|
||||
@@ -76,20 +72,7 @@ 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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -71,5 +71,14 @@ namespace OpenRA
|
||||
// for initialization syntax
|
||||
public void Add(object o) { InitDict.Add(o); }
|
||||
public IEnumerator GetEnumerator() { return InitDict.GetEnumerator(); }
|
||||
|
||||
public ActorReference Clone()
|
||||
{
|
||||
var clone = new ActorReference(Type);
|
||||
foreach (var init in InitDict)
|
||||
clone.InitDict.Add(init);
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -824,6 +824,14 @@ namespace OpenRA
|
||||
return new WDist(delta.Z);
|
||||
}
|
||||
|
||||
public WVec Offset(CVec delta, int dz)
|
||||
{
|
||||
if (Grid.Type == MapGridType.Rectangular)
|
||||
return new WVec(1024 * delta.X, 1024 * delta.Y, 0);
|
||||
|
||||
return new WVec(724 * (delta.X - delta.Y), 724 * (delta.X + delta.Y), 724 * dz);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The size of the map Height step in world units
|
||||
/// </summary>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user