Compare commits
530 Commits
playtest-2
...
devtest-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86a714a588 | ||
|
|
c01c39954a | ||
|
|
c48eb572e3 | ||
|
|
b03ab1212f | ||
|
|
8a9b5e7e01 | ||
|
|
9b90e4f25a | ||
|
|
8a4401bdcb | ||
|
|
d52e90cf23 | ||
|
|
14ef0a7740 | ||
|
|
c1f79b348a | ||
|
|
62166a50d9 | ||
|
|
283b330403 | ||
|
|
2cf6b74295 | ||
|
|
1a9f707d18 | ||
|
|
06a1c88e86 | ||
|
|
f358b566b1 | ||
|
|
15fc27d142 | ||
|
|
ad20597d74 | ||
|
|
6d409a7c97 | ||
|
|
36d5ae5421 | ||
|
|
db9744ea7f | ||
|
|
5e62fe86fc | ||
|
|
6cfa27c33b | ||
|
|
a405969199 | ||
|
|
9a6f3b4c05 | ||
|
|
7be059a79b | ||
|
|
29b55de042 | ||
|
|
235fb19aa8 | ||
|
|
c0f54fa4fc | ||
|
|
cdc216aca0 | ||
|
|
4505053618 | ||
|
|
e0d53126d6 | ||
|
|
12ff1dd14c | ||
|
|
50db3152f6 | ||
|
|
9c4fd0e3d3 | ||
|
|
8d27d22100 | ||
|
|
27f1a7ab27 | ||
|
|
d52e4793fe | ||
|
|
544ac6cb33 | ||
|
|
06fbc1a6cf | ||
|
|
edab10e6a6 | ||
|
|
dd99fc93e4 | ||
|
|
dbe824d4e5 | ||
|
|
6e73d7f5c2 | ||
|
|
960056d300 | ||
|
|
3efac3287e | ||
|
|
c4b4a8c8a5 | ||
|
|
b833f033bf | ||
|
|
ad75e2be89 | ||
|
|
7ee4fbeb0d | ||
|
|
9886f0ca9a | ||
|
|
46cf56d6ff | ||
|
|
e7af295b5e | ||
|
|
9d179d9a1a | ||
|
|
15010f9567 | ||
|
|
a7f4f6c1cf | ||
|
|
3eeb677f14 | ||
|
|
ef69a3de66 | ||
|
|
0aa5e07252 | ||
|
|
07d58337f1 | ||
|
|
b5e3f25418 | ||
|
|
19b02875c7 | ||
|
|
8a74f6ea18 | ||
|
|
a847f3eafa | ||
|
|
a751f074e7 | ||
|
|
75cb5c2166 | ||
|
|
70a86bed7a | ||
|
|
f67f8ed05e | ||
|
|
1ae53220d6 | ||
|
|
c546cb552e | ||
|
|
10f8836d7b | ||
|
|
7ecd4124ce | ||
|
|
54cd77be8e | ||
|
|
43388cb7fc | ||
|
|
4cc5104fde | ||
|
|
d519cabae3 | ||
|
|
9852e29835 | ||
|
|
3a427c3630 | ||
|
|
336656e8f7 | ||
|
|
06ad9666e8 | ||
|
|
728e0c6600 | ||
|
|
657e690bdd | ||
|
|
2d36d0a659 | ||
|
|
c42fd5d2e2 | ||
|
|
10bf97eff6 | ||
|
|
ea9992247d | ||
|
|
b90fecff76 | ||
|
|
a4fc9fea3b | ||
|
|
b7c7eff2a2 | ||
|
|
8c10dc406a | ||
|
|
05c3861426 | ||
|
|
1ef5db8896 | ||
|
|
006a87692a | ||
|
|
e019b70420 | ||
|
|
70ec5b0344 | ||
|
|
5401ace540 | ||
|
|
ab9081c852 | ||
|
|
3a9b35980c | ||
|
|
150439d215 | ||
|
|
b01a534a98 | ||
|
|
758b0b08d0 | ||
|
|
f87ba1d8a4 | ||
|
|
67fa7bdcc9 | ||
|
|
0b03aca104 | ||
|
|
3bf61f1043 | ||
|
|
ac975f4139 | ||
|
|
6d12301f88 | ||
|
|
914950c4a5 | ||
|
|
b417b267dd | ||
|
|
aae497eff1 | ||
|
|
3c9db4c2ac | ||
|
|
8c3793e7ea | ||
|
|
60a7f53491 | ||
|
|
04bfd62f2f | ||
|
|
117b8b3653 | ||
|
|
70cb0d2924 | ||
|
|
c5ea496c45 | ||
|
|
01e955ca37 | ||
|
|
fdb66c769c | ||
|
|
38b3fbbdbe | ||
|
|
3bc5d2d02c | ||
|
|
ac7eda8ca2 | ||
|
|
baf58f53b3 | ||
|
|
8513a83331 | ||
|
|
bf7fecff10 | ||
|
|
6e1f2f636c | ||
|
|
8b3db6f3d6 | ||
|
|
39d0abe982 | ||
|
|
c2026dc254 | ||
|
|
cae6c28754 | ||
|
|
3b99924799 | ||
|
|
b2b548b103 | ||
|
|
c2e3806a77 | ||
|
|
25500a7dda | ||
|
|
8c394a4cb5 | ||
|
|
2e7bd4de4b | ||
|
|
7261322e41 | ||
|
|
cada396733 | ||
|
|
9f9709f058 | ||
|
|
26fc65209d | ||
|
|
f642cead44 | ||
|
|
616d9421d6 | ||
|
|
20e5219cf4 | ||
|
|
9edda21b06 | ||
|
|
6d6822ca15 | ||
|
|
a2269e7ee7 | ||
|
|
e8f443f4a9 | ||
|
|
67f8452178 | ||
|
|
47f6e407d9 | ||
|
|
889e2152a4 | ||
|
|
173aae1f81 | ||
|
|
baed80983b | ||
|
|
b00423dc76 | ||
|
|
376ed15079 | ||
|
|
a6d8d6cd8e | ||
|
|
b3ee8b447e | ||
|
|
5f588561b6 | ||
|
|
83c53e17e0 | ||
|
|
c028488894 | ||
|
|
b066005f7e | ||
|
|
87e33a75c6 | ||
|
|
4961b0943b | ||
|
|
eda1e9c266 | ||
|
|
bc7bf174d8 | ||
|
|
2e06d5790b | ||
|
|
ab8790e8f1 | ||
|
|
7d630e63e7 | ||
|
|
77899191f3 | ||
|
|
6871873e93 | ||
|
|
fc4bd131cd | ||
|
|
d866286f82 | ||
|
|
76dfda164e | ||
|
|
134d47e48c | ||
|
|
a2dbd5e013 | ||
|
|
c27412c83a | ||
|
|
4143aba595 | ||
|
|
39ccac4022 | ||
|
|
7943f4deb6 | ||
|
|
2dda2d7689 | ||
|
|
b8f2a14ea0 | ||
|
|
595809f090 | ||
|
|
477db9cd4a | ||
|
|
67ff292d62 | ||
|
|
ae882b85a9 | ||
|
|
5e92915095 | ||
|
|
95809db03c | ||
|
|
6d5a5121bc | ||
|
|
86992751c7 | ||
|
|
2eba8b6c37 | ||
|
|
e1523e939d | ||
|
|
15a92f443d | ||
|
|
b57c68e392 | ||
|
|
e95fcb6bc0 | ||
|
|
6581fcb6a7 | ||
|
|
6551337bd8 | ||
|
|
137df52fdd | ||
|
|
b79aa7eb6a | ||
|
|
1c8c49dc8e | ||
|
|
f0c808d2fc | ||
|
|
26d9ae88df | ||
|
|
3c7f119bb1 | ||
|
|
fea35923f0 | ||
|
|
9627776318 | ||
|
|
6dcde3af72 | ||
|
|
43717a89b5 | ||
|
|
c7ba359688 | ||
|
|
dc3dbf6d85 | ||
|
|
71664c85ff | ||
|
|
cb41be113a | ||
|
|
4fe7daa85e | ||
|
|
f75afc6ee4 | ||
|
|
67fd71ab92 | ||
|
|
385e70552e | ||
|
|
1e2c67bfca | ||
|
|
56739f87fb | ||
|
|
571eb7614f | ||
|
|
c6c3a8c60d | ||
|
|
ae7cfa56b7 | ||
|
|
b856613194 | ||
|
|
86305879cb | ||
|
|
e5a1a8a706 | ||
|
|
27602a4a97 | ||
|
|
a98e460257 | ||
|
|
0349435650 | ||
|
|
c3fbdca18f | ||
|
|
acb5245a28 | ||
|
|
23561cd76b | ||
|
|
d3ab3d7d78 | ||
|
|
5b870be83f | ||
|
|
6130d5622c | ||
|
|
318c4e3456 | ||
|
|
ab701449e2 | ||
|
|
9a3447d863 | ||
|
|
5280637adf | ||
|
|
3bce55ac44 | ||
|
|
a3f79503ed | ||
|
|
02d462a82c | ||
|
|
3d17328d0d | ||
|
|
0e81abc21b | ||
|
|
803b930405 | ||
|
|
6adf45bcb4 | ||
|
|
01417c88c5 | ||
|
|
60bbbe0d93 | ||
|
|
888dfd3654 | ||
|
|
12de56ff62 | ||
|
|
b7cee41c54 | ||
|
|
10aac03f75 | ||
|
|
0eb0041f90 | ||
|
|
b38018af9c | ||
|
|
4df5ac0385 | ||
|
|
a7476bc303 | ||
|
|
cc4b3cb361 | ||
|
|
31a965b29a | ||
|
|
7a213338a2 | ||
|
|
fb27a25e52 | ||
|
|
534b09ae4a | ||
|
|
341a9f370c | ||
|
|
607d9b2d5c | ||
|
|
507ce40ad2 | ||
|
|
f58c3aed32 | ||
|
|
55e85bd9ca | ||
|
|
4bf614c5cd | ||
|
|
96b06c75d1 | ||
|
|
d261648ab0 | ||
|
|
7b81b9e806 | ||
|
|
a93aea3e4e | ||
|
|
2cfacc2c7d | ||
|
|
6d6b21a0eb | ||
|
|
7a78c37851 | ||
|
|
b8a9f41892 | ||
|
|
227567dfe1 | ||
|
|
d5ff5c672b | ||
|
|
393f6eca3a | ||
|
|
d193ef856e | ||
|
|
44d3691fa1 | ||
|
|
27d0465891 | ||
|
|
52a9fcef3c | ||
|
|
7386816f52 | ||
|
|
9c0075b233 | ||
|
|
e4c5700baf | ||
|
|
e1b7df8b6a | ||
|
|
c999b2d778 | ||
|
|
bfb6c671fb | ||
|
|
7c6ec577dc | ||
|
|
626b40f31b | ||
|
|
5af12440ba | ||
|
|
4614f6febe | ||
|
|
1354ffc32e | ||
|
|
3723939c99 | ||
|
|
e099739e13 | ||
|
|
21a48cc41d | ||
|
|
4740266308 | ||
|
|
78139413d7 | ||
|
|
f0578a75f4 | ||
|
|
7a0e55a02a | ||
|
|
f132bac80d | ||
|
|
109ea4fe5b | ||
|
|
f33feafd0e | ||
|
|
9195356e3a | ||
|
|
eff91108f4 | ||
|
|
e40c0516e6 | ||
|
|
c580a94ab7 | ||
|
|
6a545bb942 | ||
|
|
672bd2d9fe | ||
|
|
3ab4a584ab | ||
|
|
150d02ac0d | ||
|
|
6d26f60904 | ||
|
|
b42276953f | ||
|
|
7c290b9f76 | ||
|
|
51fe1d6629 | ||
|
|
8f558d2b47 | ||
|
|
3f5fadf2e9 | ||
|
|
07c16cee1d | ||
|
|
24130dfcdc | ||
|
|
15a2341a91 | ||
|
|
86f61298e6 | ||
|
|
3cd7ec3878 | ||
|
|
8b13d3e4c7 | ||
|
|
327d451abc | ||
|
|
2dac16ee02 | ||
|
|
d4b92a19d7 | ||
|
|
e0357596f5 | ||
|
|
1ef27d18c1 | ||
|
|
1d2d8ed107 | ||
|
|
1bf01bc214 | ||
|
|
0015deca47 | ||
|
|
ddfdc6e90f | ||
|
|
5db2ad54f2 | ||
|
|
b8a5750529 | ||
|
|
98d5b8c7cc | ||
|
|
3f34154a1e | ||
|
|
3119f831b3 | ||
|
|
8a07b762a2 | ||
|
|
551ab2fc59 | ||
|
|
99957e57b9 | ||
|
|
be2c59bc6e | ||
|
|
57f9a49b66 | ||
|
|
54bd0eb99d | ||
|
|
485faac294 | ||
|
|
d531d6f3ef | ||
|
|
3a9fdb82f5 | ||
|
|
5024ae1156 | ||
|
|
0135dd9ed3 | ||
|
|
a6e9b86bbe | ||
|
|
237c4444b5 | ||
|
|
a68467292e | ||
|
|
978c69d0c3 | ||
|
|
3eabc59921 | ||
|
|
2b3d99fac2 | ||
|
|
2bdefe0e9e | ||
|
|
de81fc2aca | ||
|
|
b514e0a6e7 | ||
|
|
715dfa4541 | ||
|
|
ac57a37224 | ||
|
|
afd620b092 | ||
|
|
552bceb07c | ||
|
|
fe58ed1283 | ||
|
|
361e2d463c | ||
|
|
259c8d2c98 | ||
|
|
e12c1dc9aa | ||
|
|
7fb49e383d | ||
|
|
1df3e28253 | ||
|
|
bacec2689d | ||
|
|
bf397591f9 | ||
|
|
bd1a936c7a | ||
|
|
0871d6e321 | ||
|
|
265d296db6 | ||
|
|
5ec136b57c | ||
|
|
f1e8f9c9d0 | ||
|
|
f03841c4e4 | ||
|
|
0ae58ff0ea | ||
|
|
390c1899ca | ||
|
|
89aa6d1e4e | ||
|
|
24638b02a9 | ||
|
|
4b0ab6ab37 | ||
|
|
1f02d9f141 | ||
|
|
0103c38c13 | ||
|
|
3cc76f91b4 | ||
|
|
10dc248f07 | ||
|
|
d1f89c6217 | ||
|
|
76ba4fc32d | ||
|
|
37f90fff44 | ||
|
|
9fa6da3bc7 | ||
|
|
aa8cf237ab | ||
|
|
c131728aa4 | ||
|
|
2abd137494 | ||
|
|
90815ace59 | ||
|
|
53d916d7f1 | ||
|
|
2b4035979b | ||
|
|
38cdc93010 | ||
|
|
346dad3898 | ||
|
|
42256bc262 | ||
|
|
70babb4067 | ||
|
|
3603e6373d | ||
|
|
bd1760682f | ||
|
|
52d0490f95 | ||
|
|
b3b0aa75ae | ||
|
|
e42d177920 | ||
|
|
c2156af7b0 | ||
|
|
86394eb56c | ||
|
|
2d7790f5e4 | ||
|
|
3df43529a6 | ||
|
|
a1c9b27057 | ||
|
|
089dd233a5 | ||
|
|
86a7a0bd6c | ||
|
|
7ebca36a9c | ||
|
|
d5aed5a88a | ||
|
|
dac1f270ce | ||
|
|
839be24053 | ||
|
|
91c4179f05 | ||
|
|
a4b427bfac | ||
|
|
250f5bec18 | ||
|
|
336e2a10e0 | ||
|
|
b5e9b7635e | ||
|
|
0df7fa1596 | ||
|
|
c10487d635 | ||
|
|
bc9b3bef74 | ||
|
|
e57462e7ca | ||
|
|
78bf27709f | ||
|
|
fc84cd9204 | ||
|
|
e361f7b246 | ||
|
|
b0497b7505 | ||
|
|
a894e31fa5 | ||
|
|
dd062adec2 | ||
|
|
d187575a2c | ||
|
|
85096c4ba2 | ||
|
|
e13fd693c3 | ||
|
|
cc35512472 | ||
|
|
4e548291ce | ||
|
|
3ba86f329f | ||
|
|
5b34af0f12 | ||
|
|
0a9eb1ff83 | ||
|
|
942dd0e5f7 | ||
|
|
400102f3d3 | ||
|
|
9ccdeb3d36 | ||
|
|
7e0f0dd2d2 | ||
|
|
d920cbb7f6 | ||
|
|
417677e6f4 | ||
|
|
33f3038316 | ||
|
|
429dbe3e0c | ||
|
|
471fc44751 | ||
|
|
1b8e346307 | ||
|
|
a456583a08 | ||
|
|
a63c17baab | ||
|
|
9c4faddc0f | ||
|
|
6828c4c1e9 | ||
|
|
80131e7ec0 | ||
|
|
ac381a6f58 | ||
|
|
521b516bf9 | ||
|
|
6a825f8e60 | ||
|
|
f0a243ca10 | ||
|
|
e5457a3390 | ||
|
|
47e21f8bef | ||
|
|
331b854e4e | ||
|
|
274bc9cbba | ||
|
|
827f8d95b4 | ||
|
|
2946dd35d5 | ||
|
|
74d884787d | ||
|
|
5516e16fb8 | ||
|
|
cadf4eb322 | ||
|
|
269249e86e | ||
|
|
30f87d2308 | ||
|
|
8b7e72b95e | ||
|
|
1e64048956 | ||
|
|
8512e696f5 | ||
|
|
6a03a9ec5f | ||
|
|
5e04c99d57 | ||
|
|
82f15491c0 | ||
|
|
101843fbb7 | ||
|
|
9e534f3804 | ||
|
|
ca3cfc0184 | ||
|
|
d62fb901e2 | ||
|
|
73a2b59c2c | ||
|
|
2c7a56625c | ||
|
|
e2572b214f | ||
|
|
d22cd3a74f | ||
|
|
0c8fcedfdf | ||
|
|
99009c37ce | ||
|
|
d9f5771778 | ||
|
|
d35b5070fb | ||
|
|
02f41f9afc | ||
|
|
c797aa1d5e | ||
|
|
b2f7f67756 | ||
|
|
09014ab6d5 | ||
|
|
8f8747d65e | ||
|
|
be19e137e2 | ||
|
|
3155291064 | ||
|
|
a5b22e6a36 | ||
|
|
9c251e8b6a | ||
|
|
6056568182 | ||
|
|
7b7c1da18d | ||
|
|
fb5b4b3547 | ||
|
|
19918d485e | ||
|
|
45c6c6ba10 | ||
|
|
0e436bc686 | ||
|
|
b0dfea0a09 | ||
|
|
2c4e6c4188 | ||
|
|
9f3254dbd1 | ||
|
|
88cdad4189 | ||
|
|
4ba50a4379 | ||
|
|
2b6c104011 | ||
|
|
4b446d100e | ||
|
|
f9ca2114a9 | ||
|
|
afc9c6ef85 | ||
|
|
ac200f6173 | ||
|
|
73a78eadb1 | ||
|
|
c5139fb6c2 | ||
|
|
9faf9aa1b9 | ||
|
|
3c2e9be248 | ||
|
|
5b59f6612f | ||
|
|
3d69363f35 | ||
|
|
b580b4fd33 | ||
|
|
74f86d70f8 | ||
|
|
3959104f9b | ||
|
|
1ff037a257 | ||
|
|
32700df117 | ||
|
|
b4edec215e | ||
|
|
dffa1e45f4 | ||
|
|
df3b6dde34 | ||
|
|
834bbf467e | ||
|
|
4d4f94208e | ||
|
|
416713de0c | ||
|
|
c523ca8efe | ||
|
|
9acea56108 | ||
|
|
44a7422375 | ||
|
|
0d0e7eb179 | ||
|
|
ea6c840343 | ||
|
|
d2db0913ac | ||
|
|
3721dae74d | ||
|
|
9050a2447b |
@@ -6,13 +6,101 @@ charset=utf-8
|
||||
[*]
|
||||
end_of_line = LF
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
; 4-column tab indentation
|
||||
; 4-column tab indentation and .NET coding conventions
|
||||
[*.cs]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
dotnet_separate_import_directive_groups = false
|
||||
dotnet_sort_system_directives_first = true
|
||||
|
||||
csharp_style_var_elsewhere = true:suggestion
|
||||
csharp_style_var_for_built_in_types = true:suggestion
|
||||
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||
|
||||
csharp_prefer_braces = when_multiline:suggestion
|
||||
csharp_using_directive_placement = outside_namespace:suggestion
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
|
||||
#### Naming styles ####
|
||||
|
||||
dotnet_naming_style.camel_case.capitalization = camel_case
|
||||
|
||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||
|
||||
# Symbol specifications
|
||||
|
||||
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal
|
||||
|
||||
dotnet_naming_symbols.const_private_field.applicable_kinds = field
|
||||
dotnet_naming_symbols.const_private_field.required_modifiers = const
|
||||
dotnet_naming_symbols.const_private_field.applicable_accessibilities = private
|
||||
|
||||
dotnet_naming_symbols.internal_field.applicable_kinds = field
|
||||
dotnet_naming_symbols.internal_field.applicable_accessibilities = internal
|
||||
|
||||
dotnet_naming_symbols.static_private_or_internal_field.required_modifiers = static
|
||||
dotnet_naming_symbols.static_private_or_internal_field.applicable_accessibilities = internal, private
|
||||
|
||||
dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private
|
||||
|
||||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal
|
||||
|
||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal
|
||||
|
||||
# Naming rules
|
||||
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||
|
||||
|
||||
dotnet_naming_rule.static_private_or_internal_field_should_be_pascal_case.severity = none
|
||||
dotnet_naming_rule.static_private_or_internal_field_should_be_pascal_case.symbols = static_private_or_internal_field
|
||||
dotnet_naming_rule.static_private_or_internal_field_should_be_pascal_case.style = pascal_case
|
||||
|
||||
dotnet_naming_rule.const_private_field_should_be_pascal_case.severity = warning
|
||||
dotnet_naming_rule.const_private_field_should_be_pascal_case.symbols = const_private_field
|
||||
dotnet_naming_rule.const_private_field_should_be_pascal_case.style = pascal_case
|
||||
|
||||
dotnet_naming_rule.const_private_or_internal_field_should_be_pascal_case.severity = warning
|
||||
dotnet_naming_rule.const_private_or_internal_field_should_be_pascal_case.symbols = internal_field
|
||||
dotnet_naming_rule.const_private_or_internal_field_should_be_pascal_case.style = pascal_case
|
||||
|
||||
dotnet_naming_rule.private_or_internal_field_should_be_camel_case.severity = warning
|
||||
dotnet_naming_rule.private_or_internal_field_should_be_camel_case.symbols = private_or_internal_field
|
||||
dotnet_naming_rule.private_or_internal_field_should_be_camel_case.style = camel_case
|
||||
|
||||
# Naming rules
|
||||
|
||||
#require a space before the colon for bases or interfaces in a type declaration
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
#require a space after a keyword in a control flow statement such as a for loop
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
#require a space before the colon for bases or interfaces in a type declaration
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
|
||||
#Formatting - wrapping options
|
||||
|
||||
#leave code block on single line
|
||||
csharp_preserve_single_line_blocks = true
|
||||
#leave statements and member declarations on the same line
|
||||
csharp_preserve_single_line_statements = true
|
||||
|
||||
#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them
|
||||
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||
|
||||
#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
||||
|
||||
; 4-column tab indentation
|
||||
[*.yaml]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
indent_size = 4
|
||||
|
||||
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
@@ -3,6 +3,8 @@
|
||||
|
||||
language: csharp
|
||||
mono: 6.4.0
|
||||
os: linux
|
||||
dist: xenial
|
||||
|
||||
jobs:
|
||||
include:
|
||||
@@ -12,17 +14,13 @@ jobs:
|
||||
if: tag IS present
|
||||
osx_image: xcode10
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- thirdparty/download
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- lua5.1
|
||||
- dpkg
|
||||
- zsync
|
||||
- markdown
|
||||
- imagemagick
|
||||
|
||||
# Environment variables
|
||||
env:
|
||||
@@ -34,25 +32,28 @@ env:
|
||||
# call OpenRA to check for YAML errors
|
||||
# Run the NUnit tests
|
||||
script:
|
||||
- travis_retry make all-dependencies
|
||||
- make all
|
||||
- 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"
|
||||
- |
|
||||
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
|
||||
make check || travis_terminate 1;
|
||||
make check-scripts || travis_terminate 1;
|
||||
make test || travis_terminate 1;
|
||||
mono ~/.nuget/packages/nunit.consolerunner/3.11.1/tools/nunit3-console.exe --noresult OpenRA.Test.dll || travis_terminate 1;
|
||||
fi
|
||||
|
||||
# Only watch the development branch and tagged release.
|
||||
branches:
|
||||
only:
|
||||
- /^release-.*$/
|
||||
- /^playtest-.*$/
|
||||
- /^pkgtest-.*$/
|
||||
- /^devtest-.*$/
|
||||
- /^prep-.*$/
|
||||
- bleed
|
||||
|
||||
# Notify developers when build passed/failed.
|
||||
notifications:
|
||||
irc:
|
||||
if: repo = OpenRA/OpenRA
|
||||
template:
|
||||
- "%{repository}#%{build_number} %{commit} %{author}: %{message} %{build_url}"
|
||||
channels:
|
||||
@@ -61,26 +62,31 @@ notifications:
|
||||
skip_join: true
|
||||
|
||||
before_deploy:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
|
||||
- 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
|
||||
echo ${TRAVIS_REPO_SLUG};
|
||||
if [[ "${TRAVIS_REPO_SLUG}" == "OpenRA/OpenRA" ]]; then
|
||||
cd packaging && ./update-wiki.sh ${TRAVIS_TAG} && cd ..;
|
||||
fi;
|
||||
fi
|
||||
- export PATH=${PATH}:${HOME}/usr/bin
|
||||
- DOTVERSION=`echo ${TRAVIS_TAG} | sed "s/-/\\./g"`
|
||||
- cd packaging
|
||||
- mkdir build
|
||||
- ./package-all.sh ${TRAVIS_TAG} ${PWD}/build/
|
||||
- if [[ "${TRAVIS_REPO_SLUG}" == "OpenRA/OpenRA" ]]; then
|
||||
./upload-itch.sh ${TRAVIS_TAG} ${PWD}/build/;
|
||||
fi
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: "g/LU11f+mjqv+lj0sR1UliHwogXL4ofJUwoG5Dbqlvdf5UTLWytw/OWSCv8RGyuh10miyWeaoqHh1cn2C1IFhUEqN1sSeKKKOWOTvJ2FR5mzi9uH3d/MOBzG5icQ7Qh0fZ1YPz5RaJJhYu6bmfvA/1gD49GoaX2kxQL4J5cEBgg="
|
||||
token: ${GH_DEPLOY_API_KEY}
|
||||
file_glob: true
|
||||
file: build/*
|
||||
skip_cleanup: true
|
||||
on:
|
||||
all_branches: true
|
||||
tags: true
|
||||
repo: OpenRA/OpenRA
|
||||
|
||||
7
.vscode/extensions.json
vendored
Normal file
7
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"ms-dotnettools.csharp",
|
||||
"EditorConfig.EditorConfig",
|
||||
"ms-vscode.mono-debug"
|
||||
]
|
||||
}
|
||||
61
.vscode/launch.json
vendored
Normal file
61
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch (TD)",
|
||||
"type": "clr",
|
||||
"linux": {
|
||||
"type": "mono"
|
||||
},
|
||||
"osx": {
|
||||
"type": "mono"
|
||||
},
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/OpenRA.Game.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": ["Game.Mod=cnc"]
|
||||
},
|
||||
{
|
||||
"name": "Launch (RA)",
|
||||
"type": "clr",
|
||||
"linux": {
|
||||
"type": "mono"
|
||||
},
|
||||
"osx": {
|
||||
"type": "mono"
|
||||
},
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/OpenRA.Game.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": ["Game.Mod=ra"]
|
||||
},
|
||||
{
|
||||
"name": "Launch (D2k)",
|
||||
"type": "clr",
|
||||
"linux": {
|
||||
"type": "mono"
|
||||
},
|
||||
"osx": {
|
||||
"type": "mono"
|
||||
},
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/OpenRA.Game.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": ["Game.Mod=d2k"]
|
||||
},
|
||||
{
|
||||
"name": "Launch (TS)",
|
||||
"type": "clr",
|
||||
"linux": {
|
||||
"type": "mono"
|
||||
},
|
||||
"osx": {
|
||||
"type": "mono"
|
||||
},
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/OpenRA.Game.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": ["Game.Mod=ts"]
|
||||
},
|
||||
]
|
||||
}
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"omnisharp.enableRoslynAnalyzers": true
|
||||
}
|
||||
13
.vscode/tasks.json
vendored
Normal file
13
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "make",
|
||||
"args": ["all"],
|
||||
"windows": {
|
||||
"command": "make.cmd"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
15
AUTHORS
15
AUTHORS
@@ -40,6 +40,7 @@ Also thanks to:
|
||||
* Biofreak
|
||||
* Braxton Williams (Buddytex)
|
||||
* Brendan Gluth (Mechanical_Man)
|
||||
* Brent Gardner (bggardner)
|
||||
* Bryan Wilbur
|
||||
* Bugra Cuhadaroglu (BugraC)
|
||||
* Chris Cameron (Vesuvian)
|
||||
@@ -137,6 +138,7 @@ Also thanks to:
|
||||
* Sebastien Kerguen (xanax)
|
||||
* Shawn Collins (UberWaffe)
|
||||
* Simon Verbeke (Saticmotion)
|
||||
* Stuart McHattie (SDJMcHattie)
|
||||
* Taryn Hill (Phrohdoh)
|
||||
* Teemu Nieminen (Temeez)
|
||||
* Tim Mylemans (gecko)
|
||||
@@ -147,6 +149,7 @@ Also thanks to:
|
||||
* Tristan Mühlbacher (MicroBit)
|
||||
* UnknownProgrammer
|
||||
* Vladimir Komarov (VrKomarov)
|
||||
* Wojciech Walaszek (Voidwalker)
|
||||
* Wuschel
|
||||
|
||||
Using GNU FreeFont distributed under the GNU GPL
|
||||
@@ -160,9 +163,6 @@ FreeType License.
|
||||
|
||||
Using OpenAL Soft distributed under the GNU LGPL.
|
||||
|
||||
Using MaxMind GeoIP2 .NET API distributed under
|
||||
the Apache 2.0 license.
|
||||
|
||||
Using SDL2-CS and OpenAL-CS created by Ethan
|
||||
Lee and released under the zlib license.
|
||||
|
||||
@@ -182,6 +182,15 @@ Krueger and distributed under the GNU GPL terms.
|
||||
Using rix0rrr.BeaconLib developed by Rico Huijbers
|
||||
distributed under MIT License.
|
||||
|
||||
Using DiscordRichPresence developed by Lachee
|
||||
distributed under MIT License.
|
||||
|
||||
Using Json.NET developed by James Newton-King
|
||||
distributed under MIT License.
|
||||
|
||||
This site or product includes IP2Location LITE data
|
||||
available from http://www.ip2location.com.
|
||||
|
||||
Finally, special thanks goes to the original teams
|
||||
at Westwood Studios and EA for creating the classic
|
||||
games which OpenRA aims to reimagine.
|
||||
|
||||
File diff suppressed because one or more lines are too long
41
INSTALL.md
41
INSTALL.md
@@ -8,15 +8,9 @@ Windows
|
||||
|
||||
Compiling OpenRA requires the following dependencies:
|
||||
* [Windows PowerShell >= 4.0](http://microsoft.com/powershell) (included by default in recent Windows 10 versions)
|
||||
* [.NET Framework 4.6.1 (Developer Pack)](https://dotnet.microsoft.com/download/dotnet-framework/net461) (or via Visual Studio 2017)
|
||||
* [.NET Framework 4.7.2 (Developer Pack)](https://dotnet.microsoft.com/download/dotnet-framework/net472) (or via Visual Studio 2017)
|
||||
* [.NET Core 2.2 SDK](https://dotnet.microsoft.com/download/dotnet-core/2.2) (or via Visual Studio 2017)
|
||||
|
||||
Type `make dependencies` in a command terminal to download pre-compiled native libraries for:
|
||||
* [SDL 2](http://www.libsdl.org/download-2.0.php)
|
||||
* [FreeType](http://gnuwin32.sourceforge.net/packages/freetype.htm)
|
||||
* [zlib](http://gnuwin32.sourceforge.net/packages/zlib.htm)
|
||||
* [OpenAL](http://kcat.strangesoft.net/openal.html)
|
||||
* [liblua 5.1](http://luabinaries.sourceforge.net/download.html)
|
||||
|
||||
To compile OpenRA, open the `OpenRA.sln` solution in the main folder, build it from the command-line with MSBuild or use the Makefile analogue command `make all` scripted in PowerShell syntax.
|
||||
|
||||
@@ -25,13 +19,19 @@ Run the game with `launch-game.cmd`. It can be handed arguments that specify the
|
||||
Linux
|
||||
=====
|
||||
|
||||
Mono, version 5.4 or later, is required to compile OpenRA. You can add the [upstream mono repository](https://www.mono-project.com/download/stable/#download-lin) for your distro to obtain the latest version if your system packages are not sufficient.
|
||||
Mono, version 5.18 or later, is required to compile OpenRA. You can add the [upstream mono repository](https://www.mono-project.com/download/stable/#download-lin) for your distro to obtain the latest version if your system packages are not sufficient.
|
||||
|
||||
Use `make dependencies` to map the native libraries to your system and fetch the remaining CLI dependencies to place them at the appropriate places.
|
||||
To compile OpenRA, run `make` from the command line. After this one can run the game with `./launch-game.sh`. It is also possible to specify the mod you wish to run from the command line, e.g. with `./launch-game.sh Game.Mod=ts` if you wish to try the experimental Tiberian Sun mod.
|
||||
|
||||
To compile OpenRA, run `make all` from the command line. After this one can run the game with `./launch-game.sh`. It is also possible to specify the mod you wish to run from the command line, e.g. with `./launch-game.sh Game.Mod=ts` if you wish to try the experimental Tiberian Sun mod.
|
||||
The default behaviour on the x86_64 architecture is to download several pre-compiled native libraries using the Nuget packaging manager. If you prefer to use system libraries, compile instead using `make TARGETPLATFORM=unix-generic`.
|
||||
|
||||
Type `sudo make install` for system-wide installation. Run `sudo make install-linux-shortcuts` to get startup scripts, icons and desktop files. You can then run the Red Alert by executing the `openra-ra` command, the Dune 2000 mod by running the `openra-d2k` command and Tiberian Dawn by the `openra-cnc` command. Alternatively, you can also run these mods by clicking on their desktop shortcuts if you ran `sudo make install-linux-shortcuts`.
|
||||
If you choose to use system libraries, or your system is not x86_64, you will need to install the following using your system package manager:
|
||||
* [SDL 2](http://www.libsdl.org/download-2.0.php)
|
||||
* [FreeType](http://gnuwin32.sourceforge.net/packages/freetype.htm)
|
||||
* [OpenAL](http://kcat.strangesoft.net/openal.html)
|
||||
* [liblua 5.1](http://luabinaries.sourceforge.net/download.html)
|
||||
|
||||
Type `sudo make install` for system-wide installation. Run `sudo make install-linux-shortcuts` to get startup scripts, icons and desktop files. You can then run the Red Alert by executing the `openra-ra` command, the Dune 2000 mod by running the `openra-d2k` command and Tiberian Dawn by the `openra-cnc` command. Alternatively, you can also run these mods by clicking on their desktop shortcuts if you ran `sudo make install-linux-shortcuts`.
|
||||
|
||||
Arch Linux
|
||||
----------
|
||||
@@ -89,24 +89,23 @@ sudo zypper in mono-devel openal-soft freetype2 SDL2 lua51 xdg-utils zenity
|
||||
Red Hat Enterprise Linux (and rebuilds, e.g. CentOS)
|
||||
----------------------------------------------------
|
||||
|
||||
The EPEL repository is required in order for the following command to run properly.
|
||||
The EPEL repository is required in order for the following command to run properly.
|
||||
|
||||
```
|
||||
sudo yum install "pkgconfig(mono)" SDL2 freetype "lua = 5.1" openal-soft xdg-utils zenity
|
||||
```
|
||||
|
||||
OSX
|
||||
macOS
|
||||
=====
|
||||
|
||||
Before compiling OpenRA you must install the following dependencies:
|
||||
* [Mono >= 5.4](https://www.mono-project.com/download/stable/#download-mac)
|
||||
* [Mono >= 5.18](https://www.mono-project.com/download/stable/#download-mac)
|
||||
|
||||
Use `make dependencies` to download pre-compiled native libraries for:
|
||||
* [SDL 2](http://www.libsdl.org/download-2.0.php)
|
||||
* [FreeType](http://gnuwin32.sourceforge.net/packages/freetype.htm)
|
||||
* [OpenAL](http://kcat.strangesoft.net/openal.html)
|
||||
* [liblua 5.1](http://luabinaries.sourceforge.net/download.html)
|
||||
To compile OpenRA, run `make` from the command line. Run with `./launch-game.sh`.
|
||||
|
||||
To compile OpenRA, run `make` from the command line.
|
||||
The default behaviour is to download several pre-compiled native libraries using the Nuget packaging manager. If you prefer to use system libraries, compile instead using `make TARGETPLATFORM=unix-generic`. If you choose to use system libraries you will need to install:
|
||||
* [SDL 2](http://www.libsdl.org/download-2.0.php) (`brew install sdl2`)
|
||||
* [FreeType](http://gnuwin32.sourceforge.net/packages/freetype.htm) (`brew install freetype`)
|
||||
* [OpenAL](http://kcat.strangesoft.net/openal.html) (`brew install openal-soft`)
|
||||
* [liblua 5.1](http://luabinaries.sourceforge.net/download.html) (`brew install lua@5.1`)
|
||||
|
||||
Run with `./launch-game.sh`.
|
||||
|
||||
174
Makefile
174
Makefile
@@ -3,15 +3,13 @@
|
||||
# to compile, run:
|
||||
# make [DEBUG=true]
|
||||
#
|
||||
# to check unit tests (requires NUnit version >= 2.6), run:
|
||||
# make nunit [NUNIT_CONSOLE=<path-to/nunit[2]-console>] [NUNIT_LIBS_PATH=<path-to-libs-dir>] [NUNIT_LIBS=<nunit-libs>]
|
||||
# Use NUNIT_CONSOLE if nunit[3|2]-console was not downloaded by `make dependencies` nor is it in bin search paths
|
||||
# Use NUNIT_LIBS_PATH if NUnit libs are not in search paths. Include trailing /
|
||||
# Use NUNIT_LIBS if NUnit libs have different names (such as including a prefix or suffix)
|
||||
# to compile using system libraries for native dependencies, run:
|
||||
# make [DEBUG=true] TARGETPLATFORM=unix-generic
|
||||
#
|
||||
# to check the official mods for erroneous yaml files, run:
|
||||
# make test
|
||||
#
|
||||
# to check the official mod dlls for StyleCop violations, run:
|
||||
# to check the engine and official mod dlls for code style violations, run:
|
||||
# make check
|
||||
#
|
||||
# to install, run:
|
||||
@@ -22,6 +20,7 @@
|
||||
#
|
||||
# to install the engine and common mod files (omitting the default mods):
|
||||
# make install-engine
|
||||
# make install-dependencies
|
||||
# make install-common-mod-files
|
||||
#
|
||||
# to uninstall, run:
|
||||
@@ -40,11 +39,11 @@
|
||||
WHITELISTED_OPENRA_ASSEMBLIES = OpenRA.Game.exe OpenRA.Utility.exe OpenRA.Platforms.Default.dll OpenRA.Mods.Common.dll OpenRA.Mods.Cnc.dll OpenRA.Mods.D2k.dll OpenRA.Game.dll
|
||||
|
||||
# These are explicitly shipped alongside our core files by the packaging script
|
||||
WHITELISTED_THIRDPARTY_ASSEMBLIES = ICSharpCode.SharpZipLib.dll FuzzyLogicLibrary.dll MaxMind.Db.dll Eluant.dll rix0rrr.BeaconLib.dll Open.Nat.dll SDL2-CS.dll OpenAL-CS.dll
|
||||
WHITELISTED_THIRDPARTY_ASSEMBLIES = ICSharpCode.SharpZipLib.dll FuzzyLogicLibrary.dll Eluant.dll BeaconLib.dll Open.Nat.dll SDL2-CS.dll OpenAL-CS.Core.dll DiscordRPC.dll Newtonsoft.Json.dll
|
||||
|
||||
# These are shipped in our custom minimal mono runtime and also available in the full system-installed .NET/mono stack
|
||||
# This list *must* be kept in sync with the files packaged by the AppImageSupport and OpenRALauncherOSX repositories
|
||||
WHITELISTED_CORE_ASSEMBLIES = mscorlib.dll System.dll System.Configuration.dll System.Core.dll System.Numerics.dll System.Security.dll System.Xml.dll Mono.Security.dll
|
||||
WHITELISTED_CORE_ASSEMBLIES = mscorlib.dll System.dll System.Configuration.dll System.Core.dll System.Numerics.dll System.Security.dll System.Xml.dll Mono.Security.dll netstandard.dll
|
||||
|
||||
NUNIT_LIBS_PATH :=
|
||||
NUNIT_LIBS := $(NUNIT_LIBS_PATH)nunit.framework.dll
|
||||
@@ -81,24 +80,31 @@ MSBUILD = msbuild -verbosity:m -nologo
|
||||
# Enable 32 bit builds while generating the windows installer
|
||||
WIN32 = false
|
||||
|
||||
# dependencies
|
||||
ifndef TARGETPLATFORM
|
||||
UNAME_S := $(shell uname -s)
|
||||
UNAME_M := $(shell uname -m)
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
TARGETPLATFORM = osx-x64
|
||||
else
|
||||
ifeq ($(UNAME_M),x86_64)
|
||||
TARGETPLATFORM = linux-x64
|
||||
else
|
||||
TARGETPLATFORM = unix-generic
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
# program targets
|
||||
VERSION = $(shell git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || echo git-`git rev-parse --short HEAD`)
|
||||
|
||||
# dependencies
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
os-dependencies = osx-dependencies
|
||||
else
|
||||
os-dependencies = linux-dependencies
|
||||
endif
|
||||
|
||||
check-scripts:
|
||||
@echo
|
||||
@echo "Checking for Lua syntax errors..."
|
||||
@luac -p $(shell find mods/*/maps/* -iname '*.lua')
|
||||
@luac -p $(shell find lua/* -iname '*.lua')
|
||||
|
||||
check: dependencies
|
||||
check:
|
||||
@echo
|
||||
@echo "Compiling in debug mode..."
|
||||
@$(MSBUILD) -t:build -p:Configuration=Debug
|
||||
@@ -112,26 +118,6 @@ check: dependencies
|
||||
@echo "Checking for incorrect conditional trait interface overrides..."
|
||||
@mono --debug OpenRA.Utility.exe all --check-conditional-trait-interface-overrides
|
||||
|
||||
|
||||
NUNIT_CONSOLE := $(shell test -f thirdparty/download/nunit3-console.exe && echo mono thirdparty/download/nunit3-console.exe || \
|
||||
which nunit3-console 2>/dev/null || which nunit2-console 2>/dev/null || which nunit-console 2>/dev/null)
|
||||
nunit: core
|
||||
@echo
|
||||
@echo "Checking unit tests..."
|
||||
@if [ "$(NUNIT_CONSOLE)" = "" ] ; then \
|
||||
echo 'nunit[3|2]-console not found!'; \
|
||||
echo 'Was "make dependencies" called or is NUnit installed?'>&2; \
|
||||
echo 'See "make help".'; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if $(NUNIT_CONSOLE) --help | head -n 1 | grep -E "NUnit version (1|2\.[0-5])";then \
|
||||
echo 'NUnit version >= 2.6 required'>&2; \
|
||||
echo 'Try "make dependencies" first to use NUnit from NuGet.'>&2; \
|
||||
echo 'See "make help".'; \
|
||||
exit 1; \
|
||||
fi
|
||||
@$(NUNIT_CONSOLE) --noresult OpenRA.Test.nunit
|
||||
|
||||
test: core
|
||||
@echo
|
||||
@echo "Testing Tiberian Sun mod MiniYAML..."
|
||||
@@ -148,51 +134,21 @@ test: core
|
||||
|
||||
########################## MAKE/INSTALL RULES ##########################
|
||||
#
|
||||
all: dependencies core
|
||||
all: core
|
||||
|
||||
core:
|
||||
@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
|
||||
@command -v $(firstword $(MSBUILD)) >/dev/null || (echo "OpenRA requires the '$(MSBUILD)' tool provided by Mono >= 5.18."; exit 1)
|
||||
@$(MSBUILD) -t:Build -restore -p:Configuration=Release -p:TargetPlatform=$(TARGETPLATFORM)
|
||||
ifeq ($(TARGETPLATFORM), unix-generic)
|
||||
@./configure-system-libraries.sh
|
||||
endif
|
||||
@./fetch-geoip.sh
|
||||
|
||||
clean:
|
||||
@ $(MSBUILD) -t:clean
|
||||
@-$(RM_F) *.config
|
||||
@-$(RM_F) *.exe *.dll *.dylib ./OpenRA*/*.dll *.pdb mods/**/*.dll mods/**/*.pdb *.resources
|
||||
@-$(RM_F) *.config IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP
|
||||
@-$(RM_F) *.exe *.dll *.dll.config *.so *.dylib ./OpenRA*/*.dll *.pdb mods/**/*.dll mods/**/*.pdb *.resources
|
||||
@-$(RM_RF) ./*/bin ./*/obj
|
||||
@-$(RM_RF) ./thirdparty/download
|
||||
|
||||
distclean: clean
|
||||
|
||||
cli-dependencies:
|
||||
@./thirdparty/fetch-thirdparty-deps.sh
|
||||
@ $(CP_R) thirdparty/download/*.dll .
|
||||
@ $(CP_R) thirdparty/download/*.dll.config .
|
||||
@ test -f OpenRA.Game/obj/project.assets.json || $(MSBUILD) -t:restore
|
||||
|
||||
linux-dependencies: cli-dependencies linux-native-dependencies
|
||||
|
||||
linux-native-dependencies:
|
||||
@./thirdparty/configure-native-deps.sh
|
||||
|
||||
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
|
||||
@./thirdparty/fetch-thirdparty-deps-osx.sh
|
||||
@ $(CP_R) thirdparty/download/osx/*.dylib .
|
||||
@ $(CP_R) thirdparty/download/osx/*.dll.config .
|
||||
|
||||
dependencies: $(os-dependencies)
|
||||
|
||||
all-dependencies: cli-dependencies windows-dependencies osx-dependencies
|
||||
@ $(MSBUILD) -t:clean
|
||||
|
||||
version: VERSION mods/ra/mod.yaml mods/cnc/mod.yaml mods/d2k/mod.yaml mods/ts/mod.yaml mods/modcontent/mod.yaml mods/all/mod.yaml
|
||||
@echo "$(VERSION)" > VERSION
|
||||
@@ -202,10 +158,34 @@ version: VERSION mods/ra/mod.yaml mods/cnc/mod.yaml mods/d2k/mod.yaml mods/ts/mo
|
||||
rm $${i}.tmp; \
|
||||
done
|
||||
|
||||
install: dependencies core install-core
|
||||
install: core install-engine install-common-mod-files install-default-mods
|
||||
@$(CP) *.sh "$(DATA_INSTALL_DIR)"
|
||||
|
||||
install-linux-shortcuts: install-linux-scripts install-linux-icons install-linux-desktop
|
||||
|
||||
install-dependencies:
|
||||
ifeq ($(TARGETPLATFORM), $(filter $(TARGETPLATFORM),win-x86 win-x64))
|
||||
@-echo "Installing OpenRA dependencies to $(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) soft_oal.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) SDL2.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) freetype6.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) lua51.dll "$(DATA_INSTALL_DIR)"
|
||||
endif
|
||||
ifeq ($(TARGETPLATFORM), linux-x64)
|
||||
@-echo "Installing OpenRA dependencies to $(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) soft_oal.so "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) SDL2.so "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) freetype6.so "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) lua51.so "$(DATA_INSTALL_DIR)"
|
||||
endif
|
||||
ifeq ($(TARGETPLATFORM), osx-x64)
|
||||
@-echo "Installing OpenRA dependencies to $(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) soft_oal.dylib "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) SDL2.dylib "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) freetype6.dylib "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) lua51.dylib "$(DATA_INSTALL_DIR)"
|
||||
endif
|
||||
|
||||
install-engine:
|
||||
@-echo "Installing OpenRA engine to $(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_DIR) "$(DATA_INSTALL_DIR)"
|
||||
@@ -214,10 +194,13 @@ install-engine:
|
||||
@$(INSTALL_PROGRAM) OpenRA.Utility.exe "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) OpenRA.Platforms.Default.dll "$(DATA_INSTALL_DIR)"
|
||||
|
||||
ifneq ($(TARGETPLATFORM), $(filter $(TARGETPLATFORM),win-x86 win-x64))
|
||||
@$(INSTALL_DATA) OpenRA.Platforms.Default.dll.config "$(DATA_INSTALL_DIR)"
|
||||
endif
|
||||
@$(INSTALL_DATA) VERSION "$(DATA_INSTALL_DIR)/VERSION"
|
||||
@$(INSTALL_DATA) AUTHORS "$(DATA_INSTALL_DIR)/AUTHORS"
|
||||
@$(INSTALL_DATA) COPYING "$(DATA_INSTALL_DIR)/COPYING"
|
||||
@$(INSTALL_DATA) IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP "$(DATA_INSTALL_DIR)/IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP"
|
||||
|
||||
@$(CP_R) glsl "$(DATA_INSTALL_DIR)"
|
||||
@$(CP_R) lua "$(DATA_INSTALL_DIR)"
|
||||
@@ -227,8 +210,9 @@ install-engine:
|
||||
@$(INSTALL_PROGRAM) ICSharpCode.SharpZipLib.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) FuzzyLogicLibrary.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) Open.Nat.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) MaxMind.Db.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) rix0rrr.BeaconLib.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) BeaconLib.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) DiscordRPC.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) Newtonsoft.Json.dll "$(DATA_INSTALL_DIR)"
|
||||
|
||||
install-common-mod-files:
|
||||
@-echo "Installing OpenRA common mod files to $(DATA_INSTALL_DIR)"
|
||||
@@ -247,19 +231,16 @@ install-default-mods:
|
||||
@$(INSTALL_PROGRAM) mods/d2k/OpenRA.Mods.D2k.dll "$(DATA_INSTALL_DIR)/mods/d2k"
|
||||
@$(CP_R) mods/modcontent "$(DATA_INSTALL_DIR)/mods/"
|
||||
|
||||
install-core: install-engine install-common-mod-files install-default-mods
|
||||
@$(CP) *.sh "$(DATA_INSTALL_DIR)"
|
||||
|
||||
install-linux-icons:
|
||||
for SIZE in 16x16 32x32 48x48 64x64 128x128; do \
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps"; \
|
||||
$(INSTALL_DATA) packaging/linux/icons/ra_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-ra.png"; \
|
||||
$(INSTALL_DATA) packaging/linux/icons/cnc_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-cnc.png"; \
|
||||
$(INSTALL_DATA) packaging/linux/icons/d2k_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-d2k.png"; \
|
||||
$(INSTALL_DATA) packaging/artwork/ra_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-ra.png"; \
|
||||
$(INSTALL_DATA) packaging/artwork/cnc_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-cnc.png"; \
|
||||
$(INSTALL_DATA) packaging/artwork/d2k_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-d2k.png"; \
|
||||
done
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps"
|
||||
$(INSTALL_DATA) packaging/linux/icons/ra_scalable.svg "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/openra-ra.svg"
|
||||
$(INSTALL_DATA) packaging/linux/icons/cnc_scalable.svg "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/openra-cnc.svg"
|
||||
$(INSTALL_DATA) packaging/artwork/ra_scalable.svg "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/openra-ra.svg"
|
||||
$(INSTALL_DATA) packaging/artwork/cnc_scalable.svg "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/openra-cnc.svg"
|
||||
|
||||
install-linux-desktop:
|
||||
@$(INSTALL_DIR) "$(DESTDIR)$(datadir)/applications"
|
||||
@@ -353,23 +334,28 @@ uninstall:
|
||||
|
||||
help:
|
||||
@echo 'to compile, run:'
|
||||
@echo ' make [DEBUG=false]'
|
||||
@echo ' make [DEBUG=true]'
|
||||
@echo
|
||||
@echo 'to check unit tests (requires NUnit version >= 2.6), run:'
|
||||
@echo ' make nunit [NUNIT_CONSOLE=<path-to/nunit[3|2]-console>] [NUNIT_LIBS_PATH=<path-to-libs-dir>] [NUNIT_LIBS=<nunit-libs>]'
|
||||
@echo ' Use NUNIT_CONSOLE if nunit[3|2]-console was not downloaded by `make dependencies` nor is it in bin search paths'
|
||||
@echo ' Use NUNIT_LIBS_PATH if NUnit libs are not in search paths. Include trailing /'
|
||||
@echo ' Use NUNIT_LIBS if NUnit libs have different names (such as including a prefix or suffix)'
|
||||
@echo 'to compile using system libraries for native dependencies, run:'
|
||||
@echo ' make [DEBUG=true] TARGETPLATFORM=unix-generic'
|
||||
@echo
|
||||
@echo 'to check the official mods for erroneous yaml files, run:'
|
||||
@echo ' make test'
|
||||
@echo
|
||||
@echo 'to check the engine and official mod dlls for code style violations, run:'
|
||||
@echo ' make test'
|
||||
@echo
|
||||
@echo 'to install, run:'
|
||||
@echo ' make [prefix=/foo] [bindir=/bar/bin] install'
|
||||
@echo
|
||||
@echo 'to install Linux startup scripts, desktop files and icons'
|
||||
@echo ' make install-linux-shortcuts [DEBUG=false]'
|
||||
@echo
|
||||
@echo ' to install the engine and common mod files (omitting the default mods):'
|
||||
@echo ' make install-engine'
|
||||
@echo ' make install-dependencies'
|
||||
@echo ' make install-common-mod-files'
|
||||
@echo
|
||||
@echo 'to uninstall, run:'
|
||||
@echo ' make uninstall'
|
||||
@echo
|
||||
@@ -382,4 +368,4 @@ help:
|
||||
|
||||
.SUFFIXES:
|
||||
|
||||
.PHONY: check-scripts check nunit test all core clean distclean cli-dependencies linux-dependencies linux-native-dependencies windows-dependencies osx-dependencies dependencies all-dependencies version install install-linux-shortcuts install-engine install-common-mod-files install-default-mods install-core install-linux-icons install-linux-desktop install-linux-mime install-linux-appdata install-man-page install-linux-scripts uninstall help
|
||||
.PHONY: check-scripts check test all core clean version install install-linux-shortcuts install-dependencies install-engine install-common-mod-files install-default-mods install-linux-icons install-linux-desktop install-linux-mime install-linux-appdata install-man-page install-linux-scripts uninstall help
|
||||
|
||||
@@ -185,8 +185,7 @@ namespace OpenRA.Activities
|
||||
/// </summary>
|
||||
internal void OnActorDisposeOuter(Actor self)
|
||||
{
|
||||
if (ChildActivity != null)
|
||||
ChildActivity.OnActorDisposeOuter(self);
|
||||
ChildActivity?.OnActorDisposeOuter(self);
|
||||
|
||||
OnActorDispose(self);
|
||||
}
|
||||
@@ -199,8 +198,7 @@ namespace OpenRA.Activities
|
||||
if (!IsInterruptible)
|
||||
return;
|
||||
|
||||
if (ChildActivity != null)
|
||||
ChildActivity.Cancel(self);
|
||||
ChildActivity?.Cancel(self);
|
||||
|
||||
// Directly mark activities that are queued and therefore didn't run yet as done
|
||||
State = State == ActivityState.Queued ? ActivityState.Done : ActivityState.Canceling;
|
||||
@@ -243,11 +241,9 @@ namespace OpenRA.Activities
|
||||
|
||||
Console.WriteLine(GetType().ToString().Split('.').Last());
|
||||
|
||||
if (ChildActivity != null)
|
||||
ChildActivity.PrintActivityTree(self, origin, level + 1);
|
||||
ChildActivity?.PrintActivityTree(self, origin, level + 1);
|
||||
|
||||
if (NextActivity != null)
|
||||
NextActivity.PrintActivityTree(self, origin, level);
|
||||
NextActivity?.PrintActivityTree(self, origin, level);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace OpenRA.Activities
|
||||
|
||||
public override bool Tick(Actor self)
|
||||
{
|
||||
if (a != null) a();
|
||||
a?.Invoke();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
@@ -68,12 +69,35 @@ namespace OpenRA
|
||||
{
|
||||
get
|
||||
{
|
||||
// TODO: Support non-zero pitch/roll in IFacing (IOrientation?)
|
||||
var facingValue = facing != null ? facing.Facing : 0;
|
||||
return new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(facingValue));
|
||||
return facing != null ? facing.Orientation : WRot.None;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Value used to represent an invalid token.</summary>
|
||||
public static readonly int InvalidConditionToken = -1;
|
||||
|
||||
class ConditionState
|
||||
{
|
||||
/// <summary>Delegates that have registered to be notified when this condition changes.</summary>
|
||||
public readonly List<VariableObserverNotifier> Notifiers = new List<VariableObserverNotifier>();
|
||||
|
||||
/// <summary>Unique integers identifying granted instances of the condition.</summary>
|
||||
public readonly HashSet<int> Tokens = new HashSet<int>();
|
||||
}
|
||||
|
||||
readonly Dictionary<string, ConditionState> conditionStates = new Dictionary<string, ConditionState>();
|
||||
|
||||
/// <summary>Each granted condition receives a unique token that is used when revoking.</summary>
|
||||
readonly Dictionary<int, string> conditionTokens = new Dictionary<int, string>();
|
||||
|
||||
int nextConditionToken = 1;
|
||||
|
||||
/// <summary>Cache of condition -> enabled state for quick evaluation of token counter conditions.</summary>
|
||||
readonly Dictionary<string, int> conditionCache = new Dictionary<string, int>();
|
||||
|
||||
/// <summary>Read-only version of conditionCache that is passed to IConditionConsumers.</summary>
|
||||
readonly IReadOnlyDictionary<string, int> readOnlyConditionCache;
|
||||
|
||||
internal SyncHash[] SyncHashes { get; private set; }
|
||||
|
||||
readonly IFacing facing;
|
||||
@@ -91,12 +115,21 @@ namespace OpenRA
|
||||
|
||||
internal Actor(World world, string name, TypeDictionary initDict)
|
||||
{
|
||||
var duplicateInit = initDict.WithInterface<ISingleInstanceInit>().GroupBy(i => i.GetType())
|
||||
.FirstOrDefault(i => i.Count() > 1);
|
||||
|
||||
if (duplicateInit != null)
|
||||
throw new InvalidDataException("Duplicate initializer '{0}'".F(duplicateInit.Key.Name));
|
||||
|
||||
var init = new ActorInitializer(this, initDict);
|
||||
|
||||
readOnlyConditionCache = new ReadOnlyDictionary<string, int>(conditionCache);
|
||||
|
||||
World = world;
|
||||
ActorID = world.NextAID();
|
||||
if (initDict.Contains<OwnerInit>())
|
||||
Owner = init.Get<OwnerInit, Player>();
|
||||
var ownerInit = init.GetOrDefault<OwnerInit>();
|
||||
if (ownerInit != null)
|
||||
Owner = ownerInit.Value(world);
|
||||
|
||||
if (name != null)
|
||||
{
|
||||
@@ -144,13 +177,40 @@ namespace OpenRA
|
||||
SyncHashes = TraitsImplementing<ISync>().Select(sync => new SyncHash(sync)).ToArray();
|
||||
}
|
||||
|
||||
internal void Created()
|
||||
internal void Initialize(bool addToWorld = true)
|
||||
{
|
||||
created = true;
|
||||
|
||||
// Make sure traits are usable for condition notifiers
|
||||
foreach (var t in TraitsImplementing<INotifyCreated>())
|
||||
t.Created(this);
|
||||
|
||||
var allObserverNotifiers = new HashSet<VariableObserverNotifier>();
|
||||
foreach (var provider in TraitsImplementing<IObservesVariables>())
|
||||
{
|
||||
foreach (var variableUser in provider.GetVariableObservers())
|
||||
{
|
||||
allObserverNotifiers.Add(variableUser.Notifier);
|
||||
foreach (var variable in variableUser.Variables)
|
||||
{
|
||||
var cs = conditionStates.GetOrAdd(variable);
|
||||
cs.Notifiers.Add(variableUser.Notifier);
|
||||
|
||||
// Initialize conditions that have not yet been granted to 0
|
||||
// NOTE: Some conditions may have already been granted by INotifyCreated calling GrantCondition,
|
||||
// and we choose to assign the token count to safely cover both cases instead of adding an if branch.
|
||||
conditionCache[variable] = cs.Tokens.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update all traits with their initial condition state
|
||||
foreach (var notify in allObserverNotifiers)
|
||||
notify(this, readOnlyConditionCache);
|
||||
|
||||
// TODO: Some traits may need initialization after being notified of initial condition state.
|
||||
|
||||
// TODO: A post condition initialization notification phase may allow queueing activities instead.
|
||||
// The initial activity should run before any activities queued by INotifyCreated.Created
|
||||
// However, we need to know which traits are enabled (via conditions), so wait for after the calls and insert the activity as the first
|
||||
ICreationActivity creationActivity = null;
|
||||
@@ -171,6 +231,9 @@ namespace OpenRA
|
||||
activity.Queue(CurrentActivity);
|
||||
CurrentActivity = activity;
|
||||
}
|
||||
|
||||
if (addToWorld)
|
||||
World.Add(this);
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
@@ -233,7 +296,7 @@ namespace OpenRA
|
||||
yield return r;
|
||||
}
|
||||
|
||||
public Rectangle MouseBounds(WorldRenderer wr)
|
||||
public Polygon MouseBounds(WorldRenderer wr)
|
||||
{
|
||||
foreach (var mb in mouseBounds)
|
||||
{
|
||||
@@ -242,7 +305,7 @@ namespace OpenRA
|
||||
return bounds;
|
||||
}
|
||||
|
||||
return Rectangle.Empty;
|
||||
return Polygon.Empty;
|
||||
}
|
||||
|
||||
public void QueueActivity(bool queued, Activity nextActivity)
|
||||
@@ -266,8 +329,7 @@ namespace OpenRA
|
||||
|
||||
public void CancelActivity()
|
||||
{
|
||||
if (CurrentActivity != null)
|
||||
CurrentActivity.Cancel(this);
|
||||
CurrentActivity?.Cancel(this);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
@@ -319,8 +381,7 @@ namespace OpenRA
|
||||
{
|
||||
// If CurrentActivity isn't null, run OnActorDisposeOuter in case some cleanups are needed.
|
||||
// This should be done before the FrameEndTask to avoid dependency issues.
|
||||
if (CurrentActivity != null)
|
||||
CurrentActivity.OnActorDisposeOuter(this);
|
||||
CurrentActivity?.OnActorDisposeOuter(this);
|
||||
|
||||
// Allow traits/activities to prevent a race condition when they depend on disposing the actor (e.g. Transforms)
|
||||
WillDispose = true;
|
||||
@@ -339,8 +400,7 @@ namespace OpenRA
|
||||
World.TraitDict.RemoveActor(this);
|
||||
Disposed = true;
|
||||
|
||||
if (luaInterface != null)
|
||||
luaInterface.Value.OnActorDestroyed();
|
||||
luaInterface?.Value.OnActorDestroyed();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -454,6 +514,65 @@ namespace OpenRA
|
||||
return new[] { CenterPosition };
|
||||
}
|
||||
|
||||
#region Conditions
|
||||
|
||||
void UpdateConditionState(string condition, int token, bool isRevoke)
|
||||
{
|
||||
ConditionState conditionState = conditionStates.GetOrAdd(condition);
|
||||
|
||||
if (isRevoke)
|
||||
conditionState.Tokens.Remove(token);
|
||||
else
|
||||
conditionState.Tokens.Add(token);
|
||||
|
||||
conditionCache[condition] = conditionState.Tokens.Count;
|
||||
|
||||
// Conditions may be granted or revoked before the state is initialized.
|
||||
// These notifications will be processed after INotifyCreated.Created.
|
||||
if (created)
|
||||
foreach (var notify in conditionState.Notifiers)
|
||||
notify(this, readOnlyConditionCache);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Grants a specified condition if it is valid.
|
||||
/// Otherwise, just returns InvalidConditionToken.
|
||||
/// </summary>
|
||||
/// <returns>The token that is used to revoke this condition.</returns>
|
||||
public int GrantCondition(string condition)
|
||||
{
|
||||
if (string.IsNullOrEmpty(condition))
|
||||
return InvalidConditionToken;
|
||||
|
||||
var token = nextConditionToken++;
|
||||
conditionTokens.Add(token, condition);
|
||||
UpdateConditionState(condition, token, false);
|
||||
return token;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Revokes a previously granted condition.
|
||||
/// </summary>
|
||||
/// <param name="token">The token ID returned by GrantCondition.</param>
|
||||
/// <returns>The invalid token ID.</returns>
|
||||
public int RevokeCondition(int token)
|
||||
{
|
||||
if (!conditionTokens.TryGetValue(token, out var condition))
|
||||
throw new InvalidOperationException("Attempting to revoke condition with invalid token {0} for {1}.".F(token, this));
|
||||
|
||||
conditionTokens.Remove(token);
|
||||
UpdateConditionState(condition, token, true);
|
||||
return InvalidConditionToken;
|
||||
}
|
||||
|
||||
/// <summary>Returns whether the specified token is valid for RevokeCondition</summary>
|
||||
public bool TokenValid(int token)
|
||||
{
|
||||
return conditionTokens.ContainsKey(token);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Scripting interface
|
||||
|
||||
Lazy<ScriptActorInterface> luaInterface;
|
||||
@@ -471,8 +590,7 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
Actor a, b;
|
||||
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
|
||||
if (!left.TryGetClrValue(out Actor a) || !right.TryGetClrValue(out Actor b))
|
||||
return false;
|
||||
|
||||
return a == b;
|
||||
|
||||
@@ -89,9 +89,7 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Add(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CPos a;
|
||||
CVec b;
|
||||
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
|
||||
if (!left.TryGetClrValue(out CPos a) || !right.TryGetClrValue(out CVec b))
|
||||
throw new LuaException("Attempted to call CPos.Add(CPos, CVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
|
||||
|
||||
return new LuaCustomClrObject(a + b);
|
||||
@@ -99,21 +97,18 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Subtract(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CPos a;
|
||||
var rightType = right.WrappedClrType();
|
||||
if (!left.TryGetClrValue(out a))
|
||||
if (!left.TryGetClrValue(out CPos a))
|
||||
throw new LuaException("Attempted to call CPos.Subtract(CPos, (CPos|CVec)) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, rightType.Name));
|
||||
|
||||
if (rightType == typeof(CPos))
|
||||
{
|
||||
CPos b;
|
||||
right.TryGetClrValue(out b);
|
||||
right.TryGetClrValue(out CPos b);
|
||||
return new LuaCustomClrObject(a - b);
|
||||
}
|
||||
else if (rightType == typeof(CVec))
|
||||
{
|
||||
CVec b;
|
||||
right.TryGetClrValue(out b);
|
||||
right.TryGetClrValue(out CVec b);
|
||||
return new LuaCustomClrObject(a - b);
|
||||
}
|
||||
|
||||
@@ -122,8 +117,7 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CPos a, b;
|
||||
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
|
||||
if (!left.TryGetClrValue(out CPos a) || !right.TryGetClrValue(out CPos b))
|
||||
return false;
|
||||
|
||||
return a == b;
|
||||
|
||||
@@ -75,8 +75,7 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Add(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CVec a, b;
|
||||
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
|
||||
if (!left.TryGetClrValue(out CVec a) || !right.TryGetClrValue(out CVec b))
|
||||
throw new LuaException("Attempted to call CVec.Add(CVec, CVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
|
||||
|
||||
return new LuaCustomClrObject(a + b);
|
||||
@@ -84,8 +83,7 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Subtract(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CVec a, b;
|
||||
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
|
||||
if (!left.TryGetClrValue(out CVec a) || !right.TryGetClrValue(out CVec b))
|
||||
throw new LuaException("Attempted to call CVec.Subtract(CVec, CVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
|
||||
|
||||
return new LuaCustomClrObject(a - b);
|
||||
@@ -98,8 +96,7 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
CVec a, b;
|
||||
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
|
||||
if (!left.TryGetClrValue(out CVec a) || !right.TryGetClrValue(out CVec b))
|
||||
return false;
|
||||
|
||||
return a == b;
|
||||
|
||||
20
OpenRA.Game/DefaultPlayer.cs
Normal file
20
OpenRA.Game/DefaultPlayer.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public class DefaultPlayer : IGlobalModData
|
||||
{
|
||||
public readonly Color Color = Color.FromAhsl(0, 0, 238);
|
||||
}
|
||||
}
|
||||
@@ -90,8 +90,7 @@ namespace OpenRA
|
||||
public void CancelAsync()
|
||||
{
|
||||
lock (syncObject)
|
||||
if (wc != null)
|
||||
wc.CancelAsync();
|
||||
wc?.CancelAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace OpenRA
|
||||
|
||||
static int WindingDirectionTest(int2 v0, int2 v1, int2 p)
|
||||
{
|
||||
return (v1.X - v0.X) * (p.Y - v0.Y) - (p.X - v0.X) * (v1.Y - v0.Y);
|
||||
return Math.Sign((v1.X - v0.X) * (p.Y - v0.Y) - (p.X - v0.X) * (v1.Y - v0.Y));
|
||||
}
|
||||
|
||||
public static bool PolygonContains(this int2[] polygon, int2 p)
|
||||
@@ -101,6 +101,16 @@ namespace OpenRA
|
||||
return windingNumber != 0;
|
||||
}
|
||||
|
||||
public static bool LinesIntersect(int2 a, int2 b, int2 c, int2 d)
|
||||
{
|
||||
// If line segments AB and CD intersect:
|
||||
// - the triangles ACD and BCD must have opposite sense (clockwise or anticlockwise)
|
||||
// - the triangles CAB and DAB must have opposite sense
|
||||
// Segments intersect if the orientation (clockwise or anticlockwise) of the two points in each line segment are opposite with respect to the other
|
||||
// Assumes that lines are not colinear
|
||||
return WindingDirectionTest(c, d, a) != WindingDirectionTest(c, d, b) && WindingDirectionTest(a, b, c) != WindingDirectionTest(a, b, d);
|
||||
}
|
||||
|
||||
public static bool HasModifier(this Modifiers k, Modifiers mod)
|
||||
{
|
||||
// PERF: Enum.HasFlag is slower and requires allocations.
|
||||
@@ -115,8 +125,7 @@ namespace OpenRA
|
||||
|
||||
public static V GetOrAdd<K, V>(this Dictionary<K, V> d, K k, Func<K, V> createFn)
|
||||
{
|
||||
V ret;
|
||||
if (!d.TryGetValue(k, out ret))
|
||||
if (!d.TryGetValue(k, out var ret))
|
||||
d.Add(k, ret = createFn(k));
|
||||
return ret;
|
||||
}
|
||||
@@ -343,8 +352,7 @@ namespace OpenRA
|
||||
|
||||
public static int IntegerDivisionRoundingAwayFromZero(int dividend, int divisor)
|
||||
{
|
||||
int remainder;
|
||||
var quotient = Math.DivRem(dividend, divisor, out remainder);
|
||||
var quotient = Math.DivRem(dividend, divisor, out var remainder);
|
||||
if (remainder == 0)
|
||||
return quotient;
|
||||
return quotient + (Math.Sign(dividend) == Math.Sign(divisor) ? 1 : -1);
|
||||
@@ -395,8 +403,7 @@ namespace OpenRA
|
||||
// Check for a key conflict:
|
||||
if (d.ContainsKey(key))
|
||||
{
|
||||
List<string> dupKeyMessages;
|
||||
if (!dupKeys.TryGetValue(key, out dupKeyMessages))
|
||||
if (!dupKeys.TryGetValue(key, out var dupKeyMessages))
|
||||
{
|
||||
// Log the initial conflicting value already inserted:
|
||||
dupKeyMessages = new List<string>();
|
||||
|
||||
@@ -18,7 +18,6 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Support;
|
||||
|
||||
@@ -122,8 +121,7 @@ namespace OpenRA
|
||||
{
|
||||
ret = null;
|
||||
|
||||
MiniYaml yaml;
|
||||
if (!md.TryGetValue(yamlName, out yaml))
|
||||
if (!md.TryGetValue(yamlName, out var yaml))
|
||||
return false;
|
||||
|
||||
ret = GetValue(field.Name, field.FieldType, yaml, field);
|
||||
@@ -181,42 +179,36 @@ namespace OpenRA
|
||||
|
||||
public static object GetValue(string fieldName, Type fieldType, MiniYaml yaml, MemberInfo field)
|
||||
{
|
||||
var value = yaml.Value;
|
||||
if (value != null) value = value.Trim();
|
||||
var value = yaml.Value?.Trim();
|
||||
|
||||
if (fieldType == typeof(int))
|
||||
{
|
||||
int res;
|
||||
if (Exts.TryParseIntegerInvariant(value, out res))
|
||||
if (Exts.TryParseIntegerInvariant(value, out var res))
|
||||
return res;
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
else if (fieldType == typeof(ushort))
|
||||
{
|
||||
ushort res;
|
||||
if (ushort.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out res))
|
||||
if (ushort.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out var res))
|
||||
return res;
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
|
||||
if (fieldType == typeof(long))
|
||||
{
|
||||
long res;
|
||||
if (long.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out res))
|
||||
if (long.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out var res))
|
||||
return res;
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
else if (fieldType == typeof(float))
|
||||
{
|
||||
float res;
|
||||
if (value != null && float.TryParse(value.Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
|
||||
if (value != null && float.TryParse(value.Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var res))
|
||||
return res * (value.Contains('%') ? 0.01f : 1f);
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
else if (fieldType == typeof(decimal))
|
||||
{
|
||||
decimal res;
|
||||
if (value != null && decimal.TryParse(value.Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
|
||||
if (value != null && decimal.TryParse(value.Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var res))
|
||||
return res * (value.Contains('%') ? 0.01m : 1m);
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
@@ -228,16 +220,14 @@ namespace OpenRA
|
||||
}
|
||||
else if (fieldType == typeof(Color))
|
||||
{
|
||||
Color color;
|
||||
if (value != null && Color.TryParse(value, out color))
|
||||
if (value != null && Color.TryParse(value, out var color))
|
||||
return color;
|
||||
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
else if (fieldType == typeof(Hotkey))
|
||||
{
|
||||
Hotkey res;
|
||||
if (Hotkey.TryParse(value, out res))
|
||||
if (Hotkey.TryParse(value, out var res))
|
||||
return res;
|
||||
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
@@ -248,8 +238,7 @@ namespace OpenRA
|
||||
}
|
||||
else if (fieldType == typeof(WDist))
|
||||
{
|
||||
WDist res;
|
||||
if (WDist.TryParse(value, out res))
|
||||
if (WDist.TryParse(value, out var res))
|
||||
return res;
|
||||
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
@@ -261,8 +250,7 @@ namespace OpenRA
|
||||
var parts = value.Split(',');
|
||||
if (parts.Length == 3)
|
||||
{
|
||||
WDist rx, ry, rz;
|
||||
if (WDist.TryParse(parts[0], out rx) && WDist.TryParse(parts[1], out ry) && WDist.TryParse(parts[2], out rz))
|
||||
if (WDist.TryParse(parts[0], out var rx) && WDist.TryParse(parts[1], out var ry) && WDist.TryParse(parts[2], out var rz))
|
||||
return new WVec(rx, ry, rz);
|
||||
}
|
||||
}
|
||||
@@ -282,8 +270,7 @@ namespace OpenRA
|
||||
|
||||
for (var i = 0; i < vecs.Length; ++i)
|
||||
{
|
||||
WDist rx, ry, rz;
|
||||
if (WDist.TryParse(parts[3 * i], out rx) && WDist.TryParse(parts[3 * i + 1], out ry) && WDist.TryParse(parts[3 * i + 2], out rz))
|
||||
if (WDist.TryParse(parts[3 * i], out var rx) && WDist.TryParse(parts[3 * i + 1], out var ry) && WDist.TryParse(parts[3 * i + 2], out var rz))
|
||||
vecs[i] = new WVec(rx, ry, rz);
|
||||
}
|
||||
|
||||
@@ -299,8 +286,7 @@ namespace OpenRA
|
||||
var parts = value.Split(',');
|
||||
if (parts.Length == 3)
|
||||
{
|
||||
WDist rx, ry, rz;
|
||||
if (WDist.TryParse(parts[0], out rx) && WDist.TryParse(parts[1], out ry) && WDist.TryParse(parts[2], out rz))
|
||||
if (WDist.TryParse(parts[0], out var rx) && WDist.TryParse(parts[1], out var ry) && WDist.TryParse(parts[2], out var rz))
|
||||
return new WPos(rx, ry, rz);
|
||||
}
|
||||
}
|
||||
@@ -309,8 +295,7 @@ namespace OpenRA
|
||||
}
|
||||
else if (fieldType == typeof(WAngle))
|
||||
{
|
||||
int res;
|
||||
if (Exts.TryParseIntegerInvariant(value, out res))
|
||||
if (Exts.TryParseIntegerInvariant(value, out var res))
|
||||
return new WAngle(res);
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
@@ -321,8 +306,7 @@ namespace OpenRA
|
||||
var parts = value.Split(',');
|
||||
if (parts.Length == 3)
|
||||
{
|
||||
int rr, rp, ry;
|
||||
if (Exts.TryParseIntegerInvariant(parts[0], out rr) && Exts.TryParseIntegerInvariant(parts[1], out rp) && Exts.TryParseIntegerInvariant(parts[2], out ry))
|
||||
if (Exts.TryParseIntegerInvariant(parts[0], out var rr) && Exts.TryParseIntegerInvariant(parts[1], out var rp) && Exts.TryParseIntegerInvariant(parts[2], out var ry))
|
||||
return new WRot(new WAngle(rr), new WAngle(rp), new WAngle(ry));
|
||||
}
|
||||
}
|
||||
@@ -361,8 +345,7 @@ namespace OpenRA
|
||||
var vecs = new CVec[parts.Length / 2];
|
||||
for (var i = 0; i < vecs.Length; i++)
|
||||
{
|
||||
int rx, ry;
|
||||
if (int.TryParse(parts[2 * i], out rx) && int.TryParse(parts[2 * i + 1], out ry))
|
||||
if (int.TryParse(parts[2 * i], out var rx) && int.TryParse(parts[2 * i + 1], out var ry))
|
||||
vecs[i] = new CVec(rx, ry);
|
||||
}
|
||||
|
||||
@@ -416,8 +399,7 @@ namespace OpenRA
|
||||
}
|
||||
else if (fieldType == typeof(bool))
|
||||
{
|
||||
bool result;
|
||||
if (bool.TryParse(value.ToLowerInvariant(), out result))
|
||||
if (bool.TryParse(value.ToLowerInvariant(), out var result))
|
||||
return result;
|
||||
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
@@ -510,8 +492,7 @@ namespace OpenRA
|
||||
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
float xx = 0;
|
||||
float yy = 0;
|
||||
float res;
|
||||
if (float.TryParse(parts[0].Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
|
||||
if (float.TryParse(parts[0].Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var res))
|
||||
xx = res * (parts[0].Contains('%') ? 0.01f : 1f);
|
||||
if (float.TryParse(parts[1].Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
|
||||
yy = res * (parts[1].Contains('%') ? 0.01f : 1f);
|
||||
@@ -525,13 +506,11 @@ namespace OpenRA
|
||||
if (value != null)
|
||||
{
|
||||
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
float z = 0;
|
||||
float.TryParse(parts[0], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out x);
|
||||
float.TryParse(parts[1], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out y);
|
||||
float.TryParse(parts[0], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var x);
|
||||
float.TryParse(parts[1], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var y);
|
||||
|
||||
// z component is optional for compatibility with older float2 definitions
|
||||
float z = 0;
|
||||
if (parts.Length > 2)
|
||||
float.TryParse(parts[2], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out z);
|
||||
|
||||
@@ -567,14 +546,16 @@ namespace OpenRA
|
||||
}
|
||||
else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return null;
|
||||
|
||||
var innerType = fieldType.GetGenericArguments().First();
|
||||
var innerValue = GetValue("Nullable<T>", innerType, value, field);
|
||||
return fieldType.GetConstructor(new[] { innerType }).Invoke(new[] { innerValue });
|
||||
}
|
||||
else if (fieldType == typeof(DateTime))
|
||||
{
|
||||
DateTime dt;
|
||||
if (DateTime.TryParseExact(value, "yyyy-MM-dd HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out dt))
|
||||
if (DateTime.TryParseExact(value, "yyyy-MM-dd HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var dt))
|
||||
return dt;
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
@@ -724,8 +705,7 @@ namespace OpenRA
|
||||
if (translations == null)
|
||||
return key;
|
||||
|
||||
string value;
|
||||
if (!translations.TryGetValue(key, out value))
|
||||
if (!translations.TryGetValue(key, out var value))
|
||||
return key;
|
||||
|
||||
return value;
|
||||
|
||||
@@ -15,7 +15,6 @@ using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA
|
||||
|
||||
@@ -67,15 +67,12 @@ namespace OpenRA.FileSystem
|
||||
return new Folder(resolvedPath);
|
||||
|
||||
// Children of another package require special handling
|
||||
IReadOnlyPackage parent;
|
||||
string subPath = null;
|
||||
if (TryGetPackageContaining(filename, out parent, out subPath))
|
||||
if (TryGetPackageContaining(filename, out var parent, out var subPath))
|
||||
return parent.OpenPackage(subPath, this);
|
||||
|
||||
// Try and open it normally
|
||||
IReadOnlyPackage package;
|
||||
var stream = Open(filename);
|
||||
if (TryParsePackage(stream, filename, out package))
|
||||
if (TryParsePackage(stream, filename, out var package))
|
||||
return package;
|
||||
|
||||
// No package loaders took ownership of the stream, so clean it up
|
||||
@@ -97,8 +94,7 @@ namespace OpenRA.FileSystem
|
||||
{
|
||||
name = name.Substring(1);
|
||||
|
||||
Manifest mod;
|
||||
if (!installedMods.TryGetValue(name, out mod))
|
||||
if (!installedMods.TryGetValue(name, out var mod))
|
||||
throw new InvalidOperationException("Could not load mod '{0}'. Available mods: {1}".F(name, installedMods.Keys.JoinWith(", ")));
|
||||
|
||||
package = mod.Package;
|
||||
@@ -122,8 +118,7 @@ namespace OpenRA.FileSystem
|
||||
|
||||
public void Mount(IReadOnlyPackage package, string explicitName = null)
|
||||
{
|
||||
var mountCount = 0;
|
||||
if (mountedPackages.TryGetValue(package, out mountCount))
|
||||
if (mountedPackages.TryGetValue(package, out var mountCount))
|
||||
{
|
||||
// Package is already mounted
|
||||
// Increment the mount count and bump up the file loading priority
|
||||
@@ -149,8 +144,7 @@ namespace OpenRA.FileSystem
|
||||
|
||||
public bool Unmount(IReadOnlyPackage package)
|
||||
{
|
||||
var mountCount = 0;
|
||||
if (!mountedPackages.TryGetValue(package, out mountCount))
|
||||
if (!mountedPackages.TryGetValue(package, out var mountCount))
|
||||
return false;
|
||||
|
||||
if (--mountCount <= 0)
|
||||
@@ -203,16 +197,12 @@ namespace OpenRA.FileSystem
|
||||
var package = fileIndex[filename]
|
||||
.LastOrDefault(x => x.Contains(filename));
|
||||
|
||||
if (package != null)
|
||||
return package.GetStream(filename);
|
||||
|
||||
return null;
|
||||
return package?.GetStream(filename);
|
||||
}
|
||||
|
||||
public Stream Open(string filename)
|
||||
{
|
||||
Stream s;
|
||||
if (!TryOpen(filename, out s))
|
||||
if (!TryOpen(filename, out var s))
|
||||
throw new FileNotFoundException("File not found: {0}".F(filename), filename);
|
||||
|
||||
return s;
|
||||
@@ -238,8 +228,7 @@ namespace OpenRA.FileSystem
|
||||
var explicitSplit = filename.IndexOf('|');
|
||||
if (explicitSplit > 0)
|
||||
{
|
||||
IReadOnlyPackage explicitPackage;
|
||||
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out explicitPackage))
|
||||
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out var explicitPackage))
|
||||
{
|
||||
s = explicitPackage.GetStream(filename.Substring(explicitSplit + 1));
|
||||
if (s != null)
|
||||
@@ -274,12 +263,9 @@ namespace OpenRA.FileSystem
|
||||
{
|
||||
var explicitSplit = filename.IndexOf('|');
|
||||
if (explicitSplit > 0)
|
||||
{
|
||||
IReadOnlyPackage explicitPackage;
|
||||
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out explicitPackage))
|
||||
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out var explicitPackage))
|
||||
if (explicitPackage.Contains(filename.Substring(explicitSplit + 1)))
|
||||
return true;
|
||||
}
|
||||
|
||||
return fileIndex.ContainsKey(filename);
|
||||
}
|
||||
@@ -293,8 +279,7 @@ namespace OpenRA.FileSystem
|
||||
if (explicitSplit < 0)
|
||||
return false;
|
||||
|
||||
IReadOnlyPackage explicitPackage;
|
||||
if (!explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out explicitPackage))
|
||||
if (!explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out var explicitPackage))
|
||||
return false;
|
||||
|
||||
if (installedMods[modID].Package == explicitPackage)
|
||||
@@ -321,8 +306,7 @@ namespace OpenRA.FileSystem
|
||||
|
||||
if (parentPath.StartsWith("$", StringComparison.Ordinal))
|
||||
{
|
||||
Manifest mod;
|
||||
if (!installedMods.TryGetValue(parentPath.Substring(1), out mod))
|
||||
if (!installedMods.TryGetValue(parentPath.Substring(1), out var mod))
|
||||
return null;
|
||||
|
||||
if (!(mod.Package is Folder))
|
||||
|
||||
@@ -58,17 +58,15 @@ namespace OpenRA.FileSystem
|
||||
return new Folder(resolvedPath);
|
||||
|
||||
// Zip files loaded from Folders (and *only* from Folders) can be read-write
|
||||
IReadWritePackage readWritePackage;
|
||||
if (ZipFileLoader.TryParseReadWritePackage(resolvedPath, out readWritePackage))
|
||||
if (ZipFileLoader.TryParseReadWritePackage(resolvedPath, out var readWritePackage))
|
||||
return readWritePackage;
|
||||
|
||||
// Other package types can be loaded normally
|
||||
IReadOnlyPackage package;
|
||||
var s = GetStream(filename);
|
||||
if (s == null)
|
||||
return null;
|
||||
|
||||
if (context.TryParsePackage(s, filename, out package))
|
||||
if (context.TryParsePackage(s, filename, out var package))
|
||||
return package;
|
||||
|
||||
s.Dispose();
|
||||
|
||||
@@ -14,7 +14,6 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.FileSystem
|
||||
{
|
||||
@@ -67,8 +66,7 @@ namespace OpenRA.FileSystem
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (pkg != null)
|
||||
pkg.Close();
|
||||
pkg?.Close();
|
||||
}
|
||||
|
||||
public IReadOnlyPackage OpenPackage(string filename, FileSystem context)
|
||||
@@ -82,12 +80,11 @@ namespace OpenRA.FileSystem
|
||||
return new ZipFolder(this, filename);
|
||||
|
||||
// Other package types can be loaded normally
|
||||
IReadOnlyPackage package;
|
||||
var s = GetStream(filename);
|
||||
if (s == null)
|
||||
return null;
|
||||
|
||||
if (context.TryParsePackage(s, filename, out package))
|
||||
if (context.TryParsePackage(s, filename, out var package))
|
||||
return package;
|
||||
|
||||
s.Dispose();
|
||||
|
||||
@@ -51,7 +51,6 @@ namespace OpenRA
|
||||
|
||||
public static Renderer Renderer;
|
||||
public static Sound Sound;
|
||||
public static bool HasInputFocus = false;
|
||||
|
||||
public static string EngineVersion { get; private set; }
|
||||
public static LocalPlayerProfile LocalPlayerProfile;
|
||||
@@ -62,13 +61,13 @@ namespace OpenRA
|
||||
|
||||
public static event Action OnShellmapLoaded = () => { };
|
||||
|
||||
public static OrderManager JoinServer(string host, int port, string password, bool recordReplay = true)
|
||||
public static OrderManager JoinServer(ConnectionTarget endpoint, string password, bool recordReplay = true)
|
||||
{
|
||||
var connection = new NetworkConnection(host, port);
|
||||
var connection = new NetworkConnection(endpoint);
|
||||
if (recordReplay)
|
||||
connection.StartRecording(() => { return TimestampedFilename(); });
|
||||
|
||||
var om = new OrderManager(host, port, password, connection);
|
||||
var om = new OrderManager(endpoint, password, connection);
|
||||
JoinInner(om);
|
||||
return om;
|
||||
}
|
||||
@@ -76,12 +75,12 @@ namespace OpenRA
|
||||
static string TimestampedFilename(bool includemilliseconds = false)
|
||||
{
|
||||
var format = includemilliseconds ? "yyyy-MM-ddTHHmmssfffZ" : "yyyy-MM-ddTHHmmssZ";
|
||||
return "OpenRA-" + DateTime.UtcNow.ToString(format, CultureInfo.InvariantCulture);
|
||||
return ModData.Manifest.Id + "-" + DateTime.UtcNow.ToString(format, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
static void JoinInner(OrderManager om)
|
||||
{
|
||||
if (OrderManager != null) OrderManager.Dispose();
|
||||
OrderManager?.Dispose();
|
||||
OrderManager = om;
|
||||
lastConnectionState = ConnectionState.PreConnecting;
|
||||
ConnectionStateChanged(OrderManager);
|
||||
@@ -89,12 +88,12 @@ namespace OpenRA
|
||||
|
||||
public static void JoinReplay(string replayFile)
|
||||
{
|
||||
JoinInner(new OrderManager("<no server>", -1, "", new ReplayConnection(replayFile)));
|
||||
JoinInner(new OrderManager(new ConnectionTarget(), "", new ReplayConnection(replayFile)));
|
||||
}
|
||||
|
||||
static void JoinLocal()
|
||||
{
|
||||
JoinInner(new OrderManager("<no server>", -1, "", new EchoConnection()));
|
||||
JoinInner(new OrderManager(new ConnectionTarget(), "", new EchoConnection()));
|
||||
}
|
||||
|
||||
// More accurate replacement for Environment.TickCount
|
||||
@@ -105,14 +104,14 @@ namespace OpenRA
|
||||
public static int NetFrameNumber { get { return OrderManager.NetFrameNumber; } }
|
||||
public static int LocalTick { get { return OrderManager.LocalFrameNumber; } }
|
||||
|
||||
public static event Action<string, int> OnRemoteDirectConnect = (a, b) => { };
|
||||
public static event Action<ConnectionTarget> OnRemoteDirectConnect = _ => { };
|
||||
public static event Action<OrderManager> ConnectionStateChanged = _ => { };
|
||||
static ConnectionState lastConnectionState = ConnectionState.PreConnecting;
|
||||
public static int LocalClientId { get { return OrderManager.Connection.LocalClientId; } }
|
||||
|
||||
public static void RemoteDirectConnect(string host, int port)
|
||||
public static void RemoteDirectConnect(ConnectionTarget endpoint)
|
||||
{
|
||||
OnRemoteDirectConnect(host, port);
|
||||
OnRemoteDirectConnect(endpoint);
|
||||
}
|
||||
|
||||
// Hacky workaround for orderManager visibility
|
||||
@@ -155,8 +154,7 @@ namespace OpenRA
|
||||
internal static void StartGame(string mapUID, WorldType type)
|
||||
{
|
||||
// Dispose of the old world before creating a new one.
|
||||
if (worldRenderer != null)
|
||||
worldRenderer.Dispose();
|
||||
worldRenderer?.Dispose();
|
||||
|
||||
Cursor.SetCursor(null);
|
||||
BeforeGameStart();
|
||||
@@ -234,7 +232,7 @@ namespace OpenRA
|
||||
|
||||
LobbyInfoChanged += lobbyReady;
|
||||
|
||||
om = JoinServer(IPAddress.Loopback.ToString(), CreateLocalServer(mapUID), "");
|
||||
om = JoinServer(CreateLocalServer(mapUID), "");
|
||||
}
|
||||
|
||||
public static bool IsHost
|
||||
@@ -302,6 +300,7 @@ namespace OpenRA
|
||||
Log.AddChannel("graphics", "graphics.log");
|
||||
Log.AddChannel("geoip", "geoip.log");
|
||||
Log.AddChannel("nat", "nat.log");
|
||||
Log.AddChannel("client", "client.log");
|
||||
|
||||
var platforms = new[] { Settings.Game.Platform, "Default", null };
|
||||
foreach (var p in platforms)
|
||||
@@ -330,11 +329,9 @@ namespace OpenRA
|
||||
Log.Write("graphics", "{0}", e);
|
||||
Console.WriteLine("Renderer initialization failed. Check graphics.log for details.");
|
||||
|
||||
if (Renderer != null)
|
||||
Renderer.Dispose();
|
||||
Renderer?.Dispose();
|
||||
|
||||
if (Sound != null)
|
||||
Sound.Dispose();
|
||||
Sound?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,8 +352,7 @@ namespace OpenRA
|
||||
|
||||
ExternalMods = new ExternalMods();
|
||||
|
||||
Manifest currentMod;
|
||||
if (modID != null && Mods.TryGetValue(modID, out currentMod))
|
||||
if (modID != null && Mods.TryGetValue(modID, out _))
|
||||
{
|
||||
var launchPath = args.GetValue("Engine.LaunchPath", Assembly.GetEntryAssembly().Location);
|
||||
|
||||
@@ -367,8 +363,7 @@ namespace OpenRA
|
||||
|
||||
ExternalMods.Register(Mods[modID], launchPath, ModRegistration.User);
|
||||
|
||||
ExternalMod activeMod;
|
||||
if (ExternalMods.TryGetValue(ExternalMod.MakeKey(Mods[modID]), out activeMod))
|
||||
if (ExternalMods.TryGetValue(ExternalMod.MakeKey(Mods[modID]), out var activeMod))
|
||||
ExternalMods.ClearInvalidRegistrations(activeMod, ModRegistration.User);
|
||||
}
|
||||
|
||||
@@ -385,18 +380,15 @@ namespace OpenRA
|
||||
LobbyInfoChanged = () => { };
|
||||
ConnectionStateChanged = om => { };
|
||||
BeforeGameStart = () => { };
|
||||
OnRemoteDirectConnect = (a, b) => { };
|
||||
OnRemoteDirectConnect = endpoint => { };
|
||||
delayedActions = new ActionQueue();
|
||||
|
||||
Ui.ResetAll();
|
||||
|
||||
if (worldRenderer != null)
|
||||
worldRenderer.Dispose();
|
||||
worldRenderer?.Dispose();
|
||||
worldRenderer = null;
|
||||
if (server != null)
|
||||
server.Shutdown();
|
||||
if (OrderManager != null)
|
||||
OrderManager.Dispose();
|
||||
server?.Shutdown();
|
||||
OrderManager?.Dispose();
|
||||
|
||||
if (ModData != null)
|
||||
{
|
||||
@@ -432,8 +424,7 @@ namespace OpenRA
|
||||
var grid = ModData.Manifest.Contains<MapGrid>() ? ModData.Manifest.Get<MapGrid>() : null;
|
||||
Renderer.InitializeDepthBuffer(grid);
|
||||
|
||||
if (Cursor != null)
|
||||
Cursor.Dispose();
|
||||
Cursor?.Dispose();
|
||||
|
||||
Cursor = new CursorManager(ModData.CursorProvider);
|
||||
|
||||
@@ -442,13 +433,13 @@ namespace OpenRA
|
||||
PerfHistory.Items["render_world"].HasNormalTick = false;
|
||||
PerfHistory.Items["render_widgets"].HasNormalTick = false;
|
||||
PerfHistory.Items["render_flip"].HasNormalTick = false;
|
||||
PerfHistory.Items["terrain_lighting"].HasNormalTick = false;
|
||||
|
||||
JoinLocal();
|
||||
|
||||
try
|
||||
{
|
||||
if (discoverNat != null)
|
||||
discoverNat.Wait();
|
||||
discoverNat?.Wait();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -611,8 +602,7 @@ namespace OpenRA
|
||||
Sync.RunUnsynced(Settings.Debug.SyncCheckUnsyncedCode, world, () => world.TickRender(worldRenderer));
|
||||
}
|
||||
|
||||
if (benchmark != null)
|
||||
benchmark.Tick(LocalTick);
|
||||
benchmark?.Tick(LocalTick);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -710,6 +700,7 @@ namespace OpenRA
|
||||
PerfHistory.Items["render_world"].Tick();
|
||||
PerfHistory.Items["render_widgets"].Tick();
|
||||
PerfHistory.Items["render_flip"].Tick();
|
||||
PerfHistory.Items["terrain_lighting"].Tick();
|
||||
}
|
||||
|
||||
static void Loop()
|
||||
@@ -836,12 +827,10 @@ namespace OpenRA
|
||||
finally
|
||||
{
|
||||
// Ensure that the active replay is properly saved
|
||||
if (OrderManager != null)
|
||||
OrderManager.Dispose();
|
||||
OrderManager?.Dispose();
|
||||
}
|
||||
|
||||
if (worldRenderer != null)
|
||||
worldRenderer.Dispose();
|
||||
worldRenderer?.Dispose();
|
||||
ModData.Dispose();
|
||||
ChromeProvider.Deinitialize();
|
||||
|
||||
@@ -880,8 +869,7 @@ namespace OpenRA
|
||||
|
||||
public static void Disconnect()
|
||||
{
|
||||
if (OrderManager.World != null)
|
||||
OrderManager.World.TraitDict.PrintReport();
|
||||
OrderManager.World?.TraitDict.PrintReport();
|
||||
|
||||
OrderManager.Dispose();
|
||||
CloseServer();
|
||||
@@ -890,8 +878,7 @@ namespace OpenRA
|
||||
|
||||
public static void CloseServer()
|
||||
{
|
||||
if (server != null)
|
||||
server.Shutdown();
|
||||
server?.Shutdown();
|
||||
}
|
||||
|
||||
public static T CreateObject<T>(string name)
|
||||
@@ -899,12 +886,19 @@ namespace OpenRA
|
||||
return ModData.ObjectCreator.CreateObject<T>(name);
|
||||
}
|
||||
|
||||
public static void CreateServer(ServerSettings settings)
|
||||
public static ConnectionTarget CreateServer(ServerSettings settings)
|
||||
{
|
||||
server = new Server.Server(new IPEndPoint(IPAddress.Any, settings.ListenPort), settings, ModData, ServerType.Multiplayer);
|
||||
var endpoints = new List<IPEndPoint>
|
||||
{
|
||||
new IPEndPoint(IPAddress.IPv6Any, settings.ListenPort),
|
||||
new IPEndPoint(IPAddress.Any, settings.ListenPort)
|
||||
};
|
||||
server = new Server.Server(endpoints, settings, ModData, ServerType.Multiplayer);
|
||||
|
||||
return server.GetEndpointForLocalConnection();
|
||||
}
|
||||
|
||||
public static int CreateLocalServer(string map)
|
||||
public static ConnectionTarget CreateLocalServer(string map)
|
||||
{
|
||||
var settings = new ServerSettings()
|
||||
{
|
||||
@@ -913,9 +907,14 @@ namespace OpenRA
|
||||
AdvertiseOnline = false
|
||||
};
|
||||
|
||||
server = new Server.Server(new IPEndPoint(IPAddress.Loopback, 0), settings, ModData, ServerType.Local);
|
||||
var endpoints = new List<IPEndPoint>
|
||||
{
|
||||
new IPEndPoint(IPAddress.IPv6Loopback, 0),
|
||||
new IPEndPoint(IPAddress.Loopback, 0)
|
||||
};
|
||||
server = new Server.Server(endpoints, settings, ModData, ServerType.Local);
|
||||
|
||||
return server.Port;
|
||||
return server.GetEndpointForLocalConnection();
|
||||
}
|
||||
|
||||
public static bool IsCurrentWorld(World world)
|
||||
|
||||
@@ -133,9 +133,7 @@ namespace OpenRA
|
||||
/// <summary>Gets the player information for the specified runtime player instance.</summary>
|
||||
public Player GetPlayer(OpenRA.Player runtimePlayer)
|
||||
{
|
||||
Player player;
|
||||
|
||||
playersByRuntime.TryGetValue(runtimePlayer, out player);
|
||||
playersByRuntime.TryGetValue(runtimePlayer, out var player);
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace OpenRA
|
||||
public class ActorInfo
|
||||
{
|
||||
public const string AbstractActorPrefix = "^";
|
||||
public const char TraitInstanceSeparator = '@';
|
||||
|
||||
/// <summary>
|
||||
/// The actor name can be anything, but the sprites used in the Render*: traits default to this one.
|
||||
@@ -32,7 +33,7 @@ namespace OpenRA
|
||||
/// </summary>
|
||||
public readonly string Name;
|
||||
readonly TypeDictionary traits = new TypeDictionary();
|
||||
List<ITraitInfo> constructOrderCache = null;
|
||||
List<TraitInfo> constructOrderCache = null;
|
||||
|
||||
public ActorInfo(ObjectCreator creator, string name, MiniYaml node)
|
||||
{
|
||||
@@ -46,7 +47,7 @@ namespace OpenRA
|
||||
{
|
||||
// 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);
|
||||
var trait = LoadTraitInfo(creator, t.Key, t.Value);
|
||||
if (trait != null)
|
||||
traits.Add(trait);
|
||||
}
|
||||
@@ -64,7 +65,7 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
public ActorInfo(string name, params ITraitInfo[] traitInfos)
|
||||
public ActorInfo(string name, params TraitInfo[] traitInfos)
|
||||
{
|
||||
Name = name;
|
||||
foreach (var t in traitInfos)
|
||||
@@ -72,7 +73,7 @@ namespace OpenRA
|
||||
traits.TrimExcess();
|
||||
}
|
||||
|
||||
static ITraitInfo LoadTraitInfo(ObjectCreator creator, string traitName, MiniYaml my)
|
||||
static TraitInfo LoadTraitInfo(ObjectCreator creator, string traitName, MiniYaml my)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(my.Value))
|
||||
throw new YamlException("Junk value `{0}` on trait node {1}"
|
||||
@@ -80,12 +81,16 @@ namespace OpenRA
|
||||
|
||||
// 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");
|
||||
var traitInstance = traitName.Split(TraitInstanceSeparator);
|
||||
var info = creator.CreateObject<TraitInfo>(traitInstance[0] + "Info");
|
||||
if (info == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
if (traitInstance.Length > 1)
|
||||
info.GetType().GetField("InstanceName").SetValue(info, traitInstance[1]);
|
||||
|
||||
FieldLoader.Load(info, my);
|
||||
}
|
||||
catch (FieldLoader.MissingFieldsException e)
|
||||
@@ -97,12 +102,12 @@ namespace OpenRA
|
||||
return info;
|
||||
}
|
||||
|
||||
public IEnumerable<ITraitInfo> TraitsInConstructOrder()
|
||||
public IEnumerable<TraitInfo> TraitsInConstructOrder()
|
||||
{
|
||||
if (constructOrderCache != null)
|
||||
return constructOrderCache;
|
||||
|
||||
var source = traits.WithInterface<ITraitInfo>().Select(i => new
|
||||
var source = traits.WithInterface<TraitInfo>().Select(i => new
|
||||
{
|
||||
Trait = i,
|
||||
Type = i.GetType(),
|
||||
@@ -148,7 +153,7 @@ namespace OpenRA
|
||||
return constructOrderCache;
|
||||
}
|
||||
|
||||
public static IEnumerable<Type> PrerequisitesOf(ITraitInfo info)
|
||||
public static IEnumerable<Type> PrerequisitesOf(TraitInfo info)
|
||||
{
|
||||
return info
|
||||
.GetType()
|
||||
|
||||
@@ -41,8 +41,7 @@ namespace OpenRA.GameRules
|
||||
|
||||
public void Load(IReadOnlyFileSystem fileSystem)
|
||||
{
|
||||
Stream stream;
|
||||
if (!fileSystem.TryOpen(Filename, out stream))
|
||||
if (!fileSystem.TryOpen(Filename, out var stream))
|
||||
return;
|
||||
|
||||
try
|
||||
@@ -50,8 +49,7 @@ namespace OpenRA.GameRules
|
||||
Exists = true;
|
||||
foreach (var loader in Game.ModData.SoundLoaders)
|
||||
{
|
||||
ISoundFormat soundFormat;
|
||||
if (loader.TryParseSound(stream, out soundFormat))
|
||||
if (loader.TryParseSound(stream, out var soundFormat))
|
||||
{
|
||||
Length = (int)soundFormat.LengthInSeconds;
|
||||
soundFormat.Dispose();
|
||||
|
||||
@@ -24,8 +24,8 @@ namespace OpenRA.GameRules
|
||||
public int[] DamageModifiers;
|
||||
public int[] InaccuracyModifiers;
|
||||
public int[] RangeModifiers;
|
||||
public int Facing;
|
||||
public Func<int> CurrentMuzzleFacing;
|
||||
public WAngle Facing;
|
||||
public Func<WAngle> CurrentMuzzleFacing;
|
||||
public WPos Source;
|
||||
public Func<WPos> CurrentSource;
|
||||
public Actor SourceActor;
|
||||
@@ -38,6 +38,8 @@ namespace OpenRA.GameRules
|
||||
public WeaponInfo Weapon;
|
||||
public int[] DamageModifiers = { };
|
||||
public WPos? Source;
|
||||
public WRot ImpactOrientation;
|
||||
public WPos ImpactPosition;
|
||||
public Actor SourceActor;
|
||||
public Target WeaponTarget;
|
||||
|
||||
@@ -45,11 +47,22 @@ namespace OpenRA.GameRules
|
||||
{
|
||||
Weapon = args.Weapon;
|
||||
DamageModifiers = args.DamageModifiers;
|
||||
ImpactPosition = args.PassiveTarget;
|
||||
Source = args.Source;
|
||||
SourceActor = args.SourceActor;
|
||||
WeaponTarget = args.GuidedTarget;
|
||||
}
|
||||
|
||||
// For places that only want to update some of the fields (usually DamageModifiers)
|
||||
public WarheadArgs(WarheadArgs args)
|
||||
{
|
||||
Weapon = args.Weapon;
|
||||
DamageModifiers = args.DamageModifiers;
|
||||
Source = args.Source;
|
||||
SourceActor = args.SourceActor;
|
||||
WeaponTarget = args.WeaponTarget;
|
||||
}
|
||||
|
||||
// Default empty constructor for callers that want to initialize fields themselves
|
||||
public WarheadArgs() { }
|
||||
}
|
||||
@@ -92,6 +105,12 @@ namespace OpenRA.GameRules
|
||||
[Desc("What types of targets are unaffected.", "Overrules ValidTargets.")]
|
||||
public readonly BitSet<TargetableType> InvalidTargets;
|
||||
|
||||
static readonly BitSet<TargetableType> TargetTypeAir = new BitSet<TargetableType>("Air");
|
||||
|
||||
[Desc("If weapon is not directly targeting an actor and targeted position is above this altitude,",
|
||||
"the weapon will ignore terrain target types and only check TargetTypeAir for validity.")]
|
||||
public readonly WDist AirThreshold = new WDist(128);
|
||||
|
||||
[Desc("Delay in ticks between firing shots from the same ammo magazine. If one entry, it will be used for all bursts.",
|
||||
"If multiple entries, their number needs to match Burst - 1.")]
|
||||
public readonly int[] BurstDelays = { 5 };
|
||||
@@ -118,8 +137,7 @@ namespace OpenRA.GameRules
|
||||
|
||||
static object LoadProjectile(MiniYaml yaml)
|
||||
{
|
||||
MiniYaml proj;
|
||||
if (!yaml.ToDictionary().TryGetValue("Projectile", out proj))
|
||||
if (!yaml.ToDictionary().TryGetValue("Projectile", out var proj))
|
||||
return null;
|
||||
var ret = Game.CreateObject<IProjectileInfo>(proj.Value + "Info");
|
||||
FieldLoader.Load(ret, proj);
|
||||
@@ -155,6 +173,10 @@ namespace OpenRA.GameRules
|
||||
|
||||
if (target.Type == TargetType.Terrain)
|
||||
{
|
||||
var dat = world.Map.DistanceAboveTerrain(target.CenterPosition);
|
||||
if (dat > AirThreshold)
|
||||
return IsValidTarget(TargetTypeAir);
|
||||
|
||||
var cell = world.Map.CellContaining(target.CenterPosition);
|
||||
if (!world.Map.Contains(cell))
|
||||
return false;
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace OpenRA.Graphics
|
||||
public bool IsDecoration { get; set; }
|
||||
|
||||
readonly SequenceProvider sequenceProvider;
|
||||
readonly Func<int> facingFunc;
|
||||
readonly Func<WAngle> facingFunc;
|
||||
readonly Func<bool> paused;
|
||||
|
||||
int frame;
|
||||
@@ -33,15 +33,15 @@ namespace OpenRA.Graphics
|
||||
Action tickFunc = () => { };
|
||||
|
||||
public Animation(World world, string name)
|
||||
: this(world, name, () => 0) { }
|
||||
: this(world, name, () => WAngle.Zero) { }
|
||||
|
||||
public Animation(World world, string name, Func<int> facingFunc)
|
||||
public Animation(World world, string name, Func<WAngle> facingFunc)
|
||||
: this(world, name, facingFunc, null) { }
|
||||
|
||||
public Animation(World world, string name, Func<bool> paused)
|
||||
: this(world, name, () => 0, paused) { }
|
||||
: this(world, name, () => WAngle.Zero, paused) { }
|
||||
|
||||
public Animation(World world, string name, Func<int> facingFunc, Func<bool> paused)
|
||||
public Animation(World world, string name, Func<WAngle> facingFunc, Func<bool> paused)
|
||||
{
|
||||
sequenceProvider = world.Map.Rules.Sequences;
|
||||
Name = name.ToLowerInvariant();
|
||||
@@ -54,12 +54,12 @@ namespace OpenRA.Graphics
|
||||
|
||||
public IRenderable[] Render(WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale)
|
||||
{
|
||||
var imageRenderable = new SpriteRenderable(Image, pos, offset, CurrentSequence.ZOffset + zOffset, palette, scale, IsDecoration);
|
||||
var imageRenderable = new SpriteRenderable(Image, pos, offset, CurrentSequence.ZOffset + zOffset, palette, scale, IsDecoration, CurrentSequence.IgnoreWorldTint);
|
||||
|
||||
if (CurrentSequence.ShadowStart >= 0)
|
||||
{
|
||||
var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc());
|
||||
var shadowRenderable = new SpriteRenderable(shadow, pos, offset, CurrentSequence.ShadowZOffset + zOffset, palette, scale, true);
|
||||
var shadowRenderable = new SpriteRenderable(shadow, pos, offset, CurrentSequence.ShadowZOffset + zOffset, palette, scale, true, CurrentSequence.IgnoreWorldTint);
|
||||
return new IRenderable[] { shadowRenderable, imageRenderable };
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
frame = CurrentSequence.Length - 1;
|
||||
tickFunc = () => { };
|
||||
if (after != null) after();
|
||||
after?.Invoke();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -54,10 +54,10 @@ namespace OpenRA.Graphics
|
||||
|
||||
public static IReadOnlyDictionary<string, Collection> Collections { get; private set; }
|
||||
static Dictionary<string, Collection> collections;
|
||||
static Dictionary<string, Pair<Sheet, int>> cachedSheets;
|
||||
static Dictionary<string, (Sheet Sheet, int Density)> cachedSheets;
|
||||
static Dictionary<string, Dictionary<string, Sprite>> cachedSprites;
|
||||
static Dictionary<string, Sprite[]> cachedPanelSprites;
|
||||
static Dictionary<Collection, Pair<Sheet, int>> cachedCollectionSheets;
|
||||
static Dictionary<Collection, (Sheet Sheet, int)> cachedCollectionSheets;
|
||||
|
||||
static IReadOnlyFileSystem fileSystem;
|
||||
static float dpiScale = 1;
|
||||
@@ -72,10 +72,10 @@ namespace OpenRA.Graphics
|
||||
|
||||
fileSystem = modData.DefaultFileSystem;
|
||||
collections = new Dictionary<string, Collection>();
|
||||
cachedSheets = new Dictionary<string, Pair<Sheet, int>>();
|
||||
cachedSheets = new Dictionary<string, (Sheet, int)>();
|
||||
cachedSprites = new Dictionary<string, Dictionary<string, Sprite>>();
|
||||
cachedPanelSprites = new Dictionary<string, Sprite[]>();
|
||||
cachedCollectionSheets = new Dictionary<Collection, Pair<Sheet, int>>();
|
||||
cachedCollectionSheets = new Dictionary<Collection, (Sheet, int)>();
|
||||
|
||||
Collections = new ReadOnlyDictionary<string, Collection>(collections);
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
if (cachedSheets != null)
|
||||
foreach (var sheet in cachedSheets.Values)
|
||||
sheet.First.Dispose();
|
||||
sheet.Sheet.Dispose();
|
||||
|
||||
collections = null;
|
||||
cachedSheets = null;
|
||||
@@ -108,12 +108,10 @@ namespace OpenRA.Graphics
|
||||
collections.Add(name, FieldLoader.Load<Collection>(yaml));
|
||||
}
|
||||
|
||||
static Pair<Sheet, int> SheetForCollection(Collection c)
|
||||
static (Sheet Sheet, int Density) SheetForCollection(Collection c)
|
||||
{
|
||||
Pair<Sheet, int> sheetDensity;
|
||||
|
||||
// Outer cache avoids recalculating image names
|
||||
if (!cachedCollectionSheets.TryGetValue(c, out sheetDensity))
|
||||
if (!cachedCollectionSheets.TryGetValue(c, out (Sheet, int) sheetDensity))
|
||||
{
|
||||
var image = c.Image;
|
||||
var density = 1;
|
||||
@@ -137,7 +135,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
sheet.GetTexture().ScaleFilter = TextureScaleFilter.Linear;
|
||||
|
||||
sheetDensity = Pair.New(sheet, density);
|
||||
sheetDensity = (sheet, density);
|
||||
cachedSheets.Add(image, sheetDensity);
|
||||
}
|
||||
|
||||
@@ -153,20 +151,16 @@ namespace OpenRA.Graphics
|
||||
return null;
|
||||
|
||||
// Cached sprite
|
||||
Dictionary<string, Sprite> cachedCollection;
|
||||
Sprite sprite;
|
||||
if (cachedSprites.TryGetValue(collectionName, out cachedCollection) && cachedCollection.TryGetValue(imageName, out sprite))
|
||||
if (cachedSprites.TryGetValue(collectionName, out var cachedCollection) && cachedCollection.TryGetValue(imageName, out var sprite))
|
||||
return sprite;
|
||||
|
||||
Collection collection;
|
||||
if (!collections.TryGetValue(collectionName, out collection))
|
||||
if (!collections.TryGetValue(collectionName, out var collection))
|
||||
{
|
||||
Log.Write("debug", "Could not find collection '{0}'", collectionName);
|
||||
return null;
|
||||
}
|
||||
|
||||
Rectangle mi;
|
||||
if (!collection.Regions.TryGetValue(imageName, out mi))
|
||||
if (!collection.Regions.TryGetValue(imageName, out var mi))
|
||||
return null;
|
||||
|
||||
// Cache the sprite
|
||||
@@ -177,7 +171,7 @@ namespace OpenRA.Graphics
|
||||
cachedSprites.Add(collectionName, cachedCollection);
|
||||
}
|
||||
|
||||
var image = new Sprite(sheetDensity.First, sheetDensity.Second * mi, TextureChannel.RGBA, 1f / sheetDensity.Second);
|
||||
var image = new Sprite(sheetDensity.Sheet, sheetDensity.Density * mi, TextureChannel.RGBA, 1f / sheetDensity.Density);
|
||||
cachedCollection.Add(imageName, image);
|
||||
|
||||
return image;
|
||||
@@ -189,12 +183,10 @@ namespace OpenRA.Graphics
|
||||
return null;
|
||||
|
||||
// Cached sprite
|
||||
Sprite[] cachedSprites;
|
||||
if (cachedPanelSprites.TryGetValue(collectionName, out cachedSprites))
|
||||
if (cachedPanelSprites.TryGetValue(collectionName, out var cachedSprites))
|
||||
return cachedSprites;
|
||||
|
||||
Collection collection;
|
||||
if (!collections.TryGetValue(collectionName, out collection))
|
||||
if (!collections.TryGetValue(collectionName, out var collection))
|
||||
{
|
||||
Log.Write("debug", "Could not find collection '{0}'", collectionName);
|
||||
return null;
|
||||
@@ -214,20 +206,20 @@ namespace OpenRA.Graphics
|
||||
var pr = collection.PanelRegion;
|
||||
var ps = collection.PanelSides;
|
||||
|
||||
var sides = new[]
|
||||
var sides = new (PanelSides PanelSides, Rectangle Bounds)[]
|
||||
{
|
||||
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]))
|
||||
(PanelSides.Top | PanelSides.Left, new Rectangle(pr[0], pr[1], pr[2], pr[3])),
|
||||
(PanelSides.Top, new Rectangle(pr[0] + pr[2], pr[1], pr[4], pr[3])),
|
||||
(PanelSides.Top | PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1], pr[6], pr[3])),
|
||||
(PanelSides.Left, new Rectangle(pr[0], pr[1] + pr[3], pr[2], pr[5])),
|
||||
(PanelSides.Center, new Rectangle(pr[0] + pr[2], pr[1] + pr[3], pr[4], pr[5])),
|
||||
(PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1] + pr[3], pr[6], pr[5])),
|
||||
(PanelSides.Bottom | PanelSides.Left, new Rectangle(pr[0], pr[1] + pr[3] + pr[5], pr[2], pr[7])),
|
||||
(PanelSides.Bottom, new Rectangle(pr[0] + pr[2], pr[1] + pr[3] + pr[5], pr[4], pr[7])),
|
||||
(PanelSides.Bottom | PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1] + pr[3] + pr[5], pr[6], pr[7]))
|
||||
};
|
||||
|
||||
sprites = sides.Select(x => ps.HasSide(x.First) ? new Sprite(sheetDensity.First, sheetDensity.Second * x.Second, TextureChannel.RGBA, 1f / sheetDensity.Second) : null)
|
||||
sprites = sides.Select(x => ps.HasSide(x.PanelSides) ? new Sprite(sheetDensity.Sheet, sheetDensity.Density * x.Bounds, TextureChannel.RGBA, 1f / sheetDensity.Density) : null)
|
||||
.ToArray();
|
||||
}
|
||||
else
|
||||
@@ -256,8 +248,7 @@ namespace OpenRA.Graphics
|
||||
if (string.IsNullOrEmpty(collectionName))
|
||||
return new Size(0, 0);
|
||||
|
||||
Collection collection;
|
||||
if (!collections.TryGetValue(collectionName, out collection))
|
||||
if (!collections.TryGetValue(collectionName, out var collection))
|
||||
{
|
||||
Log.Write("debug", "Could not find collection '{0}'", collectionName);
|
||||
return new Size(0, 0);
|
||||
|
||||
@@ -47,15 +47,13 @@ namespace OpenRA.Graphics
|
||||
|
||||
if (d.ContainsKey("X"))
|
||||
{
|
||||
int x;
|
||||
Exts.TryParseIntegerInvariant(d["X"].Value, out x);
|
||||
Exts.TryParseIntegerInvariant(d["X"].Value, out var x);
|
||||
Hotspot = Hotspot.WithX(x);
|
||||
}
|
||||
|
||||
if (d.ContainsKey("Y"))
|
||||
{
|
||||
int y;
|
||||
Exts.TryParseIntegerInvariant(d["Y"].Value, out y);
|
||||
Exts.TryParseIntegerInvariant(d["Y"].Value, out var y);
|
||||
Hotspot = Hotspot.WithY(y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,19 +38,16 @@ namespace OpenRA.Graphics
|
||||
|
||||
public IPalette GetPalette(string name)
|
||||
{
|
||||
MutablePalette mutable;
|
||||
if (modifiablePalettes.TryGetValue(name, out mutable))
|
||||
if (modifiablePalettes.TryGetValue(name, out var mutable))
|
||||
return mutable.AsReadOnly();
|
||||
ImmutablePalette immutable;
|
||||
if (palettes.TryGetValue(name, out immutable))
|
||||
if (palettes.TryGetValue(name, out var immutable))
|
||||
return immutable;
|
||||
throw new InvalidOperationException("Palette `{0}` does not exist".F(name));
|
||||
}
|
||||
|
||||
public int GetPaletteIndex(string name)
|
||||
{
|
||||
int ret;
|
||||
if (!indices.TryGetValue(name, out ret))
|
||||
if (!indices.TryGetValue(name, out var ret))
|
||||
throw new InvalidOperationException("Palette `{0}` does not exist".F(name));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
@@ -19,12 +18,12 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
public readonly IModel Model;
|
||||
public readonly Func<WVec> OffsetFunc;
|
||||
public readonly Func<IEnumerable<WRot>> RotationFunc;
|
||||
public readonly Func<WRot> RotationFunc;
|
||||
public readonly Func<bool> DisableFunc;
|
||||
public readonly Func<uint> FrameFunc;
|
||||
public readonly bool ShowShadow;
|
||||
|
||||
public ModelAnimation(IModel model, Func<WVec> offset, Func<IEnumerable<WRot>> rotation, Func<bool> disable, Func<uint> frame, bool showshadow)
|
||||
public ModelAnimation(IModel model, Func<WVec> offset, Func<WRot> rotation, Func<bool> disable, Func<uint> frame, bool showshadow)
|
||||
{
|
||||
Model = model;
|
||||
OffsetFunc = offset;
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
readonly Dictionary<Sheet, IFrameBuffer> mappedBuffers = new Dictionary<Sheet, IFrameBuffer>();
|
||||
readonly Stack<KeyValuePair<Sheet, IFrameBuffer>> unmappedBuffers = new Stack<KeyValuePair<Sheet, IFrameBuffer>>();
|
||||
readonly List<Pair<Sheet, Action>> doRender = new List<Pair<Sheet, Action>>();
|
||||
readonly List<(Sheet Sheet, Action Func)> doRender = new List<(Sheet, Action)>();
|
||||
|
||||
SheetBuilder sheetBuilderForFrame;
|
||||
bool isInFrame;
|
||||
@@ -114,8 +114,7 @@ namespace OpenRA.Graphics
|
||||
var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(m.OffsetFunc()));
|
||||
var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]);
|
||||
|
||||
var worldTransform = m.RotationFunc().Aggregate(Util.IdentityMatrix(),
|
||||
(x, y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x));
|
||||
var worldTransform = Util.MakeFloatMatrix(m.RotationFunc().AsMatrix());
|
||||
worldTransform = Util.MatrixMultiply(scaleTransform, worldTransform);
|
||||
worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform);
|
||||
|
||||
@@ -161,10 +160,8 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
|
||||
// Shadows are rendered at twice the resolution to reduce artifacts
|
||||
Size spriteSize, shadowSpriteSize;
|
||||
int2 spriteOffset, shadowSpriteOffset;
|
||||
CalculateSpriteGeometry(tl, br, 1, out spriteSize, out spriteOffset);
|
||||
CalculateSpriteGeometry(stl, sbr, 2, out shadowSpriteSize, out shadowSpriteOffset);
|
||||
CalculateSpriteGeometry(tl, br, 1, out var spriteSize, out var spriteOffset);
|
||||
CalculateSpriteGeometry(stl, sbr, 2, out var shadowSpriteSize, out var shadowSpriteOffset);
|
||||
|
||||
if (sheetBuilderForFrame == null)
|
||||
sheetBuilderForFrame = new SheetBuilder(SheetType.BGRA, AllocateSheet);
|
||||
@@ -181,7 +178,7 @@ namespace OpenRA.Graphics
|
||||
var correctionTransform = Util.MatrixMultiply(translateMtx, FlipMtx);
|
||||
var shadowCorrectionTransform = Util.MatrixMultiply(shadowTranslateMtx, ShadowScaleFlipMtx);
|
||||
|
||||
doRender.Add(Pair.New<Sheet, Action>(sprite.Sheet, () =>
|
||||
doRender.Add((sprite.Sheet, () =>
|
||||
{
|
||||
foreach (var m in models)
|
||||
{
|
||||
@@ -189,8 +186,7 @@ namespace OpenRA.Graphics
|
||||
var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(m.OffsetFunc()));
|
||||
var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]);
|
||||
|
||||
var rotations = m.RotationFunc().Aggregate(Util.IdentityMatrix(),
|
||||
(x, y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x));
|
||||
var rotations = Util.MakeFloatMatrix(m.RotationFunc().AsMatrix());
|
||||
var worldTransform = Util.MatrixMultiply(scaleTransform, rotations);
|
||||
worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform);
|
||||
|
||||
@@ -326,16 +322,16 @@ namespace OpenRA.Graphics
|
||||
foreach (var v in doRender)
|
||||
{
|
||||
// Change sheet
|
||||
if (v.First != currentSheet)
|
||||
if (v.Sheet != currentSheet)
|
||||
{
|
||||
if (fbo != null)
|
||||
DisableFrameBuffer(fbo);
|
||||
|
||||
currentSheet = v.First;
|
||||
currentSheet = v.Sheet;
|
||||
fbo = EnableFrameBuffer(currentSheet);
|
||||
}
|
||||
|
||||
v.Second();
|
||||
v.Func();
|
||||
}
|
||||
|
||||
if (fbo != null)
|
||||
|
||||
@@ -15,9 +15,16 @@ using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public enum GLProfile
|
||||
{
|
||||
Modern,
|
||||
Embedded,
|
||||
Legacy
|
||||
}
|
||||
|
||||
public interface IPlatform
|
||||
{
|
||||
IPlatformWindow CreateWindow(Size size, WindowMode windowMode, float scaleModifier, int batchSize, int videoDisplay);
|
||||
IPlatformWindow CreateWindow(Size size, WindowMode windowMode, float scaleModifier, int batchSize, int videoDisplay, GLProfile profile);
|
||||
ISoundEngine CreateSound(string device);
|
||||
IFont CreateFont(byte[] data);
|
||||
}
|
||||
@@ -32,7 +39,10 @@ namespace OpenRA
|
||||
Subtractive,
|
||||
Multiply,
|
||||
Multiplicative,
|
||||
DoubleMultiplicative
|
||||
DoubleMultiplicative,
|
||||
LowAdditive,
|
||||
Screen,
|
||||
Translucent
|
||||
}
|
||||
|
||||
public interface IPlatformWindow : IDisposable
|
||||
@@ -46,6 +56,7 @@ namespace OpenRA
|
||||
Size SurfaceSize { get; }
|
||||
int DisplayCount { get; }
|
||||
int CurrentDisplay { get; }
|
||||
bool HasInputFocus { get; }
|
||||
|
||||
event Action<float, float, float, float> OnWindowScaleChanged;
|
||||
|
||||
@@ -60,6 +71,10 @@ namespace OpenRA
|
||||
void SetHardwareCursor(IHardwareCursor cursor);
|
||||
void SetRelativeMouseMode(bool mode);
|
||||
void SetScaleModifier(float scale);
|
||||
|
||||
GLProfile GLProfile { get; }
|
||||
|
||||
GLProfile[] SupportedGLProfiles { get; }
|
||||
}
|
||||
|
||||
public interface IGraphicsContext : IDisposable
|
||||
|
||||
@@ -47,14 +47,13 @@ namespace OpenRA.Graphics
|
||||
remapRamp = ramp.Select(r => r - ramp[rampMaxIndex]);
|
||||
}
|
||||
|
||||
remapColors = remapRamp.Select((x, i) => Pair.New(baseIndex + i, Exts.ColorLerp(x / (float)ramp.Length, c1, c2)))
|
||||
.ToDictionary(u => u.First, u => u.Second);
|
||||
remapColors = remapRamp.Select((x, i) => (baseIndex + i, Exts.ColorLerp(x / (float)ramp.Length, c1, c2)))
|
||||
.ToDictionary(u => u.Item1, u => u.Item2);
|
||||
}
|
||||
|
||||
public Color GetRemappedColor(Color original, int index)
|
||||
{
|
||||
Color c;
|
||||
return remapColors.TryGetValue(index, out c)
|
||||
return remapColors.TryGetValue(index, out var c)
|
||||
? c : original;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,11 @@ namespace OpenRA.Graphics
|
||||
IFinalizedRenderable PrepareRender(WorldRenderer wr);
|
||||
}
|
||||
|
||||
public interface ITintableRenderable
|
||||
{
|
||||
IRenderable WithTint(float3 newTint);
|
||||
}
|
||||
|
||||
public interface IFinalizedRenderable
|
||||
{
|
||||
void Render(WorldRenderer wr);
|
||||
|
||||
@@ -45,5 +45,21 @@ namespace OpenRA.Graphics
|
||||
|
||||
parent.DrawSprite(s, a, b, c, d);
|
||||
}
|
||||
|
||||
public void DrawSpriteWithTint(Sprite s, float3 location, float3 size, float3 tint)
|
||||
{
|
||||
if (s.Channel != TextureChannel.RGBA)
|
||||
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");
|
||||
|
||||
parent.DrawSpriteWithTint(s, location, 0, size, tint);
|
||||
}
|
||||
|
||||
public void DrawSpriteWithTint(Sprite s, float3 a, float3 b, float3 c, float3 d, float3 tint)
|
||||
{
|
||||
if (s.Channel != TextureChannel.RGBA)
|
||||
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");
|
||||
|
||||
parent.DrawSpriteWithTint(s, a, b, c, d, tint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,10 +32,11 @@ namespace OpenRA.Graphics
|
||||
int ShadowZOffset { get; }
|
||||
int[] Frames { get; }
|
||||
Rectangle Bounds { get; }
|
||||
bool IgnoreWorldTint { get; }
|
||||
|
||||
Sprite GetSprite(int frame);
|
||||
Sprite GetSprite(int frame, int facing);
|
||||
Sprite GetShadow(int frame, int facing);
|
||||
Sprite GetSprite(int frame, WAngle facing);
|
||||
Sprite GetShadow(int frame, WAngle facing);
|
||||
}
|
||||
|
||||
public interface ISpriteSequenceLoader
|
||||
@@ -69,12 +70,10 @@ namespace OpenRA.Graphics
|
||||
|
||||
public ISpriteSequence GetSequence(string unitName, string sequenceName)
|
||||
{
|
||||
UnitSequences unitSeq;
|
||||
if (!sequences.Value.TryGetValue(unitName, out unitSeq))
|
||||
if (!sequences.Value.TryGetValue(unitName, out var unitSeq))
|
||||
throw new InvalidOperationException("Unit `{0}` does not have any sequences defined.".F(unitName));
|
||||
|
||||
ISpriteSequence seq;
|
||||
if (!unitSeq.Value.TryGetValue(sequenceName, out seq))
|
||||
if (!unitSeq.Value.TryGetValue(sequenceName, out var seq))
|
||||
throw new InvalidOperationException("Unit `{0}` does not have a sequence named `{1}`".F(unitName, sequenceName));
|
||||
|
||||
return seq;
|
||||
@@ -87,8 +86,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public bool HasSequence(string unitName, string sequenceName)
|
||||
{
|
||||
UnitSequences unitSeq;
|
||||
if (!sequences.Value.TryGetValue(unitName, out unitSeq))
|
||||
if (!sequences.Value.TryGetValue(unitName, out var unitSeq))
|
||||
throw new InvalidOperationException("Unit `{0}` does not have any sequences defined.".F(unitName));
|
||||
|
||||
return unitSeq.Value.ContainsKey(sequenceName);
|
||||
@@ -96,8 +94,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public IEnumerable<string> Sequences(string unitName)
|
||||
{
|
||||
UnitSequences unitSeq;
|
||||
if (!sequences.Value.TryGetValue(unitName, out unitSeq))
|
||||
if (!sequences.Value.TryGetValue(unitName, out var unitSeq))
|
||||
throw new InvalidOperationException("Unit `{0}` does not have any sequences defined.".F(unitName));
|
||||
|
||||
return unitSeq.Value.Keys;
|
||||
@@ -114,8 +111,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
var key = node.Value.ToLines(node.Key).JoinWith("|");
|
||||
|
||||
UnitSequences t;
|
||||
if (sequenceCache.TryGetValue(key, out t))
|
||||
if (sequenceCache.TryGetValue(key, out var t))
|
||||
items.Add(node.Key, t);
|
||||
else
|
||||
{
|
||||
|
||||
@@ -146,8 +146,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (texture != null)
|
||||
texture.Dispose();
|
||||
texture?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ namespace OpenRA.Graphics
|
||||
readonly SheetBuilder builder;
|
||||
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<char, GlyphInfo> glyphs;
|
||||
readonly Cache<(char C, int Radius), Sprite> contrastGlyphs;
|
||||
readonly Cache<int, float[]> dilationElements;
|
||||
|
||||
float deviceScale;
|
||||
@@ -39,17 +39,20 @@ namespace OpenRA.Graphics
|
||||
this.builder = builder;
|
||||
|
||||
font = Game.Renderer.CreateFont(data);
|
||||
|
||||
glyphs = new Cache<Pair<char, Color>, GlyphInfo>(CreateGlyph, Pair<char, Color>.EqualityComparer);
|
||||
contrastGlyphs = new Cache<Tuple<char, Color, int>, Sprite>(CreateContrastGlyph);
|
||||
glyphs = new Cache<char, GlyphInfo>(CreateGlyph);
|
||||
contrastGlyphs = new Cache<(char, int), Sprite>(CreateContrastGlyph);
|
||||
dilationElements = new Cache<int, float[]>(CreateCircularWeightMap);
|
||||
|
||||
// PERF: Cache these delegates for Measure calls.
|
||||
Func<char, float> characterWidth = character => glyphs[Pair.New(character, Color.White)].Advance;
|
||||
Func<char, float> characterWidth = character => glyphs[character].Advance;
|
||||
lineWidth = line => line.Sum(characterWidth) / deviceScale;
|
||||
|
||||
// Pre-cache small font sizes so glyphs are immediately available when we need them
|
||||
if (size <= 24)
|
||||
PrecacheColor(Color.White, name);
|
||||
using (new PerfTimer("Precache {0} {1}px".F(name, size)))
|
||||
for (var n = (char)0x20; n < (char)0x7f; n++)
|
||||
if (glyphs[n] == null)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
TopOffset = size - ascender;
|
||||
}
|
||||
@@ -61,14 +64,6 @@ namespace OpenRA.Graphics
|
||||
contrastGlyphs.Clear();
|
||||
}
|
||||
|
||||
void PrecacheColor(Color c, string name)
|
||||
{
|
||||
using (new PerfTimer("PrecacheColor {0} {1}px {2}".F(name, size, c)))
|
||||
for (var n = (char)0x20; n < (char)0x7f; n++)
|
||||
if (glyphs[Pair.New(n, c)] == null)
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
void DrawTextContrast(string text, float2 location, Color contrastColor, int contrastOffset)
|
||||
{
|
||||
// Offset from the baseline position to the top-left of the glyph for rendering
|
||||
@@ -78,6 +73,7 @@ namespace OpenRA.Graphics
|
||||
var screenContrast = (int)(contrastOffset * deviceScale);
|
||||
var screen = new int2((int)(location.X * deviceScale + 0.5f), (int)(location.Y * deviceScale + 0.5f));
|
||||
var contrastVector = new float2(screenContrast, screenContrast);
|
||||
var tint = new float3(contrastColor.R / 255f, contrastColor.G / 255f, contrastColor.B / 255f);
|
||||
foreach (var s in text)
|
||||
{
|
||||
if (s == '\n')
|
||||
@@ -87,15 +83,16 @@ namespace OpenRA.Graphics
|
||||
continue;
|
||||
}
|
||||
|
||||
var g = glyphs[Pair.New(s, Color.Black)];
|
||||
var g = glyphs[s];
|
||||
|
||||
// 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,
|
||||
var contrastSprite = contrastGlyphs[(s, screenContrast)];
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSpriteWithTint(contrastSprite,
|
||||
(screen + g.Offset - contrastVector) / deviceScale,
|
||||
contrastSprite.Size / deviceScale);
|
||||
contrastSprite.Size / deviceScale,
|
||||
tint);
|
||||
}
|
||||
|
||||
screen += new int2((int)(g.Advance + 0.5f), 0);
|
||||
@@ -109,6 +106,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
// Calculate positions in screen pixel coordinates
|
||||
var screen = new int2((int)(location.X * deviceScale + 0.5f), (int)(location.Y * deviceScale + 0.5f));
|
||||
var tint = new float3(c.R / 255f, c.G / 255f, c.B / 255f);
|
||||
foreach (var s in text)
|
||||
{
|
||||
if (s == '\n')
|
||||
@@ -118,13 +116,14 @@ namespace OpenRA.Graphics
|
||||
continue;
|
||||
}
|
||||
|
||||
var g = glyphs[Pair.New(s, c)];
|
||||
var g = glyphs[s];
|
||||
|
||||
// Convert screen coordinates back to UI coordinates for drawing
|
||||
if (g.Sprite != null)
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite,
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSpriteWithTint(g.Sprite,
|
||||
(screen + g.Offset).ToFloat2() / deviceScale,
|
||||
g.Sprite.Size / deviceScale);
|
||||
g.Sprite.Size / deviceScale,
|
||||
tint);
|
||||
|
||||
screen += new int2((int)(g.Advance + 0.5f), 0);
|
||||
}
|
||||
@@ -144,6 +143,7 @@ namespace OpenRA.Graphics
|
||||
var offset = new float2(0, size);
|
||||
var cosa = (float)Math.Cos(-angle);
|
||||
var sina = (float)Math.Sin(-angle);
|
||||
var tint = new float3(c.R / 255f, c.G / 255f, c.B / 255f);
|
||||
|
||||
var p = offset;
|
||||
foreach (var s in text)
|
||||
@@ -155,7 +155,7 @@ namespace OpenRA.Graphics
|
||||
continue;
|
||||
}
|
||||
|
||||
var g = glyphs[Pair.New(s, c)];
|
||||
var g = glyphs[s];
|
||||
if (g.Sprite != null)
|
||||
{
|
||||
var tl = new float2(
|
||||
@@ -172,11 +172,12 @@ namespace OpenRA.Graphics
|
||||
|
||||
// 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,
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSpriteWithTint(g.Sprite,
|
||||
ra + screenOffset,
|
||||
rb + screenOffset,
|
||||
rc + screenOffset,
|
||||
rd + screenOffset);
|
||||
rd + screenOffset,
|
||||
tint);
|
||||
}
|
||||
|
||||
p += new float2(g.Advance / deviceScale, 0);
|
||||
@@ -241,10 +242,9 @@ namespace OpenRA.Graphics
|
||||
return new int2((int)Math.Ceiling(lines.Max(lineWidth)), lines.Length * size);
|
||||
}
|
||||
|
||||
GlyphInfo CreateGlyph(Pair<char, Color> c)
|
||||
GlyphInfo CreateGlyph(char c)
|
||||
{
|
||||
var glyph = font.CreateGlyph(c.First, size, deviceScale);
|
||||
|
||||
var glyph = font.CreateGlyph(c, size, deviceScale);
|
||||
if (glyph.Data == null)
|
||||
{
|
||||
return new GlyphInfo
|
||||
@@ -274,12 +274,10 @@ namespace OpenRA.Graphics
|
||||
if (p != 0)
|
||||
{
|
||||
var q = destStride * (j + s.Bounds.Top) + 4 * (i + s.Bounds.Left);
|
||||
var pmc = Util.PremultiplyAlpha(Color.FromArgb(p, c.Second));
|
||||
|
||||
dest[q] = pmc.B;
|
||||
dest[q + 1] = pmc.G;
|
||||
dest[q + 2] = pmc.R;
|
||||
dest[q + 3] = pmc.A;
|
||||
dest[q] = p;
|
||||
dest[q + 1] = p;
|
||||
dest[q + 2] = p;
|
||||
dest[q + 3] = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -347,16 +345,12 @@ namespace OpenRA.Graphics
|
||||
return elem;
|
||||
}
|
||||
|
||||
Sprite CreateContrastGlyph(Tuple<char, Color, int> c)
|
||||
Sprite CreateContrastGlyph((char C, int Radius) 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 glyph = glyphs[c.C];
|
||||
var r = c.Radius;
|
||||
|
||||
var size = new Size(glyph.Sprite.Bounds.Width + 2 * r, glyph.Sprite.Bounds.Height + 2 * r);
|
||||
|
||||
var s = builder.Allocate(size);
|
||||
var s = builder.Allocate(new Size(glyph.Sprite.Bounds.Width + 2 * r, glyph.Sprite.Bounds.Height + 2 * r));
|
||||
var dest = s.Sheet.GetData();
|
||||
var destStride = s.Sheet.Size.Width * 4;
|
||||
|
||||
@@ -398,11 +392,10 @@ namespace OpenRA.Graphics
|
||||
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;
|
||||
dest[q] = alpha;
|
||||
dest[q + 1] = alpha;
|
||||
dest[q + 2] = alpha;
|
||||
dest[q + 3] = alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +76,7 @@ namespace OpenRA.Graphics
|
||||
var allSprites = sprites.GetOrAdd(filename);
|
||||
var sprite = allSprites.FirstOrDefault();
|
||||
|
||||
ISpriteFrame[] unloaded;
|
||||
if (!unloadedFrames.TryGetValue(filename, out unloaded))
|
||||
if (!unloadedFrames.TryGetValue(filename, out var unloaded))
|
||||
unloaded = null;
|
||||
|
||||
// This is the first time that the file has been requested
|
||||
@@ -85,8 +84,7 @@ namespace OpenRA.Graphics
|
||||
// the loaded cache (initially empty)
|
||||
if (sprite == null)
|
||||
{
|
||||
TypeDictionary fileMetadata = null;
|
||||
unloaded = FrameLoader.GetFrames(fileSystem, filename, loaders, out fileMetadata);
|
||||
unloaded = FrameLoader.GetFrames(fileSystem, filename, loaders, out var fileMetadata);
|
||||
unloadedFrames[filename] = unloaded;
|
||||
metadata[filename] = fileMetadata;
|
||||
|
||||
@@ -125,8 +123,7 @@ namespace OpenRA.Graphics
|
||||
/// </summary>
|
||||
public TypeDictionary FrameMetadata(string filename)
|
||||
{
|
||||
TypeDictionary fileMetadata;
|
||||
if (!metadata.TryGetValue(filename, out fileMetadata))
|
||||
if (!metadata.TryGetValue(filename, out var fileMetadata))
|
||||
{
|
||||
FrameLoader.GetFrames(fileSystem, filename, loaders, out fileMetadata);
|
||||
metadata[filename] = fileMetadata;
|
||||
@@ -142,8 +139,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public FrameCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders)
|
||||
{
|
||||
TypeDictionary metadata;
|
||||
frames = new Cache<string, ISpriteFrame[]>(filename => FrameLoader.GetFrames(fileSystem, filename, loaders, out metadata));
|
||||
frames = new Cache<string, ISpriteFrame[]>(filename => FrameLoader.GetFrames(fileSystem, filename, loaders, out _));
|
||||
}
|
||||
|
||||
public ISpriteFrame[] this[string filename] { get { return frames[filename]; } }
|
||||
@@ -165,11 +161,10 @@ namespace OpenRA.Graphics
|
||||
|
||||
public static ISpriteFrame[] GetFrames(Stream stream, ISpriteLoader[] loaders, out TypeDictionary metadata)
|
||||
{
|
||||
ISpriteFrame[] frames;
|
||||
metadata = null;
|
||||
|
||||
foreach (var loader in loaders)
|
||||
if (loader.TryParseSprite(stream, out frames, out metadata))
|
||||
if (loader.TryParseSprite(stream, out var frames, out metadata))
|
||||
return frames;
|
||||
|
||||
return null;
|
||||
|
||||
@@ -14,7 +14,7 @@ using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
public struct SpriteRenderable : IRenderable, IFinalizedRenderable
|
||||
public struct SpriteRenderable : IRenderable, ITintableRenderable, IFinalizedRenderable
|
||||
{
|
||||
public static readonly IEnumerable<IRenderable> None = new IRenderable[0];
|
||||
|
||||
@@ -24,9 +24,17 @@ namespace OpenRA.Graphics
|
||||
readonly int zOffset;
|
||||
readonly PaletteReference palette;
|
||||
readonly float scale;
|
||||
readonly float3 tint;
|
||||
readonly bool isDecoration;
|
||||
readonly bool ignoreWorldTint;
|
||||
|
||||
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, bool isDecoration)
|
||||
: this(sprite, pos, offset, zOffset, palette, scale, float3.Ones, isDecoration, false) { }
|
||||
|
||||
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, bool isDecoration, bool ignoreWorldTint)
|
||||
: this(sprite, pos, offset, zOffset, palette, scale, float3.Ones, isDecoration, ignoreWorldTint) { }
|
||||
|
||||
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, float3 tint, bool isDecoration, bool ignoreWorldTint)
|
||||
{
|
||||
this.sprite = sprite;
|
||||
this.pos = pos;
|
||||
@@ -34,7 +42,9 @@ namespace OpenRA.Graphics
|
||||
this.zOffset = zOffset;
|
||||
this.palette = palette;
|
||||
this.scale = scale;
|
||||
this.tint = tint;
|
||||
this.isDecoration = isDecoration;
|
||||
this.ignoreWorldTint = ignoreWorldTint;
|
||||
}
|
||||
|
||||
public WPos Pos { get { return pos + offset; } }
|
||||
@@ -43,10 +53,12 @@ namespace OpenRA.Graphics
|
||||
public int ZOffset { get { return zOffset; } }
|
||||
public bool IsDecoration { get { return isDecoration; } }
|
||||
|
||||
public IRenderable WithPalette(PaletteReference newPalette) { return new SpriteRenderable(sprite, pos, offset, zOffset, newPalette, scale, isDecoration); }
|
||||
public IRenderable WithZOffset(int newOffset) { return new SpriteRenderable(sprite, pos, offset, newOffset, palette, scale, isDecoration); }
|
||||
public IRenderable OffsetBy(WVec vec) { return new SpriteRenderable(sprite, pos + vec, offset, zOffset, palette, scale, isDecoration); }
|
||||
public IRenderable AsDecoration() { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, true); }
|
||||
public IRenderable WithPalette(PaletteReference newPalette) { return new SpriteRenderable(sprite, pos, offset, zOffset, newPalette, scale, tint, isDecoration, ignoreWorldTint); }
|
||||
public IRenderable WithZOffset(int newOffset) { return new SpriteRenderable(sprite, pos, offset, newOffset, palette, scale, tint, isDecoration, ignoreWorldTint); }
|
||||
public IRenderable OffsetBy(WVec vec) { return new SpriteRenderable(sprite, pos + vec, offset, zOffset, palette, scale, tint, isDecoration, ignoreWorldTint); }
|
||||
public IRenderable AsDecoration() { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, tint, true, ignoreWorldTint); }
|
||||
|
||||
public IRenderable WithTint(float3 newTint) { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, newTint, isDecoration, ignoreWorldTint); }
|
||||
|
||||
float3 ScreenPosition(WorldRenderer wr)
|
||||
{
|
||||
@@ -59,7 +71,17 @@ namespace OpenRA.Graphics
|
||||
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
|
||||
public void Render(WorldRenderer wr)
|
||||
{
|
||||
Game.Renderer.WorldSpriteRenderer.DrawSprite(sprite, ScreenPosition(wr), palette, scale * sprite.Size);
|
||||
var wsr = Game.Renderer.WorldSpriteRenderer;
|
||||
if (ignoreWorldTint)
|
||||
wsr.DrawSprite(sprite, ScreenPosition(wr), palette, scale * sprite.Size);
|
||||
else
|
||||
{
|
||||
var t = tint;
|
||||
if (wr.TerrainLighting != null)
|
||||
t *= wr.TerrainLighting.TintAt(pos);
|
||||
|
||||
wsr.DrawSpriteWithTint(sprite, ScreenPosition(wr), palette, scale * sprite.Size, t);
|
||||
}
|
||||
}
|
||||
|
||||
public void RenderDebugGeometry(WorldRenderer wr)
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace OpenRA.Graphics
|
||||
internal void DrawSprite(Sprite s, float3 location, float paletteTextureIndex, float3 size)
|
||||
{
|
||||
var samplers = SetRenderStateForSprite(s);
|
||||
Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, samplers, paletteTextureIndex, nv, size);
|
||||
Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, samplers, paletteTextureIndex, nv, size, float3.Ones);
|
||||
nv += 6;
|
||||
}
|
||||
|
||||
@@ -124,7 +124,26 @@ namespace OpenRA.Graphics
|
||||
public void DrawSprite(Sprite s, float3 a, float3 b, float3 c, float3 d)
|
||||
{
|
||||
var samplers = SetRenderStateForSprite(s);
|
||||
Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, 0, nv);
|
||||
Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, 0, float3.Ones, nv);
|
||||
nv += 6;
|
||||
}
|
||||
|
||||
internal void DrawSpriteWithTint(Sprite s, float3 location, float paletteTextureIndex, float3 size, float3 tint)
|
||||
{
|
||||
var samplers = SetRenderStateForSprite(s);
|
||||
Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, samplers, paletteTextureIndex, nv, size, tint);
|
||||
nv += 6;
|
||||
}
|
||||
|
||||
public void DrawSpriteWithTint(Sprite s, float3 location, PaletteReference pal, float3 size, float3 tint)
|
||||
{
|
||||
DrawSpriteWithTint(s, location, pal.TextureIndex, size, tint);
|
||||
}
|
||||
|
||||
public void DrawSpriteWithTint(Sprite s, float3 a, float3 b, float3 c, float3 d, float3 tint)
|
||||
{
|
||||
var samplers = SetRenderStateForSprite(s);
|
||||
Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, 0, tint, nv);
|
||||
nv += 6;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
public sealed class TerrainSpriteLayer : IDisposable
|
||||
{
|
||||
static readonly int[] CornerVertexMap = { 0, 1, 2, 2, 3, 0 };
|
||||
|
||||
public readonly Sheet Sheet;
|
||||
public readonly BlendMode BlendMode;
|
||||
|
||||
@@ -25,6 +27,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
readonly IVertexBuffer<Vertex> vertexBuffer;
|
||||
readonly Vertex[] vertices;
|
||||
readonly bool[] ignoreTint;
|
||||
readonly HashSet<int> dirtyRows = new HashSet<int>();
|
||||
readonly int rowStride;
|
||||
readonly bool restrictToBounds;
|
||||
@@ -50,6 +53,12 @@ namespace OpenRA.Graphics
|
||||
emptySprite = new Sprite(sheet, Rectangle.Empty, TextureChannel.Alpha);
|
||||
|
||||
wr.PaletteInvalidated += UpdatePaletteIndices;
|
||||
|
||||
if (wr.TerrainLighting != null)
|
||||
{
|
||||
ignoreTint = new bool[rowStride * map.MapSize.Y];
|
||||
wr.TerrainLighting.CellChanged += UpdateTint;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdatePaletteIndices()
|
||||
@@ -59,22 +68,76 @@ namespace OpenRA.Graphics
|
||||
for (var i = 0; i < vertices.Length; i++)
|
||||
{
|
||||
var v = vertices[i];
|
||||
vertices[i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C);
|
||||
vertices[i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C, v.R, v.G, v.B);
|
||||
}
|
||||
|
||||
for (var row = 0; row < map.MapSize.Y; row++)
|
||||
dirtyRows.Add(row);
|
||||
}
|
||||
|
||||
public void Update(CPos cell, Sprite sprite)
|
||||
public void Clear(CPos cell)
|
||||
{
|
||||
var xyz = sprite == null ? float3.Zero :
|
||||
worldRenderer.Screen3DPosition(map.CenterOfCell(cell)) + sprite.Offset - 0.5f * sprite.Size;
|
||||
|
||||
Update(cell.ToMPos(map.Grid.Type), sprite, xyz);
|
||||
Update(cell, null, true);
|
||||
}
|
||||
|
||||
public void Update(MPos uv, Sprite sprite, float3 pos)
|
||||
public void Update(CPos cell, ISpriteSequence sequence, int frame)
|
||||
{
|
||||
Update(cell, sequence.GetSprite(frame), sequence.IgnoreWorldTint);
|
||||
}
|
||||
|
||||
public void Update(CPos cell, Sprite sprite, bool ignoreTint)
|
||||
{
|
||||
var xyz = float3.Zero;
|
||||
if (sprite != null)
|
||||
{
|
||||
var cellOrigin = map.CenterOfCell(cell) - new WVec(0, 0, map.Grid.Ramps[map.Ramp[cell]].CenterHeightOffset);
|
||||
xyz = worldRenderer.Screen3DPosition(cellOrigin) + sprite.Offset - 0.5f * sprite.Size;
|
||||
}
|
||||
|
||||
Update(cell.ToMPos(map.Grid.Type), sprite, xyz, ignoreTint);
|
||||
}
|
||||
|
||||
void UpdateTint(MPos uv)
|
||||
{
|
||||
var offset = rowStride * uv.V + 6 * uv.U;
|
||||
if (ignoreTint[offset])
|
||||
{
|
||||
var noTint = float3.Ones;
|
||||
for (var i = 0; i < 6; i++)
|
||||
{
|
||||
var v = vertices[offset + i];
|
||||
vertices[offset + i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C, noTint);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow the terrain tint to vary linearly across the cell to smooth out the staircase effect
|
||||
// This is done by sampling the lighting the corners of the sprite, even though those pixels are
|
||||
// transparent for isometric tiles
|
||||
var tl = worldRenderer.TerrainLighting;
|
||||
var pos = map.CenterOfCell(uv.ToCPos(map));
|
||||
var step = map.Grid.Type == MapGridType.RectangularIsometric ? 724 : 512;
|
||||
var weights = new[]
|
||||
{
|
||||
tl.TintAt(pos + new WVec(-step, -step, 0)),
|
||||
tl.TintAt(pos + new WVec(step, -step, 0)),
|
||||
tl.TintAt(pos + new WVec(step, step, 0)),
|
||||
tl.TintAt(pos + new WVec(-step, step, 0))
|
||||
};
|
||||
|
||||
// Apply tint directly to the underlying vertices
|
||||
// This saves us from having to re-query the sprite information, which has not changed
|
||||
for (var i = 0; i < 6; i++)
|
||||
{
|
||||
var v = vertices[offset + i];
|
||||
vertices[offset + i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C, weights[CornerVertexMap[i]]);
|
||||
}
|
||||
|
||||
dirtyRows.Add(uv.V);
|
||||
}
|
||||
|
||||
public void Update(MPos uv, Sprite sprite, float3 pos, bool ignoreTint)
|
||||
{
|
||||
if (sprite != null)
|
||||
{
|
||||
@@ -92,7 +155,13 @@ namespace OpenRA.Graphics
|
||||
return;
|
||||
|
||||
var offset = rowStride * uv.V + 6 * uv.U;
|
||||
Util.FastCreateQuad(vertices, pos, sprite, int2.Zero, palette.TextureIndex, offset, sprite.Size);
|
||||
Util.FastCreateQuad(vertices, pos, sprite, int2.Zero, palette.TextureIndex, offset, sprite.Size, float3.Ones);
|
||||
|
||||
if (worldRenderer.TerrainLighting != null)
|
||||
{
|
||||
this.ignoreTint[offset] = ignoreTint;
|
||||
UpdateTint(uv);
|
||||
}
|
||||
|
||||
dirtyRows.Add(uv.V);
|
||||
}
|
||||
@@ -135,6 +204,9 @@ namespace OpenRA.Graphics
|
||||
public void Dispose()
|
||||
{
|
||||
worldRenderer.PaletteInvalidated -= UpdatePaletteIndices;
|
||||
if (worldRenderer.TerrainLighting != null)
|
||||
worldRenderer.TerrainLighting.CellChanged -= UpdateTint;
|
||||
|
||||
vertexBuffer.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,8 +118,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public Sprite TileSprite(TerrainTile r, int? variant = null)
|
||||
{
|
||||
TheaterTemplate template;
|
||||
if (!templates.TryGetValue(r.Type, out template))
|
||||
if (!templates.TryGetValue(r.Type, out var template))
|
||||
return missingTile;
|
||||
|
||||
if (r.Index >= template.Stride)
|
||||
|
||||
@@ -20,15 +20,18 @@ namespace OpenRA.Graphics
|
||||
// yes, our channel order is nuts.
|
||||
static readonly int[] ChannelMasks = { 2, 1, 0, 3 };
|
||||
|
||||
public static void FastCreateQuad(Vertex[] vertices, float3 o, Sprite r, int2 samplers, float paletteTextureIndex, int nv, float3 size)
|
||||
public static void FastCreateQuad(Vertex[] vertices, float3 o, Sprite r, int2 samplers, float paletteTextureIndex, int nv, float3 size, float3 tint)
|
||||
{
|
||||
var b = new float3(o.X + size.X, o.Y, o.Z);
|
||||
var c = new float3(o.X + size.X, o.Y + size.Y, o.Z + size.Z);
|
||||
var d = new float3(o.X, o.Y + size.Y, o.Z + size.Z);
|
||||
FastCreateQuad(vertices, o, b, c, d, r, samplers, paletteTextureIndex, nv);
|
||||
FastCreateQuad(vertices, o, b, c, d, r, samplers, paletteTextureIndex, tint, nv);
|
||||
}
|
||||
|
||||
public static void FastCreateQuad(Vertex[] vertices, float3 a, float3 b, float3 c, float3 d, Sprite r, int2 samplers, float paletteTextureIndex, int nv)
|
||||
public static void FastCreateQuad(Vertex[] vertices,
|
||||
float3 a, float3 b, float3 c, float3 d,
|
||||
Sprite r, int2 samplers, float paletteTextureIndex,
|
||||
float3 tint, int nv)
|
||||
{
|
||||
float sl = 0;
|
||||
float st = 0;
|
||||
@@ -51,12 +54,12 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
|
||||
var fAttribC = (float)attribC;
|
||||
vertices[nv] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC);
|
||||
vertices[nv + 1] = new Vertex(b, r.Right, r.Top, sr, st, paletteTextureIndex, fAttribC);
|
||||
vertices[nv + 2] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC);
|
||||
vertices[nv + 3] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC);
|
||||
vertices[nv + 4] = new Vertex(d, r.Left, r.Bottom, sl, sb, paletteTextureIndex, fAttribC);
|
||||
vertices[nv + 5] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC);
|
||||
vertices[nv] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC, tint);
|
||||
vertices[nv + 1] = new Vertex(b, r.Right, r.Top, sr, st, paletteTextureIndex, fAttribC, tint);
|
||||
vertices[nv + 2] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC, tint);
|
||||
vertices[nv + 3] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC, tint);
|
||||
vertices[nv + 4] = new Vertex(d, r.Left, r.Bottom, sl, sb, paletteTextureIndex, fAttribC, tint);
|
||||
vertices[nv + 5] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC, tint);
|
||||
}
|
||||
|
||||
public static void FastCopyIntoChannel(Sprite dest, byte[] src)
|
||||
|
||||
@@ -16,17 +16,34 @@ namespace OpenRA.Graphics
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Vertex
|
||||
{
|
||||
public readonly float X, Y, Z, S, T, U, V, P, C;
|
||||
// 3d position
|
||||
public readonly float X, Y, Z;
|
||||
|
||||
// Primary and secondary texture coordinates or RGBA color
|
||||
public readonly float S, T, U, V;
|
||||
|
||||
// Palette and channel flags
|
||||
public readonly float P, C;
|
||||
|
||||
// Color tint
|
||||
public readonly float R, G, B;
|
||||
|
||||
public Vertex(float3 xyz, float s, float t, float u, float v, float p, float c)
|
||||
: this(xyz.X, xyz.Y, xyz.Z, s, t, u, v, p, c) { }
|
||||
: this(xyz.X, xyz.Y, xyz.Z, s, t, u, v, p, c, float3.Ones) { }
|
||||
|
||||
public Vertex(float x, float y, float z, float s, float t, float u, float v, float p, float c)
|
||||
public Vertex(float3 xyz, float s, float t, float u, float v, float p, float c, float3 tint)
|
||||
: this(xyz.X, xyz.Y, xyz.Z, s, t, u, v, p, c, tint.X, tint.Y, tint.Z) { }
|
||||
|
||||
public Vertex(float x, float y, float z, float s, float t, float u, float v, float p, float c, float3 tint)
|
||||
: this(x, y, z, s, t, u, v, p, c, tint.X, tint.Y, tint.Z) { }
|
||||
|
||||
public Vertex(float x, float y, float z, float s, float t, float u, float v, float p, float c, float r, float g, float b)
|
||||
{
|
||||
X = x; Y = y; Z = z;
|
||||
S = s; T = t;
|
||||
U = u; V = v;
|
||||
P = p; C = c;
|
||||
R = r; G = g; B = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -84,12 +89,22 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
public float MinZoom { get { return minZoom; } }
|
||||
|
||||
public void AdjustZoom(float dz)
|
||||
{
|
||||
// Exponential ensures that equal positive and negative steps have the same effect
|
||||
Zoom = (zoom * (float)Math.Exp(dz)).Clamp(unlockMinZoom ? unlockedMinZoom : minZoom, maxZoom);
|
||||
}
|
||||
|
||||
public void AdjustZoom(float dz, int2 center)
|
||||
{
|
||||
var oldCenter = worldRenderer.Viewport.ViewToWorldPx(center);
|
||||
AdjustZoom(dz);
|
||||
var newCenter = worldRenderer.Viewport.ViewToWorldPx(center);
|
||||
CenterLocation += oldCenter - newCenter;
|
||||
}
|
||||
|
||||
public void ToggleZoom()
|
||||
{
|
||||
// Unlocked zooms always reset to the default zoom
|
||||
@@ -237,6 +252,9 @@ namespace OpenRA.Graphics
|
||||
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)
|
||||
@@ -244,7 +262,6 @@ namespace OpenRA.Graphics
|
||||
var world = worldRenderer.Viewport.ViewToWorldPx(view);
|
||||
var map = worldRenderer.World.Map;
|
||||
var candidates = CandidateMouseoverCells(world).ToList();
|
||||
var tileSet = worldRenderer.World.Map.Rules.TileSet;
|
||||
|
||||
foreach (var uv in candidates)
|
||||
{
|
||||
@@ -253,18 +270,9 @@ namespace OpenRA.Graphics
|
||||
var s = worldRenderer.ScreenPxPosition(p);
|
||||
if (Math.Abs(s.X - world.X) <= tileSize.Width && Math.Abs(s.Y - world.Y) <= tileSize.Height)
|
||||
{
|
||||
var ramp = 0;
|
||||
if (map.Contains(uv))
|
||||
{
|
||||
var ti = tileSet.GetTileInfo(map.Tiles[uv]);
|
||||
if (ti != null)
|
||||
ramp = ti.RampType;
|
||||
}
|
||||
|
||||
var corners = map.Grid.CellCorners[ramp];
|
||||
var pos = map.CenterOfCell(uv.ToCPos(map));
|
||||
var screen = corners.Select(c => worldRenderer.ScreenPxPosition(pos + c)).ToArray();
|
||||
|
||||
var ramp = map.Grid.Ramps[map.Ramp.Contains(uv) ? map.Ramp[uv] : 0];
|
||||
var pos = map.CenterOfCell(uv.ToCPos(map)) - new WVec(0, 0, ramp.CenterHeightOffset);
|
||||
var screen = ramp.Corners.Select(c => worldRenderer.ScreenPxPosition(pos + c)).ToArray();
|
||||
if (screen.PolygonContains(world))
|
||||
return uv.ToCPos(map);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace OpenRA.Graphics
|
||||
public readonly World World;
|
||||
public readonly Theater Theater;
|
||||
public Viewport Viewport { get; private set; }
|
||||
public readonly ITerrainLighting TerrainLighting;
|
||||
|
||||
public event Action PaletteInvalidated = null;
|
||||
|
||||
@@ -43,6 +44,8 @@ namespace OpenRA.Graphics
|
||||
readonly List<IFinalizedRenderable> preparedOverlayRenderables = new List<IFinalizedRenderable>();
|
||||
readonly List<IFinalizedRenderable> preparedAnnotationRenderables = new List<IFinalizedRenderable>();
|
||||
|
||||
readonly List<IRenderable> renderablesBuffer = new List<IRenderable>();
|
||||
|
||||
bool lastDepthPreviewEnabled;
|
||||
|
||||
internal WorldRenderer(ModData modData, World world)
|
||||
@@ -66,6 +69,7 @@ namespace OpenRA.Graphics
|
||||
palette.Initialize();
|
||||
|
||||
Theater = new Theater(world.Map.Rules.TileSet);
|
||||
TerrainLighting = world.WorldActor.TraitOrDefault<ITerrainLighting>();
|
||||
terrainRenderer = world.WorldActor.TraitOrDefault<IRenderTerrain>();
|
||||
|
||||
debugVis = Exts.Lazy(() => world.WorldActor.TraitOrDefault<DebugVisualizations>());
|
||||
@@ -93,8 +97,8 @@ namespace OpenRA.Graphics
|
||||
var oldHeight = palette.Height;
|
||||
palette.AddPalette(name, pal, allowModifiers);
|
||||
|
||||
if (oldHeight != palette.Height && PaletteInvalidated != null)
|
||||
PaletteInvalidated();
|
||||
if (oldHeight != palette.Height)
|
||||
PaletteInvalidated?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,78 +111,117 @@ namespace OpenRA.Graphics
|
||||
palettes[name].Palette = pal;
|
||||
}
|
||||
|
||||
IEnumerable<IFinalizedRenderable> GenerateRenderables()
|
||||
// PERF: Avoid LINQ.
|
||||
void GenerateRenderables()
|
||||
{
|
||||
var actors = onScreenActors.Append(World.WorldActor);
|
||||
if (World.RenderPlayer != null)
|
||||
actors = actors.Append(World.RenderPlayer.PlayerActor);
|
||||
foreach (var actor in onScreenActors)
|
||||
renderablesBuffer.AddRange(actor.Render(this));
|
||||
|
||||
renderablesBuffer.AddRange(World.WorldActor.Render(this));
|
||||
|
||||
if (World.RenderPlayer != null)
|
||||
renderablesBuffer.AddRange(World.RenderPlayer.PlayerActor.Render(this));
|
||||
|
||||
var worldRenderables = actors.SelectMany(a => a.Render(this));
|
||||
if (World.OrderGenerator != null)
|
||||
worldRenderables = worldRenderables.Concat(World.OrderGenerator.Render(this, World));
|
||||
renderablesBuffer.AddRange(World.OrderGenerator.Render(this, World));
|
||||
|
||||
// Unpartitioned effects
|
||||
worldRenderables = worldRenderables.Concat(World.UnpartitionedEffects.SelectMany(e => e.Render(this)));
|
||||
foreach (var e in World.UnpartitionedEffects)
|
||||
renderablesBuffer.AddRange(e.Render(this));
|
||||
|
||||
// Partitioned, currently on-screen effects
|
||||
var effectRenderables = World.ScreenMap.RenderableEffectsInBox(Viewport.TopLeft, Viewport.BottomRight);
|
||||
worldRenderables = worldRenderables.Concat(effectRenderables.SelectMany(e => e.Render(this)));
|
||||
foreach (var e in World.ScreenMap.RenderableEffectsInBox(Viewport.TopLeft, Viewport.BottomRight))
|
||||
renderablesBuffer.AddRange(e.Render(this));
|
||||
|
||||
worldRenderables = worldRenderables.OrderBy(RenderableScreenZPositionComparisonKey);
|
||||
foreach (var renderable in renderablesBuffer.OrderBy(RenderableScreenZPositionComparisonKey))
|
||||
preparedRenderables.Add(renderable.PrepareRender(this));
|
||||
|
||||
return worldRenderables.Select(r => r.PrepareRender(this));
|
||||
// PERF: Reuse collection to avoid allocations.
|
||||
renderablesBuffer.Clear();
|
||||
}
|
||||
|
||||
IEnumerable<IFinalizedRenderable> GenerateOverlayRenderables()
|
||||
// PERF: Avoid LINQ.
|
||||
void GenerateOverlayRenderables()
|
||||
{
|
||||
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));
|
||||
foreach (var a in World.ActorsWithTrait<IRenderAboveShroud>())
|
||||
{
|
||||
if (!a.Actor.IsInWorld || a.Actor.Disposed || (a.Trait.SpatiallyPartitionable && !onScreenActors.Contains(a.Actor)))
|
||||
continue;
|
||||
|
||||
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)));
|
||||
foreach (var renderable in a.Trait.RenderAboveShroud(a.Actor, this))
|
||||
preparedOverlayRenderables.Add(renderable.PrepareRender(this));
|
||||
}
|
||||
|
||||
var effects = World.Effects.Select(e => e as IEffectAboveShroud)
|
||||
.Where(e => e != null)
|
||||
.SelectMany(e => e.RenderAboveShroud(this));
|
||||
foreach (var a in World.Selection.Actors)
|
||||
{
|
||||
if (!a.IsInWorld || a.Disposed)
|
||||
continue;
|
||||
|
||||
foreach (var t in a.TraitsImplementing<IRenderAboveShroudWhenSelected>())
|
||||
{
|
||||
if (t.SpatiallyPartitionable && !onScreenActors.Contains(a))
|
||||
continue;
|
||||
|
||||
foreach (var renderable in t.RenderAboveShroud(a, this))
|
||||
preparedOverlayRenderables.Add(renderable.PrepareRender(this));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var e in World.Effects)
|
||||
{
|
||||
var ea = e as IEffectAboveShroud;
|
||||
if (ea == null)
|
||||
continue;
|
||||
|
||||
foreach (var renderable in ea.RenderAboveShroud(this))
|
||||
preparedOverlayRenderables.Add(renderable.PrepareRender(this));
|
||||
}
|
||||
|
||||
var orderGenerator = SpriteRenderable.None;
|
||||
if (World.OrderGenerator != null)
|
||||
orderGenerator = World.OrderGenerator.RenderAboveShroud(this, World);
|
||||
|
||||
return actors
|
||||
.Concat(selected)
|
||||
.Concat(effects)
|
||||
.Concat(orderGenerator)
|
||||
.Select(r => r.PrepareRender(this));
|
||||
foreach (var renderable in World.OrderGenerator.RenderAboveShroud(this, World))
|
||||
preparedOverlayRenderables.Add(renderable.PrepareRender(this));
|
||||
}
|
||||
|
||||
IEnumerable<IFinalizedRenderable> GenerateAnnotationRenderables()
|
||||
// PERF: Avoid LINQ.
|
||||
void 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));
|
||||
foreach (var a in World.ActorsWithTrait<IRenderAnnotations>())
|
||||
{
|
||||
if (!a.Actor.IsInWorld || a.Actor.Disposed || (a.Trait.SpatiallyPartitionable && !onScreenActors.Contains(a.Actor)))
|
||||
continue;
|
||||
|
||||
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)));
|
||||
foreach (var renderAnnotation in a.Trait.RenderAnnotations(a.Actor, this))
|
||||
preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this));
|
||||
}
|
||||
|
||||
var effects = World.Effects.Select(e => e as IEffectAnnotation)
|
||||
.Where(e => e != null)
|
||||
.SelectMany(e => e.RenderAnnotation(this));
|
||||
foreach (var a in World.Selection.Actors)
|
||||
{
|
||||
if (!a.IsInWorld || a.Disposed)
|
||||
continue;
|
||||
|
||||
foreach (var t in a.TraitsImplementing<IRenderAnnotationsWhenSelected>())
|
||||
{
|
||||
if (t.SpatiallyPartitionable && !onScreenActors.Contains(a))
|
||||
continue;
|
||||
|
||||
foreach (var renderAnnotation in t.RenderAnnotations(a, this))
|
||||
preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var e in World.Effects)
|
||||
{
|
||||
var ea = e as IEffectAnnotation;
|
||||
if (ea == null)
|
||||
continue;
|
||||
|
||||
foreach (var renderAnnotation in ea.RenderAnnotation(this))
|
||||
preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this));
|
||||
}
|
||||
|
||||
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));
|
||||
foreach (var renderAnnotation in World.OrderGenerator.RenderAnnotations(this, World))
|
||||
preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this));
|
||||
}
|
||||
|
||||
public void PrepareRenderables()
|
||||
@@ -190,9 +233,11 @@ namespace OpenRA.Graphics
|
||||
|
||||
// PERF: Reuse collection to avoid allocations.
|
||||
onScreenActors.UnionWith(World.ScreenMap.RenderableActorsInBox(Viewport.TopLeft, Viewport.BottomRight));
|
||||
preparedRenderables.AddRange(GenerateRenderables());
|
||||
preparedOverlayRenderables.AddRange(GenerateOverlayRenderables());
|
||||
preparedAnnotationRenderables.AddRange(GenerateAnnotationRenderables());
|
||||
|
||||
GenerateRenderables();
|
||||
GenerateOverlayRenderables();
|
||||
GenerateAnnotationRenderables();
|
||||
|
||||
onScreenActors.Clear();
|
||||
}
|
||||
|
||||
@@ -213,8 +258,7 @@ namespace OpenRA.Graphics
|
||||
if (enableDepthBuffer)
|
||||
Game.Renderer.Context.EnableDepthBuffer();
|
||||
|
||||
if (terrainRenderer != null)
|
||||
terrainRenderer.RenderTerrain(this, Viewport);
|
||||
terrainRenderer?.RenderTerrain(this, Viewport);
|
||||
|
||||
Game.Renderer.Flush();
|
||||
|
||||
@@ -277,11 +321,16 @@ namespace OpenRA.Graphics
|
||||
Game.Renderer.RgbaColorRenderer.DrawRect(tl, br, 1, Color.MediumSpringGreen);
|
||||
}
|
||||
|
||||
foreach (var r in World.ScreenMap.MouseBounds(World.RenderPlayer))
|
||||
foreach (var b in World.ScreenMap.MouseBounds(World.RenderPlayer))
|
||||
{
|
||||
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);
|
||||
var points = new float2[b.Vertices.Length];
|
||||
for (var index = 0; index < b.Vertices.Length; index++)
|
||||
{
|
||||
var vertex = b.Vertices[index];
|
||||
points[index] = Viewport.WorldToViewPx(vertex).ToFloat2();
|
||||
}
|
||||
|
||||
Game.Renderer.RgbaColorRenderer.DrawPolygon(points, 1, Color.OrangeRed);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,8 +50,7 @@ namespace OpenRA
|
||||
return () => keys[name];
|
||||
|
||||
// Try and parse as a hardcoded definition
|
||||
Hotkey key;
|
||||
if (!Hotkey.TryParse(name, out key))
|
||||
if (!Hotkey.TryParse(name, out var key))
|
||||
key = Hotkey.Invalid;
|
||||
|
||||
return () => key;
|
||||
@@ -59,8 +58,7 @@ namespace OpenRA
|
||||
|
||||
public void Set(string name, Hotkey value)
|
||||
{
|
||||
HotkeyDefinition definition;
|
||||
if (!definitions.TryGetValue(name, out definition))
|
||||
if (!definitions.TryGetValue(name, out var definition))
|
||||
return;
|
||||
|
||||
keys[name] = value;
|
||||
|
||||
@@ -32,11 +32,9 @@ namespace OpenRA
|
||||
|
||||
var parts = s.Split(' ');
|
||||
|
||||
Keycode key;
|
||||
if (!Enum<Keycode>.TryParse(parts[0], true, out key))
|
||||
if (!Enum<Keycode>.TryParse(parts[0], true, out var key))
|
||||
{
|
||||
int c;
|
||||
if (!int.TryParse(parts[0], out c))
|
||||
if (!int.TryParse(parts[0], out var c))
|
||||
return false;
|
||||
key = (Keycode)c;
|
||||
}
|
||||
|
||||
@@ -498,8 +498,7 @@ namespace OpenRA
|
||||
|
||||
public static string DisplayString(Keycode k)
|
||||
{
|
||||
string ret;
|
||||
if (!KeyNames.TryGetValue(k, out ret))
|
||||
if (!KeyNames.TryGetValue(k, out var ret))
|
||||
return k.ToString();
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -14,7 +14,6 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.FileSystem;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
@@ -35,9 +34,9 @@ namespace OpenRA
|
||||
mods = GetInstalledMods(searchPaths, explicitPaths);
|
||||
}
|
||||
|
||||
static IEnumerable<Pair<string, string>> GetCandidateMods(IEnumerable<string> searchPaths)
|
||||
static IEnumerable<(string Id, string Path)> GetCandidateMods(IEnumerable<string> searchPaths)
|
||||
{
|
||||
var mods = new List<Pair<string, string>>();
|
||||
var mods = new List<(string, string)>();
|
||||
foreach (var path in searchPaths)
|
||||
{
|
||||
try
|
||||
@@ -48,7 +47,7 @@ namespace OpenRA
|
||||
|
||||
var directory = new DirectoryInfo(resolved);
|
||||
foreach (var subdir in directory.EnumerateDirectories())
|
||||
mods.Add(Pair.New(subdir.Name, subdir.FullName));
|
||||
mods.Add((subdir.Name, subdir.FullName));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -79,8 +78,7 @@ namespace OpenRA
|
||||
Log.Write("debug", "Load mod '{0}': {1}".F(path, e));
|
||||
}
|
||||
|
||||
if (package != null)
|
||||
package.Dispose();
|
||||
package?.Dispose();
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -89,13 +87,13 @@ namespace OpenRA
|
||||
{
|
||||
var ret = new Dictionary<string, Manifest>();
|
||||
var candidates = GetCandidateMods(searchPaths)
|
||||
.Concat(explicitPaths.Select(p => Pair.New(Path.GetFileNameWithoutExtension(p), p)));
|
||||
.Concat(explicitPaths.Select(p => (Id: Path.GetFileNameWithoutExtension(p), Path: p)));
|
||||
|
||||
foreach (var pair in candidates)
|
||||
{
|
||||
var mod = LoadMod(pair.First, pair.Second);
|
||||
var mod = LoadMod(pair.Id, pair.Path);
|
||||
if (mod != null)
|
||||
ret[pair.First] = mod;
|
||||
ret[pair.Id] = mod;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -80,8 +80,6 @@ namespace OpenRA
|
||||
{
|
||||
try
|
||||
{
|
||||
innerState = LinkState.Unlinked;
|
||||
|
||||
if (i.Error != null)
|
||||
{
|
||||
innerState = LinkState.ConnectionFailed;
|
||||
@@ -100,6 +98,8 @@ namespace OpenRA
|
||||
else
|
||||
innerState = LinkState.Linked;
|
||||
}
|
||||
else
|
||||
innerState = LinkState.Unlinked;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -108,8 +108,7 @@ namespace OpenRA
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (onComplete != null)
|
||||
onComplete();
|
||||
onComplete?.Invoke();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace OpenRA
|
||||
}
|
||||
|
||||
/// <summary> Describes what is to be loaded in order to run a mod. </summary>
|
||||
public class Manifest
|
||||
public class Manifest : IDisposable
|
||||
{
|
||||
public readonly string Id;
|
||||
public readonly IReadOnlyPackage Package;
|
||||
@@ -96,8 +96,7 @@ namespace OpenRA
|
||||
// TODO: Use fieldloader
|
||||
MapFolders = YamlDictionary(yaml, "MapFolders");
|
||||
|
||||
MiniYaml packages;
|
||||
if (yaml.TryGetValue("Packages", out packages))
|
||||
if (yaml.TryGetValue("Packages", out var packages))
|
||||
Packages = packages.ToDictionary(x => x.Value).AsReadOnly();
|
||||
|
||||
Rules = YamlList(yaml, "Rules");
|
||||
@@ -217,9 +216,8 @@ namespace OpenRA
|
||||
/// </summary>
|
||||
public T Get<T>(ObjectCreator oc) where T : IGlobalModData
|
||||
{
|
||||
MiniYaml data;
|
||||
var t = typeof(T);
|
||||
if (!yaml.TryGetValue(t.Name, out data))
|
||||
if (!yaml.TryGetValue(t.Name, out var data))
|
||||
{
|
||||
// Lazily create the default values if not explicitly defined.
|
||||
return (T)oc.CreateBasic(t);
|
||||
@@ -241,5 +239,14 @@ namespace OpenRA
|
||||
|
||||
return (T)module;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var module in modules)
|
||||
{
|
||||
var disposableModule = module as IDisposable;
|
||||
disposableModule?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,17 +9,29 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public interface IActorInitializer
|
||||
{
|
||||
World World { get; }
|
||||
T Get<T>() where T : IActorInit;
|
||||
U Get<T, U>() where T : IActorInit<U>;
|
||||
bool Contains<T>() where T : IActorInit;
|
||||
T GetOrDefault<T>(TraitInfo info) where T : ActorInit;
|
||||
T Get<T>(TraitInfo info) where T : ActorInit;
|
||||
U GetValue<T, U>(TraitInfo info) where T : ValueActorInit<U>;
|
||||
U GetValue<T, U>(TraitInfo info, U fallback) where T : ValueActorInit<U>;
|
||||
bool Contains<T>(TraitInfo info) where T : ActorInit;
|
||||
|
||||
T GetOrDefault<T>() where T : ActorInit, ISingleInstanceInit;
|
||||
T Get<T>() where T : ActorInit, ISingleInstanceInit;
|
||||
U GetValue<T, U>() where T : ValueActorInit<U>, ISingleInstanceInit;
|
||||
U GetValue<T, U>(U fallback) where T : ValueActorInit<U>, ISingleInstanceInit;
|
||||
bool Contains<T>() where T : ActorInit, ISingleInstanceInit;
|
||||
}
|
||||
|
||||
public class ActorInitializer : IActorInitializer
|
||||
@@ -35,50 +47,228 @@ namespace OpenRA
|
||||
Dict = dict;
|
||||
}
|
||||
|
||||
public T Get<T>() where T : IActorInit { return Dict.Get<T>(); }
|
||||
public U Get<T, U>() where T : IActorInit<U> { return Dict.Get<T>().Value(World); }
|
||||
public bool Contains<T>() where T : IActorInit { return Dict.Contains<T>(); }
|
||||
}
|
||||
|
||||
public interface IActorInit { }
|
||||
|
||||
public interface IActorInit<T> : IActorInit
|
||||
{
|
||||
T Value(World world);
|
||||
}
|
||||
|
||||
public class LocationInit : IActorInit<CPos>
|
||||
{
|
||||
[FieldFromYamlKey]
|
||||
readonly CPos value = CPos.Zero;
|
||||
|
||||
public LocationInit() { }
|
||||
public LocationInit(CPos init) { value = init; }
|
||||
public CPos Value(World world) { return value; }
|
||||
}
|
||||
|
||||
public class OwnerInit : IActorInit<Player>
|
||||
{
|
||||
[FieldFromYamlKey]
|
||||
public readonly string PlayerName = "Neutral";
|
||||
|
||||
Player player;
|
||||
|
||||
public OwnerInit() { }
|
||||
public OwnerInit(string playerName) { PlayerName = playerName; }
|
||||
|
||||
public OwnerInit(Player player)
|
||||
public T GetOrDefault<T>(TraitInfo info) where T : ActorInit
|
||||
{
|
||||
this.player = player;
|
||||
PlayerName = player.InternalName;
|
||||
var inits = Dict.WithInterface<T>();
|
||||
|
||||
// Traits tagged with an instance name prefer inits with the same name.
|
||||
// If a more specific init is not available, fall back to an unnamed init.
|
||||
// If duplicate inits are defined, take the last to match standard yaml override expectations
|
||||
if (info != null && !string.IsNullOrEmpty(info.InstanceName))
|
||||
return inits.LastOrDefault(i => i.InstanceName == info.InstanceName) ??
|
||||
inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
|
||||
|
||||
// Untagged traits will only use untagged inits
|
||||
return inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
|
||||
}
|
||||
|
||||
public T Get<T>(TraitInfo info) where T : ActorInit
|
||||
{
|
||||
var init = GetOrDefault<T>(info);
|
||||
if (init == null)
|
||||
throw new InvalidOperationException("TypeDictionary does not contain instance of type `{0}`".F(typeof(T)));
|
||||
|
||||
return init;
|
||||
}
|
||||
|
||||
public U GetValue<T, U>(TraitInfo info) where T : ValueActorInit<U>
|
||||
{
|
||||
return Get<T>(info).Value;
|
||||
}
|
||||
|
||||
public U GetValue<T, U>(TraitInfo info, U fallback) where T : ValueActorInit<U>
|
||||
{
|
||||
var init = GetOrDefault<T>(info);
|
||||
return init != null ? init.Value : fallback;
|
||||
}
|
||||
|
||||
public bool Contains<T>(TraitInfo info) where T : ActorInit { return GetOrDefault<T>(info) != null; }
|
||||
|
||||
public T GetOrDefault<T>() where T : ActorInit, ISingleInstanceInit
|
||||
{
|
||||
return Dict.GetOrDefault<T>();
|
||||
}
|
||||
|
||||
public T Get<T>() where T : ActorInit, ISingleInstanceInit
|
||||
{
|
||||
return Dict.Get<T>();
|
||||
}
|
||||
|
||||
public U GetValue<T, U>() where T : ValueActorInit<U>, ISingleInstanceInit
|
||||
{
|
||||
return Get<T>().Value;
|
||||
}
|
||||
|
||||
public U GetValue<T, U>(U fallback) where T : ValueActorInit<U>, ISingleInstanceInit
|
||||
{
|
||||
var init = GetOrDefault<T>();
|
||||
return init != null ? init.Value : fallback;
|
||||
}
|
||||
|
||||
public bool Contains<T>() where T : ActorInit, ISingleInstanceInit { return GetOrDefault<T>() != null; }
|
||||
}
|
||||
|
||||
/*
|
||||
* Things to be aware of when writing ActorInits:
|
||||
*
|
||||
* - ActorReference and ActorGlobal can dynamically create objects without calling a constructor.
|
||||
* The object will be allocated directly then the best matching Initialize() method will be called to set valid state.
|
||||
* - ActorReference will always attempt to call Initialize(MiniYaml). ActorGlobal will use whichever one it first
|
||||
* finds with an argument type that matches the given LuaValue.
|
||||
* - Most ActorInits will want to inherit either ValueActorInit<T> or CompositeActorInit which hide the low-level plumbing.
|
||||
* - Inits that reference actors should use ActorInitActorReference which allows actors to be referenced by name in map.yaml
|
||||
* - Inits that should only have a single instance defined on an actor should implement ISingleInstanceInit to allow
|
||||
* direct queries and runtime enforcement.
|
||||
* - Inits that aren't ISingleInstanceInit should expose a ctor that accepts a TraitInfo to allow per-trait targeting.
|
||||
*/
|
||||
public abstract class ActorInit
|
||||
{
|
||||
[FieldLoader.Ignore]
|
||||
public readonly string InstanceName;
|
||||
|
||||
protected ActorInit(string instanceName)
|
||||
{
|
||||
InstanceName = instanceName;
|
||||
}
|
||||
|
||||
protected ActorInit() { }
|
||||
|
||||
public abstract MiniYaml Save();
|
||||
}
|
||||
|
||||
public interface ISingleInstanceInit { }
|
||||
|
||||
public abstract class ValueActorInit<T> : ActorInit
|
||||
{
|
||||
protected readonly T value;
|
||||
|
||||
protected ValueActorInit(TraitInfo info, T value)
|
||||
: base(info.InstanceName) { this.value = value; }
|
||||
|
||||
protected ValueActorInit(string instanceName, T value)
|
||||
: base(instanceName) { this.value = value; }
|
||||
|
||||
protected ValueActorInit(T value) { this.value = value; }
|
||||
|
||||
public virtual T Value { get { return value; } }
|
||||
|
||||
public virtual void Initialize(MiniYaml yaml)
|
||||
{
|
||||
Initialize((T)FieldLoader.GetValue("value", typeof(T), yaml.Value));
|
||||
}
|
||||
|
||||
public virtual void Initialize(T value)
|
||||
{
|
||||
var field = GetType().GetField("value", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (field != null)
|
||||
field.SetValue(this, value);
|
||||
}
|
||||
|
||||
public override MiniYaml Save()
|
||||
{
|
||||
return new MiniYaml(FieldSaver.FormatValue(value));
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class CompositeActorInit : ActorInit
|
||||
{
|
||||
protected CompositeActorInit(TraitInfo info)
|
||||
: base(info.InstanceName) { }
|
||||
|
||||
protected CompositeActorInit()
|
||||
: base() { }
|
||||
|
||||
public virtual void Initialize(MiniYaml yaml)
|
||||
{
|
||||
FieldLoader.Load(this, yaml);
|
||||
}
|
||||
|
||||
public virtual void Initialize(Dictionary<string, object> values)
|
||||
{
|
||||
foreach (var field in GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
|
||||
{
|
||||
var sa = field.GetCustomAttributes<FieldLoader.SerializeAttribute>(false).DefaultIfEmpty(FieldLoader.SerializeAttribute.Default).First();
|
||||
if (!sa.Serialize)
|
||||
continue;
|
||||
|
||||
if (values.TryGetValue(field.Name, out var value))
|
||||
field.SetValue(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Dictionary<string, Type> InitializeArgs()
|
||||
{
|
||||
var dict = new Dictionary<string, Type>();
|
||||
foreach (var field in GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
|
||||
{
|
||||
var sa = field.GetCustomAttributes<FieldLoader.SerializeAttribute>(false).DefaultIfEmpty(FieldLoader.SerializeAttribute.Default).First();
|
||||
if (!sa.Serialize)
|
||||
continue;
|
||||
|
||||
dict[field.Name] = field.FieldType;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
public override MiniYaml Save()
|
||||
{
|
||||
return FieldSaver.Save(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class LocationInit : ValueActorInit<CPos>, ISingleInstanceInit
|
||||
{
|
||||
public LocationInit(CPos value)
|
||||
: base(value) { }
|
||||
}
|
||||
|
||||
public class OwnerInit : ActorInit, ISingleInstanceInit
|
||||
{
|
||||
public readonly string InternalName;
|
||||
protected readonly Player value;
|
||||
|
||||
public OwnerInit(Player value)
|
||||
{
|
||||
this.value = value;
|
||||
InternalName = value.InternalName;
|
||||
}
|
||||
|
||||
public OwnerInit(string value)
|
||||
{
|
||||
InternalName = value;
|
||||
}
|
||||
|
||||
public Player Value(World world)
|
||||
{
|
||||
if (player != null)
|
||||
return player;
|
||||
return value ?? world.Players.First(x => x.InternalName == InternalName);
|
||||
}
|
||||
|
||||
return world.Players.First(x => x.InternalName == PlayerName);
|
||||
public void Initialize(MiniYaml yaml)
|
||||
{
|
||||
var field = GetType().GetField("InternalName", BindingFlags.Public | BindingFlags.Instance);
|
||||
if (field != null)
|
||||
field.SetValue(this, yaml.Value);
|
||||
}
|
||||
|
||||
public void Initialize(Player player)
|
||||
{
|
||||
var field = GetType().GetField("value", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (field != null)
|
||||
field.SetValue(this, player);
|
||||
}
|
||||
|
||||
public override MiniYaml Save()
|
||||
{
|
||||
return new MiniYaml(InternalName);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class RuntimeFlagInit : ActorInit, ISuppressInitExport
|
||||
{
|
||||
public override MiniYaml Save()
|
||||
{
|
||||
throw new NotImplementedException("RuntimeFlagInit cannot be saved");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,11 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
@@ -21,13 +25,10 @@ namespace OpenRA
|
||||
public class ActorReference : IEnumerable
|
||||
{
|
||||
public string Type;
|
||||
public TypeDictionary InitDict
|
||||
{
|
||||
get { return initDict.Value; }
|
||||
}
|
||||
|
||||
Lazy<TypeDictionary> initDict;
|
||||
|
||||
internal TypeDictionary InitDict { get { return initDict.Value; } }
|
||||
|
||||
public ActorReference(string type)
|
||||
: this(type, new Dictionary<string, MiniYaml>()) { }
|
||||
|
||||
@@ -38,47 +39,168 @@ namespace OpenRA
|
||||
{
|
||||
var dict = new TypeDictionary();
|
||||
foreach (var i in inits)
|
||||
dict.Add(LoadInit(i.Key, i.Value));
|
||||
{
|
||||
var init = LoadInit(i.Key, i.Value);
|
||||
if (init is ISingleInstanceInit && dict.Contains(init.GetType()))
|
||||
throw new InvalidDataException("Duplicate initializer '{0}'".F(init.GetType().Name));
|
||||
|
||||
dict.Add(init);
|
||||
}
|
||||
|
||||
return dict;
|
||||
});
|
||||
}
|
||||
|
||||
static IActorInit LoadInit(string traitName, MiniYaml my)
|
||||
public ActorReference(string type, TypeDictionary inits)
|
||||
{
|
||||
var info = Game.CreateObject<IActorInit>(traitName + "Init");
|
||||
FieldLoader.Load(info, my);
|
||||
return info;
|
||||
Type = type;
|
||||
initDict = new Lazy<TypeDictionary>(() =>
|
||||
{
|
||||
var dict = new TypeDictionary();
|
||||
foreach (var i in inits)
|
||||
dict.Add(i);
|
||||
return dict;
|
||||
});
|
||||
}
|
||||
|
||||
public MiniYaml Save(Func<object, bool> initFilter = null)
|
||||
static ActorInit LoadInit(string initName, MiniYaml initYaml)
|
||||
{
|
||||
var initInstance = initName.Split(ActorInfo.TraitInstanceSeparator);
|
||||
var type = Game.ModData.ObjectCreator.FindType(initInstance[0] + "Init");
|
||||
if (type == null)
|
||||
throw new InvalidDataException("Unknown initializer type '{0}Init'".F(initInstance[0]));
|
||||
|
||||
var init = (ActorInit)FormatterServices.GetUninitializedObject(type);
|
||||
if (initInstance.Length > 1)
|
||||
type.GetField("InstanceName").SetValue(init, initInstance[1]);
|
||||
|
||||
var loader = type.GetMethod("Initialize", new[] { typeof(MiniYaml) });
|
||||
if (loader == null)
|
||||
throw new InvalidDataException("{0}Init does not define a yaml-assignable type.".F(initInstance[0]));
|
||||
|
||||
loader.Invoke(init, new[] { initYaml });
|
||||
return init;
|
||||
}
|
||||
|
||||
public MiniYaml Save(Func<ActorInit, bool> initFilter = null)
|
||||
{
|
||||
var ret = new MiniYaml(Type);
|
||||
foreach (var init in InitDict)
|
||||
foreach (var o in initDict.Value)
|
||||
{
|
||||
if (init is ISuppressInitExport)
|
||||
var init = o as ActorInit;
|
||||
if (init == null || o is ISuppressInitExport)
|
||||
continue;
|
||||
|
||||
if (initFilter != null && !initFilter(init))
|
||||
continue;
|
||||
|
||||
var initName = init.GetType().Name;
|
||||
ret.Nodes.Add(new MiniYamlNode(initName.Substring(0, initName.Length - 4), FieldSaver.Save(init)));
|
||||
var initTypeName = init.GetType().Name;
|
||||
var initName = initTypeName.Substring(0, initTypeName.Length - 4);
|
||||
if (!string.IsNullOrEmpty(init.InstanceName))
|
||||
initName += ActorInfo.TraitInstanceSeparator + init.InstanceName;
|
||||
|
||||
ret.Nodes.Add(new MiniYamlNode(initName, init.Save()));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// for initialization syntax
|
||||
public void Add(object o) { InitDict.Add(o); }
|
||||
public IEnumerator GetEnumerator() { return InitDict.GetEnumerator(); }
|
||||
public IEnumerator GetEnumerator() { return initDict.Value.GetEnumerator(); }
|
||||
|
||||
public ActorReference Clone()
|
||||
{
|
||||
var clone = new ActorReference(Type);
|
||||
foreach (var init in InitDict)
|
||||
clone.InitDict.Add(init);
|
||||
foreach (var init in initDict.Value)
|
||||
clone.initDict.Value.Add(init);
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
public void Add(ActorInit init)
|
||||
{
|
||||
if (init is ISingleInstanceInit && InitDict.Contains(init.GetType()))
|
||||
throw new InvalidDataException("Duplicate initializer '{0}'".F(init.GetType().Name));
|
||||
|
||||
InitDict.Add(init);
|
||||
}
|
||||
|
||||
public void Remove(ActorInit o) { initDict.Value.Remove(o); }
|
||||
|
||||
public int RemoveAll<T>() where T : ActorInit
|
||||
{
|
||||
var removed = 0;
|
||||
foreach (var o in initDict.Value.WithInterface<T>().ToList())
|
||||
{
|
||||
removed++;
|
||||
initDict.Value.Remove(o);
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetAll<T>() where T : ActorInit
|
||||
{
|
||||
return initDict.Value.WithInterface<T>();
|
||||
}
|
||||
|
||||
public T GetOrDefault<T>(TraitInfo info) where T : ActorInit
|
||||
{
|
||||
var inits = initDict.Value.WithInterface<T>();
|
||||
|
||||
// Traits tagged with an instance name prefer inits with the same name.
|
||||
// If a more specific init is not available, fall back to an unnamed init.
|
||||
// If duplicate inits are defined, take the last to match standard yaml override expectations
|
||||
if (info != null && !string.IsNullOrEmpty(info.InstanceName))
|
||||
return inits.LastOrDefault(i => i.InstanceName == info.InstanceName) ??
|
||||
inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
|
||||
|
||||
// Untagged traits will only use untagged inits
|
||||
return inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
|
||||
}
|
||||
|
||||
public T Get<T>(TraitInfo info) where T : ActorInit
|
||||
{
|
||||
var init = GetOrDefault<T>(info);
|
||||
if (init == null)
|
||||
throw new InvalidOperationException("TypeDictionary does not contain instance of type `{0}`".F(typeof(T)));
|
||||
|
||||
return init;
|
||||
}
|
||||
|
||||
public U GetValue<T, U>(TraitInfo info) where T : ValueActorInit<U>
|
||||
{
|
||||
return Get<T>(info).Value;
|
||||
}
|
||||
|
||||
public U GetValue<T, U>(TraitInfo info, U fallback) where T : ValueActorInit<U>
|
||||
{
|
||||
var init = GetOrDefault<T>(info);
|
||||
return init != null ? init.Value : fallback;
|
||||
}
|
||||
|
||||
public bool Contains<T>(TraitInfo info) where T : ActorInit { return GetOrDefault<T>(info) != null; }
|
||||
|
||||
public T GetOrDefault<T>() where T : ActorInit, ISingleInstanceInit
|
||||
{
|
||||
return initDict.Value.GetOrDefault<T>();
|
||||
}
|
||||
|
||||
public T Get<T>() where T : ActorInit, ISingleInstanceInit
|
||||
{
|
||||
return initDict.Value.Get<T>();
|
||||
}
|
||||
|
||||
public U GetValue<T, U>() where T : ValueActorInit<U>, ISingleInstanceInit
|
||||
{
|
||||
return Get<T>().Value;
|
||||
}
|
||||
|
||||
public U GetValue<T, U>(U fallback) where T : ValueActorInit<U>, ISingleInstanceInit
|
||||
{
|
||||
var init = GetOrDefault<T>();
|
||||
return init != null ? init.Value : fallback;
|
||||
}
|
||||
|
||||
public bool Contains<T>() where T : ActorInit, ISingleInstanceInit { return GetOrDefault<T>() != null; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,42 +10,28 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
// Represents a layer of "something" that covers the map
|
||||
public class CellLayer<T> : IEnumerable<T>
|
||||
public sealed class CellLayer<T> : CellLayerBase<T>
|
||||
{
|
||||
public readonly Size Size;
|
||||
readonly Rectangle bounds;
|
||||
public readonly MapGridType GridType;
|
||||
public event Action<CPos> CellEntryChanged = null;
|
||||
|
||||
readonly T[] entries;
|
||||
|
||||
public CellLayer(Map map)
|
||||
: this(map.Grid.Type, new Size(map.MapSize.X, map.MapSize.Y)) { }
|
||||
: base(map) { }
|
||||
|
||||
public CellLayer(MapGridType gridType, Size size)
|
||||
{
|
||||
Size = size;
|
||||
bounds = new Rectangle(0, 0, Size.Width, Size.Height);
|
||||
GridType = gridType;
|
||||
entries = new T[size.Width * size.Height];
|
||||
}
|
||||
: base(gridType, size) { }
|
||||
|
||||
public void CopyValuesFrom(CellLayer<T> anotherLayer)
|
||||
public override void CopyValuesFrom(CellLayerBase<T> anotherLayer)
|
||||
{
|
||||
if (Size != anotherLayer.Size || GridType != anotherLayer.GridType)
|
||||
throw new ArgumentException(
|
||||
"layers must have a matching size and shape (grid type).", "anotherLayer");
|
||||
if (CellEntryChanged != null)
|
||||
throw new InvalidOperationException(
|
||||
"Cannot copy values when there are listeners attached to the CellEntryChanged event.");
|
||||
Array.Copy(anotherLayer.entries, entries, entries.Length);
|
||||
|
||||
base.CopyValuesFrom(anotherLayer);
|
||||
}
|
||||
|
||||
public static CellLayer<T> CreateInstance(Func<MPos, T> initialCellValueFactory, Size size, MapGridType mapGridType)
|
||||
@@ -87,8 +73,7 @@ namespace OpenRA
|
||||
{
|
||||
entries[Index(cell)] = value;
|
||||
|
||||
if (CellEntryChanged != null)
|
||||
CellEntryChanged(cell);
|
||||
CellEntryChanged?.Invoke(cell);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,28 +89,10 @@ namespace OpenRA
|
||||
{
|
||||
entries[Index(uv)] = value;
|
||||
|
||||
if (CellEntryChanged != null)
|
||||
CellEntryChanged(uv.ToCPos(GridType));
|
||||
CellEntryChanged?.Invoke(uv.ToCPos(GridType));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Clears the layer contents with a known value</summary>
|
||||
public void Clear(T clearValue)
|
||||
{
|
||||
for (var i = 0; i < entries.Length; i++)
|
||||
entries[i] = clearValue;
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<T>)entries).GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public bool Contains(CPos cell)
|
||||
{
|
||||
// .ToMPos() returns the same result if the X and Y coordinates
|
||||
|
||||
63
OpenRA.Game/Map/CellLayerBase.cs
Normal file
63
OpenRA.Game/Map/CellLayerBase.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
#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;
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public abstract class CellLayerBase<T> : IEnumerable<T>
|
||||
{
|
||||
public readonly Size Size;
|
||||
public readonly MapGridType GridType;
|
||||
|
||||
protected readonly T[] entries;
|
||||
protected readonly Rectangle bounds;
|
||||
|
||||
public CellLayerBase(Map map)
|
||||
: this(map.Grid.Type, new Size(map.MapSize.X, map.MapSize.Y)) { }
|
||||
|
||||
public CellLayerBase(MapGridType gridType, Size size)
|
||||
{
|
||||
Size = size;
|
||||
bounds = new Rectangle(0, 0, Size.Width, Size.Height);
|
||||
GridType = gridType;
|
||||
entries = new T[size.Width * size.Height];
|
||||
}
|
||||
|
||||
public virtual void CopyValuesFrom(CellLayerBase<T> anotherLayer)
|
||||
{
|
||||
if (Size != anotherLayer.Size || GridType != anotherLayer.GridType)
|
||||
throw new ArgumentException("Layers must have a matching size and shape (grid type).", "anotherLayer");
|
||||
|
||||
Array.Copy(anotherLayer.entries, entries, entries.Length);
|
||||
}
|
||||
|
||||
/// <summary>Clears the layer contents with a known value</summary>
|
||||
public void Clear(T clearValue)
|
||||
{
|
||||
for (var i = 0; i < entries.Length; i++)
|
||||
entries[i] = clearValue;
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<T>)entries).GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,7 @@ namespace OpenRA
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public sealed class CellRegionEnumerator : IEnumerator<CPos>
|
||||
public struct CellRegionEnumerator : IEnumerator<CPos>
|
||||
{
|
||||
readonly CellRegion r;
|
||||
|
||||
@@ -128,9 +128,11 @@ namespace OpenRA
|
||||
CPos current;
|
||||
|
||||
public CellRegionEnumerator(CellRegion region)
|
||||
: this()
|
||||
{
|
||||
r = region;
|
||||
Reset();
|
||||
current = new MPos(u, v).ToCPos(r.gridType);
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
|
||||
@@ -230,9 +230,10 @@ namespace OpenRA
|
||||
public CellLayer<TerrainTile> Tiles { get; private set; }
|
||||
public CellLayer<ResourceTile> Resources { get; private set; }
|
||||
public CellLayer<byte> Height { get; private set; }
|
||||
public CellLayer<byte> Ramp { get; private set; }
|
||||
public CellLayer<byte> CustomTerrain { get; private set; }
|
||||
|
||||
public ProjectedCellRegion ProjectedCellBounds { get; private set; }
|
||||
public PPos[] ProjectedCells { get; private set; }
|
||||
public CellRegion AllCells { get; private set; }
|
||||
public List<CPos> AllEdgeCells { get; private set; }
|
||||
|
||||
@@ -301,10 +302,12 @@ namespace OpenRA
|
||||
Tiles = new CellLayer<TerrainTile>(Grid.Type, size);
|
||||
Resources = new CellLayer<ResourceTile>(Grid.Type, size);
|
||||
Height = new CellLayer<byte>(Grid.Type, size);
|
||||
Ramp = new CellLayer<byte>(Grid.Type, size);
|
||||
if (Grid.MaximumTerrainHeight > 0)
|
||||
{
|
||||
Height.CellEntryChanged += UpdateProjection;
|
||||
Tiles.CellEntryChanged += UpdateProjection;
|
||||
Tiles.CellEntryChanged += UpdateRamp;
|
||||
}
|
||||
|
||||
Tiles.Clear(tileRef);
|
||||
@@ -336,6 +339,7 @@ namespace OpenRA
|
||||
Tiles = new CellLayer<TerrainTile>(Grid.Type, size);
|
||||
Resources = new CellLayer<ResourceTile>(Grid.Type, size);
|
||||
Height = new CellLayer<byte>(Grid.Type, size);
|
||||
Ramp = new CellLayer<byte>(Grid.Type, size);
|
||||
|
||||
using (var s = Package.GetStream("map.bin"))
|
||||
{
|
||||
@@ -384,6 +388,7 @@ namespace OpenRA
|
||||
|
||||
if (Grid.MaximumTerrainHeight > 0)
|
||||
{
|
||||
Tiles.CellEntryChanged += UpdateRamp;
|
||||
Tiles.CellEntryChanged += UpdateProjection;
|
||||
Height.CellEntryChanged += UpdateProjection;
|
||||
}
|
||||
@@ -422,9 +427,23 @@ namespace OpenRA
|
||||
foreach (var uv in AllCells.MapCoords)
|
||||
CustomTerrain[uv] = byte.MaxValue;
|
||||
|
||||
// Cache initial ramp state
|
||||
var tileset = Rules.TileSet;
|
||||
foreach (var uv in AllCells)
|
||||
{
|
||||
var tile = tileset.GetTileInfo(Tiles[uv]);
|
||||
Ramp[uv] = tile != null ? tile.RampType : (byte)0;
|
||||
}
|
||||
|
||||
AllEdgeCells = UpdateEdgeCells();
|
||||
}
|
||||
|
||||
void UpdateRamp(CPos cell)
|
||||
{
|
||||
var tile = Rules.TileSet.GetTileInfo(Tiles[cell]);
|
||||
Ramp[cell] = tile != null ? tile.RampType : (byte)0;
|
||||
}
|
||||
|
||||
void InitializeCellProjection()
|
||||
{
|
||||
if (initializedCellProjection)
|
||||
@@ -531,12 +550,8 @@ namespace OpenRA
|
||||
return new[] { (PPos)uv };
|
||||
|
||||
// Odd-height ramps get bumped up a level to the next even height layer
|
||||
if ((height & 1) == 1)
|
||||
{
|
||||
var ti = Rules.TileSet.GetTileInfo(Tiles[uv]);
|
||||
if (ti != null && ti.RampType != 0)
|
||||
height += 1;
|
||||
}
|
||||
if ((height & 1) == 1 && Ramp[uv] != 0)
|
||||
height += 1;
|
||||
|
||||
var candidates = new List<PPos>();
|
||||
|
||||
@@ -647,12 +662,40 @@ namespace OpenRA
|
||||
return dataStream.ToArray();
|
||||
}
|
||||
|
||||
public (Color Left, Color Right) GetTerrainColorPair(MPos uv)
|
||||
{
|
||||
Color left, right;
|
||||
var tileset = Rules.TileSet;
|
||||
var type = tileset.GetTileInfo(Tiles[uv]);
|
||||
if (type != null)
|
||||
{
|
||||
if (type.MinColor != type.MaxColor)
|
||||
{
|
||||
left = Exts.ColorLerp(Game.CosmeticRandom.NextFloat(), type.MinColor, type.MaxColor);
|
||||
right = Exts.ColorLerp(Game.CosmeticRandom.NextFloat(), type.MinColor, type.MaxColor);
|
||||
}
|
||||
else
|
||||
left = right = type.MinColor;
|
||||
|
||||
if (tileset.MinHeightColorBrightness != 1.0f || tileset.MaxHeightColorBrightness != 1.0f)
|
||||
{
|
||||
var scale = float2.Lerp(tileset.MinHeightColorBrightness, tileset.MaxHeightColorBrightness, Height[uv] * 1f / Grid.MaximumTerrainHeight);
|
||||
left = Color.FromArgb((int)(scale * left.R).Clamp(0, 255), (int)(scale * left.G).Clamp(0, 255), (int)(scale * left.B).Clamp(0, 255));
|
||||
right = Color.FromArgb((int)(scale * right.R).Clamp(0, 255), (int)(scale * right.G).Clamp(0, 255), (int)(scale * right.B).Clamp(0, 255));
|
||||
}
|
||||
}
|
||||
else
|
||||
left = right = Color.Black;
|
||||
|
||||
return (left, right);
|
||||
}
|
||||
|
||||
public byte[] SavePreview()
|
||||
{
|
||||
var tileset = Rules.TileSet;
|
||||
var actorTypes = Rules.Actors.Values.Where(a => a.HasTraitInfo<IMapPreviewSignatureInfo>());
|
||||
var actors = ActorDefinitions.Where(a => actorTypes.Where(ai => ai.Name == a.Value.Value).Any());
|
||||
var positions = new List<Pair<MPos, Color>>();
|
||||
var positions = new List<(MPos Position, Color Color)>();
|
||||
foreach (var actor in actors)
|
||||
{
|
||||
var s = new ActorReference(actor.Value.Value, actor.Value.ToDictionary());
|
||||
@@ -686,24 +729,18 @@ namespace OpenRA
|
||||
var stride = bitmapWidth * 4;
|
||||
var pxStride = 4;
|
||||
var minimapData = new byte[stride * height];
|
||||
Color leftColor, rightColor;
|
||||
(Color Left, Color Right) terrainColor = default((Color, Color));
|
||||
|
||||
for (var y = 0; y < height; y++)
|
||||
{
|
||||
for (var x = 0; x < width; x++)
|
||||
{
|
||||
var uv = new MPos(x + Bounds.Left, y + Bounds.Top);
|
||||
var actorsThere = positions.Where(ap => ap.First == uv);
|
||||
if (actorsThere.Any())
|
||||
{
|
||||
leftColor = rightColor = actorsThere.First().Second;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cell contains terrain
|
||||
var type = tileset.GetTileInfo(Tiles[uv]);
|
||||
leftColor = type != null ? type.LeftColor : Color.Black;
|
||||
rightColor = type != null ? type.RightColor : Color.Black;
|
||||
}
|
||||
|
||||
// FirstOrDefault will return a (MPos.Zero, Color.Transparent) if positions is empty
|
||||
var actorColor = positions.FirstOrDefault(ap => ap.Position == uv).Color;
|
||||
if (actorColor.A == 0)
|
||||
terrainColor = GetTerrainColorPair(uv);
|
||||
|
||||
if (isRectangularIsometric)
|
||||
{
|
||||
@@ -713,28 +750,31 @@ namespace OpenRA
|
||||
if (x + dx > 0)
|
||||
{
|
||||
var z = y * stride + xOffset - pxStride;
|
||||
minimapData[z++] = leftColor.R;
|
||||
minimapData[z++] = leftColor.G;
|
||||
minimapData[z++] = leftColor.B;
|
||||
minimapData[z++] = leftColor.A;
|
||||
var c = actorColor.A == 0 ? terrainColor.Left : actorColor;
|
||||
minimapData[z++] = c.R;
|
||||
minimapData[z++] = c.G;
|
||||
minimapData[z++] = c.B;
|
||||
minimapData[z] = c.A;
|
||||
}
|
||||
|
||||
if (xOffset < stride)
|
||||
{
|
||||
var z = y * stride + xOffset;
|
||||
minimapData[z++] = rightColor.R;
|
||||
minimapData[z++] = rightColor.G;
|
||||
minimapData[z++] = rightColor.B;
|
||||
minimapData[z++] = rightColor.A;
|
||||
var c = actorColor.A == 0 ? terrainColor.Right : actorColor;
|
||||
minimapData[z++] = c.R;
|
||||
minimapData[z++] = c.G;
|
||||
minimapData[z++] = c.B;
|
||||
minimapData[z] = c.A;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var z = y * stride + pxStride * x;
|
||||
minimapData[z++] = leftColor.R;
|
||||
minimapData[z++] = leftColor.G;
|
||||
minimapData[z++] = leftColor.B;
|
||||
minimapData[z++] = leftColor.A;
|
||||
var c = actorColor.A == 0 ? terrainColor.Left : actorColor;
|
||||
minimapData[z++] = c.R;
|
||||
minimapData[z++] = c.G;
|
||||
minimapData[z++] = c.B;
|
||||
minimapData[z] = c.A;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -770,8 +810,9 @@ namespace OpenRA
|
||||
|
||||
bool ContainsAllProjectedCellsCovering(MPos uv)
|
||||
{
|
||||
// PERF: Checking the bounds directly here is the same as calling Contains((PPos)uv) but saves an allocation
|
||||
if (Grid.MaximumTerrainHeight == 0)
|
||||
return Contains((PPos)uv);
|
||||
return Bounds.Contains(uv.U, uv.V);
|
||||
|
||||
// If the cell has no valid projection, then we're off the map.
|
||||
var projectedCells = ProjectedCellsCovering(uv);
|
||||
@@ -781,6 +822,7 @@ namespace OpenRA
|
||||
foreach (var puv in projectedCells)
|
||||
if (!Contains(puv))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -805,7 +847,7 @@ namespace OpenRA
|
||||
// (c) u, v coordinates run diagonally to the cell axes, and we define
|
||||
// 1024 as the length projected onto the primary cell axis
|
||||
// - 512 * sqrt(2) = 724
|
||||
var z = Height.Contains(cell) ? 724 * Height[cell] : 0;
|
||||
var z = Height.Contains(cell) ? 724 * Height[cell] + Grid.Ramps[Ramp[cell]].CenterHeightOffset : 0;
|
||||
return new WPos(724 * (cell.X - cell.Y + 1), 724 * (cell.X + cell.Y + 1), z);
|
||||
}
|
||||
|
||||
@@ -813,15 +855,42 @@ namespace OpenRA
|
||||
{
|
||||
var index = (int)subCell;
|
||||
if (index >= 0 && index < Grid.SubCellOffsets.Length)
|
||||
return CenterOfCell(cell) + Grid.SubCellOffsets[index];
|
||||
{
|
||||
var center = CenterOfCell(cell);
|
||||
var offset = Grid.SubCellOffsets[index];
|
||||
var ramp = Ramp.Contains(cell) ? Ramp[cell] : 0;
|
||||
if (ramp != 0)
|
||||
{
|
||||
var r = Grid.Ramps[ramp];
|
||||
offset += new WVec(0, 0, r.HeightOffset(offset.X, offset.Y) - r.CenterHeightOffset);
|
||||
}
|
||||
|
||||
return center + offset;
|
||||
}
|
||||
|
||||
return CenterOfCell(cell);
|
||||
}
|
||||
|
||||
public WDist DistanceAboveTerrain(WPos pos)
|
||||
{
|
||||
if (Grid.Type == MapGridType.Rectangular)
|
||||
return new WDist(pos.Z);
|
||||
|
||||
// Apply ramp offset
|
||||
var cell = CellContaining(pos);
|
||||
var delta = pos - CenterOfCell(cell);
|
||||
return new WDist(delta.Z);
|
||||
var offset = pos - CenterOfCell(cell);
|
||||
|
||||
if (!Ramp.Contains(cell))
|
||||
return new WDist(offset.Z);
|
||||
|
||||
var ramp = Ramp[cell];
|
||||
if (ramp != 0)
|
||||
{
|
||||
var r = Grid.Ramps[ramp];
|
||||
return new WDist(offset.Z + r.CenterHeightOffset - r.HeightOffset(offset.X, offset.Y));
|
||||
}
|
||||
|
||||
return new WDist(offset.Z);
|
||||
}
|
||||
|
||||
public WVec Offset(CVec delta, int dz)
|
||||
@@ -899,13 +968,13 @@ namespace OpenRA
|
||||
return projectedHeight[(MPos)puv];
|
||||
}
|
||||
|
||||
public int FacingBetween(CPos cell, CPos towards, int fallbackfacing)
|
||||
public WAngle FacingBetween(CPos cell, CPos towards, WAngle fallbackfacing)
|
||||
{
|
||||
var delta = CenterOfCell(towards) - CenterOfCell(cell);
|
||||
if (delta.HorizontalLengthSquared == 0)
|
||||
return fallbackfacing;
|
||||
|
||||
return delta.Yaw.Facing;
|
||||
return delta.Yaw;
|
||||
}
|
||||
|
||||
public void Resize(int width, int height)
|
||||
@@ -913,11 +982,13 @@ namespace OpenRA
|
||||
var oldMapTiles = Tiles;
|
||||
var oldMapResources = Resources;
|
||||
var oldMapHeight = Height;
|
||||
var oldMapRamp = Ramp;
|
||||
var newSize = new Size(width, height);
|
||||
|
||||
Tiles = CellLayer.Resize(oldMapTiles, newSize, oldMapTiles[MPos.Zero]);
|
||||
Resources = CellLayer.Resize(oldMapResources, newSize, oldMapResources[MPos.Zero]);
|
||||
Height = CellLayer.Resize(oldMapHeight, newSize, oldMapHeight[MPos.Zero]);
|
||||
Ramp = CellLayer.Resize(oldMapRamp, newSize, oldMapRamp[MPos.Zero]);
|
||||
MapSize = new int2(newSize);
|
||||
|
||||
var tl = new MPos(0, 0);
|
||||
@@ -947,7 +1018,8 @@ namespace OpenRA
|
||||
ProjectedBottomRight = new WPos(br.U * 1024 - 1, (br.V + 1) * 1024 - 1, 0);
|
||||
}
|
||||
|
||||
ProjectedCellBounds = new ProjectedCellRegion(this, tl, br);
|
||||
// PERF: This enumeration isn't going to change during the game
|
||||
ProjectedCells = new ProjectedCellRegion(this, tl, br).ToArray();
|
||||
}
|
||||
|
||||
public void FixOpenAreas()
|
||||
|
||||
@@ -111,8 +111,7 @@ namespace OpenRA
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (mapPackage != null)
|
||||
mapPackage.Dispose();
|
||||
mapPackage?.Dispose();
|
||||
Console.WriteLine("Failed to load map: {0}", map);
|
||||
Console.WriteLine("Details: {0}", e);
|
||||
Log.Write("debug", "Failed to load map: {0}", map);
|
||||
@@ -131,8 +130,7 @@ namespace OpenRA
|
||||
// Enumerate map directories
|
||||
foreach (var kv in modData.Manifest.MapFolders)
|
||||
{
|
||||
MapClassification packageClassification;
|
||||
if (!Enum.TryParse(kv.Value, out packageClassification))
|
||||
if (!Enum.TryParse(kv.Value, out MapClassification packageClassification))
|
||||
continue;
|
||||
|
||||
if (!classification.HasFlag(packageClassification))
|
||||
@@ -193,8 +191,7 @@ namespace OpenRA
|
||||
foreach (var p in maps.Values)
|
||||
p.UpdateRemoteSearch(MapStatus.Unavailable, null);
|
||||
|
||||
if (queryFailed != null)
|
||||
queryFailed();
|
||||
queryFailed?.Invoke();
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -214,8 +211,7 @@ namespace OpenRA
|
||||
{
|
||||
Log.Write("debug", "Can't parse remote map search data:\n{0}", data);
|
||||
Log.Write("debug", "Exception: {0}", e);
|
||||
if (queryFailed != null)
|
||||
queryFailed();
|
||||
queryFailed?.Invoke();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -299,8 +295,7 @@ namespace OpenRA
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
// Wait for any existing thread to exit before starting a new one.
|
||||
if (previewLoaderThread != null)
|
||||
previewLoaderThread.Join();
|
||||
previewLoaderThread?.Join();
|
||||
|
||||
previewLoaderThread = new Thread(LoadAsyncInternal)
|
||||
{
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -20,6 +19,85 @@ namespace OpenRA
|
||||
{
|
||||
public enum MapGridType { Rectangular, RectangularIsometric }
|
||||
|
||||
public enum RampSplit { Flat, X, Y }
|
||||
public enum RampCornerHeight { Low = 0, Half = 1, Full = 2 }
|
||||
|
||||
public struct CellRamp
|
||||
{
|
||||
public readonly int CenterHeightOffset;
|
||||
public readonly WVec[] Corners;
|
||||
public readonly WVec[][] Polygons;
|
||||
|
||||
public CellRamp(MapGridType type, RampCornerHeight tl = RampCornerHeight.Low, RampCornerHeight tr = RampCornerHeight.Low, RampCornerHeight br = RampCornerHeight.Low, RampCornerHeight bl = RampCornerHeight.Low, RampSplit split = RampSplit.Flat)
|
||||
{
|
||||
if (type == MapGridType.RectangularIsometric)
|
||||
{
|
||||
Corners = new[]
|
||||
{
|
||||
new WVec(0, -724, 724 * (int)tl),
|
||||
new WVec(724, 0, 724 * (int)tr),
|
||||
new WVec(0, 724, 724 * (int)br),
|
||||
new WVec(-724, 0, 724 * (int)bl),
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
Corners = new[]
|
||||
{
|
||||
new WVec(-512, -512, 512 * (int)tl),
|
||||
new WVec(512, -512, 512 * (int)tr),
|
||||
new WVec(512, 512, 512 * (int)br),
|
||||
new WVec(-512, 512, 512 * (int)bl)
|
||||
};
|
||||
}
|
||||
|
||||
if (split == RampSplit.X)
|
||||
{
|
||||
Polygons = new[]
|
||||
{
|
||||
new[] { Corners[0], Corners[1], Corners[3] },
|
||||
new[] { Corners[1], Corners[2], Corners[3] }
|
||||
};
|
||||
}
|
||||
else if (split == RampSplit.Y)
|
||||
{
|
||||
Polygons = new[]
|
||||
{
|
||||
new[] { Corners[0], Corners[1], Corners[2] },
|
||||
new[] { Corners[0], Corners[2], Corners[3] }
|
||||
};
|
||||
}
|
||||
else
|
||||
Polygons = new[] { Corners };
|
||||
|
||||
// Initial value must be asigned before HeightOffset can be called
|
||||
CenterHeightOffset = 0;
|
||||
CenterHeightOffset = HeightOffset(0, 0);
|
||||
}
|
||||
|
||||
public int HeightOffset(int dX, int dY)
|
||||
{
|
||||
// Enumerate over the polygons, assuming that they are triangles
|
||||
// If the ramp is not split we will take the first three vertices of the corners as a valid triangle
|
||||
WVec[] p = null;
|
||||
var u = 0;
|
||||
var v = 0;
|
||||
for (var i = 0; i < Polygons.Length; i++)
|
||||
{
|
||||
p = Polygons[i];
|
||||
u = ((p[1].Y - p[2].Y) * (dX - p[2].X) - (p[1].X - p[2].X) * (dY - p[2].Y)) / 1024;
|
||||
v = ((p[0].X - p[2].X) * (dY - p[2].Y) - (p[0].Y - p[2].Y) * (dX - p[2].X)) / 1024;
|
||||
|
||||
// Point is within the triangle if 0 <= u,v <= 1024
|
||||
if (u >= 0 && u <= 1024 && v >= 0 && v <= 1024)
|
||||
break;
|
||||
}
|
||||
|
||||
// Calculate w from u,v and interpolate height
|
||||
return (u * p[0].Z + v * p[1].Z + (1024 - u - v) * p[2].Z) / 1024;
|
||||
}
|
||||
}
|
||||
|
||||
public class MapGrid : IGlobalModData
|
||||
{
|
||||
public readonly MapGridType Type = MapGridType.Rectangular;
|
||||
@@ -41,43 +119,7 @@ namespace OpenRA
|
||||
new WVec(256, 256, 0), // bottom right - index 5
|
||||
};
|
||||
|
||||
public WVec[][] CellCorners { get; private set; }
|
||||
|
||||
readonly int[][] cellCornerHalfHeights = new int[][]
|
||||
{
|
||||
// Flat
|
||||
new[] { 0, 0, 0, 0 },
|
||||
|
||||
// Slopes (two corners high)
|
||||
new[] { 0, 0, 1, 1 },
|
||||
new[] { 1, 0, 0, 1 },
|
||||
new[] { 1, 1, 0, 0 },
|
||||
new[] { 0, 1, 1, 0 },
|
||||
|
||||
// Slopes (one corner high)
|
||||
new[] { 0, 0, 0, 1 },
|
||||
new[] { 1, 0, 0, 0 },
|
||||
new[] { 0, 1, 0, 0 },
|
||||
new[] { 0, 0, 1, 0 },
|
||||
|
||||
// Slopes (three corners high)
|
||||
new[] { 1, 0, 1, 1 },
|
||||
new[] { 1, 1, 0, 1 },
|
||||
new[] { 1, 1, 1, 0 },
|
||||
new[] { 0, 1, 1, 1 },
|
||||
|
||||
// Slopes (two corners high, one corner double high)
|
||||
new[] { 1, 0, 1, 2 },
|
||||
new[] { 2, 1, 0, 1 },
|
||||
new[] { 1, 2, 1, 0 },
|
||||
new[] { 0, 1, 2, 1 },
|
||||
|
||||
// Slopes (two corners high, alternating)
|
||||
new[] { 1, 0, 1, 0 },
|
||||
new[] { 0, 1, 0, 1 },
|
||||
new[] { 1, 0, 1, 0 },
|
||||
new[] { 0, 1, 0, 1 }
|
||||
};
|
||||
public CellRamp[] Ramps { get; private set; }
|
||||
|
||||
internal readonly CVec[][] TilesByDistance;
|
||||
|
||||
@@ -96,34 +138,46 @@ namespace OpenRA
|
||||
throw new InvalidDataException("Subcell default index must be a valid index into the offset triples and must be greater than 0 for mods with subcells");
|
||||
}
|
||||
|
||||
var makeCorners = Type == MapGridType.RectangularIsometric ?
|
||||
(Func<int[], WVec[]>)IsometricCellCorners : RectangularCellCorners;
|
||||
CellCorners = cellCornerHalfHeights.Select(makeCorners).ToArray();
|
||||
// Slope types are hardcoded following the convention from the TS and RA2 map format
|
||||
Ramps = new[]
|
||||
{
|
||||
// Flat
|
||||
new CellRamp(Type),
|
||||
|
||||
// Two adjacent corners raised by half a cell
|
||||
new CellRamp(Type, tr: RampCornerHeight.Half, br: RampCornerHeight.Half),
|
||||
new CellRamp(Type, br: RampCornerHeight.Half, bl: RampCornerHeight.Half),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, bl: RampCornerHeight.Half),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Half),
|
||||
|
||||
// One corner raised by half a cell
|
||||
new CellRamp(Type, br: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, bl: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, tr: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
|
||||
// Three corners raised by half a cell
|
||||
new CellRamp(Type, tr: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
|
||||
// Full tile sloped (mid corners raised by half cell, far corner by full cell)
|
||||
new CellRamp(Type, tr: RampCornerHeight.Half, br: RampCornerHeight.Full, bl: RampCornerHeight.Half),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Full),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Full, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Full, br: RampCornerHeight.Half),
|
||||
|
||||
// Two opposite corners raised by half a cell
|
||||
new CellRamp(Type, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.Y),
|
||||
new CellRamp(Type, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X),
|
||||
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.X),
|
||||
};
|
||||
|
||||
TilesByDistance = CreateTilesByDistance();
|
||||
}
|
||||
|
||||
static WVec[] IsometricCellCorners(int[] cornerHeight)
|
||||
{
|
||||
return new WVec[]
|
||||
{
|
||||
new WVec(-724, 0, 724 * cornerHeight[0]),
|
||||
new WVec(0, -724, 724 * cornerHeight[1]),
|
||||
new WVec(724, 0, 724 * cornerHeight[2]),
|
||||
new WVec(0, 724, 724 * cornerHeight[3])
|
||||
};
|
||||
}
|
||||
|
||||
static WVec[] RectangularCellCorners(int[] cornerHeight)
|
||||
{
|
||||
return new WVec[]
|
||||
{
|
||||
new WVec(-512, -512, 512 * cornerHeight[0]),
|
||||
new WVec(512, -512, 512 * cornerHeight[1]),
|
||||
new WVec(512, 512, 512 * cornerHeight[2]),
|
||||
new WVec(-512, 512, 512 * cornerHeight[3])
|
||||
};
|
||||
}
|
||||
|
||||
CVec[][] CreateTilesByDistance()
|
||||
{
|
||||
var ts = new List<CVec>[MaximumTileSearchRange + 1];
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace OpenRA
|
||||
public bool DefinesUnsafeCustomRules { get; private set; }
|
||||
public bool RulesLoaded { get; private set; }
|
||||
|
||||
public void SetRulesetGenerator(ModData modData, Func<Pair<Ruleset, bool>> generator)
|
||||
public void SetRulesetGenerator(ModData modData, Func<(Ruleset Ruleset, bool DefinesUnsafeCustomRules)> generator)
|
||||
{
|
||||
InvalidCustomRules = false;
|
||||
RulesLoaded = false;
|
||||
@@ -106,8 +106,8 @@ namespace OpenRA
|
||||
try
|
||||
{
|
||||
var ret = generator();
|
||||
DefinesUnsafeCustomRules = ret.Second;
|
||||
return ret.First;
|
||||
DefinesUnsafeCustomRules = ret.DefinesUnsafeCustomRules;
|
||||
return ret.Ruleset;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -233,8 +233,7 @@ namespace OpenRA
|
||||
newData.GridType = gridType;
|
||||
newData.Class = classification;
|
||||
|
||||
MiniYaml temp;
|
||||
if (yaml.TryGetValue("MapFormat", out temp))
|
||||
if (yaml.TryGetValue("MapFormat", out var temp))
|
||||
{
|
||||
var format = FieldLoader.GetValue<int>("MapFormat", temp.Value);
|
||||
if (format != Map.SupportedMapFormat)
|
||||
@@ -269,14 +268,13 @@ namespace OpenRA
|
||||
try
|
||||
{
|
||||
// Actor definitions may change if the map format changes
|
||||
MiniYaml actorDefinitions;
|
||||
if (yaml.TryGetValue("Actors", out actorDefinitions))
|
||||
if (yaml.TryGetValue("Actors", out var actorDefinitions))
|
||||
{
|
||||
var spawns = new List<CPos>();
|
||||
foreach (var kv in actorDefinitions.Nodes.Where(d => d.Value.Value == "mpspawn"))
|
||||
{
|
||||
var s = new ActorReference(kv.Value.Value, kv.Value.ToDictionary());
|
||||
spawns.Add(s.InitDict.Get<LocationInit>().Value(null));
|
||||
spawns.Add(s.Get<LocationInit>().Value);
|
||||
}
|
||||
|
||||
newData.SpawnPoints = spawns.ToArray();
|
||||
@@ -293,8 +291,7 @@ namespace OpenRA
|
||||
try
|
||||
{
|
||||
// Player definitions may change if the map format changes
|
||||
MiniYaml playerDefinitions;
|
||||
if (yaml.TryGetValue("Players", out playerDefinitions))
|
||||
if (yaml.TryGetValue("Players", out var playerDefinitions))
|
||||
{
|
||||
newData.Players = new MapPlayers(playerDefinitions.Nodes);
|
||||
newData.PlayerCount = newData.Players.Players.Count(x => x.Value.Playable);
|
||||
@@ -318,7 +315,7 @@ namespace OpenRA
|
||||
voiceDefinitions, notificationDefinitions, musicDefinitions, sequenceDefinitions, modelSequenceDefinitions);
|
||||
var flagged = Ruleset.DefinesUnsafeCustomRules(modData, this, ruleDefinitions,
|
||||
weaponDefinitions, voiceDefinitions, notificationDefinitions, sequenceDefinitions);
|
||||
return Pair.New(rules, flagged);
|
||||
return (rules, flagged);
|
||||
});
|
||||
|
||||
if (p.Contains("map.png"))
|
||||
@@ -331,8 +328,7 @@ namespace OpenRA
|
||||
|
||||
MiniYaml LoadRuleSection(Dictionary<string, MiniYaml> yaml, string section)
|
||||
{
|
||||
MiniYaml node;
|
||||
if (!yaml.TryGetValue(section, out node))
|
||||
if (!yaml.TryGetValue(section, out var node))
|
||||
return null;
|
||||
|
||||
return node;
|
||||
@@ -402,7 +398,7 @@ namespace OpenRA
|
||||
voiceDefinitions, notificationDefinitions, musicDefinitions, sequenceDefinitions, modelSequenceDefinitions);
|
||||
var flagged = Ruleset.DefinesUnsafeCustomRules(modData, this, ruleDefinitions,
|
||||
weaponDefinitions, voiceDefinitions, notificationDefinitions, sequenceDefinitions);
|
||||
return Pair.New(rules, flagged);
|
||||
return (rules, flagged);
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -416,8 +412,7 @@ namespace OpenRA
|
||||
if (innerData.Preview != null)
|
||||
cache.CacheMinimap(this);
|
||||
|
||||
if (parseMetadata != null)
|
||||
parseMetadata(this);
|
||||
parseMetadata?.Invoke(this);
|
||||
}
|
||||
|
||||
// Update the status and class unconditionally
|
||||
@@ -526,9 +521,7 @@ namespace OpenRA
|
||||
public void Delete()
|
||||
{
|
||||
Invalidate();
|
||||
var deleteFromPackage = parentPackage as IReadWritePackage;
|
||||
if (deleteFromPackage != null)
|
||||
deleteFromPackage.Delete(Package.Name);
|
||||
(parentPackage as IReadWritePackage)?.Delete(Package.Name);
|
||||
}
|
||||
|
||||
Stream IReadOnlyFileSystem.Open(string filename)
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace OpenRA
|
||||
public string Faction;
|
||||
|
||||
public bool LockColor = false;
|
||||
public Color Color = Color.FromAhsl(0, 0, 238);
|
||||
public Color Color = Game.ModData.Manifest.Get<DefaultPlayer>().Color;
|
||||
|
||||
public bool LockSpawn = false;
|
||||
public int Spawn = 0;
|
||||
|
||||
49
OpenRA.Game/Map/ProjectedCellLayer.cs
Normal file
49
OpenRA.Game/Map/ProjectedCellLayer.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public sealed class ProjectedCellLayer<T> : CellLayerBase<T>
|
||||
{
|
||||
public ProjectedCellLayer(Map map)
|
||||
: base(map) { }
|
||||
|
||||
public ProjectedCellLayer(MapGridType gridType, Size size)
|
||||
: base(gridType, size) { }
|
||||
|
||||
// Resolve an array index from map coordinates.
|
||||
int Index(PPos uv)
|
||||
{
|
||||
return uv.V * Size.Width + uv.U;
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets the layer contents using projected map coordinates.</summary>
|
||||
public T this[PPos uv]
|
||||
{
|
||||
get
|
||||
{
|
||||
return entries[Index(uv)];
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
entries[Index(uv)] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(PPos uv)
|
||||
{
|
||||
return bounds.Contains(uv.U, uv.V);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ namespace OpenRA
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public sealed class ProjectedCellRegionEnumerator : IEnumerator<PPos>
|
||||
public struct ProjectedCellRegionEnumerator : IEnumerator<PPos>
|
||||
{
|
||||
readonly ProjectedCellRegion r;
|
||||
|
||||
@@ -86,9 +86,11 @@ namespace OpenRA
|
||||
PPos current;
|
||||
|
||||
public ProjectedCellRegionEnumerator(ProjectedCellRegion region)
|
||||
: this()
|
||||
{
|
||||
r = region;
|
||||
Reset();
|
||||
current = new PPos(u, v);
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
|
||||
@@ -24,9 +24,8 @@ namespace OpenRA
|
||||
public readonly byte TerrainType = byte.MaxValue;
|
||||
public readonly byte Height;
|
||||
public readonly byte RampType;
|
||||
public readonly Color LeftColor;
|
||||
public readonly Color RightColor;
|
||||
|
||||
public readonly Color MinColor;
|
||||
public readonly Color MaxColor;
|
||||
public readonly float ZOffset = 0.0f;
|
||||
public readonly float ZRamp = 1.0f;
|
||||
}
|
||||
@@ -73,8 +72,7 @@ namespace OpenRA
|
||||
tileInfo = new TerrainTileInfo[Size.X * Size.Y];
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
int key;
|
||||
if (!int.TryParse(node.Key, out key) || key < 0 || key >= tileInfo.Length)
|
||||
if (!int.TryParse(node.Key, out var key) || key < 0 || key >= tileInfo.Length)
|
||||
throw new InvalidDataException("Invalid tile key '{0}' on template '{1}' of tileset '{2}'.".F(node.Key, Id, tileSet.Id));
|
||||
|
||||
tileInfo[key] = LoadTileInfo(tileSet, node.Value);
|
||||
@@ -87,8 +85,7 @@ namespace OpenRA
|
||||
var i = 0;
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
int key;
|
||||
if (!int.TryParse(node.Key, out key) || key != i++)
|
||||
if (!int.TryParse(node.Key, out var key) || key != i++)
|
||||
throw new InvalidDataException("Invalid tile key '{0}' on template '{1}' of tileset '{2}'.".F(node.Key, Id, tileSet.Id));
|
||||
|
||||
tileInfo[key] = LoadTileInfo(tileSet, node.Value);
|
||||
@@ -106,11 +103,11 @@ namespace OpenRA
|
||||
|
||||
// Fall back to the terrain-type color if necessary
|
||||
var overrideColor = tileSet.TerrainInfo[tile.TerrainType].Color;
|
||||
if (tile.LeftColor == default(Color))
|
||||
tile.GetType().GetField("LeftColor").SetValue(tile, overrideColor);
|
||||
if (tile.MinColor == default(Color))
|
||||
tile.GetType().GetField("MinColor").SetValue(tile, overrideColor);
|
||||
|
||||
if (tile.RightColor == default(Color))
|
||||
tile.GetType().GetField("RightColor").SetValue(tile, overrideColor);
|
||||
if (tile.MaxColor == default(Color))
|
||||
tile.GetType().GetField("MaxColor").SetValue(tile, overrideColor);
|
||||
|
||||
return tile;
|
||||
}
|
||||
@@ -139,6 +136,8 @@ namespace OpenRA
|
||||
public readonly string[] EditorTemplateOrder;
|
||||
public readonly bool IgnoreTileSpriteOffsets;
|
||||
public readonly bool EnableDepth = false;
|
||||
public readonly float MinHeightColorBrightness = 1.0f;
|
||||
public readonly float MaxHeightColorBrightness = 1.0f;
|
||||
|
||||
[FieldLoader.Ignore]
|
||||
public readonly IReadOnlyDictionary<ushort, TerrainTemplateInfo> Templates;
|
||||
@@ -215,8 +214,7 @@ namespace OpenRA
|
||||
|
||||
public byte GetTerrainIndex(string type)
|
||||
{
|
||||
byte index;
|
||||
if (terrainIndexByType.TryGetValue(type, out index))
|
||||
if (terrainIndexByType.TryGetValue(type, out var index))
|
||||
return index;
|
||||
|
||||
throw new InvalidDataException("Tileset '{0}' lacks terrain type '{1}'".F(Id, type));
|
||||
@@ -224,8 +222,7 @@ namespace OpenRA
|
||||
|
||||
public byte GetTerrainIndex(TerrainTile r)
|
||||
{
|
||||
TerrainTemplateInfo tpl;
|
||||
if (!Templates.TryGetValue(r.Type, out tpl))
|
||||
if (!Templates.TryGetValue(r.Type, out var tpl))
|
||||
return defaultWalkableTerrainIndex;
|
||||
|
||||
if (tpl.Contains(r.Index))
|
||||
@@ -240,8 +237,7 @@ namespace OpenRA
|
||||
|
||||
public TerrainTileInfo GetTileInfo(TerrainTile r)
|
||||
{
|
||||
TerrainTemplateInfo tpl;
|
||||
if (!Templates.TryGetValue(r.Type, out tpl))
|
||||
if (!Templates.TryGetValue(r.Type, out var tpl))
|
||||
return null;
|
||||
|
||||
return tpl.Contains(r.Index) ? tpl[r.Index] : null;
|
||||
|
||||
@@ -339,8 +339,7 @@ namespace OpenRA
|
||||
{
|
||||
if (n.Key == "Inherits" || n.Key.StartsWith("Inherits@", StringComparison.Ordinal))
|
||||
{
|
||||
MiniYaml parent;
|
||||
if (!tree.TryGetValue(n.Value.Value, out parent))
|
||||
if (!tree.TryGetValue(n.Value.Value, out var parent))
|
||||
throw new YamlException(
|
||||
"{0}: Parent type `{1}` not found".F(n.Location, n.Value.Value));
|
||||
|
||||
@@ -428,9 +427,8 @@ namespace OpenRA
|
||||
|
||||
foreach (var key in allKeys)
|
||||
{
|
||||
MiniYamlNode existingNode, overrideNode;
|
||||
existingDict.TryGetValue(key, out existingNode);
|
||||
overrideDict.TryGetValue(key, out overrideNode);
|
||||
existingDict.TryGetValue(key, out var existingNode);
|
||||
overrideDict.TryGetValue(key, out var overrideNode);
|
||||
|
||||
var loc = overrideNode == null ? default(MiniYamlNode.SourceLocation) : overrideNode.Location;
|
||||
var comment = (overrideNode ?? existingNode).Comment;
|
||||
|
||||
@@ -177,8 +177,7 @@ namespace OpenRA
|
||||
|
||||
public Map PrepareMap(string uid)
|
||||
{
|
||||
if (LoadScreen != null)
|
||||
LoadScreen.Display();
|
||||
LoadScreen?.Display();
|
||||
|
||||
if (MapCache[uid].Status != MapStatus.Available)
|
||||
throw new InvalidDataException("Invalid map uid: {0}".F(uid));
|
||||
@@ -202,12 +201,12 @@ namespace OpenRA
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (LoadScreen != null)
|
||||
LoadScreen.Dispose();
|
||||
LoadScreen?.Dispose();
|
||||
MapCache.Dispose();
|
||||
|
||||
if (ObjectCreator != null)
|
||||
ObjectCreator.Dispose();
|
||||
ObjectCreator?.Dispose();
|
||||
|
||||
Manifest.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,11 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using OpenRA.Server;
|
||||
@@ -30,12 +33,63 @@ namespace OpenRA.Network
|
||||
{
|
||||
int LocalClientId { get; }
|
||||
ConnectionState ConnectionState { get; }
|
||||
IPEndPoint EndPoint { get; }
|
||||
string ErrorMessage { get; }
|
||||
void Send(int frame, List<byte[]> orders);
|
||||
void SendImmediate(IEnumerable<byte[]> orders);
|
||||
void SendSync(int frame, byte[] syncData);
|
||||
void Receive(Action<int, byte[]> packetFn);
|
||||
}
|
||||
|
||||
public class ConnectionTarget
|
||||
{
|
||||
readonly DnsEndPoint[] endpoints;
|
||||
|
||||
public ConnectionTarget()
|
||||
{
|
||||
endpoints = new[] { new DnsEndPoint("invalid", 0) };
|
||||
}
|
||||
|
||||
public ConnectionTarget(string host, int port)
|
||||
{
|
||||
endpoints = new[] { new DnsEndPoint(host, port) };
|
||||
}
|
||||
|
||||
public ConnectionTarget(IEnumerable<DnsEndPoint> endpoints)
|
||||
{
|
||||
this.endpoints = endpoints.ToArray();
|
||||
if (this.endpoints.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("ConnectionTarget must have at least one address.");
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<IPEndPoint> GetConnectEndPoints()
|
||||
{
|
||||
return endpoints
|
||||
.SelectMany(e =>
|
||||
{
|
||||
try
|
||||
{
|
||||
return Dns.GetHostAddresses(e.Host)
|
||||
.Select(a => new IPEndPoint(a, e.Port));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return Enumerable.Empty<IPEndPoint>();
|
||||
}
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return endpoints
|
||||
.Select(e => "{0}:{1}".F(e.Host, e.Port))
|
||||
.JoinWith("/");
|
||||
}
|
||||
}
|
||||
|
||||
class EchoConnection : IConnection
|
||||
{
|
||||
protected struct ReceivedPacket
|
||||
@@ -57,6 +111,16 @@ namespace OpenRA.Network
|
||||
get { return ConnectionState.PreConnecting; }
|
||||
}
|
||||
|
||||
public virtual IPEndPoint EndPoint
|
||||
{
|
||||
get { throw new NotSupportedException("An echo connection doesn't have an endpoint"); }
|
||||
}
|
||||
|
||||
public virtual string ErrorMessage
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public virtual void Send(int frame, List<byte[]> orders)
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
@@ -110,23 +174,21 @@ namespace OpenRA.Network
|
||||
foreach (var p in packets)
|
||||
{
|
||||
packetFn(p.FromClient, p.Data);
|
||||
if (Recorder != null)
|
||||
Recorder.Receive(p.FromClient, p.Data);
|
||||
Recorder?.Receive(p.FromClient, p.Data);
|
||||
}
|
||||
}
|
||||
|
||||
public void StartRecording(Func<string> chooseFilename)
|
||||
{
|
||||
// If we have a previous recording then save/dispose it and start a new one.
|
||||
if (Recorder != null)
|
||||
Recorder.Dispose();
|
||||
Recorder?.Dispose();
|
||||
Recorder = new ReplayRecorder(chooseFilename);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && Recorder != null)
|
||||
Recorder.Dispose();
|
||||
if (disposing)
|
||||
Recorder?.Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -138,35 +200,100 @@ namespace OpenRA.Network
|
||||
|
||||
sealed class NetworkConnection : EchoConnection
|
||||
{
|
||||
readonly TcpClient tcp;
|
||||
readonly ConnectionTarget target;
|
||||
TcpClient tcp;
|
||||
IPEndPoint endpoint;
|
||||
readonly List<byte[]> queuedSyncPackets = new List<byte[]>();
|
||||
volatile ConnectionState connectionState = ConnectionState.Connecting;
|
||||
volatile int clientId;
|
||||
bool disposed;
|
||||
string errorMessage;
|
||||
|
||||
public NetworkConnection(string host, int port)
|
||||
public override IPEndPoint EndPoint { get { return endpoint; } }
|
||||
|
||||
public override string ErrorMessage { get { return errorMessage; } }
|
||||
|
||||
public NetworkConnection(ConnectionTarget target)
|
||||
{
|
||||
try
|
||||
this.target = target;
|
||||
new Thread(NetworkConnectionConnect)
|
||||
{
|
||||
tcp = new TcpClient(host, port) { NoDelay = true };
|
||||
Name = "{0} (connect to {1})".F(GetType().Name, target),
|
||||
IsBackground = true
|
||||
}.Start();
|
||||
}
|
||||
|
||||
void NetworkConnectionConnect()
|
||||
{
|
||||
var queue = new BlockingCollection<TcpClient>();
|
||||
|
||||
var atLeastOneEndpoint = false;
|
||||
foreach (var endpoint in target.GetConnectEndPoints())
|
||||
{
|
||||
atLeastOneEndpoint = true;
|
||||
new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var client = new TcpClient(endpoint.AddressFamily) { NoDelay = true };
|
||||
client.Connect(endpoint.Address, endpoint.Port);
|
||||
|
||||
try
|
||||
{
|
||||
queue.Add(client);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// Another connection was faster, close this one.
|
||||
client.Close();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errorMessage = "Failed to connect";
|
||||
Log.Write("client", "Failed to connect to {0}: {1}".F(endpoint, ex.Message));
|
||||
}
|
||||
})
|
||||
{
|
||||
Name = "{0} (connect to {1})".F(GetType().Name, endpoint),
|
||||
IsBackground = true
|
||||
}.Start();
|
||||
}
|
||||
|
||||
if (!atLeastOneEndpoint)
|
||||
{
|
||||
errorMessage = "Failed to resolve address";
|
||||
connectionState = ConnectionState.NotConnected;
|
||||
}
|
||||
|
||||
// Wait up to 5s for a successful connection. This should hopefully be enough because such high latency makes the game unplayable anyway.
|
||||
else if (queue.TryTake(out tcp, 5000))
|
||||
{
|
||||
// Copy endpoint here to have it even after getting disconnected.
|
||||
endpoint = (IPEndPoint)tcp.Client.RemoteEndPoint;
|
||||
|
||||
new Thread(NetworkConnectionReceive)
|
||||
{
|
||||
Name = GetType().Name + " " + host + ":" + port,
|
||||
Name = "{0} (receive from {1})".F(GetType().Name, tcp.Client.RemoteEndPoint),
|
||||
IsBackground = true
|
||||
}.Start(tcp.GetStream());
|
||||
}.Start();
|
||||
}
|
||||
catch
|
||||
else
|
||||
{
|
||||
connectionState = ConnectionState.NotConnected;
|
||||
}
|
||||
|
||||
// Close all unneeded connections in the queue and make sure new ones are closed on the connect thread.
|
||||
queue.CompleteAdding();
|
||||
foreach (var client in queue)
|
||||
client.Close();
|
||||
}
|
||||
|
||||
void NetworkConnectionReceive(object networkStreamObject)
|
||||
void NetworkConnectionReceive()
|
||||
{
|
||||
try
|
||||
{
|
||||
var networkStream = (NetworkStream)networkStreamObject;
|
||||
var reader = new BinaryReader(networkStream);
|
||||
var reader = new BinaryReader(tcp.GetStream());
|
||||
var handshakeProtocol = reader.ReadInt32();
|
||||
|
||||
if (handshakeProtocol != ProtocolVersion.Handshake)
|
||||
@@ -187,7 +314,11 @@ namespace OpenRA.Network
|
||||
AddPacket(new ReceivedPacket { FromClient = client, Data = buf });
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
catch (Exception ex)
|
||||
{
|
||||
errorMessage = "Connection failed";
|
||||
Log.Write("client", "Connection to {0} failed: {1}".F(endpoint, ex.Message));
|
||||
}
|
||||
finally
|
||||
{
|
||||
connectionState = ConnectionState.NotConnected;
|
||||
@@ -239,8 +370,7 @@ namespace OpenRA.Network
|
||||
|
||||
// Closing the stream will cause any reads on the receiving thread to throw.
|
||||
// This will mark the connection as no longer connected and the thread will terminate cleanly.
|
||||
if (tcp != null)
|
||||
tcp.Close();
|
||||
tcp?.Close();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
@@ -167,28 +167,22 @@ namespace OpenRA.Network
|
||||
|
||||
// Games advertised using the old API calculated the play time locally
|
||||
if (State == 2 && PlayTime < 0)
|
||||
{
|
||||
DateTime startTime;
|
||||
if (DateTime.TryParse(Started, out startTime))
|
||||
if (DateTime.TryParse(Started, out var startTime))
|
||||
PlayTime = (int)(DateTime.UtcNow - startTime).TotalSeconds;
|
||||
}
|
||||
|
||||
ExternalMod external;
|
||||
var externalKey = ExternalMod.MakeKey(Mod, Version);
|
||||
if (Game.ExternalMods.TryGetValue(externalKey, out external) && external.Version == Version)
|
||||
if (Game.ExternalMods.TryGetValue(externalKey, out var external) && external.Version == Version)
|
||||
IsCompatible = true;
|
||||
|
||||
// Games advertised using the old API used local mod metadata
|
||||
if (string.IsNullOrEmpty(ModTitle))
|
||||
{
|
||||
Manifest mod;
|
||||
|
||||
if (external != null && external.Version == Version)
|
||||
{
|
||||
// Use external mod registration to populate the section header
|
||||
ModTitle = external.Title;
|
||||
}
|
||||
else if (Game.Mods.TryGetValue(Mod, out mod))
|
||||
else if (Game.Mods.TryGetValue(Mod, out var mod))
|
||||
{
|
||||
// Use internal mod data to populate the section header, but
|
||||
// on-connect switching must use the external mod plumbing.
|
||||
|
||||
@@ -11,63 +11,119 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using ICSharpCode.SharpZipLib.GZip;
|
||||
using MaxMind.Db;
|
||||
using System.Net.Sockets;
|
||||
using System.Numerics;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
public class GeoIP
|
||||
{
|
||||
public class GeoIP2Record
|
||||
class IP2LocationReader
|
||||
{
|
||||
[Constructor]
|
||||
public GeoIP2Record(GeoIP2Country country)
|
||||
public readonly DateTime Date;
|
||||
readonly Stream stream;
|
||||
readonly uint columnCount;
|
||||
readonly uint v4Count;
|
||||
readonly uint v4Offset;
|
||||
readonly uint v6Count;
|
||||
readonly uint v6Offset;
|
||||
|
||||
public IP2LocationReader(Stream source)
|
||||
{
|
||||
Country = country;
|
||||
// Copy stream data for reuse
|
||||
stream = new MemoryStream();
|
||||
source.CopyTo(stream);
|
||||
stream.Position = 0;
|
||||
|
||||
if (stream.ReadUInt8() != 1)
|
||||
throw new InvalidDataException("Only IP2Location type 1 databases are supported.");
|
||||
|
||||
columnCount = stream.ReadUInt8();
|
||||
var year = stream.ReadUInt8();
|
||||
var month = stream.ReadUInt8();
|
||||
var day = stream.ReadUInt8();
|
||||
Date = new DateTime(2000 + year, month, day);
|
||||
|
||||
v4Count = stream.ReadUInt32();
|
||||
v4Offset = stream.ReadUInt32();
|
||||
v6Count = stream.ReadUInt32();
|
||||
v6Offset = stream.ReadUInt32();
|
||||
}
|
||||
|
||||
public GeoIP2Country Country { get; set; }
|
||||
}
|
||||
|
||||
public class GeoIP2Country
|
||||
{
|
||||
[Constructor]
|
||||
public GeoIP2Country(GeoIP2CountryNames names)
|
||||
BigInteger AddressForIndex(long index, bool isIPv6)
|
||||
{
|
||||
Names = names;
|
||||
var start = isIPv6 ? v6Offset : v4Offset;
|
||||
var offset = isIPv6 ? 12 : 0;
|
||||
stream.Seek(start + index * (4 * columnCount + offset) - 1, SeekOrigin.Begin);
|
||||
return new BigInteger(stream.ReadBytes(isIPv6 ? 16 : 4).Append((byte)0).ToArray());
|
||||
}
|
||||
|
||||
public GeoIP2CountryNames Names { get; set; }
|
||||
}
|
||||
|
||||
public class GeoIP2CountryNames
|
||||
{
|
||||
[Constructor]
|
||||
public GeoIP2CountryNames(string en)
|
||||
string CountryForIndex(long index, bool isIPv6)
|
||||
{
|
||||
English = en;
|
||||
// Read file offset for country entry
|
||||
var start = isIPv6 ? v6Offset : v4Offset;
|
||||
var offset = isIPv6 ? 12 : 0;
|
||||
stream.Seek(start + index * (4 * columnCount + offset) + offset + 3, SeekOrigin.Begin);
|
||||
var countryOffset = stream.ReadUInt32();
|
||||
|
||||
// Read length-prefixed country name
|
||||
stream.Seek(countryOffset + 3, SeekOrigin.Begin);
|
||||
var length = stream.ReadUInt8();
|
||||
|
||||
// "-" is used to represent an unknown country in the database
|
||||
var country = stream.ReadASCII(length);
|
||||
return country != "-" ? country : null;
|
||||
}
|
||||
|
||||
public string English { get; set; }
|
||||
public string LookupCountry(IPAddress ip)
|
||||
{
|
||||
var isIPv6 = ip.AddressFamily == AddressFamily.InterNetworkV6;
|
||||
if (!isIPv6 && ip.AddressFamily != AddressFamily.InterNetwork)
|
||||
return null;
|
||||
|
||||
// Locate IP using a binary search
|
||||
// The IP2Location python parser has an additional
|
||||
// optimization that can jump directly to the row, but this adds
|
||||
// extra complexity that isn't obviously needed for our limited database size
|
||||
long low = 0;
|
||||
long high = isIPv6 ? v6Count : v4Count;
|
||||
|
||||
// Append an empty byte to force the data to be treated as unsigned
|
||||
var ipNumber = new BigInteger(ip.GetAddressBytes().Reverse().Append((byte)0).ToArray());
|
||||
while (low <= high)
|
||||
{
|
||||
var mid = (low + high) / 2;
|
||||
var min = AddressForIndex(mid, isIPv6);
|
||||
var max = AddressForIndex(mid + 1, isIPv6);
|
||||
if (min <= ipNumber && ipNumber < max)
|
||||
return CountryForIndex(mid, isIPv6);
|
||||
|
||||
if (ipNumber < min)
|
||||
high = mid - 1;
|
||||
else
|
||||
low = mid + 1;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static Reader database;
|
||||
static IP2LocationReader database;
|
||||
|
||||
public static void Initialize(string databasePath)
|
||||
public static void Initialize(string databasePath = "IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP")
|
||||
{
|
||||
if (string.IsNullOrEmpty(databasePath))
|
||||
if (!File.Exists(databasePath))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
using (var fileStream = new FileStream(databasePath, FileMode.Open, FileAccess.Read))
|
||||
using (var z = new ZipFile(databasePath))
|
||||
{
|
||||
if (databasePath.EndsWith(".gz"))
|
||||
using (var gzipStream = new GZipInputStream(fileStream))
|
||||
database = new Reader(gzipStream);
|
||||
else
|
||||
database = new Reader(fileStream);
|
||||
var entry = z.FindEntry("IP2LOCATION-LITE-DB1.IPV6.BIN", false);
|
||||
database = new IP2LocationReader(z.GetInputStream(entry));
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -82,9 +138,7 @@ namespace OpenRA.Network
|
||||
{
|
||||
try
|
||||
{
|
||||
var record = database.Find<GeoIP2Record>(ip);
|
||||
if (record != null)
|
||||
return record.Country.Names.English;
|
||||
return database.LookupCountry(ip);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace OpenRA
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum OrderFields : byte
|
||||
enum OrderFields : short
|
||||
{
|
||||
None = 0x0,
|
||||
Target = 0x01,
|
||||
@@ -35,7 +35,8 @@ namespace OpenRA
|
||||
ExtraLocation = 0x10,
|
||||
ExtraData = 0x20,
|
||||
TargetIsCell = 0x40,
|
||||
Subject = 0x80
|
||||
Subject = 0x80,
|
||||
Grouped = 0x100
|
||||
}
|
||||
|
||||
static class OrderFieldsExts
|
||||
@@ -52,6 +53,8 @@ namespace OpenRA
|
||||
public readonly Actor Subject;
|
||||
public readonly bool Queued;
|
||||
public readonly Target Target;
|
||||
public readonly Actor[] GroupedActors;
|
||||
|
||||
public string TargetString;
|
||||
public CPos ExtraLocation;
|
||||
public Actor[] ExtraActors;
|
||||
@@ -64,7 +67,7 @@ namespace OpenRA
|
||||
|
||||
public Player Player { get { return Subject != null ? Subject.Owner : null; } }
|
||||
|
||||
Order(string orderString, Actor subject, Target target, string targetString, bool queued, Actor[] extraActors, CPos extraLocation, uint extraData)
|
||||
Order(string orderString, Actor subject, Target target, string targetString, bool queued, Actor[] extraActors, CPos extraLocation, uint extraData, Actor[] groupedActors = null)
|
||||
{
|
||||
OrderString = orderString ?? "";
|
||||
Subject = subject;
|
||||
@@ -74,6 +77,7 @@ namespace OpenRA
|
||||
ExtraActors = extraActors;
|
||||
ExtraLocation = extraLocation;
|
||||
ExtraData = extraData;
|
||||
GroupedActors = groupedActors;
|
||||
}
|
||||
|
||||
public static Order Deserialize(World world, BinaryReader r)
|
||||
@@ -86,7 +90,7 @@ namespace OpenRA
|
||||
case OrderType.Fields:
|
||||
{
|
||||
var order = r.ReadString();
|
||||
var flags = (OrderFields)r.ReadByte();
|
||||
var flags = (OrderFields)r.ReadInt16();
|
||||
|
||||
Actor subject = null;
|
||||
if (flags.HasField(OrderFields.Subject))
|
||||
@@ -103,8 +107,7 @@ namespace OpenRA
|
||||
{
|
||||
case TargetType.Actor:
|
||||
{
|
||||
Actor targetActor;
|
||||
if (world != null && TryGetActorFromUInt(world, r.ReadUInt32(), out targetActor))
|
||||
if (world != null && TryGetActorFromUInt(world, r.ReadUInt32(), out var targetActor))
|
||||
target = Target.FromActor(targetActor);
|
||||
break;
|
||||
}
|
||||
@@ -114,8 +117,7 @@ namespace OpenRA
|
||||
var playerActorID = r.ReadUInt32();
|
||||
var frozenActorID = r.ReadUInt32();
|
||||
|
||||
Actor playerActor;
|
||||
if (world == null || !TryGetActorFromUInt(world, playerActorID, out playerActor))
|
||||
if (world == null || !TryGetActorFromUInt(world, playerActorID, out var playerActor))
|
||||
break;
|
||||
|
||||
if (playerActor.Owner.FrozenActorLayer == null)
|
||||
@@ -164,13 +166,23 @@ namespace OpenRA
|
||||
var extraLocation = flags.HasField(OrderFields.ExtraLocation) ? new CPos(r.ReadInt32()) : CPos.Zero;
|
||||
var extraData = flags.HasField(OrderFields.ExtraData) ? r.ReadUInt32() : 0;
|
||||
|
||||
Actor[] groupedActors = null;
|
||||
if (flags.HasField(OrderFields.Grouped))
|
||||
{
|
||||
var count = r.ReadInt32();
|
||||
if (world != null)
|
||||
groupedActors = Exts.MakeArray(count, _ => world.GetActorById(r.ReadUInt32()));
|
||||
else
|
||||
r.ReadBytes(4 * count);
|
||||
}
|
||||
|
||||
if (world == null)
|
||||
return new Order(order, null, target, targetString, queued, extraActors, extraLocation, extraData);
|
||||
return new Order(order, null, target, targetString, queued, extraActors, extraLocation, extraData, groupedActors);
|
||||
|
||||
if (subject == null && flags.HasField(OrderFields.Subject))
|
||||
return null;
|
||||
|
||||
return new Order(order, subject, target, targetString, queued, extraActors, extraLocation, extraData);
|
||||
return new Order(order, subject, target, targetString, queued, extraActors, extraLocation, extraData, groupedActors);
|
||||
}
|
||||
|
||||
case OrderType.Handshake:
|
||||
@@ -231,6 +243,11 @@ namespace OpenRA
|
||||
return new Order(order, null, false) { IsImmediate = isImmediate, TargetString = targetString };
|
||||
}
|
||||
|
||||
public static Order FromGroupedOrder(Order grouped, Actor subject)
|
||||
{
|
||||
return new Order(grouped.OrderString, subject, grouped.Target, grouped.TargetString, grouped.Queued, grouped.ExtraActors, grouped.ExtraLocation, grouped.ExtraData);
|
||||
}
|
||||
|
||||
public static Order Command(string text)
|
||||
{
|
||||
return new Order("Command", null, false) { IsImmediate = true, TargetString = text };
|
||||
@@ -255,11 +272,11 @@ namespace OpenRA
|
||||
public Order()
|
||||
: this(null, null, Target.Invalid, null, false, null, CPos.Zero, 0) { }
|
||||
|
||||
public Order(string orderString, Actor subject, bool queued, Actor[] extraActors = null)
|
||||
: this(orderString, subject, Target.Invalid, null, queued, extraActors, CPos.Zero, 0) { }
|
||||
public Order(string orderString, Actor subject, bool queued, Actor[] extraActors = null, Actor[] groupedActors = null)
|
||||
: this(orderString, subject, Target.Invalid, null, queued, extraActors, CPos.Zero, 0, groupedActors) { }
|
||||
|
||||
public Order(string orderString, Actor subject, Target target, bool queued, Actor[] extraActors = null)
|
||||
: this(orderString, subject, target, null, queued, extraActors, CPos.Zero, 0) { }
|
||||
public Order(string orderString, Actor subject, Target target, bool queued, Actor[] extraActors = null, Actor[] groupedActors = null)
|
||||
: this(orderString, subject, target, null, queued, extraActors, CPos.Zero, 0, groupedActors) { }
|
||||
|
||||
public byte[] Serialize()
|
||||
{
|
||||
@@ -267,7 +284,7 @@ namespace OpenRA
|
||||
if (Type == OrderType.Handshake)
|
||||
minLength += TargetString.Length + 1;
|
||||
else if (Type == OrderType.Fields)
|
||||
minLength += 4 + 1 + 13 + (TargetString != null ? TargetString.Length + 1 : 0) + 4 + 4 + 4;
|
||||
minLength += 4 + 2 + 13 + (TargetString != null ? TargetString.Length + 1 : 0) + 4 + 4 + 4;
|
||||
|
||||
if (ExtraActors != null)
|
||||
minLength += ExtraActors.Length * 4;
|
||||
@@ -308,6 +325,9 @@ namespace OpenRA
|
||||
if (Queued)
|
||||
fields |= OrderFields.Queued;
|
||||
|
||||
if (GroupedActors != null)
|
||||
fields |= OrderFields.Grouped;
|
||||
|
||||
if (ExtraActors != null)
|
||||
fields |= OrderFields.ExtraActors;
|
||||
|
||||
@@ -317,7 +337,7 @@ namespace OpenRA
|
||||
if (Target.SerializableCell != null)
|
||||
fields |= OrderFields.TargetIsCell;
|
||||
|
||||
w.Write((byte)fields);
|
||||
w.Write((short)fields);
|
||||
|
||||
if (fields.HasField(OrderFields.Subject))
|
||||
w.Write(UIntFromActor(Subject));
|
||||
@@ -362,6 +382,13 @@ namespace OpenRA
|
||||
if (fields.HasField(OrderFields.ExtraData))
|
||||
w.Write(ExtraData);
|
||||
|
||||
if (fields.HasField(OrderFields.Grouped))
|
||||
{
|
||||
w.Write(GroupedActors.Length);
|
||||
foreach (var a in GroupedActors)
|
||||
w.Write(UIntFromActor(a));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,11 +28,10 @@ namespace OpenRA.Network
|
||||
public Session.Client LocalClient { get { return LobbyInfo.ClientWithIndex(Connection.LocalClientId); } }
|
||||
public World World;
|
||||
|
||||
public readonly string Host;
|
||||
public readonly int Port;
|
||||
public readonly ConnectionTarget Endpoint;
|
||||
public readonly string Password = "";
|
||||
|
||||
public string ServerError = "Server is not responding";
|
||||
public string ServerError = null;
|
||||
public bool AuthenticationFailed = false;
|
||||
public ExternalMod ServerExternalMod = null;
|
||||
|
||||
@@ -80,10 +79,9 @@ namespace OpenRA.Network
|
||||
Connection.Send(i, new List<byte[]>());
|
||||
}
|
||||
|
||||
public OrderManager(string host, int port, string password, IConnection conn)
|
||||
public OrderManager(ConnectionTarget endpoint, string password, IConnection conn)
|
||||
{
|
||||
Host = host;
|
||||
Port = port;
|
||||
Endpoint = endpoint;
|
||||
Password = password;
|
||||
Connection = conn;
|
||||
syncReport = new SyncReport(this);
|
||||
@@ -117,7 +115,7 @@ namespace OpenRA.Network
|
||||
Connection.SendImmediate(localImmediateOrders.Select(o => o.Serialize()));
|
||||
localImmediateOrders.Clear();
|
||||
|
||||
var immediatePackets = new List<Pair<int, byte[]>>();
|
||||
var immediatePackets = new List<(int ClientId, byte[] Packet)>();
|
||||
|
||||
Connection.Receive(
|
||||
(clientId, packet) =>
|
||||
@@ -128,16 +126,16 @@ namespace OpenRA.Network
|
||||
else if (packet.Length >= 5 && packet[4] == (byte)OrderType.SyncHash)
|
||||
CheckSync(packet);
|
||||
else if (frame == 0)
|
||||
immediatePackets.Add(Pair.New(clientId, packet));
|
||||
immediatePackets.Add((clientId, packet));
|
||||
else
|
||||
frameData.AddFrameOrders(clientId, frame, packet);
|
||||
});
|
||||
|
||||
foreach (var p in immediatePackets)
|
||||
{
|
||||
foreach (var o in p.Second.ToOrderList(World))
|
||||
foreach (var o in p.Packet.ToOrderList(World))
|
||||
{
|
||||
UnitOrders.ProcessOrder(this, World, p.First, o);
|
||||
UnitOrders.ProcessOrder(this, World, p.ClientId, o);
|
||||
|
||||
// A mod switch or other event has pulled the ground from beneath us
|
||||
if (disposed)
|
||||
@@ -151,8 +149,7 @@ namespace OpenRA.Network
|
||||
void CheckSync(byte[] packet)
|
||||
{
|
||||
var frame = BitConverter.ToInt32(packet, 0);
|
||||
byte[] existingSync;
|
||||
if (syncForFrame.TryGetValue(frame, out existingSync))
|
||||
if (syncForFrame.TryGetValue(frame, out var existingSync))
|
||||
{
|
||||
if (packet.Length != existingSync.Length)
|
||||
OutOfSync(frame);
|
||||
@@ -167,14 +164,14 @@ namespace OpenRA.Network
|
||||
|
||||
public bool IsReadyForNextFrame
|
||||
{
|
||||
get { return NetFrameNumber >= 1 && frameData.IsReadyForFrame(NetFrameNumber); }
|
||||
get { return GameStarted && frameData.IsReadyForFrame(NetFrameNumber); }
|
||||
}
|
||||
|
||||
public IEnumerable<Session.Client> GetClientsNotReadyForNextFrame
|
||||
{
|
||||
get
|
||||
{
|
||||
return NetFrameNumber >= 1
|
||||
return GameStarted
|
||||
? frameData.ClientsNotReadyForFrame(NetFrameNumber)
|
||||
.Select(a => LobbyInfo.ClientWithIndex(a))
|
||||
: NoClients;
|
||||
@@ -209,8 +206,7 @@ namespace OpenRA.Network
|
||||
public void Dispose()
|
||||
{
|
||||
disposed = true;
|
||||
if (Connection != null)
|
||||
Connection.Dispose();
|
||||
Connection?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
@@ -22,7 +22,7 @@ namespace OpenRA.Network
|
||||
class Chunk
|
||||
{
|
||||
public int Frame;
|
||||
public Pair<int, byte[]>[] Packets;
|
||||
public (int ClientId, byte[] Packet)[] Packets;
|
||||
}
|
||||
|
||||
Queue<Chunk> chunks = new Queue<Chunk>();
|
||||
@@ -32,6 +32,13 @@ namespace OpenRA.Network
|
||||
|
||||
public int LocalClientId { get { return -1; } }
|
||||
public ConnectionState ConnectionState { get { return ConnectionState.Connected; } }
|
||||
public IPEndPoint EndPoint
|
||||
{
|
||||
get { throw new NotSupportedException("A replay connection doesn't have an endpoint"); }
|
||||
}
|
||||
|
||||
public string ErrorMessage { get { return null; } }
|
||||
|
||||
public readonly int TickCount;
|
||||
public readonly int FinalGameTick;
|
||||
public readonly bool IsValid;
|
||||
@@ -47,7 +54,7 @@ namespace OpenRA.Network
|
||||
// to avoid issues with all immediate orders being resolved on the first tick.
|
||||
using (var rs = File.OpenRead(replayFilename))
|
||||
{
|
||||
var packets = new List<Pair<int, byte[]>>();
|
||||
var packets = new List<(int ClientId, byte[] Packet)>();
|
||||
|
||||
var chunk = new Chunk();
|
||||
|
||||
@@ -59,7 +66,7 @@ namespace OpenRA.Network
|
||||
var packetLen = rs.ReadInt32();
|
||||
var packet = rs.ReadBytes(packetLen);
|
||||
var frame = BitConverter.ToInt32(packet, 0);
|
||||
packets.Add(Pair.New(client, packet));
|
||||
packets.Add((client, packet));
|
||||
|
||||
if (frame != int.MaxValue &&
|
||||
(!lastClientsFrame.ContainsKey(client) || frame > lastClientsFrame[client]))
|
||||
@@ -103,13 +110,13 @@ namespace OpenRA.Network
|
||||
{
|
||||
foreach (var tmpPacketPair in tmpChunk.Packets)
|
||||
{
|
||||
var client = tmpPacketPair.First;
|
||||
var client = tmpPacketPair.ClientId;
|
||||
|
||||
// Don't replace the final disconnection packet - we still want this to end the replay.
|
||||
if (client == lastClientToDisconnect)
|
||||
continue;
|
||||
|
||||
var packet = tmpPacketPair.Second;
|
||||
var packet = tmpPacketPair.Packet;
|
||||
if (packet.Length == 5 && packet[4] == (byte)OrderType.Disconnect)
|
||||
{
|
||||
var lastClientFrame = lastClientsFrame[client];
|
||||
@@ -148,7 +155,7 @@ namespace OpenRA.Network
|
||||
|
||||
while (chunks.Count != 0 && chunks.Peek().Frame <= ordersFrame)
|
||||
foreach (var o in chunks.Dequeue().Packets)
|
||||
packetFn(o.First, o.Second);
|
||||
packetFn(o.ClientId, o.Packet);
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
@@ -100,8 +100,7 @@ namespace OpenRA.Network
|
||||
Metadata.Write(writer);
|
||||
}
|
||||
|
||||
if (preStartBuffer != null)
|
||||
preStartBuffer.Dispose();
|
||||
preStartBuffer?.Dispose();
|
||||
writer.Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,8 +250,7 @@ namespace OpenRA.Network
|
||||
|
||||
public bool OptionOrDefault(string id, bool def)
|
||||
{
|
||||
LobbyOptionState option;
|
||||
if (LobbyOptions.TryGetValue(id, out option))
|
||||
if (LobbyOptions.TryGetValue(id, out var option))
|
||||
return option.IsEnabled;
|
||||
|
||||
return def;
|
||||
@@ -259,8 +258,7 @@ namespace OpenRA.Network
|
||||
|
||||
public string OptionOrDefault(string id, string def)
|
||||
{
|
||||
LobbyOptionState option;
|
||||
if (LobbyOptions.TryGetValue(id, out option))
|
||||
if (LobbyOptions.TryGetValue(id, out var option))
|
||||
return option.Value;
|
||||
|
||||
return def;
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace OpenRA.Network
|
||||
readonly Report[] syncReports = new Report[NumSyncReports];
|
||||
int curIndex = 0;
|
||||
|
||||
static Pair<string[], Values> DumpSyncTrait(ISync sync)
|
||||
static (string[] Names, Values Values) DumpSyncTrait(ISync sync)
|
||||
{
|
||||
var type = sync.GetType();
|
||||
TypeInfo typeInfo;
|
||||
@@ -41,7 +41,7 @@ namespace OpenRA.Network
|
||||
foreach (var func in typeInfo.SerializableCopyOfMemberFunctions)
|
||||
values[index++] = func(sync);
|
||||
|
||||
return Pair.New(typeInfo.Names, values);
|
||||
return (typeInfo.Names, values);
|
||||
}
|
||||
|
||||
public SyncReport(OrderManager orderManager)
|
||||
@@ -120,9 +120,9 @@ namespace OpenRA.Network
|
||||
Log.Write("sync", "\t {0} {1} {2} {3} ({4})".F(a.ActorID, a.Type, a.Owner, a.Trait, a.Hash));
|
||||
|
||||
var nvp = a.NamesValues;
|
||||
for (int i = 0; i < nvp.First.Length; i++)
|
||||
if (nvp.Second[i] != null)
|
||||
Log.Write("sync", "\t\t {0}: {1}".F(nvp.First[i], nvp.Second[i]));
|
||||
for (int i = 0; i < nvp.Names.Length; i++)
|
||||
if (nvp.Values[i] != null)
|
||||
Log.Write("sync", "\t\t {0}: {1}".F(nvp.Names[i], nvp.Values[i]));
|
||||
}
|
||||
|
||||
Log.Write("sync", "Synced Effects:");
|
||||
@@ -131,9 +131,9 @@ namespace OpenRA.Network
|
||||
Log.Write("sync", "\t {0} ({1})", e.Name, e.Hash);
|
||||
|
||||
var nvp = e.NamesValues;
|
||||
for (int i = 0; i < nvp.First.Length; i++)
|
||||
if (nvp.Second[i] != null)
|
||||
Log.Write("sync", "\t\t {0}: {1}".F(nvp.First[i], nvp.Second[i]));
|
||||
for (int i = 0; i < nvp.Names.Length; i++)
|
||||
if (nvp.Values[i] != null)
|
||||
Log.Write("sync", "\t\t {0}: {1}".F(nvp.Names[i], nvp.Values[i]));
|
||||
}
|
||||
|
||||
Log.Write("sync", "Orders Issued:");
|
||||
@@ -163,14 +163,14 @@ namespace OpenRA.Network
|
||||
public string Owner;
|
||||
public string Trait;
|
||||
public int Hash;
|
||||
public Pair<string[], Values> NamesValues;
|
||||
public (string[] Names, Values Values) NamesValues;
|
||||
}
|
||||
|
||||
struct EffectReport
|
||||
{
|
||||
public string Name;
|
||||
public int Hash;
|
||||
public Pair<string[], Values> NamesValues;
|
||||
public (string[] Names, Values Values) NamesValues;
|
||||
}
|
||||
|
||||
struct TypeInfo
|
||||
|
||||
@@ -9,10 +9,8 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Server;
|
||||
using OpenRA.Traits;
|
||||
|
||||
@@ -147,8 +145,7 @@ namespace OpenRA.Network
|
||||
var data = MiniYaml.FromString(order.TargetString)[0];
|
||||
var traitIndex = int.Parse(data.Key);
|
||||
|
||||
if (world != null)
|
||||
world.AddGameSaveTraitData(traitIndex, data.Value);
|
||||
world?.AddGameSaveTraitData(traitIndex, data.Value);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -192,9 +189,8 @@ namespace OpenRA.Network
|
||||
var request = HandshakeRequest.Deserialize(order.TargetString);
|
||||
|
||||
var externalKey = ExternalMod.MakeKey(request.Mod, request.Version);
|
||||
ExternalMod external;
|
||||
if ((request.Mod != mod.Id || request.Version != mod.Metadata.Version)
|
||||
&& Game.ExternalMods.TryGetValue(externalKey, out external))
|
||||
if ((request.Mod != mod.Id || request.Version != mod.Metadata.Version) &&
|
||||
Game.ExternalMods.TryGetValue(externalKey, out var external))
|
||||
{
|
||||
// The ConnectionFailedLogic will prompt the user to switch mods
|
||||
orderManager.ServerExternalMod = external;
|
||||
@@ -306,7 +302,10 @@ namespace OpenRA.Network
|
||||
{
|
||||
var strings = node.Key.Split('@');
|
||||
if (strings[0] == "GlobalSettings")
|
||||
{
|
||||
orderManager.LobbyInfo.GlobalSettings = Session.Global.Deserialize(node.Value);
|
||||
orderManager.IssueOrder(Order.Command("state {0}".F(Session.ClientState.NotReady)));
|
||||
}
|
||||
}
|
||||
|
||||
SetOrderLag(orderManager);
|
||||
@@ -337,7 +336,12 @@ namespace OpenRA.Network
|
||||
|
||||
default:
|
||||
{
|
||||
ResolveOrder(order);
|
||||
if (order.GroupedActors == null)
|
||||
ResolveOrder(order);
|
||||
else
|
||||
foreach (var subject in order.GroupedActors)
|
||||
ResolveOrder(Order.FromGroupedOrder(order, subject));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace OpenRA
|
||||
|
||||
readonly Cache<string, Type> typeCache;
|
||||
readonly Cache<Type, ConstructorInfo> ctorCache;
|
||||
readonly Pair<Assembly, string>[] assemblies;
|
||||
readonly (Assembly Assembly, string Namespace)[] assemblies;
|
||||
|
||||
public ObjectCreator(Manifest manifest, InstalledMods mods)
|
||||
{
|
||||
@@ -48,8 +48,7 @@ namespace OpenRA
|
||||
// We can't check the internal name of the assembly, so we'll work off the data instead
|
||||
var hash = CryptoUtil.SHA1Hash(File.ReadAllBytes(resolvedPath));
|
||||
|
||||
Assembly assembly;
|
||||
if (!ResolvedAssemblies.TryGetValue(hash, out assembly))
|
||||
if (!ResolvedAssemblies.TryGetValue(hash, out var assembly))
|
||||
{
|
||||
assembly = Assembly.LoadFile(resolvedPath);
|
||||
ResolvedAssemblies.Add(hash, assembly);
|
||||
@@ -59,7 +58,7 @@ namespace OpenRA
|
||||
}
|
||||
|
||||
AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
|
||||
assemblies = assemblyList.SelectMany(asm => asm.GetNamespaces().Select(ns => Pair.New(asm, ns))).ToArray();
|
||||
assemblies = assemblyList.SelectMany(asm => asm.GetNamespaces().Select(ns => (asm, ns))).ToArray();
|
||||
}
|
||||
|
||||
Assembly ResolveAssembly(object sender, ResolveEventArgs e)
|
||||
@@ -68,10 +67,7 @@ namespace OpenRA
|
||||
if (a.FullName == e.Name)
|
||||
return a;
|
||||
|
||||
if (assemblies == null)
|
||||
return null;
|
||||
|
||||
return assemblies.Select(a => a.First).FirstOrDefault(a => a.FullName == e.Name);
|
||||
return assemblies?.Select(a => a.Assembly).FirstOrDefault(a => a.FullName == e.Name);
|
||||
}
|
||||
|
||||
// Only used by the linter to prevent exceptions from being thrown during a lint run
|
||||
@@ -106,7 +102,7 @@ namespace OpenRA
|
||||
public Type FindType(string className)
|
||||
{
|
||||
return assemblies
|
||||
.Select(pair => pair.First.GetType(pair.Second + "." + className, false))
|
||||
.Select(pair => pair.Assembly.GetType(pair.Namespace + "." + className, false))
|
||||
.FirstOrDefault(t => t != null);
|
||||
}
|
||||
|
||||
@@ -146,7 +142,7 @@ namespace OpenRA
|
||||
|
||||
public IEnumerable<Type> GetTypes()
|
||||
{
|
||||
return assemblies.Select(ma => ma.First).Distinct()
|
||||
return assemblies.Select(ma => ma.Assembly).Distinct()
|
||||
.SelectMany(ma => ma.GetTypes());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net461</TargetFramework>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Optimize>true</Optimize>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<LangVersion>5</LangVersion>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
|
||||
@@ -16,8 +16,12 @@
|
||||
<ExternalConsole>false</ExternalConsole>
|
||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||
<CodeAnalysisRuleSet>..\OpenRA.ruleset</CodeAnalysisRuleSet>
|
||||
<Configurations>Release;Debug;Release-x86</Configurations>
|
||||
<ManagedExeLauncher Condition="('$(OS)' != 'Windows_NT')">mono </ManagedExeLauncher>
|
||||
<Configurations>Release;Debug</Configurations>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetPlatform Condition="$([MSBuild]::IsOsPlatform('Windows'))">win-x64</TargetPlatform>
|
||||
<TargetPlatform Condition="$([MSBuild]::IsOsPlatform('Linux'))">linux-x64</TargetPlatform>
|
||||
<TargetPlatform Condition="$([MSBuild]::IsOsPlatform('OSX'))">osx-x64</TargetPlatform>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- Work around an issue where Rider does not detect files in the project root using the default glob -->
|
||||
@@ -27,7 +31,7 @@
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<Optimize>false</Optimize>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release-x86'">
|
||||
<PropertyGroup Condition="'$(TargetPlatform)' == 'win-x86'">
|
||||
<Prefer32bit>true</Prefer32bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(RunConfiguration)' == 'Red Alert'">
|
||||
@@ -47,26 +51,12 @@
|
||||
<StartArguments>Game.Mod=ts</StartArguments>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Open.Nat">
|
||||
<HintPath>..\thirdparty\download\Open.Nat.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Eluant">
|
||||
<HintPath>..\thirdparty\download\Eluant.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="ICSharpCode.SharpZipLib">
|
||||
<HintPath>..\thirdparty\download\ICSharpCode.SharpZipLib.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="MaxMind.Db">
|
||||
<HintPath>..\thirdparty\download\MaxMind.Db.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<ProjectReference Include="..\OpenRA.PostProcess\OpenRA.PostProcess.csproj">
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
<None Include="App.config" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
|
||||
<PackageReference Include="OpenRA-Eluant" Version="1.0.17" />
|
||||
<PackageReference Include="OpenRA-Open.NAT" Version="1.0.0" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.2.0" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
|
||||
<AdditionalFiles Include="../stylecop.json" />
|
||||
<AdditionalFiles Include="Properties/launchSettings.json" />
|
||||
</ItemGroup>
|
||||
@@ -76,8 +66,4 @@
|
||||
<Analyzer Remove="@(Analyzer)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
<Target Name="PostProcess" AfterTargets="Build" Inputs="$(TargetPath)" Outputs="$(IntermediateOutputPath)\$(TargetFileName).processed">
|
||||
<Exec Command="$(ManagedExeLauncher)"$(TargetDir)OpenRA.PostProcess.exe" "$(TargetPath)" -LAA" />
|
||||
<Touch Files="$(IntermediateOutputPath)\$(TargetFileName).processed" AlwaysCreate="true" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Graphics;
|
||||
using System.Linq;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Orders
|
||||
@@ -18,13 +18,14 @@ namespace OpenRA.Orders
|
||||
public class GenericSelectTarget : UnitOrderGenerator
|
||||
{
|
||||
public readonly string OrderName;
|
||||
protected readonly IEnumerable<Actor> Subjects;
|
||||
protected readonly string Cursor;
|
||||
protected readonly MouseButton ExpectedButton;
|
||||
|
||||
protected IEnumerable<Actor> subjects;
|
||||
|
||||
public GenericSelectTarget(IEnumerable<Actor> subjects, string order, string cursor, MouseButton button)
|
||||
{
|
||||
Subjects = subjects;
|
||||
this.subjects = subjects;
|
||||
OrderName = order;
|
||||
Cursor = cursor;
|
||||
ExpectedButton = button;
|
||||
@@ -53,8 +54,7 @@ namespace OpenRA.Orders
|
||||
world.CancelInputMode();
|
||||
|
||||
var queued = mi.Modifiers.HasModifier(Modifiers.Shift);
|
||||
foreach (var subject in Subjects)
|
||||
yield return new Order(OrderName, subject, Target.FromCell(world, cell), queued);
|
||||
yield return new Order(OrderName, null, Target.FromCell(world, cell), queued, null, subjects.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,11 @@ namespace OpenRA.Orders
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void SelectionChanged(World world, IEnumerable<Actor> selected)
|
||||
{
|
||||
subjects = selected;
|
||||
}
|
||||
|
||||
public override bool ClearSelectionOnLeftClick { get { return false; } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,5 +24,6 @@ namespace OpenRA
|
||||
string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi);
|
||||
void Deactivate();
|
||||
bool HandleKeyPress(KeyInput e);
|
||||
void SelectionChanged(World world, IEnumerable<Actor> selected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace OpenRA.Orders
|
||||
|
||||
bool useSelect;
|
||||
if (Game.Settings.Game.UseClassicMouseStyle && !InputOverridesSelection(world, worldPixel, mi))
|
||||
useSelect = target.Type == TargetType.Actor && target.Actor.Info.HasTraitInfo<SelectableInfo>();
|
||||
useSelect = target.Type == TargetType.Actor && target.Actor.Info.HasTraitInfo<ISelectableInfo>();
|
||||
else
|
||||
{
|
||||
var ordersWithCursor = world.Selection.Actors
|
||||
@@ -81,7 +81,7 @@ namespace OpenRA.Orders
|
||||
if (cursorOrder != null)
|
||||
return cursorOrder.Cursor;
|
||||
|
||||
useSelect = target.Type == TargetType.Actor && target.Actor.Info.HasTraitInfo<SelectableInfo>() &&
|
||||
useSelect = target.Type == TargetType.Actor && target.Actor.Info.HasTraitInfo<ISelectableInfo>() &&
|
||||
(mi.Modifiers.HasModifier(Modifiers.Shift) || !world.Selection.Actors.Any());
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace OpenRA.Orders
|
||||
public virtual bool InputOverridesSelection(World world, int2 xy, MouseInput mi)
|
||||
{
|
||||
var actor = world.ScreenMap.ActorsAtMouse(xy)
|
||||
.Where(a => !a.Actor.IsDead)
|
||||
.Where(a => !a.Actor.IsDead && a.Actor.Info.HasTraitInfo<ISelectableInfo>() && (a.Actor.Owner.IsAlliedWith(world.RenderPlayer) || !world.FogObscures(a.Actor)))
|
||||
.WithHighestSelectionPriority(xy, mi.Modifiers);
|
||||
|
||||
if (actor == null)
|
||||
@@ -124,6 +124,8 @@ namespace OpenRA.Orders
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual void SelectionChanged(World world, IEnumerable<Actor> selected) { }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the most appropriate order for a given actor and target.
|
||||
/// First priority is given to orders that interact with the given actors.
|
||||
|
||||
@@ -14,7 +14,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Eluant;
|
||||
using Eluant.ObjectBinding;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Scripting;
|
||||
@@ -37,6 +36,9 @@ namespace OpenRA
|
||||
|
||||
public class Player : IScriptBindable, IScriptNotifyBind, ILuaTableBinding, ILuaEqualityBinding, ILuaToStringBinding
|
||||
{
|
||||
public const string PlayerActorType = "Player";
|
||||
public const string EditorPlayerActorType = "EditorPlayer";
|
||||
|
||||
struct StanceColors
|
||||
{
|
||||
public Color Self;
|
||||
@@ -164,13 +166,22 @@ namespace OpenRA
|
||||
if (!Spectating)
|
||||
PlayerMask = new LongBitSet<PlayerBitMask>(InternalName);
|
||||
|
||||
var playerActorType = world.Type == WorldType.Editor ? "EditorPlayer" : "Player";
|
||||
PlayerActor = world.CreateActor(playerActorType, new TypeDictionary { new OwnerInit(this) });
|
||||
// Set this property before running any Created callbacks on the player actor
|
||||
IsBot = BotType != null;
|
||||
|
||||
// Special case handling is required for the Player actor:
|
||||
// Since Actor.Created would be called before PlayerActor is assigned here
|
||||
// querying player traits in INotifyCreated.Created would crash.
|
||||
// Therefore assign the uninitialized actor and run the Created callbacks
|
||||
// by calling Initialize ourselves.
|
||||
var playerActorType = world.Type == WorldType.Editor ? EditorPlayerActorType : PlayerActorType;
|
||||
PlayerActor = new Actor(world, playerActorType, new TypeDictionary { new OwnerInit(this) });
|
||||
PlayerActor.Initialize(true);
|
||||
|
||||
Shroud = PlayerActor.Trait<Shroud>();
|
||||
FrozenActorLayer = PlayerActor.TraitOrDefault<FrozenActorLayer>();
|
||||
|
||||
// Enable the bot logic on the host
|
||||
IsBot = BotType != null;
|
||||
if (IsBot && Game.IsHost)
|
||||
{
|
||||
var logic = PlayerActor.TraitsImplementing<IBot>().FirstOrDefault(b => b.Info.Type == BotType);
|
||||
@@ -242,8 +253,7 @@ namespace OpenRA
|
||||
|
||||
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
|
||||
{
|
||||
Player a, b;
|
||||
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
|
||||
if (!left.TryGetClrValue(out Player a) || !right.TryGetClrValue(out Player b))
|
||||
return false;
|
||||
|
||||
return a == b;
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -33,7 +32,7 @@ namespace OpenRA
|
||||
SheetBuilder sheetBuilder;
|
||||
|
||||
[FieldLoader.Ignore]
|
||||
Cache<Pair<PlayerBadge, int>, Sprite> iconCache;
|
||||
Cache<(PlayerBadge, int), Sprite> iconCache;
|
||||
|
||||
Sprite LoadSprite(string url, int density)
|
||||
{
|
||||
@@ -84,15 +83,15 @@ namespace OpenRA
|
||||
{
|
||||
sheetBuilder = new SheetBuilder(SheetType.BGRA, CreateSheet);
|
||||
|
||||
iconCache = new Cache<Pair<PlayerBadge, int>, Sprite>(p =>
|
||||
iconCache = new Cache<(PlayerBadge Badge, int Density), Sprite>(p =>
|
||||
{
|
||||
if (p.Second > 2 && !string.IsNullOrEmpty(p.First.Icon3x))
|
||||
return LoadSprite(p.First.Icon3x, 3);
|
||||
if (p.Density > 2 && !string.IsNullOrEmpty(p.Badge.Icon3x))
|
||||
return LoadSprite(p.Badge.Icon3x, 3);
|
||||
|
||||
if (p.Second > 1 && !string.IsNullOrEmpty(p.First.Icon2x))
|
||||
return LoadSprite(p.First.Icon2x, 2);
|
||||
if (p.Density > 1 && !string.IsNullOrEmpty(p.Badge.Icon2x))
|
||||
return LoadSprite(p.Badge.Icon2x, 2);
|
||||
|
||||
return LoadSprite(p.First.Icon, 1);
|
||||
return LoadSprite(p.Badge.Icon, 1);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -114,7 +113,7 @@ namespace OpenRA
|
||||
{
|
||||
var ws = Game.Renderer.WindowScale;
|
||||
var density = ws > 2 ? 3 : ws > 1 ? 2 : 1;
|
||||
return iconCache[Pair.New(badge, density)];
|
||||
return iconCache[(badge, density)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,14 +47,9 @@ namespace OpenRA.Primitives
|
||||
BitSetIndex bits = 0;
|
||||
|
||||
lock (Bits)
|
||||
{
|
||||
foreach (var value in values)
|
||||
{
|
||||
BitSetIndex valueBit;
|
||||
if (Bits.TryGetValue(value, out valueBit))
|
||||
if (Bits.TryGetValue(value, out var valueBit))
|
||||
bits |= valueBit;
|
||||
}
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
@@ -124,10 +124,10 @@ namespace OpenRA.Primitives
|
||||
if (value.Length != 6 && value.Length != 8)
|
||||
return false;
|
||||
|
||||
byte red, green, blue, alpha = 255;
|
||||
if (!byte.TryParse(value.Substring(0, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out red)
|
||||
|| !byte.TryParse(value.Substring(2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out green)
|
||||
|| !byte.TryParse(value.Substring(4, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out blue))
|
||||
byte alpha = 255;
|
||||
if (!byte.TryParse(value.Substring(0, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var red)
|
||||
|| !byte.TryParse(value.Substring(2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var green)
|
||||
|| !byte.TryParse(value.Substring(4, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var blue))
|
||||
return false;
|
||||
|
||||
if (value.Length == 8
|
||||
|
||||
@@ -50,14 +50,9 @@ namespace OpenRA.Primitives
|
||||
long bits = 0;
|
||||
|
||||
lock (Bits)
|
||||
{
|
||||
foreach (var value in values)
|
||||
{
|
||||
long valueBit;
|
||||
if (Bits.TryGetValue(value, out valueBit))
|
||||
if (Bits.TryGetValue(value, out var valueBit))
|
||||
bits |= valueBit;
|
||||
}
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
#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;
|
||||
|
||||
namespace OpenRA.Primitives
|
||||
{
|
||||
public struct Pair<T, U> : IEquatable<Pair<T, U>>
|
||||
{
|
||||
public T First;
|
||||
public U Second;
|
||||
|
||||
public Pair(T first, U second)
|
||||
{
|
||||
First = first;
|
||||
Second = second;
|
||||
}
|
||||
|
||||
internal static IEqualityComparer<T> Tcomparer = EqualityComparer<T>.Default;
|
||||
internal static IEqualityComparer<U> Ucomparer = EqualityComparer<U>.Default;
|
||||
|
||||
public static bool operator ==(Pair<T, U> a, Pair<T, U> b)
|
||||
{
|
||||
return Tcomparer.Equals(a.First, b.First) && Ucomparer.Equals(a.Second, b.Second);
|
||||
}
|
||||
|
||||
public static bool operator !=(Pair<T, U> a, Pair<T, U> b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
public override int GetHashCode() { return First.GetHashCode() ^ Second.GetHashCode(); }
|
||||
|
||||
public bool Equals(Pair<T, U> other) { return this == other; }
|
||||
public override bool Equals(object obj) { return obj is Pair<T, U> && Equals((Pair<T, U>)obj); }
|
||||
|
||||
public Pair<T, U> WithFirst(T t) { return new Pair<T, U>(t, Second); }
|
||||
public Pair<T, U> WithSecond(U u) { return new Pair<T, U>(First, u); }
|
||||
|
||||
public static T AsFirst(Pair<T, U> p) { return p.First; }
|
||||
public static U AsSecond(Pair<T, U> p) { return p.Second; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "({0},{1})".F(First, Second);
|
||||
}
|
||||
|
||||
class PairEqualityComparer : IEqualityComparer<Pair<T, U>>
|
||||
{
|
||||
public bool Equals(Pair<T, U> x, Pair<T, U> y) { return x == y; }
|
||||
public int GetHashCode(Pair<T, U> obj) { return obj.GetHashCode(); }
|
||||
}
|
||||
|
||||
public static IEqualityComparer<Pair<T, U>> EqualityComparer { get { return new PairEqualityComparer(); } }
|
||||
}
|
||||
|
||||
public static class Pair
|
||||
{
|
||||
public static Pair<T, U> New<T, U>(T t, U u) { return new Pair<T, U>(t, u); }
|
||||
}
|
||||
}
|
||||
114
OpenRA.Game/Primitives/Polygon.cs
Normal file
114
OpenRA.Game/Primitives/Polygon.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
#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.Linq;
|
||||
|
||||
namespace OpenRA.Primitives
|
||||
{
|
||||
public struct Polygon
|
||||
{
|
||||
public static readonly Polygon Empty = new Polygon(Rectangle.Empty);
|
||||
|
||||
public readonly Rectangle BoundingRect;
|
||||
public readonly int2[] Vertices;
|
||||
bool isRectangle;
|
||||
|
||||
public Polygon(Rectangle bounds)
|
||||
{
|
||||
BoundingRect = bounds;
|
||||
Vertices = new[] { bounds.TopLeft, bounds.BottomLeft, bounds.BottomRight, bounds.TopRight };
|
||||
isRectangle = true;
|
||||
}
|
||||
|
||||
public Polygon(int2[] vertices)
|
||||
{
|
||||
if (vertices != null && vertices.Length > 0)
|
||||
{
|
||||
Vertices = vertices;
|
||||
var left = int.MaxValue;
|
||||
var right = int.MinValue;
|
||||
var top = int.MaxValue;
|
||||
var bottom = int.MinValue;
|
||||
foreach (var p in vertices)
|
||||
{
|
||||
left = Math.Min(left, p.X);
|
||||
right = Math.Max(right, p.X);
|
||||
top = Math.Min(top, p.Y);
|
||||
bottom = Math.Max(bottom, p.Y);
|
||||
}
|
||||
|
||||
BoundingRect = Rectangle.FromLTRB(left, top, right, bottom);
|
||||
isRectangle = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
isRectangle = true;
|
||||
BoundingRect = Rectangle.Empty;
|
||||
Vertices = Exts.MakeArray(4, _ => int2.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEmpty { get { return BoundingRect.IsEmpty; } }
|
||||
public bool Contains(int2 xy)
|
||||
{
|
||||
return isRectangle ? BoundingRect.Contains(xy) : Vertices.PolygonContains(xy);
|
||||
}
|
||||
|
||||
public bool IntersectsWith(Rectangle rect)
|
||||
{
|
||||
var intersectsBoundingRect = BoundingRect.Left < rect.Right && BoundingRect.Right > rect.Left && BoundingRect.Top < rect.Bottom && BoundingRect.Bottom > rect.Top;
|
||||
if (isRectangle)
|
||||
return intersectsBoundingRect;
|
||||
|
||||
// Easy case 1: Rect and bounding box don't intersect
|
||||
if (!intersectsBoundingRect)
|
||||
return false;
|
||||
|
||||
// Easy case 2: Rect and bounding box intersect in a cross shape
|
||||
if ((rect.Left <= BoundingRect.Left && rect.Right >= BoundingRect.Right) || (rect.Top <= BoundingRect.Top && rect.Bottom >= BoundingRect.Bottom))
|
||||
return true;
|
||||
|
||||
// Easy case 3: Corner of rect is inside the polygon
|
||||
if (Vertices.PolygonContains(rect.TopLeft) || Vertices.PolygonContains(rect.TopRight) || Vertices.PolygonContains(rect.BottomLeft) || Vertices.PolygonContains(rect.BottomRight))
|
||||
return true;
|
||||
|
||||
// Easy case 4: Polygon vertex is inside rect
|
||||
if (Vertices.Any(p => rect.Contains(p)))
|
||||
return true;
|
||||
|
||||
// Hard case: check intersection of every line segment pair
|
||||
var rectVertices = new[]
|
||||
{
|
||||
rect.TopLeft,
|
||||
rect.BottomLeft,
|
||||
rect.BottomRight,
|
||||
rect.TopRight
|
||||
};
|
||||
|
||||
for (var i = 0; i < Vertices.Length; i++)
|
||||
for (var j = 0; j < 4; j++)
|
||||
if (Exts.LinesIntersect(Vertices[i], Vertices[(i + 1) % Vertices.Length], rectVertices[j], rectVertices[(j + 1) % 4]))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var code = BoundingRect.GetHashCode();
|
||||
foreach (var v in Vertices)
|
||||
code = ((code << 5) + code) ^ v.GetHashCode();
|
||||
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,11 @@ namespace OpenRA.Primitives
|
||||
public int2 Location { get { return new int2(X, Y); } }
|
||||
public Size Size { get { return new Size(Width, Height); } }
|
||||
|
||||
public int2 TopLeft { get { return Location; } }
|
||||
public int2 TopRight { get { return new int2(X + Width, Y); } }
|
||||
public int2 BottomLeft { get { return new int2(X, Y + Height); } }
|
||||
public int2 BottomRight { get { return new int2(X + Width, Y + Height); } }
|
||||
|
||||
public bool Contains(int x, int y)
|
||||
{
|
||||
return x >= Left && x < Right && y >= Top && y < Bottom;
|
||||
|
||||
@@ -120,8 +120,7 @@ namespace OpenRA.Primitives
|
||||
/// <param name="count">The length of the segment.</param>
|
||||
public static Stream CreateWithoutOwningStream(Stream stream, long offset, int count)
|
||||
{
|
||||
Stream parentStream;
|
||||
var nestedOffset = offset + GetOverallNestedOffset(stream, out parentStream);
|
||||
var nestedOffset = offset + GetOverallNestedOffset(stream, out var parentStream);
|
||||
|
||||
// Special case FileStream - instead of creating an in-memory copy,
|
||||
// just reference the portion of the on-disk file that we need to save memory.
|
||||
|
||||
@@ -52,8 +52,7 @@ namespace OpenRA.Primitives
|
||||
|
||||
public bool Remove(T item)
|
||||
{
|
||||
Rectangle bounds;
|
||||
if (!itemBounds.TryGetValue(item, out bounds))
|
||||
if (!itemBounds.TryGetValue(item, out var bounds))
|
||||
return false;
|
||||
|
||||
MutateBins(item, bounds, removeItem);
|
||||
@@ -91,8 +90,7 @@ namespace OpenRA.Primitives
|
||||
|
||||
void MutateBins(T actor, Rectangle bounds, Action<Dictionary<T, Rectangle>, T, Rectangle> action)
|
||||
{
|
||||
int minRow, maxRow, minCol, maxCol;
|
||||
BoundsToBinRowsAndCols(bounds, out minRow, out maxRow, out minCol, out maxCol);
|
||||
BoundsToBinRowsAndCols(bounds, out var minRow, out var maxRow, out var minCol, out var maxCol);
|
||||
|
||||
for (var row = minRow; row < maxRow; row++)
|
||||
for (var col = minCol; col < maxCol; col++)
|
||||
@@ -110,8 +108,7 @@ namespace OpenRA.Primitives
|
||||
|
||||
public IEnumerable<T> InBox(Rectangle box)
|
||||
{
|
||||
int minRow, maxRow, minCol, maxCol;
|
||||
BoundsToBinRowsAndCols(box, out minRow, out maxRow, out minCol, out maxCol);
|
||||
BoundsToBinRowsAndCols(box, out var minRow, out var maxRow, out var minCol, out var maxCol);
|
||||
|
||||
// We want to return any items intersecting the box.
|
||||
// If the box covers multiple bins, we must handle items that are contained in multiple bins and avoid
|
||||
@@ -139,5 +136,7 @@ namespace OpenRA.Primitives
|
||||
}
|
||||
|
||||
public IEnumerable<Rectangle> ItemBounds { get { return itemBounds.Values; } }
|
||||
|
||||
public IEnumerable<T> Items { get { return itemBounds.Keys; } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,11 @@ namespace OpenRA.Primitives
|
||||
return data.ContainsKey(typeof(T));
|
||||
}
|
||||
|
||||
public bool Contains(Type t)
|
||||
{
|
||||
return data.ContainsKey(t);
|
||||
}
|
||||
|
||||
public T Get<T>()
|
||||
{
|
||||
return (T)Get(typeof(T), true);
|
||||
@@ -56,8 +61,7 @@ namespace OpenRA.Primitives
|
||||
|
||||
object Get(Type t, bool throwsIfMissing)
|
||||
{
|
||||
List<object> ret;
|
||||
if (!data.TryGetValue(t, out ret))
|
||||
if (!data.TryGetValue(t, out var ret))
|
||||
{
|
||||
if (throwsIfMissing)
|
||||
throw new InvalidOperationException("TypeDictionary does not contain instance of type `{0}`".F(t));
|
||||
@@ -71,8 +75,7 @@ namespace OpenRA.Primitives
|
||||
|
||||
public IEnumerable<T> WithInterface<T>()
|
||||
{
|
||||
List<object> objs;
|
||||
if (data.TryGetValue(typeof(T), out objs))
|
||||
if (data.TryGetValue(typeof(T), out var objs))
|
||||
return objs.Cast<T>();
|
||||
return new T[0];
|
||||
}
|
||||
@@ -89,8 +92,7 @@ namespace OpenRA.Primitives
|
||||
|
||||
void InnerRemove(Type t, object val)
|
||||
{
|
||||
List<object> objs;
|
||||
if (!data.TryGetValue(t, out objs))
|
||||
if (!data.TryGetValue(t, out var objs))
|
||||
return;
|
||||
objs.Remove(val);
|
||||
if (objs.Count == 0)
|
||||
|
||||
@@ -62,5 +62,6 @@ namespace OpenRA
|
||||
public override string ToString() { return "{0},{1},{2}".F(X, Y, Z); }
|
||||
|
||||
public static readonly float3 Zero = new float3(0, 0, 0);
|
||||
public static readonly float3 Ones = new float3(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,15 @@ namespace OpenRA
|
||||
public RgbaColorRenderer RgbaColorRenderer { get; private set; }
|
||||
public SpriteRenderer SpriteRenderer { get; private set; }
|
||||
public RgbaSpriteRenderer RgbaSpriteRenderer { get; private set; }
|
||||
|
||||
public bool WindowHasInputFocus
|
||||
{
|
||||
get
|
||||
{
|
||||
return Window.HasInputFocus;
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<string, SpriteFont> Fonts;
|
||||
|
||||
internal IPlatformWindow Window { get; private set; }
|
||||
@@ -66,7 +75,10 @@ namespace OpenRA
|
||||
this.platform = platform;
|
||||
var resolution = GetResolution(graphicSettings);
|
||||
|
||||
Window = platform.CreateWindow(new Size(resolution.Width, resolution.Height), graphicSettings.Mode, graphicSettings.UIScale, graphicSettings.BatchSize, graphicSettings.VideoDisplay);
|
||||
Window = platform.CreateWindow(new Size(resolution.Width, resolution.Height),
|
||||
graphicSettings.Mode, graphicSettings.UIScale, graphicSettings.BatchSize,
|
||||
graphicSettings.VideoDisplay, graphicSettings.GLProfile);
|
||||
|
||||
Context = Window.Context;
|
||||
|
||||
TempBufferSize = graphicSettings.BatchSize;
|
||||
@@ -103,8 +115,7 @@ namespace OpenRA
|
||||
font.Dispose();
|
||||
using (new PerfTimer("SpriteFonts"))
|
||||
{
|
||||
if (fontSheetBuilder != null)
|
||||
fontSheetBuilder.Dispose();
|
||||
fontSheetBuilder?.Dispose();
|
||||
fontSheetBuilder = new SheetBuilder(SheetType.BGRA, 512);
|
||||
Fonts = modData.Manifest.Get<Fonts>().FontList.ToDictionary(x => x.Key,
|
||||
x => new SpriteFont(x.Value.Font, modData.DefaultFileSystem.Open(x.Value.Font).ReadAllBytes(),
|
||||
@@ -144,8 +155,7 @@ namespace OpenRA
|
||||
|
||||
if (screenSprite == null || screenSprite.Sheet.Size != surfaceBufferSize)
|
||||
{
|
||||
if (screenBuffer != null)
|
||||
screenBuffer.Dispose();
|
||||
screenBuffer?.Dispose();
|
||||
|
||||
// Render the screen into a frame buffer to simplify reading back screenshots
|
||||
screenBuffer = Context.CreateFrameBuffer(surfaceBufferSize, Color.FromArgb(0xFF, 0, 0, 0));
|
||||
@@ -183,8 +193,7 @@ namespace OpenRA
|
||||
var worldBufferSize = worldViewport.Size.NextPowerOf2();
|
||||
if (worldSprite == null || worldSprite.Sheet.Size != worldBufferSize)
|
||||
{
|
||||
if (worldBuffer != null)
|
||||
worldBuffer.Dispose();
|
||||
worldBuffer?.Dispose();
|
||||
|
||||
// Render the world into a framebuffer at 1:1 scaling to allow the depth buffer to match the artwork at all zoom levels
|
||||
worldBuffer = Context.CreateFrameBuffer(worldBufferSize);
|
||||
@@ -301,6 +310,8 @@ namespace OpenRA
|
||||
public Size NativeResolution { get { return Window.NativeWindowSize; } }
|
||||
public float WindowScale { get { return Window.EffectiveWindowScale; } }
|
||||
public float NativeWindowScale { get { return Window.NativeWindowScale; } }
|
||||
public GLProfile GLProfile { get { return Window.GLProfile; } }
|
||||
public GLProfile[] SupportedGLProfiles { get { return Window.SupportedGLProfiles; } }
|
||||
|
||||
public interface IBatchRenderer { void Flush(); }
|
||||
|
||||
@@ -315,8 +326,7 @@ namespace OpenRA
|
||||
{
|
||||
if (currentBatchRenderer == value)
|
||||
return;
|
||||
if (currentBatchRenderer != null)
|
||||
currentBatchRenderer.Flush();
|
||||
currentBatchRenderer?.Flush();
|
||||
currentBatchRenderer = value;
|
||||
}
|
||||
}
|
||||
@@ -446,8 +456,7 @@ namespace OpenRA
|
||||
{
|
||||
WorldModelRenderer.Dispose();
|
||||
tempBuffer.Dispose();
|
||||
if (fontSheetBuilder != null)
|
||||
fontSheetBuilder.Dispose();
|
||||
fontSheetBuilder?.Dispose();
|
||||
if (Fonts != null)
|
||||
foreach (var font in Fonts.Values)
|
||||
font.Dispose();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user