Compare commits
763 Commits
playtest-2
...
playtest-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36c3e07535 | ||
|
|
58547fda89 | ||
|
|
73da5e7a20 | ||
|
|
7f20d1474d | ||
|
|
ee728be537 | ||
|
|
7b056509ec | ||
|
|
2eb090f153 | ||
|
|
f8abd5d319 | ||
|
|
b3cde077fc | ||
|
|
47fa9e496d | ||
|
|
19a9c70d5c | ||
|
|
94a062b3e7 | ||
|
|
11db40a2b3 | ||
|
|
dec11f4fa0 | ||
|
|
ae394f937b | ||
|
|
3353215b66 | ||
|
|
cd6dfd2185 | ||
|
|
e70b61c4b0 | ||
|
|
2f6bbd906b | ||
|
|
8fe45cb894 | ||
|
|
61df53ea82 | ||
|
|
38c4c10c73 | ||
|
|
1aa57cd5b1 | ||
|
|
b7830d97f2 | ||
|
|
a80924ebb3 | ||
|
|
69e9b94c5d | ||
|
|
45f346c004 | ||
|
|
7fba6a2155 | ||
|
|
235b16d4b3 | ||
|
|
4ef11f2763 | ||
|
|
b3c3c7b414 | ||
|
|
9b629a8f39 | ||
|
|
cefe3d2c8f | ||
|
|
9871abe562 | ||
|
|
14f6601f2b | ||
|
|
1d8b2a9a08 | ||
|
|
5c53172ab0 | ||
|
|
3effa5cec4 | ||
|
|
0208d0cc10 | ||
|
|
759bd044ed | ||
|
|
4cdab30c48 | ||
|
|
98ec28e850 | ||
|
|
ef680dbbfe | ||
|
|
0eaec5d861 | ||
|
|
7014393211 | ||
|
|
841c873276 | ||
|
|
15354f52c1 | ||
|
|
1a947907d3 | ||
|
|
3d4095cffd | ||
|
|
104148378f | ||
|
|
6b567722b8 | ||
|
|
a4595af1e3 | ||
|
|
382c0b5f1d | ||
|
|
e6835cef6e | ||
|
|
3fe808e0fb | ||
|
|
6de90b02d0 | ||
|
|
eefaf23885 | ||
|
|
1bac76fdd1 | ||
|
|
f0ee87f078 | ||
|
|
7fb595a437 | ||
|
|
88704ebac6 | ||
|
|
aa6ddc6a6e | ||
|
|
b70e4de5ee | ||
|
|
3d62b1f9b3 | ||
|
|
4ea6a3c18c | ||
|
|
e734668f56 | ||
|
|
be89ce9efd | ||
|
|
ede72410ff | ||
|
|
3a9ee8d51a | ||
|
|
27beed402f | ||
|
|
e7ce9aa263 | ||
|
|
86a17e9260 | ||
|
|
c6ad768a76 | ||
|
|
5425a5a28e | ||
|
|
b8ec20a010 | ||
|
|
c445a20acf | ||
|
|
7f77e4cf40 | ||
|
|
acf25354b0 | ||
|
|
5ac0625f97 | ||
|
|
fc07391c8c | ||
|
|
f7de5d46be | ||
|
|
7b6b79493c | ||
|
|
a3b186ccbc | ||
|
|
c73ce50a09 | ||
|
|
8fc2caa01a | ||
|
|
bccc0f8f17 | ||
|
|
3ba610b535 | ||
|
|
a3882501b1 | ||
|
|
ec97214c16 | ||
|
|
4be593123d | ||
|
|
73bd80ebc5 | ||
|
|
ebd8a2b193 | ||
|
|
0b446167b6 | ||
|
|
959804b167 | ||
|
|
5256fb7bbd | ||
|
|
205c45198c | ||
|
|
38cb3dea05 | ||
|
|
a6258485c9 | ||
|
|
b99d21b818 | ||
|
|
b9987dcbda | ||
|
|
d4d9959159 | ||
|
|
eab93ccf97 | ||
|
|
503b91d391 | ||
|
|
bd52e56aaf | ||
|
|
e42e766bb6 | ||
|
|
4c64a37e1d | ||
|
|
e2f3989f46 | ||
|
|
65e4019424 | ||
|
|
4490eb3a2a | ||
|
|
d762411b95 | ||
|
|
395eb36cd1 | ||
|
|
fbc18dfb18 | ||
|
|
04fb0f209d | ||
|
|
6ef95405bf | ||
|
|
4993e74748 | ||
|
|
2794b14f52 | ||
|
|
7bf3d5d7f6 | ||
|
|
27cc487575 | ||
|
|
3af3299921 | ||
|
|
41dd7ca428 | ||
|
|
028f7c6546 | ||
|
|
2ad42b6a7e | ||
|
|
9dc330b300 | ||
|
|
b9e03ac009 | ||
|
|
b221787350 | ||
|
|
d24d80f483 | ||
|
|
cd4f54ade3 | ||
|
|
1a73a2578b | ||
|
|
e79680e22c | ||
|
|
f8b75bbed4 | ||
|
|
956dbb762c | ||
|
|
14c608786d | ||
|
|
cfb293a53a | ||
|
|
ec4cf0646f | ||
|
|
5b51261dd3 | ||
|
|
9c88fc154f | ||
|
|
987d0b77ac | ||
|
|
ca01a1f186 | ||
|
|
5d8c9a560a | ||
|
|
3f8c1ad5df | ||
|
|
c269525397 | ||
|
|
77ef13816f | ||
|
|
09f4c6339e | ||
|
|
2251078dde | ||
|
|
365251f845 | ||
|
|
c6f0b3e1a0 | ||
|
|
31206de855 | ||
|
|
fe3534ebcd | ||
|
|
0dd52acd98 | ||
|
|
a36242d524 | ||
|
|
ecc09256d8 | ||
|
|
fe01afe794 | ||
|
|
6f41be209c | ||
|
|
e35e93557c | ||
|
|
30cafcbc25 | ||
|
|
85c54e04d9 | ||
|
|
01a88862b3 | ||
|
|
c1cba4ecc1 | ||
|
|
383840135f | ||
|
|
3aa8b3ae29 | ||
|
|
be761de768 | ||
|
|
49f0e4ebcf | ||
|
|
1a405b17ba | ||
|
|
a9d1d374b8 | ||
|
|
de38313579 | ||
|
|
bf21fc5213 | ||
|
|
13edaefcac | ||
|
|
dd2ae9fe5e | ||
|
|
86f9b8807e | ||
|
|
72a11ec2a5 | ||
|
|
60633dfdf2 | ||
|
|
a6dc9c3b21 | ||
|
|
5b51f2a0fa | ||
|
|
f78c3bef33 | ||
|
|
c8c7629bce | ||
|
|
502c3e2bf5 | ||
|
|
488cec64b8 | ||
|
|
1bf59e885d | ||
|
|
50185d3ccd | ||
|
|
64a3187fd1 | ||
|
|
a1b3fe2bda | ||
|
|
56634564f2 | ||
|
|
fd1aa07f83 | ||
|
|
3ad6a87920 | ||
|
|
3f67feab0e | ||
|
|
36fccbc453 | ||
|
|
ea32c758eb | ||
|
|
99908c4d80 | ||
|
|
2e24c14503 | ||
|
|
2c17780b94 | ||
|
|
763630f547 | ||
|
|
97cdce7448 | ||
|
|
b71b2ad523 | ||
|
|
7ad989fc43 | ||
|
|
4e62531b11 | ||
|
|
2e681ba674 | ||
|
|
917d6b7627 | ||
|
|
261126194c | ||
|
|
8069247726 | ||
|
|
a429a62bf9 | ||
|
|
e049452f22 | ||
|
|
7760c41bd9 | ||
|
|
0899d02377 | ||
|
|
c69df4eedf | ||
|
|
6633483d12 | ||
|
|
55b6084b60 | ||
|
|
c87409ed1a | ||
|
|
bf57eceeec | ||
|
|
ff5b4b15b3 | ||
|
|
6f5d035e79 | ||
|
|
8fcc80b05a | ||
|
|
fa65fef4d1 | ||
|
|
86bfe28ade | ||
|
|
46f6263061 | ||
|
|
373aaee004 | ||
|
|
9e18ec7314 | ||
|
|
cdc426d162 | ||
|
|
29255c8e01 | ||
|
|
506b677527 | ||
|
|
041431d966 | ||
|
|
85c5259361 | ||
|
|
b5ffe17d60 | ||
|
|
9ad0d78cdd | ||
|
|
4d553900cf | ||
|
|
70ffa99203 | ||
|
|
9241c0f8b7 | ||
|
|
c75a866f0d | ||
|
|
ec84b61316 | ||
|
|
2dd1bd2d39 | ||
|
|
8ef8c60a1a | ||
|
|
5338784e45 | ||
|
|
7a7eed4fb7 | ||
|
|
cb670d83b3 | ||
|
|
1a20dd15cb | ||
|
|
2218b1a078 | ||
|
|
ce88d50d9c | ||
|
|
7ebea32be9 | ||
|
|
eb3ab682e8 | ||
|
|
b4c5346346 | ||
|
|
74c390d1d0 | ||
|
|
2f79173044 | ||
|
|
35a3df3736 | ||
|
|
f98907f42e | ||
|
|
811427adc4 | ||
|
|
c4237d6a1a | ||
|
|
6dd42f7ab1 | ||
|
|
62ab6ae6f1 | ||
|
|
da036c5728 | ||
|
|
3d8ab75faf | ||
|
|
9d2935935c | ||
|
|
0f512088d2 | ||
|
|
1a1c6368fc | ||
|
|
1b94378f71 | ||
|
|
f4086e4fe0 | ||
|
|
77ae802b9c | ||
|
|
8f5787a57f | ||
|
|
45a2f5e6c1 | ||
|
|
649971d517 | ||
|
|
450cbeea96 | ||
|
|
e09d502ce1 | ||
|
|
bd67bd24c0 | ||
|
|
f249554b4f | ||
|
|
53dabfeb49 | ||
|
|
3a94312fcd | ||
|
|
616ee6cce4 | ||
|
|
6a97502e09 | ||
|
|
c48ddbdfa5 | ||
|
|
d21992130a | ||
|
|
4994716cf7 | ||
|
|
65a2410738 | ||
|
|
8fea476a0d | ||
|
|
ed67cea852 | ||
|
|
c83dda4ce7 | ||
|
|
4c707ad6a6 | ||
|
|
02d57c5fee | ||
|
|
2043576857 | ||
|
|
15b77f8d91 | ||
|
|
b898b5001a | ||
|
|
ea05221180 | ||
|
|
8a38c6ef23 | ||
|
|
43d100246f | ||
|
|
73134b0417 | ||
|
|
d5622ae1ca | ||
|
|
3ed18f51e3 | ||
|
|
855042894c | ||
|
|
5ec3ad0957 | ||
|
|
e215c5019e | ||
|
|
6e59fe3bc2 | ||
|
|
456d32f3eb | ||
|
|
68487d1197 | ||
|
|
cea2658f31 | ||
|
|
00cd7a8da5 | ||
|
|
ede6d5a57d | ||
|
|
9c729c77bc | ||
|
|
7c901c27f1 | ||
|
|
8ce460ad8c | ||
|
|
56b6aabbb8 | ||
|
|
2501a93b87 | ||
|
|
5a596d27c9 | ||
|
|
6711af63eb | ||
|
|
be290cfabd | ||
|
|
ca1448c7ba | ||
|
|
4cbc2ee6f3 | ||
|
|
5030a2257e | ||
|
|
586dd6541b | ||
|
|
9c61f524f0 | ||
|
|
10fb487abf | ||
|
|
25968ee66f | ||
|
|
7b12cf887d | ||
|
|
01c1e08bd8 | ||
|
|
3e5829355d | ||
|
|
48df21c4a5 | ||
|
|
8654bcc244 | ||
|
|
dddc5dc80f | ||
|
|
3e3d98c735 | ||
|
|
f16f9b8ae3 | ||
|
|
29ec16f15d | ||
|
|
1e94ded9dd | ||
|
|
edd9e39dcd | ||
|
|
cfe1a8cd02 | ||
|
|
e6f73dc68f | ||
|
|
c70efd4a14 | ||
|
|
b7c3bc4e25 | ||
|
|
471d7ae40d | ||
|
|
a71a5cc71d | ||
|
|
713cdaef5d | ||
|
|
f4502e9aa7 | ||
|
|
a5d7218dee | ||
|
|
773a619d2d | ||
|
|
20a25c2f5e | ||
|
|
070c54d069 | ||
|
|
2836cec0a3 | ||
|
|
f35f6c0813 | ||
|
|
ecdfcda43e | ||
|
|
3efce265a8 | ||
|
|
9d45528496 | ||
|
|
8bec765925 | ||
|
|
fd9cd78810 | ||
|
|
bb600620a9 | ||
|
|
c762453607 | ||
|
|
a7620c97f0 | ||
|
|
7f5f2eac6f | ||
|
|
d328b9b7d8 | ||
|
|
bb790b83e6 | ||
|
|
e0e1f56af5 | ||
|
|
7537daf74e | ||
|
|
99edc71c54 | ||
|
|
917c6884ed | ||
|
|
786a0eb07f | ||
|
|
6c02e3f2b7 | ||
|
|
8d7eb0bc47 | ||
|
|
d602ec6485 | ||
|
|
18c371d702 | ||
|
|
9dabc9d672 | ||
|
|
8b7a71685c | ||
|
|
ded92f394c | ||
|
|
6f95080aa4 | ||
|
|
a017018bee | ||
|
|
d49c98ce18 | ||
|
|
3092927a7f | ||
|
|
3ce6cac9f3 | ||
|
|
fdac023a8d | ||
|
|
f4dbf55510 | ||
|
|
e65ffc8be2 | ||
|
|
ef878c6aeb | ||
|
|
3aa91a08cc | ||
|
|
a8d27257eb | ||
|
|
dadfc0b09c | ||
|
|
0377c8ae20 | ||
|
|
eac9286b78 | ||
|
|
ba50fbba18 | ||
|
|
4c78a05081 | ||
|
|
8914227811 | ||
|
|
a3636c69d9 | ||
|
|
faf2634e3d | ||
|
|
d2f0e5ac2d | ||
|
|
1caf982c1f | ||
|
|
77feb3f76d | ||
|
|
4fe7ffed85 | ||
|
|
1376ad674e | ||
|
|
47634b25f9 | ||
|
|
4dba9f5b88 | ||
|
|
7afd219742 | ||
|
|
c9c7c5744c | ||
|
|
bfe540cd8b | ||
|
|
025d5c8d05 | ||
|
|
817449c018 | ||
|
|
636a9a74a1 | ||
|
|
6a750d7a65 | ||
|
|
b8326bfead | ||
|
|
5f9a67ed87 | ||
|
|
29c423772f | ||
|
|
6e1c1eaa94 | ||
|
|
77a7453347 | ||
|
|
88c94ee841 | ||
|
|
aadfcdac66 | ||
|
|
cfeeb6563e | ||
|
|
cba7c60f6f | ||
|
|
cfac996438 | ||
|
|
315cc966f4 | ||
|
|
5540eb1f2f | ||
|
|
3a9abda441 | ||
|
|
7da7764a65 | ||
|
|
9bdedda43a | ||
|
|
69b4b541d2 | ||
|
|
bee3f33f5f | ||
|
|
cdedd1c7ac | ||
|
|
7675c87593 | ||
|
|
80ce6d9988 | ||
|
|
ebb982789e | ||
|
|
b27289106d | ||
|
|
d4340fa799 | ||
|
|
db2d432c39 | ||
|
|
95dbcc1273 | ||
|
|
5471a3e729 | ||
|
|
d967c564a2 | ||
|
|
4896a90b8d | ||
|
|
19b2c33514 | ||
|
|
10740de6e9 | ||
|
|
f2b5040d30 | ||
|
|
c8b2a7dc04 | ||
|
|
7ccf41be77 | ||
|
|
97e0f17e15 | ||
|
|
a8c6d12c43 | ||
|
|
0e3de71036 | ||
|
|
ef43d8a86f | ||
|
|
75d4062698 | ||
|
|
249e813444 | ||
|
|
e3e5dc1b49 | ||
|
|
f425cbe0a4 | ||
|
|
f96c2710cd | ||
|
|
4bca0c4c24 | ||
|
|
49e1af7f3f | ||
|
|
0727f7e9ff | ||
|
|
5fdb828b60 | ||
|
|
98170b0452 | ||
|
|
27345c1f6a | ||
|
|
24c293e821 | ||
|
|
2366490fa0 | ||
|
|
f48ba0ff86 | ||
|
|
80bf36e1a8 | ||
|
|
fb74c8351f | ||
|
|
8533debc44 | ||
|
|
8ec3d5ddb8 | ||
|
|
0ce3c113e1 | ||
|
|
7ca9679b34 | ||
|
|
edeb980f5b | ||
|
|
5b16bb952f | ||
|
|
a8288a38f8 | ||
|
|
d7323e07cc | ||
|
|
2102fad2b5 | ||
|
|
a4b0bf5c52 | ||
|
|
049096efd7 | ||
|
|
dfe2076826 | ||
|
|
f84699454f | ||
|
|
8c387c3bcf | ||
|
|
4e45747b41 | ||
|
|
d64a9e6afc | ||
|
|
370ee0841f | ||
|
|
18c6fe09db | ||
|
|
d170262e09 | ||
|
|
18f6317560 | ||
|
|
178e7afd30 | ||
|
|
4aee91cd47 | ||
|
|
cf4c16b6bb | ||
|
|
b55ae1dcf0 | ||
|
|
a1347a7f32 | ||
|
|
64afe4cfde | ||
|
|
e93183f0eb | ||
|
|
36af2107d6 | ||
|
|
d8f53a9ccd | ||
|
|
fa8ab19dfd | ||
|
|
9aaf800bca | ||
|
|
5026dfe5d3 | ||
|
|
6f790938d0 | ||
|
|
2b391d5724 | ||
|
|
d7c2c6afc4 | ||
|
|
a8e6cd2604 | ||
|
|
69daa7e7cc | ||
|
|
a0e028ee0e | ||
|
|
cc17b7419a | ||
|
|
1d6cd81690 | ||
|
|
2b96c2ed78 | ||
|
|
c9b4568117 | ||
|
|
b86d632c8c | ||
|
|
205c947670 | ||
|
|
030c942f73 | ||
|
|
da02620c4e | ||
|
|
2cf102d81a | ||
|
|
95278b1f07 | ||
|
|
faad941714 | ||
|
|
7549abc516 | ||
|
|
1d8a50cdab | ||
|
|
0018bf3063 | ||
|
|
7160c8a1a9 | ||
|
|
7ed769421e | ||
|
|
4ae92a5c22 | ||
|
|
593e86325b | ||
|
|
eea14ec922 | ||
|
|
eb3b68e24a | ||
|
|
1d402d4f8b | ||
|
|
800f6adc21 | ||
|
|
d17e414648 | ||
|
|
44cfa793df | ||
|
|
48a018d994 | ||
|
|
3af0b1a7a0 | ||
|
|
3b4904775a | ||
|
|
94942cb140 | ||
|
|
f4c2b36778 | ||
|
|
c3ece99796 | ||
|
|
be370cb855 | ||
|
|
9aa861eca9 | ||
|
|
46132ed5c6 | ||
|
|
1aebf9857c | ||
|
|
8ca43e3d6b | ||
|
|
1f6b9a7638 | ||
|
|
32df83d3c4 | ||
|
|
28e1f391e0 | ||
|
|
85e60ef77f | ||
|
|
03c3a5f310 | ||
|
|
ec354f89cd | ||
|
|
83afcc3448 | ||
|
|
761a4f29ab | ||
|
|
ddfed13db4 | ||
|
|
a1ef581749 | ||
|
|
0c73baab3e | ||
|
|
89c143ae85 | ||
|
|
d99a472456 | ||
|
|
42c4c7b7eb | ||
|
|
e04ae9aa2c | ||
|
|
83d522d945 | ||
|
|
0e3bfcfb35 | ||
|
|
983f9c4cde | ||
|
|
25e88008ef | ||
|
|
c759a68492 | ||
|
|
8c4afa414a | ||
|
|
c6fe1639db | ||
|
|
7459050af9 | ||
|
|
08aaa998aa | ||
|
|
dbcfb0c92e | ||
|
|
c611b5aeeb | ||
|
|
c0b8bb3fcf | ||
|
|
11a990e352 | ||
|
|
7721d0b328 | ||
|
|
8027bed6b2 | ||
|
|
5ddf28fa81 | ||
|
|
0f95febefa | ||
|
|
ed598d2ab4 | ||
|
|
17f7aac35c | ||
|
|
71f2026b32 | ||
|
|
049ed086f9 | ||
|
|
646a6aa1e8 | ||
|
|
3575e82078 | ||
|
|
d588854cc5 | ||
|
|
c46d1944d9 | ||
|
|
a81749e102 | ||
|
|
ffee45cd76 | ||
|
|
1e4640dc0b | ||
|
|
458c913264 | ||
|
|
b3b2efa781 | ||
|
|
2a6bb0678e | ||
|
|
4f00d62237 | ||
|
|
19000219af | ||
|
|
1180c5ddaa | ||
|
|
f5e38a4e2c | ||
|
|
1de767d9bf | ||
|
|
3f72076e1a | ||
|
|
e54cf9acc3 | ||
|
|
0648fc9f31 | ||
|
|
e3212d1e64 | ||
|
|
0f6dda3f5f | ||
|
|
0161d68237 | ||
|
|
b68c935807 | ||
|
|
23382dc508 | ||
|
|
b78736acb7 | ||
|
|
48a071c756 | ||
|
|
1dfba51665 | ||
|
|
43664a12b5 | ||
|
|
9cd81a6310 | ||
|
|
024887b268 | ||
|
|
898ef8fe50 | ||
|
|
f4f27f8980 | ||
|
|
2bfc7cdd46 | ||
|
|
1649f3aa60 | ||
|
|
3ce8026337 | ||
|
|
6774c9ce01 | ||
|
|
fd0c34bf61 | ||
|
|
2980decc93 | ||
|
|
855f1816f4 | ||
|
|
4526344eed | ||
|
|
c63620e979 | ||
|
|
df9c07da41 | ||
|
|
96e98a24ae | ||
|
|
e9e3e9c22e | ||
|
|
6eb7a98cd2 | ||
|
|
49919e3d73 | ||
|
|
3c1e7896a5 | ||
|
|
0ce4d91dd6 | ||
|
|
b3dbeb13ca | ||
|
|
15c636525d | ||
|
|
918690f9b2 | ||
|
|
3c0b95cf6e | ||
|
|
3cf9d47b39 | ||
|
|
4a2988e676 | ||
|
|
2a3a27aa65 | ||
|
|
9607c09516 | ||
|
|
7997a24559 | ||
|
|
2f66f83051 | ||
|
|
28ffcfea3c | ||
|
|
3b01da737c | ||
|
|
5d58374962 | ||
|
|
df94765eac | ||
|
|
c9ff362ea2 | ||
|
|
5cfb5aaf2d | ||
|
|
93f385e9fc | ||
|
|
d1a4133752 | ||
|
|
69251d508b | ||
|
|
1bd1a185f5 | ||
|
|
0525408664 | ||
|
|
9a5b5d9b6f | ||
|
|
8d4ffee32a | ||
|
|
1a95aeda19 | ||
|
|
fd6b2c0107 | ||
|
|
c61cd37bec | ||
|
|
62c76b8ae9 | ||
|
|
f51323daa3 | ||
|
|
c7d3c3ec73 | ||
|
|
72236b66f6 | ||
|
|
d6d8cf05b0 | ||
|
|
eed9af88f5 | ||
|
|
b1c63a4752 | ||
|
|
a7c2f3e7e3 | ||
|
|
8ae2a65d9d | ||
|
|
a445049e3d | ||
|
|
49d5283ce1 | ||
|
|
739d0c0bb2 | ||
|
|
cb36dfa532 | ||
|
|
a29360f313 | ||
|
|
1e0148e092 | ||
|
|
42bf232b37 | ||
|
|
13a06c6e8d | ||
|
|
988353e431 | ||
|
|
1e1109034f | ||
|
|
43880ea7b8 | ||
|
|
8a825f2999 | ||
|
|
2ccba3768b | ||
|
|
dc72b8080c | ||
|
|
80b69af30c | ||
|
|
120d7f9976 | ||
|
|
0103b7ca87 | ||
|
|
90a4fe7ca1 | ||
|
|
c75e64a952 | ||
|
|
951ee2210c | ||
|
|
28e28bf34c | ||
|
|
cba0fc3475 | ||
|
|
01d631b228 | ||
|
|
f7983692ae | ||
|
|
287290bbdf | ||
|
|
a4ef199b75 | ||
|
|
c4bfb052d9 | ||
|
|
98357cf2a9 | ||
|
|
5581688781 | ||
|
|
7704ae655e | ||
|
|
834a40b18d | ||
|
|
4ce2e82ff0 | ||
|
|
98b1468801 | ||
|
|
406f482a19 | ||
|
|
bac8d3233a | ||
|
|
e9a420a9e5 | ||
|
|
95d3d61ffa | ||
|
|
6c3ff0dc02 | ||
|
|
cab6a96b16 | ||
|
|
f3f2621eeb | ||
|
|
eb5fb5abba | ||
|
|
c848b30e9e | ||
|
|
daee217431 | ||
|
|
9fc0970cfc | ||
|
|
7156c2c09d | ||
|
|
dd9d5450bc | ||
|
|
6840177776 | ||
|
|
d78b29b545 | ||
|
|
b66605033a | ||
|
|
b329bf2642 | ||
|
|
02c371f68a | ||
|
|
6907929cb4 | ||
|
|
e07779942e | ||
|
|
a942ca8f57 | ||
|
|
7f0c7cee0d | ||
|
|
3bf217aaf7 | ||
|
|
6d1ca72a80 | ||
|
|
944dfeb476 | ||
|
|
ab5d2766f6 | ||
|
|
eb21c4bddd | ||
|
|
5435e34b9a | ||
|
|
8f26b4e92b | ||
|
|
62a006ed3e | ||
|
|
cf386c6149 | ||
|
|
84d6fa1540 | ||
|
|
2dad2de0ff | ||
|
|
34a44acf85 | ||
|
|
b937bf43f7 | ||
|
|
f4d4a7eed4 | ||
|
|
4e493f265f | ||
|
|
6e8dd5058e | ||
|
|
ad2c46ea73 | ||
|
|
49f42401e6 | ||
|
|
de7a0c9861 | ||
|
|
0209c1c848 | ||
|
|
64dfc38c2e | ||
|
|
d1ab421240 | ||
|
|
22d7031819 | ||
|
|
d0d8062dbf | ||
|
|
dd709a2679 | ||
|
|
86cff9a774 | ||
|
|
0c67ca3321 | ||
|
|
f744a429a4 | ||
|
|
11d6dc2d3a | ||
|
|
6e2418d940 | ||
|
|
1b28cb9f60 | ||
|
|
ab6d18eab6 | ||
|
|
c40ec62511 | ||
|
|
94a386eda8 | ||
|
|
88a3656f0a | ||
|
|
3c4a75e4b6 | ||
|
|
1d1802a163 | ||
|
|
c4d2fdbd83 | ||
|
|
baa4b61d3f | ||
|
|
b388c6186a | ||
|
|
97bbfd34fd | ||
|
|
6945ae1747 | ||
|
|
f4499c1e84 | ||
|
|
20efd1fc29 | ||
|
|
d61939f59f | ||
|
|
d1328212c6 | ||
|
|
d949e17b88 | ||
|
|
99dac05fd7 | ||
|
|
fdad5e2c48 | ||
|
|
1a54b36e2b | ||
|
|
55ea54ff2d | ||
|
|
59d88199cf | ||
|
|
0a150bcc75 | ||
|
|
879ab2ac1f | ||
|
|
68607872bd | ||
|
|
9fdb7dc51b | ||
|
|
57fb551545 | ||
|
|
8ee742d66a | ||
|
|
26be2a3e99 | ||
|
|
e847f49256 | ||
|
|
514bd12885 | ||
|
|
bdfa8a361b | ||
|
|
dc61c98098 | ||
|
|
4c222e5f2b | ||
|
|
cd9fc96c37 | ||
|
|
ef0b0847bc | ||
|
|
97306f2bcd | ||
|
|
d4e9e0e069 | ||
|
|
8403adba37 | ||
|
|
9a25b33beb | ||
|
|
94fa24088b | ||
|
|
3bdd35fd2d | ||
|
|
4903912e3c | ||
|
|
aaaf66a6a3 | ||
|
|
0573f52a37 |
16
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
16
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
Thank you for your contribution to OpenRA!
|
||||
|
||||
Please be aware that we do not have enough project maintainers to match the rate of contributions, so it may take several days before somebody is able to respond to your Pull Request.
|
||||
|
||||
You can help speed up the review process by following a few steps:
|
||||
|
||||
* Make sure that you have read and understand the OpenRA Coding Standard (see https://github.com/OpenRA/OpenRA/wiki/Coding-Standard).
|
||||
* Write quality commit messages (see https://chris.beams.io/posts/git-commit/).
|
||||
* Only commit changes that directly relate to your Pull Request. Use your Git interface to unstage any unrelated changes to project files, line endings, whitespace, or other files.
|
||||
* Review the code diff view below to double check that your changes satisfy the above three points.
|
||||
* Use the `make test` and `make check` commands to check for (and fix!) any issues that are reported by our automated tests.
|
||||
* If you are changing shared mod or engine code, make sure that you have tested your changes in all four default mods.
|
||||
* Respond to review comments as soon as you reasonably can. Reviewers will usually prioritize Pull Requests that are still fresh in their minds. Make sure to leave a comment when you push new changes, otherwise GitHub does not automatically notify reviewers!
|
||||
* Leave a polite comment asking for reviews if a week or more has passed without feedback.
|
||||
|
||||
If you need any help you can ask in the #openra IRC channel on freenode (most active during European evenings).
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -62,7 +62,9 @@ OpenRA.Launcher.Mac/OpenRA.xcodeproj/*.mode1v3
|
||||
|
||||
# auto-generated documentation
|
||||
DOCUMENTATION.md
|
||||
WEAPONS.md
|
||||
Lua-API.md
|
||||
Settings.md
|
||||
*.html
|
||||
openra.6
|
||||
|
||||
@@ -80,3 +82,6 @@ StyleCopViolations.xml
|
||||
|
||||
# Support directory
|
||||
/Support
|
||||
|
||||
# IntelliJ files
|
||||
.idea
|
||||
|
||||
@@ -78,6 +78,7 @@ deploy:
|
||||
file:
|
||||
- build/OpenRA-${TRAVIS_TAG}.exe
|
||||
- build/OpenRA-${TRAVIS_TAG}.dmg
|
||||
- build/OpenRA-${TRAVIS_TAG}-source.tar.bz2
|
||||
- build/openra_${DOTVERSION}_all.deb
|
||||
skip_cleanup: true
|
||||
on:
|
||||
|
||||
9
AUTHORS
9
AUTHORS
@@ -69,12 +69,14 @@ Also thanks to:
|
||||
* Ian T. Jacobsen (Smilex)
|
||||
* Imago
|
||||
* Iran
|
||||
* Ishan Bhargava (ishantheperson)
|
||||
* Jacob Dufault (jacobdufault)
|
||||
* James Dunne (jsd)
|
||||
* James Gilbert (DSUK)
|
||||
* Jan-Willem Buurlage (jwbuurlage)
|
||||
* Jason (atlimit8)
|
||||
* Jeff Harris (jeff_1amstudios)
|
||||
* Jefri Sevkin (Arular)
|
||||
* Jes
|
||||
* Joakim Lindberg (booom3)
|
||||
* John Turner (whinis)
|
||||
@@ -97,6 +99,7 @@ Also thanks to:
|
||||
* Max621
|
||||
* Max Ugrumov (katzsmile)
|
||||
* Michael Rätzel
|
||||
* Michael Silber (frühstück)
|
||||
* Michael Sztolcman (s1w_)
|
||||
* Mustafa Alperen Seki (MustaphaTR)
|
||||
* Neil Shivkar (havok13888)
|
||||
@@ -108,8 +111,9 @@ Also thanks to:
|
||||
* Paul Dovydaitis (pdovy)
|
||||
* Pavlos Touboulidis (pav)
|
||||
* Pedro Ferreira Ramos (bateramos)
|
||||
* Peter Amrehn (jongleur1983)
|
||||
* Pizzaoverhead
|
||||
* Piët Delport (pjdelport)
|
||||
* Pi Delport (pjdelport)
|
||||
* Psydev
|
||||
* Raphael Vogt (TheRaffy, Yellow)
|
||||
* Raymond Bedrossian (Squiggles211)
|
||||
@@ -168,9 +172,6 @@ under the MIT license.
|
||||
Using ICSharpCode.SharpZipLib initially by Mike
|
||||
Krueger and distributed under the GNU GPL terms.
|
||||
|
||||
Using SmartIrc4Net developed by Mirco Bauer
|
||||
distributed under the LGPL version 2.1 or later.
|
||||
|
||||
Using rix0rrr.BeaconLib developed by Rico Huijbers
|
||||
distributed under MIT License.
|
||||
|
||||
|
||||
76
CODE_OF_CONDUCT.md
Normal file
76
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by private-messaging a project team member (users with a + in front
|
||||
of their name) via our IRC channel (#openra on freenode –
|
||||
[webchat](http://webchat.freenode.net/?channels=openra)). All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
18
INSTALL.md
18
INSTALL.md
@@ -14,7 +14,7 @@ Windows
|
||||
* [OpenAL](http://kcat.strangesoft.net/openal.html) (included)
|
||||
* [liblua 5.1](http://luabinaries.sourceforge.net/download.html) (included)
|
||||
|
||||
You need to fetch the thirdparty dependencies using [NuGet](http://www.nuget.org) and place them at the appropriate places by typing `make dependencies` in a command terminal.
|
||||
You need to fetch the thirdparty dependencies and place them at the appropriate places by typing `make dependencies` in a command terminal.
|
||||
|
||||
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.
|
||||
|
||||
@@ -23,7 +23,7 @@ Run the game with `OpenRA.Game.exe Game.Mod=ra` for Red Alert or `OpenRA.Game.ex
|
||||
Linux
|
||||
=====
|
||||
|
||||
Use `make dependencies` to map the native libraries to your system, fetch the remaining CLI dependencies using [NuGet](http://www.nuget.org) and place them at the appropriate places.
|
||||
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 all` from the command line.
|
||||
|
||||
@@ -34,7 +34,6 @@ Type `sudo make install` for system wide installation. Run `make install-linux-s
|
||||
Debian/Ubuntu
|
||||
-------------
|
||||
|
||||
* nuget
|
||||
* mono-devel
|
||||
* libfreetype6
|
||||
* libopenal1
|
||||
@@ -47,22 +46,15 @@ Debian/Ubuntu
|
||||
openSUSE
|
||||
--------
|
||||
|
||||
* mono-devel
|
||||
* nuget
|
||||
* openal
|
||||
* freetype2
|
||||
* SDL2
|
||||
* lua51
|
||||
* xdg-utils
|
||||
* zenity
|
||||
* curl
|
||||
```
|
||||
sudo zypper in mono-devel openal-soft freetype2 SDL2 lua51 xdg-utils zenity curl
|
||||
```
|
||||
|
||||
Gentoo
|
||||
------
|
||||
|
||||
* dev-lang/mono
|
||||
* dev-dotnet/libgdiplus
|
||||
* dev-dotnet/nuget
|
||||
* media-libs/freetype:2
|
||||
* media-libs/libsdl2
|
||||
* media-libs/openal
|
||||
|
||||
19
Makefile
19
Makefile
@@ -14,9 +14,6 @@
|
||||
# to check the official mod dlls for StyleCop violations, run:
|
||||
# make check
|
||||
#
|
||||
# to generate documentation aimed at modders, run:
|
||||
# make docs
|
||||
#
|
||||
# to install, run:
|
||||
# make [prefix=/foo] [bindir=/bar/bin] install
|
||||
#
|
||||
@@ -42,9 +39,9 @@
|
||||
#
|
||||
SDK ?=
|
||||
CSC = mcs $(SDK)
|
||||
CSFLAGS = -nologo -warn:4 -codepage:utf8 -unsafe -warnaserror
|
||||
CSFLAGS = -nologo -warn:4 -codepage:utf8 -langversion:5 -unsafe -warnaserror
|
||||
DEFINE = TRACE
|
||||
COMMON_LIBS = System.dll System.Core.dll System.Data.dll System.Data.DataSetExtensions.dll System.Drawing.dll System.Xml.dll thirdparty/download/ICSharpCode.SharpZipLib.dll thirdparty/download/FuzzyLogicLibrary.dll thirdparty/download/MaxMind.Db.dll thirdparty/download/Eluant.dll thirdparty/download/SmarIrc4net.dll thirdparty/download/rix0rrr.BeaconLib.dll
|
||||
COMMON_LIBS = System.dll System.Core.dll System.Data.dll System.Data.DataSetExtensions.dll System.Drawing.dll System.Xml.dll thirdparty/download/ICSharpCode.SharpZipLib.dll thirdparty/download/FuzzyLogicLibrary.dll thirdparty/download/MaxMind.Db.dll thirdparty/download/Eluant.dll thirdparty/download/rix0rrr.BeaconLib.dll
|
||||
NUNIT_LIBS_PATH :=
|
||||
NUNIT_LIBS := $(NUNIT_LIBS_PATH)nunit.framework.dll
|
||||
|
||||
@@ -360,7 +357,6 @@ install-engine:
|
||||
@$(CP) SharpFont.dll.config "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) Open.Nat.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) MaxMind.Db.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) SmarIrc4net.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) rix0rrr.BeaconLib.dll "$(DATA_INSTALL_DIR)"
|
||||
|
||||
install-common-mod-files:
|
||||
@@ -414,7 +410,13 @@ install-linux-mime:
|
||||
|
||||
install-linux-appdata:
|
||||
@$(INSTALL_DIR) "$(DESTDIR)$(datadir)/appdata/"
|
||||
@$(INSTALL_DATA) packaging/linux/openra.appdata.xml "$(DESTDIR)$(datadir)/appdata/"
|
||||
@sed 's/{MOD}/ra/g' packaging/linux/openra.appdata.xml.in | sed 's/{MOD_NAME}/Red Alert/g' | sed 's/{SCREENSHOT_RA}/ type="default"/g' | sed 's/{SCREENSHOT_CNC}//g' | sed 's/{SCREENSHOT_D2K}//g'> packaging/linux/openra-ra.appdata.xml
|
||||
@$(INSTALL_DATA) packaging/linux/openra-ra.appdata.xml "$(DESTDIR)$(datadir)/appdata/"
|
||||
@sed 's/{MOD}/cnc/g' packaging/linux/openra.appdata.xml.in | sed 's/{MOD_NAME}/Tiberian Dawn/g' | sed 's/{SCREENSHOT_RA}//g' | sed 's/{SCREENSHOT_CNC}/ type="default"/g' | sed 's/{SCREENSHOT_D2K}//g'> packaging/linux/openra-cnc.appdata.xml
|
||||
@$(INSTALL_DATA) packaging/linux/openra-cnc.appdata.xml "$(DESTDIR)$(datadir)/appdata/"
|
||||
@sed 's/{MOD}/d2k/g' packaging/linux/openra.appdata.xml.in | sed 's/{MOD_NAME}/Dune 2000/g' | sed 's/{SCREENSHOT_RA}//g' | sed 's/{SCREENSHOT_CNC}//g' | sed 's/{SCREENSHOT_D2K}/ type="default"/g'> packaging/linux/openra-d2k.appdata.xml
|
||||
@$(INSTALL_DATA) packaging/linux/openra-d2k.appdata.xml "$(DESTDIR)$(datadir)/appdata/"
|
||||
@-$(RM) packaging/linux/openra-ra.appdata.xml packaging/linux/openra-cnc.appdata.xml packaging/linux/openra-d2k.appdata.xml
|
||||
|
||||
install-man-page: man-page
|
||||
@$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man6/"
|
||||
@@ -495,9 +497,6 @@ help:
|
||||
@echo 'to check the official mods for erroneous yaml files, run:'
|
||||
@echo ' make test'
|
||||
@echo
|
||||
@echo 'to generate documentation aimed at modders, run:'
|
||||
@echo ' make docs'
|
||||
@echo
|
||||
@echo 'to install, run:'
|
||||
@echo ' make [prefix=/foo] [bindir=/bar/bin] install'
|
||||
@echo
|
||||
|
||||
@@ -152,7 +152,6 @@ namespace OpenRA.Activities
|
||||
set { NextActivity = value; }
|
||||
}
|
||||
|
||||
public bool IsIdle { get; protected set; }
|
||||
public bool IsInterruptible { get; protected set; }
|
||||
public bool IsCanceled { get { return State == ActivityState.Canceled; } }
|
||||
|
||||
|
||||
@@ -28,8 +28,9 @@ namespace OpenRA
|
||||
internal struct SyncHash
|
||||
{
|
||||
public readonly ISync Trait;
|
||||
public readonly int Hash;
|
||||
public SyncHash(ISync trait, int hash) { Trait = trait; Hash = hash; }
|
||||
readonly Func<object, int> hashFunction;
|
||||
public SyncHash(ISync trait) { Trait = trait; hashFunction = Sync.GetHashFunction(trait); }
|
||||
public int Hash() { return hashFunction(Trait); }
|
||||
}
|
||||
|
||||
public readonly ActorInfo Info;
|
||||
@@ -47,13 +48,11 @@ namespace OpenRA
|
||||
|
||||
public int Generation;
|
||||
|
||||
public Rectangle Bounds { get; private set; }
|
||||
public Rectangle VisualBounds { get; private set; }
|
||||
public IEffectiveOwner EffectiveOwner { get; private set; }
|
||||
public IOccupySpace OccupiesSpace { get; private set; }
|
||||
public ITargetable[] Targetables { get; private set; }
|
||||
|
||||
public bool IsIdle { get { return CurrentActivity == null || CurrentActivity.IsIdle; } }
|
||||
public bool IsIdle { get { return CurrentActivity == null; } }
|
||||
public bool IsDead { get { return Disposed || (health != null && health.IsDead); } }
|
||||
|
||||
public CPos Location { get { return OccupiesSpace.TopLeft; } }
|
||||
@@ -69,13 +68,13 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<SyncHash> SyncHashes { get; private set; }
|
||||
internal SyncHash[] SyncHashes { get; private set; }
|
||||
|
||||
readonly IFacing facing;
|
||||
readonly IHealth health;
|
||||
readonly IRenderModifier[] renderModifiers;
|
||||
readonly IRender[] renders;
|
||||
readonly IDisable[] disables;
|
||||
readonly IMouseBounds[] mouseBounds;
|
||||
readonly IVisibilityModifier[] visibilityModifiers;
|
||||
readonly IDefaultVisibility defaultVisibility;
|
||||
|
||||
@@ -110,51 +109,17 @@ namespace OpenRA
|
||||
// PERF: Cache all these traits as soon as the actor is created. This is a fairly cheap one-off cost per
|
||||
// actor that allows us to provide some fast implementations of commonly used methods that are relied on by
|
||||
// performance-sensitive parts of the core game engine, such as pathfinding, visibility and rendering.
|
||||
Bounds = DetermineBounds();
|
||||
VisualBounds = DetermineVisualBounds();
|
||||
EffectiveOwner = TraitOrDefault<IEffectiveOwner>();
|
||||
facing = TraitOrDefault<IFacing>();
|
||||
health = TraitOrDefault<IHealth>();
|
||||
renderModifiers = TraitsImplementing<IRenderModifier>().ToArray();
|
||||
renders = TraitsImplementing<IRender>().ToArray();
|
||||
disables = TraitsImplementing<IDisable>().ToArray();
|
||||
mouseBounds = TraitsImplementing<IMouseBounds>().ToArray();
|
||||
visibilityModifiers = TraitsImplementing<IVisibilityModifier>().ToArray();
|
||||
defaultVisibility = Trait<IDefaultVisibility>();
|
||||
Targetables = TraitsImplementing<ITargetable>().ToArray();
|
||||
|
||||
SyncHashes =
|
||||
TraitsImplementing<ISync>()
|
||||
.Select(sync => Pair.New(sync, Sync.GetHashFunction(sync)))
|
||||
.ToArray()
|
||||
.Select(pair => new SyncHash(pair.First, pair.Second(pair.First)));
|
||||
}
|
||||
|
||||
Rectangle DetermineBounds()
|
||||
{
|
||||
var si = Info.TraitInfoOrDefault<SelectableInfo>();
|
||||
var size = (si != null && si.Bounds != null) ? new int2(si.Bounds[0], si.Bounds[1]) :
|
||||
TraitsImplementing<IAutoSelectionSize>().Select(x => x.SelectionSize(this)).FirstOrDefault();
|
||||
|
||||
var offset = -size / 2;
|
||||
if (si != null && si.Bounds != null && si.Bounds.Length > 2)
|
||||
offset += new int2(si.Bounds[2], si.Bounds[3]);
|
||||
|
||||
return new Rectangle(offset.X, offset.Y, size.X, size.Y);
|
||||
}
|
||||
|
||||
Rectangle DetermineVisualBounds()
|
||||
{
|
||||
var sd = Info.TraitInfoOrDefault<ISelectionDecorationsInfo>();
|
||||
if (sd == null || sd.SelectionBoxBounds == null)
|
||||
return Bounds;
|
||||
|
||||
var size = new int2(sd.SelectionBoxBounds[0], sd.SelectionBoxBounds[1]);
|
||||
|
||||
var offset = -size / 2;
|
||||
if (sd.SelectionBoxBounds.Length > 2)
|
||||
offset += new int2(sd.SelectionBoxBounds[2], sd.SelectionBoxBounds[3]);
|
||||
|
||||
return new Rectangle(offset.X, offset.Y, size.X, size.Y);
|
||||
SyncHashes = TraitsImplementing<ISync>().Select(sync => new SyncHash(sync)).ToArray();
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
@@ -190,6 +155,35 @@ namespace OpenRA
|
||||
yield return renderable;
|
||||
}
|
||||
|
||||
public IEnumerable<Rectangle> ScreenBounds(WorldRenderer wr)
|
||||
{
|
||||
var bounds = Bounds(wr);
|
||||
foreach (var modifier in renderModifiers)
|
||||
bounds = modifier.ModifyScreenBounds(this, wr, bounds);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
IEnumerable<Rectangle> Bounds(WorldRenderer wr)
|
||||
{
|
||||
// PERF: Avoid LINQ. See comments for Renderables
|
||||
foreach (var render in renders)
|
||||
foreach (var r in render.ScreenBounds(this, wr))
|
||||
if (!r.IsEmpty)
|
||||
yield return r;
|
||||
}
|
||||
|
||||
public Rectangle MouseBounds(WorldRenderer wr)
|
||||
{
|
||||
foreach (var mb in mouseBounds)
|
||||
{
|
||||
var bounds = mb.MouseoverBounds(this, wr);
|
||||
if (!bounds.IsEmpty)
|
||||
return bounds;
|
||||
}
|
||||
|
||||
return Rectangle.Empty;
|
||||
}
|
||||
|
||||
public void QueueActivity(bool queued, Activity nextActivity)
|
||||
{
|
||||
if (!queued)
|
||||
@@ -282,27 +276,33 @@ namespace OpenRA
|
||||
// TODO: move elsewhere.
|
||||
public void ChangeOwner(Player newOwner)
|
||||
{
|
||||
World.AddFrameEndTask(w =>
|
||||
{
|
||||
if (Disposed)
|
||||
return;
|
||||
World.AddFrameEndTask(_ => ChangeOwnerSync(newOwner));
|
||||
}
|
||||
|
||||
var oldOwner = Owner;
|
||||
var wasInWorld = IsInWorld;
|
||||
/// <summary>
|
||||
/// Change the actors owner without queuing a FrameEndTask.
|
||||
/// This must only be called from inside an existing FrameEndTask.
|
||||
/// </summary>
|
||||
public void ChangeOwnerSync(Player newOwner)
|
||||
{
|
||||
if (Disposed)
|
||||
return;
|
||||
|
||||
// momentarily remove from world so the ownership queries don't get confused
|
||||
if (wasInWorld)
|
||||
w.Remove(this);
|
||||
var oldOwner = Owner;
|
||||
var wasInWorld = IsInWorld;
|
||||
|
||||
Owner = newOwner;
|
||||
Generation++;
|
||||
// momentarily remove from world so the ownership queries don't get confused
|
||||
if (wasInWorld)
|
||||
World.Remove(this);
|
||||
|
||||
foreach (var t in TraitsImplementing<INotifyOwnerChanged>())
|
||||
t.OnOwnerChanged(this, oldOwner, newOwner);
|
||||
Owner = newOwner;
|
||||
Generation++;
|
||||
|
||||
if (wasInWorld)
|
||||
w.Add(this);
|
||||
});
|
||||
foreach (var t in TraitsImplementing<INotifyOwnerChanged>())
|
||||
t.OnOwnerChanged(this, oldOwner, newOwner);
|
||||
|
||||
if (wasInWorld)
|
||||
World.Add(this);
|
||||
}
|
||||
|
||||
public DamageState GetDamageState()
|
||||
@@ -329,15 +329,6 @@ namespace OpenRA
|
||||
health.Kill(this, attacker);
|
||||
}
|
||||
|
||||
public bool IsDisabled()
|
||||
{
|
||||
// PERF: Avoid LINQ.
|
||||
foreach (var disable in disables)
|
||||
if (disable.Disabled)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool CanBeViewedByPlayer(Player player)
|
||||
{
|
||||
// PERF: Avoid LINQ.
|
||||
|
||||
@@ -20,5 +20,8 @@ namespace OpenRA.Effects
|
||||
IEnumerable<IRenderable> Render(WorldRenderer r);
|
||||
}
|
||||
|
||||
// Identifier interface for effects that are added to ScreenMap
|
||||
public interface ISpatiallyPartitionable { }
|
||||
|
||||
public interface IEffectAboveShroud { IEnumerable<IRenderable> RenderAboveShroud(WorldRenderer wr); }
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
void LoadMod(MiniYaml yaml, string path = null)
|
||||
void LoadMod(MiniYaml yaml, string path = null, bool forceRegistration = false)
|
||||
{
|
||||
var mod = FieldLoader.Load<ExternalMod>(yaml);
|
||||
var iconNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon");
|
||||
@@ -83,7 +83,7 @@ namespace OpenRA
|
||||
|
||||
// Avoid possibly overwriting a valid mod with an obviously bogus one
|
||||
var key = ExternalMod.MakeKey(mod);
|
||||
if (File.Exists(mod.LaunchPath) && (path == null || Path.GetFileNameWithoutExtension(path) == key))
|
||||
if ((forceRegistration || File.Exists(mod.LaunchPath)) && (path == null || Path.GetFileNameWithoutExtension(path) == key))
|
||||
mods[key] = mod;
|
||||
}
|
||||
|
||||
@@ -119,13 +119,10 @@ namespace OpenRA
|
||||
sources.Add(Platform.SupportDir);
|
||||
|
||||
// Make sure the mod is available for this session, even if saving it fails
|
||||
LoadMod(yaml.First().Value);
|
||||
LoadMod(yaml.First().Value, forceRegistration: true);
|
||||
|
||||
foreach (var source in sources.Distinct())
|
||||
{
|
||||
if (!Directory.Exists(source))
|
||||
continue;
|
||||
|
||||
var metadataPath = Path.Combine(source, "ModMetadata");
|
||||
|
||||
try
|
||||
@@ -157,6 +154,7 @@ namespace OpenRA
|
||||
if (registration.HasFlag(ModRegistration.User))
|
||||
sources.Add(Platform.SupportDir);
|
||||
|
||||
var activeModKey = ExternalMod.MakeKey(activeMod);
|
||||
foreach (var source in sources.Distinct())
|
||||
{
|
||||
var metadataPath = Path.Combine(source, "ModMetadata");
|
||||
@@ -172,6 +170,10 @@ namespace OpenRA
|
||||
var m = FieldLoader.Load<ExternalMod>(yaml);
|
||||
modKey = ExternalMod.MakeKey(m);
|
||||
|
||||
// Continue to the next entry if it is the active mod (even if the LaunchPath is bogus)
|
||||
if (modKey == activeModKey)
|
||||
continue;
|
||||
|
||||
// Continue to the next entry if this one is valid
|
||||
if (File.Exists(m.LaunchPath) && Path.GetFileNameWithoutExtension(path) == modKey &&
|
||||
!(activeMod != null && m.LaunchPath == activeMod.LaunchPath && m.Id == activeMod.Id && m.Version != activeMod.Version))
|
||||
|
||||
@@ -494,14 +494,30 @@ namespace OpenRA
|
||||
return long.TryParse(s, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out i);
|
||||
}
|
||||
|
||||
public static bool IsTraitEnabled(this object trait)
|
||||
public static bool IsTraitEnabled<T>(this T trait)
|
||||
{
|
||||
return trait as IDisabledTrait == null || !(trait as IDisabledTrait).IsTraitDisabled;
|
||||
var disabledTrait = trait as IDisabledTrait;
|
||||
return disabledTrait == null || !disabledTrait.IsTraitDisabled;
|
||||
}
|
||||
|
||||
public static bool IsTraitEnabled<T>(T t)
|
||||
public static T FirstEnabledTraitOrDefault<T>(this IEnumerable<T> ts)
|
||||
{
|
||||
return IsTraitEnabled(t as object);
|
||||
// PERF: Avoid LINQ.
|
||||
foreach (var t in ts)
|
||||
if (t.IsTraitEnabled())
|
||||
return t;
|
||||
|
||||
return default(T);
|
||||
}
|
||||
|
||||
public static T FirstEnabledTraitOrDefault<T>(this T[] ts)
|
||||
{
|
||||
// PERF: Avoid LINQ.
|
||||
foreach (var t in ts)
|
||||
if (t.IsTraitEnabled())
|
||||
return t;
|
||||
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,11 @@ namespace OpenRA
|
||||
static readonly ConcurrentCache<MemberInfo, bool> MemberHasTranslateAttribute =
|
||||
new ConcurrentCache<MemberInfo, bool>(member => member.HasAttribute<TranslateAttribute>());
|
||||
|
||||
static readonly ConcurrentCache<string, BooleanExpression> BooleanExpressionCache =
|
||||
new ConcurrentCache<string, BooleanExpression>(expression => new BooleanExpression(expression));
|
||||
static readonly ConcurrentCache<string, IntegerExpression> IntegerExpressionCache =
|
||||
new ConcurrentCache<string, IntegerExpression>(expression => new IntegerExpression(expression));
|
||||
|
||||
static readonly object TranslationsLock = new object();
|
||||
static Dictionary<string, string> translations;
|
||||
|
||||
@@ -273,6 +278,10 @@ namespace OpenRA
|
||||
|
||||
return InvalidValueAction(value, fieldType, fieldName);
|
||||
}
|
||||
else if (fieldType == typeof(HotkeyReference))
|
||||
{
|
||||
return Game.ModData.Hotkeys[value];
|
||||
}
|
||||
else if (fieldType == typeof(WDist))
|
||||
{
|
||||
WDist res;
|
||||
@@ -404,7 +413,7 @@ namespace OpenRA
|
||||
{
|
||||
try
|
||||
{
|
||||
return new BooleanExpression(value);
|
||||
return BooleanExpressionCache[value];
|
||||
}
|
||||
catch (InvalidDataException e)
|
||||
{
|
||||
@@ -420,7 +429,7 @@ namespace OpenRA
|
||||
{
|
||||
try
|
||||
{
|
||||
return new IntegerExpression(value);
|
||||
return IntegerExpressionCache[value];
|
||||
}
|
||||
catch (InvalidDataException e)
|
||||
{
|
||||
|
||||
@@ -149,7 +149,7 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
var cp = temp.Palette;
|
||||
for (var i = 0; i < 256; i++)
|
||||
cp.Entries[i] = palette[i]; // finalize the palette.
|
||||
cp.Entries[i] = palette[i]; // finalize the palette.
|
||||
bitmap.Palette = cp;
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace OpenRA.FileSystem
|
||||
|
||||
using (var z = pkg.GetInputStream(entry))
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
var ms = new MemoryStream((int)entry.Size);
|
||||
z.CopyTo(ms);
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
return ms;
|
||||
@@ -104,7 +104,13 @@ namespace OpenRA.FileSystem
|
||||
// SharpZipLib breaks when asked to update archives loaded from outside streams or files
|
||||
// We can work around this by creating a clean in-memory-only file, cutting all outside references
|
||||
if (!create)
|
||||
new MemoryStream(File.ReadAllBytes(filename)).CopyTo(pkgStream);
|
||||
{
|
||||
using (var copy = new MemoryStream(File.ReadAllBytes(filename)))
|
||||
{
|
||||
pkgStream.Capacity = (int)copy.Length;
|
||||
copy.CopyTo(pkgStream);
|
||||
}
|
||||
}
|
||||
|
||||
pkgStream.Position = 0;
|
||||
pkg = ZipFileHelper.Create(pkgStream);
|
||||
|
||||
@@ -21,7 +21,6 @@ using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using OpenRA.Chat;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Primitives;
|
||||
@@ -55,11 +54,10 @@ namespace OpenRA
|
||||
|
||||
public static bool BenchmarkMode = false;
|
||||
|
||||
public static GlobalChat GlobalChat;
|
||||
|
||||
public static string EngineVersion { get; private set; }
|
||||
|
||||
static Task discoverNat;
|
||||
static bool takeScreenshot = false;
|
||||
|
||||
public static OrderManager JoinServer(string host, int port, string password, bool recordReplay = true)
|
||||
{
|
||||
@@ -169,9 +167,13 @@ namespace OpenRA
|
||||
|
||||
worldRenderer = new WorldRenderer(ModData, OrderManager.World);
|
||||
|
||||
GC.Collect();
|
||||
|
||||
using (new PerfTimer("LoadComplete"))
|
||||
OrderManager.World.LoadComplete(worldRenderer);
|
||||
|
||||
GC.Collect();
|
||||
|
||||
if (OrderManager.GameStarted)
|
||||
return;
|
||||
|
||||
@@ -244,7 +246,14 @@ namespace OpenRA
|
||||
Settings = new Settings(Platform.ResolvePath(Path.Combine("^", "settings.yaml")), args);
|
||||
}
|
||||
|
||||
internal static void Initialize(Arguments args)
|
||||
public static RunStatus InitializeAndRun(string[] args)
|
||||
{
|
||||
Initialize(new Arguments(args));
|
||||
GC.Collect();
|
||||
return Run();
|
||||
}
|
||||
|
||||
static void Initialize(Arguments args)
|
||||
{
|
||||
Console.WriteLine("Platform is {0}", Platform.CurrentPlatform);
|
||||
|
||||
@@ -318,15 +327,8 @@ namespace OpenRA
|
||||
|
||||
GeoIP.Initialize();
|
||||
|
||||
if (!Settings.Server.DiscoverNatDevices)
|
||||
Settings.Server.AllowPortForward = false;
|
||||
else
|
||||
{
|
||||
if (Settings.Server.DiscoverNatDevices)
|
||||
discoverNat = UPnP.DiscoverNatDevices(Settings.Server.NatDiscoveryTimeout);
|
||||
Settings.Server.AllowPortForward = true;
|
||||
}
|
||||
|
||||
GlobalChat = new GlobalChat();
|
||||
|
||||
var modSearchArg = args.GetValue("Engine.ModSearchPaths", null);
|
||||
var modSearchPaths = modSearchArg != null ?
|
||||
@@ -454,7 +456,6 @@ namespace OpenRA
|
||||
{
|
||||
Console.WriteLine("NAT discovery failed: {0}", e.Message);
|
||||
Log.Write("nat", e.ToString());
|
||||
Settings.Server.AllowPortForward = false;
|
||||
}
|
||||
|
||||
ModData.LoadScreen.StartGame(args);
|
||||
@@ -630,7 +631,10 @@ namespace OpenRA
|
||||
InnerLogicTick(worldRenderer.World.OrderManager);
|
||||
}
|
||||
|
||||
public static bool TakeScreenshot = false;
|
||||
public static void TakeScreenshot()
|
||||
{
|
||||
takeScreenshot = true;
|
||||
}
|
||||
|
||||
static void RenderTick()
|
||||
{
|
||||
@@ -666,9 +670,9 @@ namespace OpenRA
|
||||
using (new PerfSample("render_flip"))
|
||||
Renderer.EndFrame(new DefaultInputHandler(OrderManager.World));
|
||||
|
||||
if (TakeScreenshot)
|
||||
if (takeScreenshot)
|
||||
{
|
||||
TakeScreenshot = false;
|
||||
takeScreenshot = false;
|
||||
TakeScreenshotInner();
|
||||
}
|
||||
}
|
||||
@@ -782,7 +786,7 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
internal static RunStatus Run()
|
||||
static RunStatus Run()
|
||||
{
|
||||
if (Settings.Graphics.MaxFramerate < 1)
|
||||
{
|
||||
@@ -806,7 +810,6 @@ namespace OpenRA
|
||||
ModData.Dispose();
|
||||
ChromeProvider.Deinitialize();
|
||||
|
||||
GlobalChat.Dispose();
|
||||
Sound.Dispose();
|
||||
Renderer.Dispose();
|
||||
|
||||
@@ -862,8 +865,7 @@ namespace OpenRA
|
||||
{
|
||||
Name = "Skirmish Game",
|
||||
Map = map,
|
||||
AdvertiseOnline = false,
|
||||
AllowPortForward = false
|
||||
AdvertiseOnline = false
|
||||
};
|
||||
|
||||
server = new Server.Server(new IPEndPoint(IPAddress.Loopback, 0), settings, ModData, false);
|
||||
|
||||
@@ -69,8 +69,9 @@ namespace OpenRA.GameRules
|
||||
[Desc("What types of targets are unaffected.", "Overrules ValidTargets.")]
|
||||
public readonly HashSet<string> InvalidTargets = new HashSet<string>();
|
||||
|
||||
[Desc("Delay in ticks between firing shots from the same ammo magazine.")]
|
||||
public readonly int BurstDelay = 5;
|
||||
[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 };
|
||||
|
||||
[Desc("The minimum range the weapon can fire.")]
|
||||
public readonly WDist MinRange = WDist.Zero;
|
||||
|
||||
@@ -1,387 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2017 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.Linq;
|
||||
using System.Threading;
|
||||
using Meebey.SmartIrc4net;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Chat
|
||||
{
|
||||
public enum ChatConnectionStatus { Disconnected, Connecting, Connected, Disconnecting, Joined, Error }
|
||||
public enum ChatMessageType { Message, Notification }
|
||||
|
||||
public sealed class ChatUser
|
||||
{
|
||||
public readonly string Name;
|
||||
public bool IsOp;
|
||||
public bool IsVoiced;
|
||||
|
||||
public ChatUser(string name, bool isOp, bool isVoice)
|
||||
{
|
||||
Name = name;
|
||||
IsOp = isOp;
|
||||
IsVoiced = isVoice;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ChatMessage
|
||||
{
|
||||
static long nextUID;
|
||||
|
||||
public readonly DateTime Time;
|
||||
public readonly ChatMessageType Type;
|
||||
public readonly string Nick;
|
||||
public readonly string Message;
|
||||
public readonly string UID;
|
||||
|
||||
public ChatMessage(DateTime time, ChatMessageType type, string nick, string message)
|
||||
{
|
||||
Time = time;
|
||||
Type = type;
|
||||
Nick = nick;
|
||||
Message = message;
|
||||
|
||||
UID = Interlocked.Increment(ref nextUID).ToString();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var time = Time.ToString(Game.Settings.Chat.TimestampFormat);
|
||||
if (Type == ChatMessageType.Notification)
|
||||
return "{0} {1}".F(time, Message);
|
||||
|
||||
return "{0} {1}: {2}".F(time, Nick, Message);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class GlobalChat : IDisposable
|
||||
{
|
||||
readonly IrcClient client = new IrcClient();
|
||||
volatile Channel channel;
|
||||
|
||||
public readonly ObservableSortedDictionary<string, ChatUser> Users = new ObservableSortedDictionary<string, ChatUser>(StringComparer.InvariantCultureIgnoreCase);
|
||||
public readonly ObservableList<ChatMessage> History = new ObservableList<ChatMessage>();
|
||||
|
||||
volatile string topic;
|
||||
public string Topic { get { return topic; } }
|
||||
|
||||
volatile ChatConnectionStatus connectionStatus = ChatConnectionStatus.Disconnected;
|
||||
public ChatConnectionStatus ConnectionStatus { get { return connectionStatus; } }
|
||||
|
||||
string nickname;
|
||||
|
||||
public GlobalChat()
|
||||
{
|
||||
client.Encoding = System.Text.Encoding.UTF8;
|
||||
client.SendDelay = 100;
|
||||
client.ActiveChannelSyncing = true;
|
||||
|
||||
client.OnConnecting += OnConnecting;
|
||||
client.OnConnected += OnConnected;
|
||||
client.OnDisconnecting += OnDisconnecting;
|
||||
client.OnDisconnected += OnDisconnected;
|
||||
client.OnError += OnError;
|
||||
client.OnKick += OnKick;
|
||||
|
||||
client.OnRawMessage += (_, e) => Game.RunAfterTick(() => Log.Write("irc", e.Data.RawMessage));
|
||||
client.OnJoin += OnJoin;
|
||||
client.OnChannelActiveSynced += OnChannelActiveSynced;
|
||||
client.OnTopic += (_, e) => topic = e.Topic;
|
||||
client.OnTopicChange += (_, e) => topic = e.NewTopic;
|
||||
client.OnNickChange += OnNickChange;
|
||||
|
||||
client.OnChannelMessage += (_, e) => AddMessage(e.Data.Nick, e.Data.Message);
|
||||
client.OnOp += (_, e) => SetUserOp(e.Whom, true);
|
||||
client.OnDeop += (_, e) => SetUserOp(e.Whom, false);
|
||||
client.OnVoice += (_, e) => SetUserVoiced(e.Whom, true);
|
||||
client.OnDevoice += (_, e) => SetUserVoiced(e.Whom, false);
|
||||
client.OnPart += OnPart;
|
||||
client.OnQuit += OnQuit;
|
||||
}
|
||||
|
||||
void SetUserOp(string whom, bool isOp)
|
||||
{
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
ChatUser user;
|
||||
if (Users.TryGetValue(whom, out user))
|
||||
user.IsOp = isOp;
|
||||
});
|
||||
}
|
||||
|
||||
void SetUserVoiced(string whom, bool isVoiced)
|
||||
{
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
ChatUser user;
|
||||
if (Users.TryGetValue(whom, out user))
|
||||
user.IsVoiced = isVoiced;
|
||||
});
|
||||
}
|
||||
|
||||
public void Connect(string nickname)
|
||||
{
|
||||
if (client.IsConnected || !IsValidNickname(nickname))
|
||||
return;
|
||||
|
||||
this.nickname = nickname;
|
||||
|
||||
new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
client.Connect(Game.Settings.Chat.Hostname, Game.Settings.Chat.Port);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
connectionStatus = ChatConnectionStatus.Error;
|
||||
AddNotification(e.Message);
|
||||
Game.RunAfterTick(() => Log.Write("irc", e.ToString()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
client.Listen();
|
||||
}) { Name = "IrcListenThread", IsBackground = true }.Start();
|
||||
}
|
||||
|
||||
void AddNotification(string text)
|
||||
{
|
||||
var message = new ChatMessage(DateTime.Now, ChatMessageType.Notification, null, text);
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
History.Add(message);
|
||||
Log.Write("irc", text);
|
||||
});
|
||||
}
|
||||
|
||||
void AddMessage(string nick, string text)
|
||||
{
|
||||
var message = new ChatMessage(DateTime.Now, ChatMessageType.Message, nick, text);
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
History.Add(message);
|
||||
Log.Write("irc", text);
|
||||
});
|
||||
}
|
||||
|
||||
void OnConnecting(object sender, EventArgs e)
|
||||
{
|
||||
AddNotification("Connecting to {0}:{1}...".F(Game.Settings.Chat.Hostname, Game.Settings.Chat.Port));
|
||||
connectionStatus = ChatConnectionStatus.Connecting;
|
||||
}
|
||||
|
||||
void OnConnected(object sender, EventArgs e)
|
||||
{
|
||||
AddNotification("Connected.");
|
||||
connectionStatus = ChatConnectionStatus.Connected;
|
||||
|
||||
client.Login(nickname, "in-game IRC client", 0, "OpenRA");
|
||||
client.RfcJoin("#" + Game.Settings.Chat.Channel);
|
||||
}
|
||||
|
||||
void OnDisconnecting(object sender, EventArgs e)
|
||||
{
|
||||
if (connectionStatus != ChatConnectionStatus.Error)
|
||||
connectionStatus = ChatConnectionStatus.Disconnecting;
|
||||
}
|
||||
|
||||
void OnDisconnected(object sender, EventArgs e)
|
||||
{
|
||||
Game.RunAfterTick(Users.Clear);
|
||||
|
||||
// Keep the chat window open if there is an error
|
||||
// It will be cleared by the Disconnect button
|
||||
if (connectionStatus != ChatConnectionStatus.Error)
|
||||
{
|
||||
Game.RunAfterTick(History.Clear);
|
||||
topic = null;
|
||||
connectionStatus = ChatConnectionStatus.Disconnected;
|
||||
}
|
||||
}
|
||||
|
||||
void OnError(object sender, ErrorEventArgs e)
|
||||
{
|
||||
// Ignore any errors that happen during disconnect
|
||||
if (connectionStatus != ChatConnectionStatus.Disconnecting)
|
||||
{
|
||||
connectionStatus = ChatConnectionStatus.Error;
|
||||
AddNotification("Error: " + e.ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
void OnKick(object sender, KickEventArgs e)
|
||||
{
|
||||
if (e.Whom == client.Nickname)
|
||||
{
|
||||
Disconnect();
|
||||
connectionStatus = ChatConnectionStatus.Error;
|
||||
AddNotification("You were kicked from the chat by {0}. ({1})".F(e.Who, e.KickReason));
|
||||
}
|
||||
else
|
||||
{
|
||||
Users.Remove(e.Whom);
|
||||
AddNotification("{0} was kicked from the chat by {1}. ({2})".F(e.Whom, e.Who, e.KickReason));
|
||||
}
|
||||
}
|
||||
|
||||
void OnJoin(object sender, JoinEventArgs e)
|
||||
{
|
||||
if (e.Who == client.Nickname || channel == null || e.Channel != channel.Name)
|
||||
return;
|
||||
|
||||
AddNotification("{0} joined the chat.".F(e.Who));
|
||||
Game.RunAfterTick(() => Users.Add(e.Who, new ChatUser(e.Who, false, false)));
|
||||
}
|
||||
|
||||
void OnChannelActiveSynced(object sender, IrcEventArgs e)
|
||||
{
|
||||
channel = client.GetChannel(e.Data.Channel);
|
||||
AddNotification("{0} users online".F(channel.Users.Count));
|
||||
connectionStatus = ChatConnectionStatus.Joined;
|
||||
|
||||
foreach (DictionaryEntry user in channel.Users)
|
||||
{
|
||||
var u = (ChannelUser)user.Value;
|
||||
Game.RunAfterTick(() => Users.Add(u.Nick, new ChatUser(u.Nick, u.IsOp, u.IsVoice)));
|
||||
}
|
||||
}
|
||||
|
||||
void OnNickChange(object sender, NickChangeEventArgs e)
|
||||
{
|
||||
AddNotification("{0} is now known as {1}.".F(e.OldNickname, e.NewNickname));
|
||||
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
ChatUser user;
|
||||
if (!Users.TryGetValue(e.OldNickname, out user))
|
||||
return;
|
||||
|
||||
Users.Remove(e.OldNickname);
|
||||
Users.Add(e.NewNickname, new ChatUser(e.NewNickname, user.IsOp, user.IsVoiced));
|
||||
});
|
||||
}
|
||||
|
||||
void OnQuit(object sender, QuitEventArgs e)
|
||||
{
|
||||
AddNotification("{0} left the chat.".F(e.Who));
|
||||
Game.RunAfterTick(() => Users.Remove(e.Who));
|
||||
}
|
||||
|
||||
void OnPart(object sender, PartEventArgs e)
|
||||
{
|
||||
if (channel == null || e.Data.Channel != channel.Name)
|
||||
return;
|
||||
|
||||
AddNotification("{0} left the chat.".F(e.Who));
|
||||
Game.RunAfterTick(() => Users.Remove(e.Who));
|
||||
}
|
||||
|
||||
public string SanitizedName(string dirty)
|
||||
{
|
||||
if (string.IsNullOrEmpty(dirty))
|
||||
return null;
|
||||
|
||||
// There is no need to mangle the nick if it is already valid
|
||||
if (Rfc2812.IsValidNickname(dirty))
|
||||
return dirty;
|
||||
|
||||
// TODO: some special chars are allowed as well, but not at every position
|
||||
var clean = new string(dirty.Where(c => char.IsLetterOrDigit(c)).ToArray());
|
||||
|
||||
if (string.IsNullOrEmpty(clean))
|
||||
return null;
|
||||
|
||||
if (char.IsDigit(clean[0]))
|
||||
return SanitizedName(clean.Substring(1));
|
||||
|
||||
// Source: https://tools.ietf.org/html/rfc2812#section-1.2.1
|
||||
if (clean.Length > 9)
|
||||
clean = clean.Substring(0, 9);
|
||||
|
||||
return clean;
|
||||
}
|
||||
|
||||
public bool IsValidNickname(string name)
|
||||
{
|
||||
return Rfc2812.IsValidNickname(name);
|
||||
}
|
||||
|
||||
public void SendMessage(string text)
|
||||
{
|
||||
if (connectionStatus != ChatConnectionStatus.Joined)
|
||||
return;
|
||||
|
||||
// Guard against a last-moment disconnection
|
||||
try
|
||||
{
|
||||
client.SendMessage(SendType.Message, channel.Name, text);
|
||||
AddMessage(client.Nickname, text);
|
||||
}
|
||||
catch (NotConnectedException) { }
|
||||
}
|
||||
|
||||
public bool TrySetNickname(string nick)
|
||||
{
|
||||
// TODO: This is inconsistent with the other check
|
||||
if (Rfc2812.IsValidNickname(nick))
|
||||
{
|
||||
client.RfcNick(nick);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
// Error is an alias for disconnect, but keeps the panel open
|
||||
// so that clients can see the error
|
||||
if (connectionStatus == ChatConnectionStatus.Error)
|
||||
{
|
||||
Game.RunAfterTick(History.Clear);
|
||||
topic = null;
|
||||
connectionStatus = ChatConnectionStatus.Disconnected;
|
||||
}
|
||||
else
|
||||
connectionStatus = ChatConnectionStatus.Disconnecting;
|
||||
|
||||
if (!client.IsConnected)
|
||||
return;
|
||||
|
||||
client.RfcQuit(Game.Settings.Chat.QuitMessage);
|
||||
|
||||
AddNotification("Disconnecting from {0}...".F(client.Address));
|
||||
|
||||
Game.RunAfterTick(() => { Game.Settings.Chat.ConnectAutomatically = false; Game.Settings.Save(); });
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// HACK: The IRC library we are using has terrible thread-handling code that relies on Thread.Abort.
|
||||
// There is a thread reading from the network socket which is aborted, however on Windows this is inside
|
||||
// native code so this abort call hangs until the network socket reads something and returns to managed
|
||||
// code where it can then be aborted.
|
||||
//
|
||||
// This means we may hang for several seconds during shutdown (until we receive something over IRC!) before
|
||||
// closing.
|
||||
//
|
||||
// Since our IRC client currently lives forever, the only time we call this Dispose method is during the
|
||||
// shutdown of our process. Therefore, we can work around the problem by just not bothering to disconnect
|
||||
// properly. Since our process is about to die anyway, it's not like anyone will care.
|
||||
////if (client.IsConnected)
|
||||
//// client.Disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Support;
|
||||
|
||||
@@ -66,6 +67,17 @@ namespace OpenRA.Graphics
|
||||
return new IRenderable[] { imageRenderable };
|
||||
}
|
||||
|
||||
public Rectangle ScreenBounds(WorldRenderer wr, WPos pos, WVec offset, float scale)
|
||||
{
|
||||
var xy = wr.ScreenPxPosition(pos) + wr.ScreenPxOffset(offset);
|
||||
var cb = CurrentSequence.Bounds;
|
||||
return Rectangle.FromLTRB(
|
||||
xy.X + (int)(cb.Left * scale),
|
||||
xy.Y + (int)(cb.Top * scale),
|
||||
xy.X + (int)(cb.Right * scale),
|
||||
xy.Y + (int)(cb.Bottom * scale));
|
||||
}
|
||||
|
||||
public IEnumerable<IRenderable> Render(WPos pos, PaletteReference palette)
|
||||
{
|
||||
return Render(pos, WVec.Zero, 0, palette, 1f);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
@@ -44,6 +45,14 @@ namespace OpenRA.Graphics
|
||||
return Animation.Render(center, offset, z, pal, scale);
|
||||
}
|
||||
|
||||
public Rectangle ScreenBounds(Actor self, WorldRenderer wr, float scale)
|
||||
{
|
||||
var center = self.CenterPosition;
|
||||
var offset = OffsetFunc != null ? OffsetFunc() : WVec.Zero;
|
||||
|
||||
return Animation.ScreenBounds(wr, center, offset, scale);
|
||||
}
|
||||
|
||||
public static implicit operator AnimationWithOffset(Animation a)
|
||||
{
|
||||
return new AnimationWithOffset(a, null, null, null);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using OpenRA.FileSystem;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
@@ -23,6 +24,9 @@ namespace OpenRA.Graphics
|
||||
float[] Size { get; }
|
||||
float[] Bounds(uint frame);
|
||||
ModelRenderData RenderData(uint section);
|
||||
|
||||
/// <summary>Returns the smallest rectangle that covers all rotations of all frames in a model</summary>
|
||||
Rectangle AggregateBounds { get; }
|
||||
}
|
||||
|
||||
public struct ModelRenderData
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
@@ -32,5 +33,26 @@ namespace OpenRA.Graphics
|
||||
FrameFunc = frame;
|
||||
ShowShadow = showshadow;
|
||||
}
|
||||
|
||||
public Rectangle ScreenBounds(WPos pos, WorldRenderer wr, float scale)
|
||||
{
|
||||
var r = Model.AggregateBounds;
|
||||
var offset = OffsetFunc != null ? OffsetFunc() : WVec.Zero;
|
||||
var xy = wr.ScreenPxPosition(pos) + wr.ScreenPxOffset(offset);
|
||||
|
||||
return Rectangle.FromLTRB(
|
||||
xy.X + (int)(r.Left * scale),
|
||||
xy.Y + (int)(r.Top * scale),
|
||||
xy.X + (int)(r.Right * scale),
|
||||
xy.Y + (int)(r.Bottom * scale));
|
||||
}
|
||||
|
||||
public bool IsVisible
|
||||
{
|
||||
get
|
||||
{
|
||||
return DisableFunc == null || !DisableFunc();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,8 @@ namespace OpenRA.Graphics
|
||||
readonly Stack<KeyValuePair<Sheet, IFrameBuffer>> unmappedBuffers = new Stack<KeyValuePair<Sheet, IFrameBuffer>>();
|
||||
readonly List<Pair<Sheet, Action>> doRender = new List<Pair<Sheet, Action>>();
|
||||
|
||||
SheetBuilder sheetBuilder;
|
||||
SheetBuilder sheetBuilderForFrame;
|
||||
bool isInFrame;
|
||||
|
||||
public ModelRenderer(Renderer renderer, IShader shader)
|
||||
{
|
||||
@@ -83,6 +84,9 @@ namespace OpenRA.Graphics
|
||||
float[] groundNormal, WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
|
||||
PaletteReference color, PaletteReference normals, PaletteReference shadowPalette)
|
||||
{
|
||||
if (!isInFrame)
|
||||
throw new InvalidOperationException("BeginFrame has not been called. You cannot render until a frame has been started.");
|
||||
|
||||
// Correct for inverted y-axis
|
||||
var scaleTransform = Util.ScaleMatrix(scale, scale, scale);
|
||||
|
||||
@@ -163,8 +167,11 @@ namespace OpenRA.Graphics
|
||||
CalculateSpriteGeometry(tl, br, 1, out spriteSize, out spriteOffset);
|
||||
CalculateSpriteGeometry(stl, sbr, 2, out shadowSpriteSize, out shadowSpriteOffset);
|
||||
|
||||
var sprite = sheetBuilder.Allocate(spriteSize, 0, spriteOffset);
|
||||
var shadowSprite = sheetBuilder.Allocate(shadowSpriteSize, 0, shadowSpriteOffset);
|
||||
if (sheetBuilderForFrame == null)
|
||||
sheetBuilderForFrame = new SheetBuilder(SheetType.BGRA, AllocateSheet);
|
||||
|
||||
var sprite = sheetBuilderForFrame.Allocate(spriteSize, 0, spriteOffset);
|
||||
var shadowSprite = sheetBuilderForFrame.Allocate(shadowSpriteSize, 0, shadowSpriteOffset);
|
||||
var sb = sprite.Bounds;
|
||||
var ssb = shadowSprite.Bounds;
|
||||
var spriteCenter = new float2(sb.Left + sb.Width / 2, sb.Top + sb.Height / 2);
|
||||
@@ -276,12 +283,14 @@ namespace OpenRA.Graphics
|
||||
|
||||
public void BeginFrame()
|
||||
{
|
||||
if (isInFrame)
|
||||
throw new InvalidOperationException("BeginFrame has already been called. A new frame cannot be started until EndFrame has been called.");
|
||||
|
||||
isInFrame = true;
|
||||
|
||||
foreach (var kv in mappedBuffers)
|
||||
unmappedBuffers.Push(kv);
|
||||
mappedBuffers.Clear();
|
||||
|
||||
sheetBuilder = new SheetBuilder(SheetType.BGRA, AllocateSheet);
|
||||
doRender.Clear();
|
||||
}
|
||||
|
||||
IFrameBuffer EnableFrameBuffer(Sheet s)
|
||||
@@ -303,6 +312,12 @@ namespace OpenRA.Graphics
|
||||
|
||||
public void EndFrame()
|
||||
{
|
||||
if (!isInFrame)
|
||||
throw new InvalidOperationException("BeginFrame has not been called. There is no frame to end.");
|
||||
|
||||
isInFrame = false;
|
||||
sheetBuilderForFrame = null;
|
||||
|
||||
if (doRender.Count == 0)
|
||||
return;
|
||||
|
||||
@@ -325,6 +340,8 @@ namespace OpenRA.Graphics
|
||||
|
||||
if (fbo != null)
|
||||
DisableFrameBuffer(fbo);
|
||||
|
||||
doRender.Clear();
|
||||
}
|
||||
|
||||
public Sheet AllocateSheet()
|
||||
|
||||
@@ -204,11 +204,6 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawLine(IEnumerable<float2> points, float width, Color color, bool connectSegments = false)
|
||||
{
|
||||
DrawLine(points.Select(p => new float3(p, 0)), width, color, connectSegments);
|
||||
}
|
||||
|
||||
public void DrawLine(IEnumerable<float3> points, float width, Color color, bool connectSegments = false)
|
||||
{
|
||||
if (!connectSegments)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Drawing;
|
||||
using OpenRA.FileSystem;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
@@ -31,6 +31,7 @@ namespace OpenRA.Graphics
|
||||
int ShadowStart { get; }
|
||||
int ShadowZOffset { get; }
|
||||
int[] Frames { get; }
|
||||
Rectangle Bounds { get; }
|
||||
|
||||
Sprite GetSprite(int frame);
|
||||
Sprite GetSprite(int frame, int facing);
|
||||
|
||||
@@ -49,8 +49,8 @@ namespace OpenRA.Graphics
|
||||
Func<char, float> characterWidth = character => glyphs[Pair.New(character, Color.White)].Advance;
|
||||
lineWidth = line => line.Sum(characterWidth) / deviceScale;
|
||||
|
||||
PrecacheColor(Color.White, name);
|
||||
PrecacheColor(Color.Red, name);
|
||||
if (size <= 24)
|
||||
PrecacheColor(Color.White, name);
|
||||
}
|
||||
|
||||
public void SetScale(float scale)
|
||||
@@ -70,25 +70,28 @@ namespace OpenRA.Graphics
|
||||
|
||||
public void DrawText(string text, float2 location, Color c)
|
||||
{
|
||||
location.Y += size; // baseline vs top
|
||||
// Offset from the baseline position to the top-left of the glyph for rendering
|
||||
location += new float2(0, size);
|
||||
|
||||
var p = location;
|
||||
foreach (var s in text)
|
||||
{
|
||||
if (s == '\n')
|
||||
{
|
||||
location.Y += size;
|
||||
location += new float2(0, size);
|
||||
p = location;
|
||||
continue;
|
||||
}
|
||||
|
||||
var g = glyphs[Pair.New(s, c)];
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite,
|
||||
new float2(
|
||||
(int)Math.Round(p.X * deviceScale + g.Offset.X, 0) / deviceScale,
|
||||
p.Y + g.Offset.Y / deviceScale),
|
||||
g.Sprite.Size / deviceScale);
|
||||
p.X += g.Advance / deviceScale;
|
||||
if (g.Sprite != null)
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite,
|
||||
new float2(
|
||||
(int)Math.Round(p.X * deviceScale + g.Offset.X, 0) / deviceScale,
|
||||
p.Y + g.Offset.Y / deviceScale),
|
||||
g.Sprite.Size / deviceScale);
|
||||
|
||||
p += new float2(g.Advance / deviceScale, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +137,20 @@ namespace OpenRA.Graphics
|
||||
|
||||
GlyphInfo CreateGlyph(Pair<char, Color> c)
|
||||
{
|
||||
face.LoadChar(c.First, LoadFlags.Default, LoadTarget.Normal);
|
||||
try
|
||||
{
|
||||
face.LoadChar(c.First, LoadFlags.Default, LoadTarget.Normal);
|
||||
}
|
||||
catch (FreeTypeException)
|
||||
{
|
||||
return new GlyphInfo
|
||||
{
|
||||
Sprite = null,
|
||||
Advance = 0,
|
||||
Offset = int2.Zero
|
||||
};
|
||||
}
|
||||
|
||||
face.Glyph.RenderGlyph(RenderMode.Normal);
|
||||
|
||||
var size = new Size((int)face.Glyph.Metrics.Width, (int)face.Glyph.Metrics.Height);
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace OpenRA.Graphics
|
||||
readonly HardwarePalette palette = new HardwarePalette();
|
||||
readonly Dictionary<string, PaletteReference> palettes = new Dictionary<string, PaletteReference>();
|
||||
readonly TerrainRenderer terrainRenderer;
|
||||
readonly Lazy<DeveloperMode> devTrait;
|
||||
readonly Lazy<DebugVisualizations> debugVis;
|
||||
readonly Func<string, PaletteReference> createPaletteReference;
|
||||
readonly bool enableDepthBuffer;
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace OpenRA.Graphics
|
||||
Theater = new Theater(world.Map.Rules.TileSet);
|
||||
terrainRenderer = new TerrainRenderer(world, this);
|
||||
|
||||
devTrait = Exts.Lazy(() => world.LocalPlayer != null ? world.LocalPlayer.PlayerActor.Trait<DeveloperMode>() : null);
|
||||
debugVis = Exts.Lazy(() => world.WorldActor.TraitOrDefault<DebugVisualizations>());
|
||||
}
|
||||
|
||||
public void UpdatePalettesForPlayer(string internalName, HSLColor color, bool replaceExisting)
|
||||
@@ -100,9 +100,9 @@ namespace OpenRA.Graphics
|
||||
palettes[name].Palette = pal;
|
||||
}
|
||||
|
||||
List<IFinalizedRenderable> GenerateRenderables()
|
||||
List<IFinalizedRenderable> GenerateRenderables(HashSet<Actor> actorsInBox)
|
||||
{
|
||||
var actors = World.ScreenMap.ActorsInBox(Viewport.TopLeft, Viewport.BottomRight).Append(World.WorldActor);
|
||||
var actors = actorsInBox.Append(World.WorldActor);
|
||||
if (World.RenderPlayer != null)
|
||||
actors = actors.Append(World.RenderPlayer.PlayerActor);
|
||||
|
||||
@@ -110,7 +110,13 @@ namespace OpenRA.Graphics
|
||||
if (World.OrderGenerator != null)
|
||||
worldRenderables = worldRenderables.Concat(World.OrderGenerator.Render(this, World));
|
||||
|
||||
worldRenderables = worldRenderables.Concat(World.Effects.SelectMany(e => e.Render(this)));
|
||||
// Unpartitioned effects
|
||||
worldRenderables = worldRenderables.Concat(World.UnpartitionedEffects.SelectMany(e => 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)));
|
||||
|
||||
worldRenderables = worldRenderables.OrderBy(RenderableScreenZPositionComparisonKey);
|
||||
|
||||
Game.Renderer.WorldModelRenderer.BeginFrame();
|
||||
@@ -120,21 +126,53 @@ namespace OpenRA.Graphics
|
||||
return renderables;
|
||||
}
|
||||
|
||||
List<IFinalizedRenderable> GenerateOverlayRenderables(HashSet<Actor> actorsInBox)
|
||||
{
|
||||
var aboveShroud = World.ActorsWithTrait<IRenderAboveShroud>()
|
||||
.Where(a => a.Actor.IsInWorld && !a.Actor.Disposed && (!a.Trait.SpatiallyPartitionable || actorsInBox.Contains(a.Actor)))
|
||||
.SelectMany(a => a.Trait.RenderAboveShroud(a.Actor, this));
|
||||
|
||||
var aboveShroudSelected = World.Selection.Actors.Where(a => a.IsInWorld && !a.Disposed)
|
||||
.SelectMany(a => a.TraitsImplementing<IRenderAboveShroudWhenSelected>()
|
||||
.Where(t => !t.SpatiallyPartitionable || actorsInBox.Contains(a))
|
||||
.SelectMany(t => t.RenderAboveShroud(a, this)));
|
||||
|
||||
var aboveShroudEffects = World.Effects.Select(e => e as IEffectAboveShroud)
|
||||
.Where(e => e != null)
|
||||
.SelectMany(e => e.RenderAboveShroud(this));
|
||||
|
||||
var aboveShroudOrderGenerator = SpriteRenderable.None;
|
||||
if (World.OrderGenerator != null)
|
||||
aboveShroudOrderGenerator = World.OrderGenerator.RenderAboveShroud(this, World);
|
||||
|
||||
var overlayRenderables = aboveShroud
|
||||
.Concat(aboveShroudSelected)
|
||||
.Concat(aboveShroudEffects)
|
||||
.Concat(aboveShroudOrderGenerator);
|
||||
|
||||
Game.Renderer.WorldModelRenderer.BeginFrame();
|
||||
var finalOverlayRenderables = overlayRenderables.Select(r => r.PrepareRender(this)).ToList();
|
||||
Game.Renderer.WorldModelRenderer.EndFrame();
|
||||
|
||||
return finalOverlayRenderables;
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
if (World.WorldActor.Disposed)
|
||||
return;
|
||||
|
||||
if (devTrait.Value != null)
|
||||
if (debugVis.Value != null)
|
||||
{
|
||||
Game.Renderer.WorldSpriteRenderer.SetDepthPreviewEnabled(devTrait.Value.ShowDepthPreview);
|
||||
Game.Renderer.WorldRgbaSpriteRenderer.SetDepthPreviewEnabled(devTrait.Value.ShowDepthPreview);
|
||||
Game.Renderer.WorldRgbaColorRenderer.SetDepthPreviewEnabled(devTrait.Value.ShowDepthPreview);
|
||||
Game.Renderer.WorldSpriteRenderer.SetDepthPreviewEnabled(debugVis.Value.DepthBuffer);
|
||||
Game.Renderer.WorldRgbaSpriteRenderer.SetDepthPreviewEnabled(debugVis.Value.DepthBuffer);
|
||||
Game.Renderer.WorldRgbaColorRenderer.SetDepthPreviewEnabled(debugVis.Value.DepthBuffer);
|
||||
}
|
||||
|
||||
RefreshPalette();
|
||||
|
||||
var renderables = GenerateRenderables();
|
||||
var onScreenActors = World.ScreenMap.RenderableActorsInBox(Viewport.TopLeft, Viewport.BottomRight).ToHashSet();
|
||||
var renderables = GenerateRenderables(onScreenActors);
|
||||
var bounds = Viewport.GetScissorBounds(World.Type != WorldType.Editor);
|
||||
Game.Renderer.EnableScissor(bounds);
|
||||
|
||||
@@ -167,45 +205,39 @@ namespace OpenRA.Graphics
|
||||
|
||||
Game.Renderer.DisableScissor();
|
||||
|
||||
var aboveShroud = World.ActorsWithTrait<IRenderAboveShroud>().Where(a => a.Actor.IsInWorld && !a.Actor.Disposed)
|
||||
.SelectMany(a => a.Trait.RenderAboveShroud(a.Actor, this));
|
||||
|
||||
var aboveShroudSelected = World.Selection.Actors.Where(a => !a.Disposed)
|
||||
.SelectMany(a => a.TraitsImplementing<IRenderAboveShroudWhenSelected>()
|
||||
.SelectMany(t => t.RenderAboveShroud(a, this)));
|
||||
|
||||
var aboveShroudEffects = World.Effects.Select(e => e as IEffectAboveShroud)
|
||||
.Where(e => e != null)
|
||||
.SelectMany(e => e.RenderAboveShroud(this));
|
||||
|
||||
var aboveShroudOrderGenerator = SpriteRenderable.None;
|
||||
if (World.OrderGenerator != null)
|
||||
aboveShroudOrderGenerator = World.OrderGenerator.RenderAboveShroud(this, World);
|
||||
|
||||
Game.Renderer.WorldModelRenderer.BeginFrame();
|
||||
var finalOverlayRenderables = aboveShroud
|
||||
.Concat(aboveShroudSelected)
|
||||
.Concat(aboveShroudEffects)
|
||||
.Concat(aboveShroudOrderGenerator)
|
||||
.Select(r => r.PrepareRender(this))
|
||||
.ToList();
|
||||
Game.Renderer.WorldModelRenderer.EndFrame();
|
||||
var finalOverlayRenderables = GenerateOverlayRenderables(onScreenActors);
|
||||
|
||||
// HACK: Keep old grouping behaviour
|
||||
foreach (var g in finalOverlayRenderables.GroupBy(prs => prs.GetType()))
|
||||
var groupedOverlayRenderables = finalOverlayRenderables.GroupBy(prs => prs.GetType());
|
||||
foreach (var g in groupedOverlayRenderables)
|
||||
foreach (var r in g)
|
||||
r.Render(this);
|
||||
|
||||
if (devTrait.Value != null && devTrait.Value.ShowDebugGeometry)
|
||||
if (debugVis.Value != null && debugVis.Value.RenderGeometry)
|
||||
{
|
||||
for (var i = 0; i < renderables.Count; i++)
|
||||
renderables[i].RenderDebugGeometry(this);
|
||||
|
||||
foreach (var g in finalOverlayRenderables.GroupBy(prs => prs.GetType()))
|
||||
foreach (var g in groupedOverlayRenderables)
|
||||
foreach (var r in g)
|
||||
r.RenderDebugGeometry(this);
|
||||
}
|
||||
|
||||
if (debugVis.Value != null && debugVis.Value.ScreenMap)
|
||||
{
|
||||
foreach (var r in World.ScreenMap.RenderBounds(World.RenderPlayer))
|
||||
Game.Renderer.WorldRgbaColorRenderer.DrawRect(
|
||||
new float3(r.Left, r.Top, r.Bottom),
|
||||
new float3(r.Right, r.Bottom, r.Bottom),
|
||||
1 / Viewport.Zoom, Color.MediumSpringGreen);
|
||||
|
||||
foreach (var r in World.ScreenMap.MouseBounds(World.RenderPlayer))
|
||||
Game.Renderer.WorldRgbaColorRenderer.DrawRect(
|
||||
new float3(r.Left, r.Top, r.Bottom),
|
||||
new float3(r.Right, r.Bottom, r.Bottom),
|
||||
1 / Viewport.Zoom, Color.OrangeRed);
|
||||
}
|
||||
|
||||
Game.Renderer.Flush();
|
||||
}
|
||||
|
||||
|
||||
40
OpenRA.Game/HotkeyDefinition.cs
Normal file
40
OpenRA.Game/HotkeyDefinition.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2017 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public sealed class HotkeyDefinition
|
||||
{
|
||||
public readonly string Name;
|
||||
public readonly Hotkey Default = Hotkey.Invalid;
|
||||
public readonly string Description = "";
|
||||
public readonly HashSet<string> Types = new HashSet<string>();
|
||||
|
||||
public HotkeyDefinition(string name, MiniYaml node)
|
||||
{
|
||||
Name = name;
|
||||
|
||||
if (!string.IsNullOrEmpty(node.Value))
|
||||
Default = FieldLoader.GetValue<Hotkey>("value", node.Value);
|
||||
|
||||
var descriptionNode = node.Nodes.FirstOrDefault(n => n.Key == "Description");
|
||||
if (descriptionNode != null)
|
||||
Description = descriptionNode.Value.Value;
|
||||
|
||||
var typesNode = node.Nodes.FirstOrDefault(n => n.Key == "Types");
|
||||
if (typesNode != null)
|
||||
Types = FieldLoader.GetValue<HashSet<string>>("Types", typesNode.Value.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
77
OpenRA.Game/HotkeyManager.cs
Normal file
77
OpenRA.Game/HotkeyManager.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2017 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.FileSystem;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public sealed class HotkeyManager
|
||||
{
|
||||
readonly Dictionary<string, Hotkey> settings;
|
||||
readonly Dictionary<string, HotkeyDefinition> definitions = new Dictionary<string, HotkeyDefinition>();
|
||||
readonly Dictionary<string, Hotkey> keys = new Dictionary<string, Hotkey>();
|
||||
|
||||
public HotkeyManager(IReadOnlyFileSystem fileSystem, Dictionary<string, Hotkey> settings, Manifest manifest)
|
||||
{
|
||||
this.settings = settings;
|
||||
|
||||
var keyDefinitions = MiniYaml.Load(fileSystem, manifest.Hotkeys, null);
|
||||
foreach (var kd in keyDefinitions)
|
||||
{
|
||||
var definition = new HotkeyDefinition(kd.Key, kd.Value);
|
||||
definitions[kd.Key] = definition;
|
||||
keys[kd.Key] = definition.Default;
|
||||
}
|
||||
|
||||
foreach (var kv in settings)
|
||||
keys[kv.Key] = kv.Value;
|
||||
}
|
||||
|
||||
internal Func<Hotkey> GetHotkeyReference(string name)
|
||||
{
|
||||
// Is this a mod-defined hotkey?
|
||||
if (keys.ContainsKey(name))
|
||||
return () => keys[name];
|
||||
|
||||
// Try and parse as a hardcoded definition
|
||||
Hotkey key;
|
||||
if (!Hotkey.TryParse(name, out key))
|
||||
key = Hotkey.Invalid;
|
||||
|
||||
return () => key;
|
||||
}
|
||||
|
||||
public void Set(string name, Hotkey value)
|
||||
{
|
||||
HotkeyDefinition definition;
|
||||
if (!definitions.TryGetValue(name, out definition))
|
||||
return;
|
||||
|
||||
keys[name] = value;
|
||||
if (value != definition.Default)
|
||||
settings[name] = value;
|
||||
else
|
||||
settings.Remove(name);
|
||||
}
|
||||
|
||||
public HotkeyReference this[string name]
|
||||
{
|
||||
get
|
||||
{
|
||||
return new HotkeyReference(GetHotkeyReference(name));
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<HotkeyDefinition> Definitions { get { return definitions.Values; } }
|
||||
}
|
||||
}
|
||||
46
OpenRA.Game/Input/HotkeyReference.cs
Normal file
46
OpenRA.Game/Input/HotkeyReference.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2017 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;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
/// <summary>
|
||||
/// A reference to either a named hotkey (defined in the game settings) or a statically assigned hotkey
|
||||
/// </summary>
|
||||
public class HotkeyReference
|
||||
{
|
||||
static readonly Func<Hotkey> Invalid = () => Hotkey.Invalid;
|
||||
|
||||
readonly Func<Hotkey> getValue;
|
||||
|
||||
public HotkeyReference()
|
||||
{
|
||||
getValue = Invalid;
|
||||
}
|
||||
|
||||
internal HotkeyReference(Func<Hotkey> getValue)
|
||||
{
|
||||
this.getValue = getValue;
|
||||
}
|
||||
|
||||
public Hotkey GetValue()
|
||||
{
|
||||
return getValue();
|
||||
}
|
||||
|
||||
public bool IsActivatedBy(KeyInput e)
|
||||
{
|
||||
var currentValue = getValue();
|
||||
return currentValue.Key == e.Key && currentValue.Modifiers == e.Modifiers;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ namespace OpenRA
|
||||
Rules, ServerTraits,
|
||||
Sequences, ModelSequences, Cursors, Chrome, Assemblies, ChromeLayout,
|
||||
Weapons, Voices, Notifications, Music, Translations, TileSets,
|
||||
ChromeMetrics, MapCompatibility, Missions;
|
||||
ChromeMetrics, MapCompatibility, Missions, Hotkeys;
|
||||
|
||||
public readonly IReadOnlyDictionary<string, string> Packages;
|
||||
public readonly IReadOnlyDictionary<string, string> MapFolders;
|
||||
@@ -72,7 +72,7 @@ namespace OpenRA
|
||||
|
||||
readonly string[] reservedModuleNames = { "Metadata", "Folders", "MapFolders", "Packages", "Rules",
|
||||
"Sequences", "ModelSequences", "Cursors", "Chrome", "Assemblies", "ChromeLayout", "Weapons",
|
||||
"Voices", "Notifications", "Music", "Translations", "TileSets", "ChromeMetrics", "Missions",
|
||||
"Voices", "Notifications", "Music", "Translations", "TileSets", "ChromeMetrics", "Missions", "Hotkeys",
|
||||
"ServerTraits", "LoadScreen", "Fonts", "SupportsMapsFrom", "SoundFormats", "SpriteFormats",
|
||||
"RequiresMods", "PackageFormats" };
|
||||
|
||||
@@ -111,6 +111,7 @@ namespace OpenRA
|
||||
TileSets = YamlList(yaml, "TileSets");
|
||||
ChromeMetrics = YamlList(yaml, "ChromeMetrics");
|
||||
Missions = YamlList(yaml, "Missions");
|
||||
Hotkeys = YamlList(yaml, "Hotkeys");
|
||||
|
||||
ServerTraits = YamlList(yaml, "ServerTraits");
|
||||
|
||||
|
||||
@@ -40,6 +40,16 @@ namespace OpenRA
|
||||
mapBottomRight = BottomRight.ToMPos(gridType);
|
||||
}
|
||||
|
||||
public CellRegion(MapGridType gridType, MPos topLeft, MPos bottomRight)
|
||||
{
|
||||
this.gridType = gridType;
|
||||
mapTopLeft = topLeft;
|
||||
mapBottomRight = bottomRight;
|
||||
|
||||
TopLeft = topLeft.ToCPos(gridType);
|
||||
BottomRight = bottomRight.ToCPos(gridType);
|
||||
}
|
||||
|
||||
/// <summary>Expand the specified region with an additional cordon. This may expand the region outside the map borders.</summary>
|
||||
public static CellRegion Expand(CellRegion region, int cordon)
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@ using System.Reflection;
|
||||
using System.Text;
|
||||
using OpenRA.FileSystem;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Traits;
|
||||
|
||||
@@ -253,16 +254,27 @@ namespace OpenRA
|
||||
if (!contents.Contains(required))
|
||||
throw new FileNotFoundException("Required file {0} not present in this map".F(required));
|
||||
|
||||
using (var ms = new MemoryStream())
|
||||
var streams = new List<Stream>();
|
||||
try
|
||||
{
|
||||
foreach (var filename in contents)
|
||||
if (filename.EndsWith(".yaml") || filename.EndsWith(".bin") || filename.EndsWith(".lua"))
|
||||
using (var s = package.GetStream(filename))
|
||||
s.CopyTo(ms);
|
||||
streams.Add(package.GetStream(filename));
|
||||
|
||||
// Take the SHA1
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
return CryptoUtil.SHA1Hash(ms);
|
||||
if (streams.Count == 0)
|
||||
return CryptoUtil.SHA1Hash(new byte[0]);
|
||||
|
||||
var merged = streams[0];
|
||||
for (var i = 1; i < streams.Count; i++)
|
||||
merged = new MergedStream(merged, streams[i]);
|
||||
|
||||
return CryptoUtil.SHA1Hash(merged);
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (var stream in streams)
|
||||
stream.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,7 +441,7 @@ namespace OpenRA
|
||||
{
|
||||
var uv = cell.ToMPos(Grid.Type);
|
||||
cellProjection[uv] = new PPos[0];
|
||||
inverseCellProjection[uv] = new List<MPos>();
|
||||
inverseCellProjection[uv] = new List<MPos>(1);
|
||||
}
|
||||
|
||||
// Initialize projections
|
||||
@@ -784,6 +796,19 @@ namespace OpenRA
|
||||
return new WDist(delta.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The size of the map Height step in world units
|
||||
/// </summary>
|
||||
public WDist CellHeightStep
|
||||
{
|
||||
get
|
||||
{
|
||||
// RectangularIsometric defines 1024 units along the diagonal axis,
|
||||
// giving a half-tile height step of sqrt(2) * 512
|
||||
return new WDist(Grid.Type == MapGridType.RectangularIsometric ? 724 : 512);
|
||||
}
|
||||
}
|
||||
|
||||
public CPos CellContaining(WPos pos)
|
||||
{
|
||||
if (Grid.Type == MapGridType.Rectangular)
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace OpenRA
|
||||
{
|
||||
public static readonly MapPreview UnknownMap = new MapPreview(null, null, MapGridType.Rectangular, null);
|
||||
public readonly IReadOnlyDictionary<IReadOnlyPackage, MapClassification> MapLocations;
|
||||
readonly Dictionary<IReadOnlyPackage, MapClassification> mapLocations = new Dictionary<IReadOnlyPackage, MapClassification>();
|
||||
|
||||
readonly Cache<string, MapPreview> previews;
|
||||
readonly ModData modData;
|
||||
@@ -45,8 +46,16 @@ namespace OpenRA
|
||||
previews = new Cache<string, MapPreview>(uid => new MapPreview(modData, uid, gridType.Value, this));
|
||||
sheetBuilder = new SheetBuilder(SheetType.BGRA);
|
||||
|
||||
MapLocations = new ReadOnlyDictionary<IReadOnlyPackage, MapClassification>(mapLocations);
|
||||
}
|
||||
|
||||
public void LoadMaps()
|
||||
{
|
||||
// Utility mod that does not support maps
|
||||
if (!modData.Manifest.Contains<MapGrid>())
|
||||
return;
|
||||
|
||||
// Enumerate map directories
|
||||
var mapLocations = new Dictionary<IReadOnlyPackage, MapClassification>();
|
||||
foreach (var kv in modData.Manifest.MapFolders)
|
||||
{
|
||||
var name = kv.Key;
|
||||
@@ -82,15 +91,6 @@ namespace OpenRA
|
||||
mapLocations.Add(package, classification);
|
||||
}
|
||||
|
||||
MapLocations = new ReadOnlyDictionary<IReadOnlyPackage, MapClassification>(mapLocations);
|
||||
}
|
||||
|
||||
public void LoadMaps()
|
||||
{
|
||||
// Utility mod that does not support maps
|
||||
if (!modData.Manifest.Contains<MapGrid>())
|
||||
return;
|
||||
|
||||
var mapGrid = modData.Manifest.Get<MapGrid>();
|
||||
foreach (var kv in MapLocations)
|
||||
{
|
||||
@@ -122,6 +122,47 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Map> EnumerateMapsWithoutCaching(MapClassification classification = MapClassification.System)
|
||||
{
|
||||
// Utility mod that does not support maps
|
||||
if (!modData.Manifest.Contains<MapGrid>())
|
||||
yield break;
|
||||
|
||||
// Enumerate map directories
|
||||
foreach (var kv in modData.Manifest.MapFolders)
|
||||
{
|
||||
MapClassification packageClassification;
|
||||
if (!Enum.TryParse(kv.Value, out packageClassification))
|
||||
continue;
|
||||
|
||||
if (!classification.HasFlag(packageClassification))
|
||||
continue;
|
||||
|
||||
var name = kv.Key;
|
||||
var optional = name.StartsWith("~", StringComparison.Ordinal);
|
||||
if (optional)
|
||||
name = name.Substring(1);
|
||||
|
||||
// Don't try to open the map directory in the support directory if it doesn't exist
|
||||
if (name.StartsWith("^", StringComparison.Ordinal))
|
||||
{
|
||||
var resolved = Platform.ResolvePath(name);
|
||||
if (!Directory.Exists(resolved) || !File.Exists(resolved))
|
||||
continue;
|
||||
}
|
||||
|
||||
using (var package = (IReadWritePackage)modData.ModFiles.OpenPackage(name))
|
||||
{
|
||||
foreach (var map in package.Contents)
|
||||
{
|
||||
var mapPackage = package.OpenPackage(map, modData.ModFiles);
|
||||
if (mapPackage != null)
|
||||
yield return new Map(modData, mapPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void QueryRemoteMapDetails(string repositoryUrl, IEnumerable<string> uids, Action<MapPreview> mapDetailsReceived = null, Action queryFailed = null)
|
||||
{
|
||||
var maps = uids.Distinct()
|
||||
|
||||
@@ -28,7 +28,14 @@ namespace OpenRA
|
||||
public enum MapStatus { Available, Unavailable, Searching, DownloadAvailable, Downloading, DownloadError }
|
||||
|
||||
// Used for grouping maps in the UI
|
||||
public enum MapClassification { Unknown, System, User, Remote }
|
||||
[Flags]
|
||||
public enum MapClassification
|
||||
{
|
||||
Unknown = 0,
|
||||
System = 1,
|
||||
User = 2,
|
||||
Remote = 4
|
||||
}
|
||||
|
||||
// Used for verifying map availability in the lobby
|
||||
public enum MapRuleStatus { Unknown, Cached, Invalid }
|
||||
@@ -420,7 +427,7 @@ namespace OpenRA
|
||||
|
||||
public void Install(string mapRepositoryUrl, Action onSuccess)
|
||||
{
|
||||
if (Status != MapStatus.DownloadAvailable || !Game.Settings.Game.AllowDownloading)
|
||||
if ((Status != MapStatus.DownloadError && Status != MapStatus.DownloadAvailable) || !Game.Settings.Game.AllowDownloading)
|
||||
return;
|
||||
|
||||
innerData.Status = MapStatus.Downloading;
|
||||
|
||||
@@ -185,7 +185,6 @@ namespace OpenRA
|
||||
public readonly string Id;
|
||||
public readonly int SheetSize = 512;
|
||||
public readonly string Palette;
|
||||
public readonly string PlayerPalette;
|
||||
public readonly Color[] HeightDebugColors = new[] { Color.Red };
|
||||
public readonly string[] EditorTemplateOrder;
|
||||
public readonly bool IgnoreTileSpriteOffsets;
|
||||
|
||||
@@ -257,7 +257,9 @@ namespace OpenRA
|
||||
if (!sources.Any())
|
||||
return new List<MiniYamlNode>();
|
||||
|
||||
var tree = sources.Where(s => s != null).Aggregate(MergePartial)
|
||||
var tree = sources.Where(s => s != null)
|
||||
.Select(MergeSelfPartial)
|
||||
.Aggregate(MergePartial)
|
||||
.ToDictionary(n => n.Key, n => n.Value);
|
||||
|
||||
var resolved = new Dictionary<string, MiniYaml>();
|
||||
@@ -325,6 +327,42 @@ namespace OpenRA
|
||||
return resolved;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges any duplicate keys that are defined within the same set of nodes.
|
||||
/// Does not resolve inheritance or node removals.
|
||||
/// </summary>
|
||||
static MiniYaml MergeSelfPartial(MiniYaml existingNodes)
|
||||
{
|
||||
// Nothing to do
|
||||
if (existingNodes.Nodes == null || existingNodes.Nodes.Count == 0)
|
||||
return existingNodes;
|
||||
|
||||
return new MiniYaml(existingNodes.Value, MergeSelfPartial(existingNodes.Nodes));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges any duplicate keys that are defined within the same set of nodes.
|
||||
/// Does not resolve inheritance or node removals.
|
||||
/// </summary>
|
||||
static List<MiniYamlNode> MergeSelfPartial(List<MiniYamlNode> existingNodes)
|
||||
{
|
||||
var keys = new HashSet<string>();
|
||||
var ret = new List<MiniYamlNode>();
|
||||
foreach (var n in existingNodes)
|
||||
{
|
||||
if (keys.Add(n.Key))
|
||||
ret.Add(n);
|
||||
else
|
||||
{
|
||||
// Node with the same key has already been added: merge new node over the existing one
|
||||
var original = ret.First(r => r.Key == n.Key);
|
||||
original.Value = MergePartial(original.Value, n.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static MiniYaml MergePartial(MiniYaml existingNodes, MiniYaml overrideNodes)
|
||||
{
|
||||
if (existingNodes == null)
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace OpenRA
|
||||
public readonly ISpriteLoader[] SpriteLoaders;
|
||||
public readonly ISpriteSequenceLoader SpriteSequenceLoader;
|
||||
public readonly IModelSequenceLoader ModelSequenceLoader;
|
||||
public readonly HotkeyManager Hotkeys;
|
||||
public ILoadScreen LoadScreen { get; private set; }
|
||||
public CursorProvider CursorProvider { get; private set; }
|
||||
public FS ModFiles;
|
||||
@@ -89,6 +90,8 @@ namespace OpenRA
|
||||
ModelSequenceLoader = (IModelSequenceLoader)modelCtor.Invoke(new[] { this });
|
||||
ModelSequenceLoader.OnMissingModelError = s => Log.Write("debug", s);
|
||||
|
||||
Hotkeys = new HotkeyManager(ModFiles, Game.Settings.Keys, Manifest);
|
||||
|
||||
defaultRules = Exts.Lazy(() => Ruleset.LoadDefaults(this));
|
||||
defaultTileSets = Exts.Lazy(() =>
|
||||
{
|
||||
|
||||
@@ -77,10 +77,10 @@ namespace OpenRA.Network
|
||||
|
||||
public virtual void SendSync(int frame, byte[] syncData)
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
var ms = new MemoryStream(4 + syncData.Length);
|
||||
ms.Write(BitConverter.GetBytes(frame));
|
||||
ms.Write(syncData);
|
||||
Send(ms.ToArray());
|
||||
Send(ms.GetBuffer());
|
||||
}
|
||||
|
||||
protected virtual void Send(byte[] packet)
|
||||
@@ -197,10 +197,10 @@ namespace OpenRA.Network
|
||||
|
||||
public override void SendSync(int frame, byte[] syncData)
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
var ms = new MemoryStream(4 + syncData.Length);
|
||||
ms.Write(BitConverter.GetBytes(frame));
|
||||
ms.Write(syncData);
|
||||
queuedSyncPackets.Add(ms.ToArray());
|
||||
queuedSyncPackets.Add(ms.GetBuffer());
|
||||
}
|
||||
|
||||
protected override void Send(byte[] packet)
|
||||
|
||||
@@ -9,62 +9,220 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using OpenRA.Graphics;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
public class GameClient
|
||||
{
|
||||
public readonly string Name;
|
||||
public readonly HSLColor Color;
|
||||
public readonly string Faction;
|
||||
public readonly int Team;
|
||||
public readonly int SpawnPoint;
|
||||
public readonly bool IsAdmin;
|
||||
public readonly bool IsSpectator;
|
||||
public readonly bool IsBot;
|
||||
|
||||
public GameClient() { }
|
||||
|
||||
public GameClient(Session.Client c)
|
||||
{
|
||||
Name = c.Name;
|
||||
Color = c.Color;
|
||||
Faction = c.Faction;
|
||||
Team = c.Team;
|
||||
SpawnPoint = c.SpawnPoint;
|
||||
IsAdmin = c.IsAdmin;
|
||||
IsSpectator = c.Slot == null && c.Bot == null;
|
||||
IsBot = c.Bot != null;
|
||||
}
|
||||
}
|
||||
|
||||
public class GameServer
|
||||
{
|
||||
public readonly int Id = 0;
|
||||
static readonly string[] SerializeFields = { "Name", "Address", "Mod", "Version", "Map", "State", "MaxPlayers", "Protected" };
|
||||
public const int ProtocolVersion = 2;
|
||||
|
||||
/// <summary>Online game number or -1 for LAN games</summary>
|
||||
public readonly int Id = -1;
|
||||
|
||||
/// <summary>Name of the server</summary>
|
||||
public readonly string Name = null;
|
||||
|
||||
/// <summary>ip:port string to connect to.</summary>
|
||||
public readonly string Address = null;
|
||||
|
||||
/// <summary>Port of the server</summary>
|
||||
public readonly int Port = 0;
|
||||
|
||||
/// <summary>The current state of the server (waiting/playing/completed)</summary>
|
||||
public readonly int State = 0;
|
||||
public readonly int Players = 0;
|
||||
|
||||
/// <summary>The number of slots available for non-bot players</summary>
|
||||
public readonly int MaxPlayers = 0;
|
||||
public readonly int Bots = 0;
|
||||
public readonly int Spectators = 0;
|
||||
|
||||
/// <summary>UID of the map</summary>
|
||||
public readonly string Map = null;
|
||||
public readonly string Mods = "";
|
||||
public readonly int TTL = 0;
|
||||
|
||||
/// <summary>Mod ID</summary>
|
||||
public readonly string Mod = "";
|
||||
|
||||
/// <summary>Mod Version</summary>
|
||||
public readonly string Version = "";
|
||||
|
||||
/// <summary>Password protected</summary>
|
||||
public readonly bool Protected = false;
|
||||
|
||||
/// <summary>UTC datetime string when the game changed to the Playing state</summary>
|
||||
public readonly string Started = null;
|
||||
|
||||
/// <summary>Number of non-spectator, non-bot players. Only defined if GameServer is parsed from yaml.</summary>
|
||||
public readonly int Players = 0;
|
||||
|
||||
/// <summary>Number of bot players. Only defined if GameServer is parsed from yaml.</summary>
|
||||
public readonly int Bots = 0;
|
||||
|
||||
/// <summary>Number of spectators. Only defined if GameServer is parsed from yaml.</summary>
|
||||
public readonly int Spectators = 0;
|
||||
|
||||
/// <summary>Number of seconds that the game has been in the Playing state. Only defined if GameServer is parsed from yaml.</summary>
|
||||
public readonly int PlayTime = -1;
|
||||
|
||||
/// <summary>Can we join this server (after switching mods if required)? Only defined if GameServer is parsed from yaml.</summary>
|
||||
[FieldLoader.Ignore]
|
||||
public readonly bool IsCompatible = false;
|
||||
|
||||
/// <summary>Can we join this server (after switching mods if required)? Only defined if GameServer is parsed from yaml.</summary>
|
||||
[FieldLoader.Ignore]
|
||||
public readonly bool IsJoinable = false;
|
||||
|
||||
/// <summary>Label to display in the multiplayer browser. Only defined if GameServer is parsed from yaml.</summary>
|
||||
[FieldLoader.Ignore]
|
||||
public readonly string ModLabel = "";
|
||||
public readonly string ModId = "";
|
||||
public readonly string ModVersion = "";
|
||||
|
||||
[FieldLoader.LoadUsing("LoadClients")]
|
||||
public readonly GameClient[] Clients;
|
||||
|
||||
static object LoadClients(MiniYaml yaml)
|
||||
{
|
||||
var clients = new List<GameClient>();
|
||||
var clientsNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Clients");
|
||||
if (clientsNode != null)
|
||||
{
|
||||
var regex = new Regex(@"Client@\d+");
|
||||
foreach (var client in clientsNode.Value.Nodes)
|
||||
if (regex.IsMatch(client.Key))
|
||||
clients.Add(FieldLoader.Load<GameClient>(client.Value));
|
||||
}
|
||||
|
||||
return clients.ToArray();
|
||||
}
|
||||
|
||||
public GameServer(MiniYaml yaml)
|
||||
{
|
||||
FieldLoader.Load(this, yaml);
|
||||
|
||||
// Games advertised using the old API used a single Mods field
|
||||
if (Mod == null || Version == null)
|
||||
{
|
||||
var modsNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Mods");
|
||||
if (modsNode != null)
|
||||
{
|
||||
var modVersion = modsNode.Value.Value.Split('@');
|
||||
Mod = modVersion[0];
|
||||
Version = modVersion[1];
|
||||
}
|
||||
}
|
||||
|
||||
// Games advertised using the old API calculated the play time locally
|
||||
if (State == 2 && PlayTime < 0)
|
||||
{
|
||||
DateTime startTime;
|
||||
if (DateTime.TryParse(Started, out startTime))
|
||||
PlayTime = (int)(DateTime.UtcNow - startTime).TotalSeconds;
|
||||
}
|
||||
|
||||
Manifest mod;
|
||||
ExternalMod external;
|
||||
var modVersion = Mods.Split('@');
|
||||
|
||||
ModLabel = "Unknown mod: {0}".F(Mods);
|
||||
if (modVersion.Length == 2)
|
||||
var externalKey = ExternalMod.MakeKey(Mod, Version);
|
||||
if (Game.ExternalMods.TryGetValue(externalKey, out external) && external.Version == Version)
|
||||
{
|
||||
ModId = modVersion[0];
|
||||
ModVersion = modVersion[1];
|
||||
ModLabel = "{0} ({1})".F(external.Title, external.Version);
|
||||
IsCompatible = true;
|
||||
}
|
||||
else if (Game.Mods.TryGetValue(Mod, out mod))
|
||||
{
|
||||
// Use internal mod data to populate the section header, but
|
||||
// on-connect switching must use the external mod plumbing.
|
||||
ModLabel = "{0} ({1})".F(mod.Metadata.Title, Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Some platforms (e.g. macOS) package each mod separately, so the Mods check above won't work.
|
||||
// Guess based on the most recent ExternalMod instead.
|
||||
var guessMod = Game.ExternalMods.Values
|
||||
.OrderByDescending(m => m.Version)
|
||||
.FirstOrDefault(m => m.Id == Mod);
|
||||
|
||||
var externalKey = ExternalMod.MakeKey(modVersion[0], modVersion[1]);
|
||||
if (Game.ExternalMods.TryGetValue(externalKey, out external)
|
||||
&& external.Version == modVersion[1])
|
||||
{
|
||||
ModLabel = "{0} ({1})".F(external.Title, external.Version);
|
||||
IsCompatible = true;
|
||||
}
|
||||
else if (Game.Mods.TryGetValue(modVersion[0], out mod))
|
||||
{
|
||||
// Use internal mod data to populate the section header, but
|
||||
// on-connect switching must use the external mod plumbing.
|
||||
ModLabel = "{0} ({1})".F(mod.Metadata.Title, modVersion[1]);
|
||||
}
|
||||
if (guessMod != null)
|
||||
ModLabel = "{0} ({1})".F(guessMod.Title, Version);
|
||||
else
|
||||
ModLabel = "Unknown mod: {0} ({1})".F(Mod, Version);
|
||||
}
|
||||
|
||||
var mapAvailable = Game.Settings.Game.AllowDownloading || Game.ModData.MapCache[Map].Status == MapStatus.Available;
|
||||
IsJoinable = IsCompatible && State == 1 && mapAvailable;
|
||||
}
|
||||
|
||||
public GameServer(Server.Server server)
|
||||
{
|
||||
Name = server.Settings.Name;
|
||||
|
||||
// IP address will be replaced with a real value by the master server / receiving LAN client
|
||||
Address = "0.0.0.0:" + server.Settings.ListenPort.ToString();
|
||||
State = (int)server.State;
|
||||
MaxPlayers = server.LobbyInfo.Slots.Count(s => !s.Value.Closed) - server.LobbyInfo.Clients.Count(c1 => c1.Bot != null);
|
||||
Map = server.Map.Uid;
|
||||
Mod = server.ModData.Manifest.Id;
|
||||
Version = server.ModData.Manifest.Metadata.Version;
|
||||
Protected = !string.IsNullOrEmpty(server.Settings.Password);
|
||||
Clients = server.LobbyInfo.Clients.Select(c => new GameClient(c)).ToArray();
|
||||
}
|
||||
|
||||
public string ToPOSTData(bool lanGame)
|
||||
{
|
||||
var root = new List<MiniYamlNode>() { new MiniYamlNode("Protocol", ProtocolVersion.ToString()) };
|
||||
foreach (var field in SerializeFields)
|
||||
root.Add(FieldSaver.SaveField(this, field));
|
||||
|
||||
if (lanGame)
|
||||
{
|
||||
// Add fields that are normally generated by the master server
|
||||
// LAN games overload the Id with a GUID string (rather than an ID) to allow deduplication
|
||||
root.Add(new MiniYamlNode("Id", Platform.SessionGUID.ToString()));
|
||||
root.Add(new MiniYamlNode("Players", Clients.Count(c => !c.IsBot && !c.IsSpectator).ToString()));
|
||||
root.Add(new MiniYamlNode("Spectators", Clients.Count(c => c.IsSpectator).ToString()));
|
||||
root.Add(new MiniYamlNode("Bots", Clients.Count(c => c.IsBot).ToString()));
|
||||
|
||||
// Included for backwards compatibility with older clients that don't support separated Mod/Version.
|
||||
root.Add(new MiniYamlNode("Mods", Mod + "@" + Version));
|
||||
}
|
||||
|
||||
var clientsNode = new MiniYaml("");
|
||||
var i = 0;
|
||||
foreach (var c in Clients)
|
||||
clientsNode.Nodes.Add(new MiniYamlNode("Client@" + (i++).ToString(), FieldSaver.Save(c)));
|
||||
|
||||
root.Add(new MiniYamlNode("Clients", clientsNode));
|
||||
return new MiniYaml("", root)
|
||||
.ToLines("Game")
|
||||
.JoinWith("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,15 +11,16 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
[Flags]
|
||||
enum OrderFields : byte
|
||||
{
|
||||
TargetActor = 0x01,
|
||||
TargetLocation = 0x02,
|
||||
Target = 0x01,
|
||||
TargetString = 0x04,
|
||||
Queued = 0x08,
|
||||
ExtraLocation = 0x10,
|
||||
@@ -39,8 +40,7 @@ namespace OpenRA
|
||||
public readonly string OrderString;
|
||||
public readonly Actor Subject;
|
||||
public readonly bool Queued;
|
||||
public Actor TargetActor;
|
||||
public CPos TargetLocation;
|
||||
public readonly Target Target;
|
||||
public string TargetString;
|
||||
public CPos ExtraLocation;
|
||||
public uint ExtraData;
|
||||
@@ -48,15 +48,29 @@ namespace OpenRA
|
||||
public bool SuppressVisualFeedback;
|
||||
public Actor VisualFeedbackTarget;
|
||||
|
||||
/// <summary>
|
||||
/// DEPRECATED: Use Target instead.
|
||||
/// </summary>
|
||||
public Actor TargetActor { get { return Target.SerializableActor; } }
|
||||
|
||||
/// <summary>
|
||||
/// DEPRECATED: Use Target instead.
|
||||
/// </summary>
|
||||
public CPos TargetLocation
|
||||
{
|
||||
get
|
||||
{
|
||||
return Target.SerializableCell.HasValue ? Target.SerializableCell.Value : CPos.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
public Player Player { get { return Subject != null ? Subject.Owner : null; } }
|
||||
|
||||
Order(string orderString, Actor subject,
|
||||
Actor targetActor, CPos targetLocation, string targetString, bool queued, CPos extraLocation, uint extraData)
|
||||
Order(string orderString, Actor subject, Target target, string targetString, bool queued, CPos extraLocation, uint extraData)
|
||||
{
|
||||
OrderString = orderString;
|
||||
Subject = subject;
|
||||
TargetActor = targetActor;
|
||||
TargetLocation = targetLocation;
|
||||
Target = target;
|
||||
TargetString = targetString;
|
||||
Queued = queued;
|
||||
ExtraLocation = extraLocation;
|
||||
@@ -74,21 +88,65 @@ namespace OpenRA
|
||||
var subjectId = r.ReadUInt32();
|
||||
var flags = (OrderFields)r.ReadByte();
|
||||
|
||||
var targetActorId = flags.HasField(OrderFields.TargetActor) ? r.ReadUInt32() : uint.MaxValue;
|
||||
var targetLocation = (CPos)(flags.HasField(OrderFields.TargetLocation) ? r.ReadInt2() : int2.Zero);
|
||||
Actor subject = null;
|
||||
if (world != null)
|
||||
TryGetActorFromUInt(world, subjectId, out subject);
|
||||
|
||||
var target = Target.Invalid;
|
||||
if (flags.HasField(OrderFields.Target))
|
||||
{
|
||||
switch ((TargetType)r.ReadByte())
|
||||
{
|
||||
case TargetType.Actor:
|
||||
{
|
||||
Actor targetActor;
|
||||
if (world != null && TryGetActorFromUInt(world, r.ReadUInt32(), out targetActor))
|
||||
target = Target.FromActor(targetActor);
|
||||
break;
|
||||
}
|
||||
|
||||
case TargetType.FrozenActor:
|
||||
{
|
||||
var playerActorID = r.ReadUInt32();
|
||||
var frozenActorID = r.ReadUInt32();
|
||||
|
||||
Actor playerActor;
|
||||
if (world == null || !TryGetActorFromUInt(world, playerActorID, out playerActor))
|
||||
break;
|
||||
|
||||
var frozenLayer = playerActor.TraitOrDefault<FrozenActorLayer>();
|
||||
if (frozenLayer == null)
|
||||
break;
|
||||
|
||||
var frozen = frozenLayer.FromID(frozenActorID);
|
||||
if (frozen != null)
|
||||
target = Target.FromFrozenActor(frozen);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case TargetType.Terrain:
|
||||
{
|
||||
if (world != null)
|
||||
target = Target.FromCell(world, (CPos)r.ReadInt2());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var targetString = flags.HasField(OrderFields.TargetString) ? r.ReadString() : null;
|
||||
var queued = flags.HasField(OrderFields.Queued);
|
||||
var extraLocation = (CPos)(flags.HasField(OrderFields.ExtraLocation) ? r.ReadInt2() : int2.Zero);
|
||||
var extraData = flags.HasField(OrderFields.ExtraData) ? r.ReadUInt32() : 0;
|
||||
|
||||
if (world == null)
|
||||
return new Order(order, null, null, targetLocation, targetString, queued, extraLocation, extraData);
|
||||
return new Order(order, null, target, targetString, queued, extraLocation, extraData);
|
||||
|
||||
Actor subject, targetActor;
|
||||
if (!TryGetActorFromUInt(world, subjectId, out subject) || !TryGetActorFromUInt(world, targetActorId, out targetActor))
|
||||
if (subject == null && subjectId != uint.MaxValue)
|
||||
return null;
|
||||
|
||||
return new Order(order, subject, targetActor, targetLocation, targetString, queued, extraLocation, extraData);
|
||||
return new Order(order, subject, target, targetString, queued, extraLocation, extraData);
|
||||
}
|
||||
|
||||
case 0xfe:
|
||||
@@ -169,68 +227,83 @@ namespace OpenRA
|
||||
|
||||
// For scripting special powers
|
||||
public Order()
|
||||
: this(null, null, null, CPos.Zero, null, false, CPos.Zero, 0) { }
|
||||
: this(null, null, Target.Invalid, null, false, CPos.Zero, 0) { }
|
||||
|
||||
public Order(string orderString, Actor subject, bool queued)
|
||||
: this(orderString, subject, null, CPos.Zero, null, queued, CPos.Zero, 0) { }
|
||||
: this(orderString, subject, Target.Invalid, null, queued, CPos.Zero, 0) { }
|
||||
|
||||
public Order(string orderString, Actor subject, Target target, bool queued)
|
||||
: this(orderString, subject, target, null, queued, CPos.Zero, 0) { }
|
||||
|
||||
public Order(string orderstring, Order order)
|
||||
: this(orderstring, order.Subject, order.TargetActor, order.TargetLocation,
|
||||
: this(orderstring, order.Subject, order.Target,
|
||||
order.TargetString, order.Queued, order.ExtraLocation, order.ExtraData) { }
|
||||
|
||||
public byte[] Serialize()
|
||||
{
|
||||
var minLength = OrderString.Length + 1 + (IsImmediate ? 1 + TargetString.Length + 1 : 6);
|
||||
var ret = new MemoryStream(minLength);
|
||||
var w = new BinaryWriter(ret);
|
||||
|
||||
if (IsImmediate)
|
||||
{
|
||||
var ret = new MemoryStream();
|
||||
var w = new BinaryWriter(ret);
|
||||
w.Write((byte)0xfe);
|
||||
w.Write((byte)0xFE);
|
||||
w.Write(OrderString);
|
||||
w.Write(TargetString);
|
||||
return ret.ToArray();
|
||||
}
|
||||
|
||||
switch (OrderString)
|
||||
w.Write((byte)0xFF);
|
||||
w.Write(OrderString);
|
||||
w.Write(UIntFromActor(Subject));
|
||||
|
||||
OrderFields fields = 0;
|
||||
if (Target.SerializableType != TargetType.Invalid)
|
||||
fields |= OrderFields.Target;
|
||||
|
||||
if (TargetString != null)
|
||||
fields |= OrderFields.TargetString;
|
||||
|
||||
if (Queued)
|
||||
fields |= OrderFields.Queued;
|
||||
|
||||
if (ExtraLocation != CPos.Zero)
|
||||
fields |= OrderFields.ExtraLocation;
|
||||
|
||||
if (ExtraData != 0)
|
||||
fields |= OrderFields.ExtraData;
|
||||
|
||||
w.Write((byte)fields);
|
||||
|
||||
if (fields.HasField(OrderFields.Target))
|
||||
{
|
||||
/*
|
||||
* Format:
|
||||
* u8: orderID.
|
||||
* 0xFF: Full serialized order.
|
||||
* varies: rest of order.
|
||||
*/
|
||||
default:
|
||||
// TODO: specific serializers for specific orders.
|
||||
{
|
||||
var ret = new MemoryStream();
|
||||
var w = new BinaryWriter(ret);
|
||||
w.Write((byte)0xFF);
|
||||
w.Write(OrderString);
|
||||
w.Write(UIntFromActor(Subject));
|
||||
|
||||
OrderFields fields = 0;
|
||||
if (TargetActor != null) fields |= OrderFields.TargetActor;
|
||||
if (TargetLocation != CPos.Zero) fields |= OrderFields.TargetLocation;
|
||||
if (TargetString != null) fields |= OrderFields.TargetString;
|
||||
if (Queued) fields |= OrderFields.Queued;
|
||||
if (ExtraLocation != CPos.Zero) fields |= OrderFields.ExtraLocation;
|
||||
if (ExtraData != 0) fields |= OrderFields.ExtraData;
|
||||
|
||||
w.Write((byte)fields);
|
||||
|
||||
if (TargetActor != null)
|
||||
w.Write(UIntFromActor(TargetActor));
|
||||
if (TargetLocation != CPos.Zero)
|
||||
w.Write(TargetLocation);
|
||||
if (TargetString != null)
|
||||
w.Write(TargetString);
|
||||
if (ExtraLocation != CPos.Zero)
|
||||
w.Write(ExtraLocation);
|
||||
if (ExtraData != 0)
|
||||
w.Write(ExtraData);
|
||||
|
||||
return ret.ToArray();
|
||||
}
|
||||
w.Write((byte)Target.SerializableType);
|
||||
switch (Target.SerializableType)
|
||||
{
|
||||
case TargetType.Actor:
|
||||
w.Write(UIntFromActor(Target.SerializableActor));
|
||||
break;
|
||||
case TargetType.FrozenActor:
|
||||
w.Write(Target.FrozenActor.Viewer.PlayerActor.ActorID);
|
||||
w.Write(Target.FrozenActor.ID);
|
||||
break;
|
||||
case TargetType.Terrain:
|
||||
// SerializableCell is guaranteed to be non-null if Type == TargetType.Terrain
|
||||
w.Write(Target.SerializableCell.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fields.HasField(OrderFields.TargetString))
|
||||
w.Write(TargetString);
|
||||
|
||||
if (fields.HasField(OrderFields.ExtraLocation))
|
||||
w.Write(ExtraLocation);
|
||||
|
||||
if (fields.HasField(OrderFields.ExtraData))
|
||||
w.Write(ExtraData);
|
||||
|
||||
return ret.ToArray();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
||||
@@ -38,14 +38,14 @@ namespace OpenRA.Network
|
||||
|
||||
public static byte[] SerializeSync(int sync)
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
var ms = new MemoryStream(1 + 4);
|
||||
using (var writer = new BinaryWriter(ms))
|
||||
{
|
||||
writer.Write((byte)0x65);
|
||||
writer.Write(sync);
|
||||
}
|
||||
|
||||
return ms.ToArray();
|
||||
return ms.GetBuffer();
|
||||
}
|
||||
|
||||
public static int2 ReadInt2(this BinaryReader r)
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace OpenRA.Network
|
||||
class Chunk
|
||||
{
|
||||
public int Frame;
|
||||
public List<Pair<int, byte[]>> Packets = new List<Pair<int, byte[]>>();
|
||||
public Pair<int, byte[]>[] Packets;
|
||||
}
|
||||
|
||||
Queue<Chunk> chunks = new Queue<Chunk>();
|
||||
@@ -45,6 +45,8 @@ 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 chunk = new Chunk();
|
||||
|
||||
while (rs.Position < rs.Length)
|
||||
@@ -55,7 +57,7 @@ namespace OpenRA.Network
|
||||
var packetLen = rs.ReadInt32();
|
||||
var packet = rs.ReadBytes(packetLen);
|
||||
var frame = BitConverter.ToInt32(packet, 0);
|
||||
chunk.Packets.Add(Pair.New(client, packet));
|
||||
packets.Add(Pair.New(client, packet));
|
||||
|
||||
if (frame != int.MaxValue &&
|
||||
(!lastClientsFrame.ContainsKey(client) || frame > lastClientsFrame[client]))
|
||||
@@ -81,6 +83,8 @@ namespace OpenRA.Network
|
||||
{
|
||||
// Regular order - finalize the chunk
|
||||
chunk.Frame = frame;
|
||||
chunk.Packets = packets.ToArray();
|
||||
packets.Clear();
|
||||
chunks.Enqueue(chunk);
|
||||
chunk = new Chunk();
|
||||
|
||||
@@ -123,10 +127,10 @@ namespace OpenRA.Network
|
||||
|
||||
public void SendSync(int frame, byte[] syncData)
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
var ms = new MemoryStream(4 + syncData.Length);
|
||||
ms.Write(BitConverter.GetBytes(frame));
|
||||
ms.Write(syncData);
|
||||
sync.Add(ms.ToArray());
|
||||
sync.Add(ms.GetBuffer());
|
||||
|
||||
// Store the current frame so Receive() can return the next chunk of orders.
|
||||
ordersFrame = frame + LobbyInfo.GlobalSettings.OrderLatency;
|
||||
|
||||
@@ -180,13 +180,11 @@ namespace OpenRA.Network
|
||||
|
||||
public class LobbyOptionState
|
||||
{
|
||||
public bool Locked;
|
||||
public string Value;
|
||||
public string PreferredValue;
|
||||
|
||||
public LobbyOptionState() { }
|
||||
|
||||
public bool Enabled { get { return Value == "True"; } }
|
||||
public bool IsLocked;
|
||||
public bool IsEnabled { get { return Value == "True"; } }
|
||||
}
|
||||
|
||||
public class Global
|
||||
@@ -228,7 +226,7 @@ namespace OpenRA.Network
|
||||
{
|
||||
LobbyOptionState option;
|
||||
if (LobbyOptions.TryGetValue(id, out option))
|
||||
return option.Enabled;
|
||||
return option.IsEnabled;
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ using OpenRA.Primitives;
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
using System.Globalization;
|
||||
using NamesValuesPair = Pair<string[], object[]>;
|
||||
|
||||
class SyncReport
|
||||
{
|
||||
@@ -31,13 +30,13 @@ namespace OpenRA.Network
|
||||
readonly Report[] syncReports = new Report[NumSyncReports];
|
||||
int curIndex = 0;
|
||||
|
||||
static NamesValuesPair DumpSyncTrait(ISync sync)
|
||||
static Pair<string[], Values> DumpSyncTrait(ISync sync)
|
||||
{
|
||||
var type = sync.GetType();
|
||||
TypeInfo typeInfo;
|
||||
lock (typeInfoCache)
|
||||
typeInfo = typeInfoCache[type];
|
||||
var values = new object[typeInfo.Names.Length];
|
||||
var values = new Values(typeInfo.Names.Length);
|
||||
var index = 0;
|
||||
|
||||
foreach (var func in typeInfo.SerializableCopyOfMemberFunctions)
|
||||
@@ -68,28 +67,37 @@ namespace OpenRA.Network
|
||||
report.Effects.Clear();
|
||||
|
||||
foreach (var actor in orderManager.World.ActorsHavingTrait<ISync>())
|
||||
{
|
||||
foreach (var syncHash in actor.SyncHashes)
|
||||
if (syncHash.Hash != 0)
|
||||
{
|
||||
var hash = syncHash.Hash();
|
||||
if (hash != 0)
|
||||
{
|
||||
report.Traits.Add(new TraitReport()
|
||||
{
|
||||
ActorID = actor.ActorID,
|
||||
Type = actor.Info.Name,
|
||||
Owner = (actor.Owner == null) ? "null" : actor.Owner.PlayerName,
|
||||
Trait = syncHash.Trait.GetType().Name,
|
||||
Hash = syncHash.Hash,
|
||||
Hash = hash,
|
||||
NamesValues = DumpSyncTrait(syncHash.Trait)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var sync in orderManager.World.SyncedEffects)
|
||||
{
|
||||
var hash = Sync.Hash(sync);
|
||||
if (hash != 0)
|
||||
{
|
||||
report.Effects.Add(new EffectReport()
|
||||
{
|
||||
Name = sync.GetType().Name,
|
||||
Hash = hash,
|
||||
NamesValues = DumpSyncTrait(sync)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,14 +164,14 @@ namespace OpenRA.Network
|
||||
public string Owner;
|
||||
public string Trait;
|
||||
public int Hash;
|
||||
public NamesValuesPair NamesValues;
|
||||
public Pair<string[], Values> NamesValues;
|
||||
}
|
||||
|
||||
struct EffectReport
|
||||
{
|
||||
public string Name;
|
||||
public int Hash;
|
||||
public NamesValuesPair NamesValues;
|
||||
public Pair<string[], Values> NamesValues;
|
||||
}
|
||||
|
||||
struct TypeInfo
|
||||
@@ -249,5 +257,68 @@ namespace OpenRA.Network
|
||||
return Expression.Lambda<Func<ISync, string>>(getString, name, new[] { SyncParam }).Compile();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Holds up to 4 objects directly, or else allocates an array to hold the items. This allows us to record
|
||||
/// trait values for traits with up to 4 sync members inline without having to allocate extra memory.
|
||||
/// </summary>
|
||||
struct Values
|
||||
{
|
||||
static readonly object Sentinel = new object();
|
||||
|
||||
object item1OrArray;
|
||||
object item2OrSentinel;
|
||||
object item3;
|
||||
object item4;
|
||||
|
||||
public Values(int size)
|
||||
{
|
||||
item1OrArray = null;
|
||||
item2OrSentinel = null;
|
||||
item3 = null;
|
||||
item4 = null;
|
||||
if (size > 4)
|
||||
{
|
||||
item1OrArray = new object[size];
|
||||
item2OrSentinel = Sentinel;
|
||||
}
|
||||
}
|
||||
|
||||
public object this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (item2OrSentinel == Sentinel)
|
||||
return ((object[])item1OrArray)[index];
|
||||
|
||||
switch (index)
|
||||
{
|
||||
case 0: return item1OrArray;
|
||||
case 1: return item2OrSentinel;
|
||||
case 2: return item3;
|
||||
case 3: return item4;
|
||||
default: throw new ArgumentOutOfRangeException("index");
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (item2OrSentinel == Sentinel)
|
||||
{
|
||||
((object[])item1OrArray)[index] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (index)
|
||||
{
|
||||
case 0: item1OrArray = value; break;
|
||||
case 1: item2OrSentinel = value; break;
|
||||
case 2: item3 = value; break;
|
||||
case 3: item4 = value; break;
|
||||
default: throw new ArgumentOutOfRangeException("index");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,15 +20,22 @@ using Open.Nat;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
public enum UPnPStatus { Enabled, Disabled, NotSupported }
|
||||
|
||||
public class UPnP
|
||||
{
|
||||
static NatDevice natDevice;
|
||||
static Mapping mapping;
|
||||
static bool initialized;
|
||||
|
||||
public static IPAddress ExternalIP { get; private set; }
|
||||
public static UPnPStatus Status { get { return initialized ? natDevice != null ?
|
||||
UPnPStatus.Enabled : UPnPStatus.NotSupported : UPnPStatus.Disabled; } }
|
||||
|
||||
public static async Task DiscoverNatDevices(int timeout)
|
||||
{
|
||||
initialized = true;
|
||||
|
||||
NatDiscoverer.TraceSource.Switch.Level = SourceLevels.Verbose;
|
||||
var logChannel = Log.Channel("nat");
|
||||
NatDiscoverer.TraceSource.Listeners.Add(new TextWriterTraceListener(logChannel.Writer));
|
||||
|
||||
@@ -16,8 +16,9 @@ using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
static class UnitOrders
|
||||
public static class UnitOrders
|
||||
{
|
||||
public const int ChatMessageMaxLength = 2500;
|
||||
const string ServerChatName = "Battlefield Control";
|
||||
|
||||
static Player FindPlayerByClient(this World world, Session.Client c)
|
||||
@@ -28,7 +29,7 @@ namespace OpenRA.Network
|
||||
p => (p.ClientIndex == c.Index && p.PlayerReference.Playable));
|
||||
}
|
||||
|
||||
public static void ProcessOrder(OrderManager orderManager, World world, int clientId, Order order)
|
||||
internal static void ProcessOrder(OrderManager orderManager, World world, int clientId, Order order)
|
||||
{
|
||||
if (world != null)
|
||||
{
|
||||
@@ -42,6 +43,12 @@ namespace OpenRA.Network
|
||||
case "Chat":
|
||||
{
|
||||
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
|
||||
|
||||
// Cut chat messages to the hard limit to avoid exploits
|
||||
var message = order.TargetString;
|
||||
if (message.Length > ChatMessageMaxLength)
|
||||
message = order.TargetString.Substring(0, ChatMessageMaxLength);
|
||||
|
||||
if (client != null)
|
||||
{
|
||||
var player = world != null ? world.FindPlayerByClient(client) : null;
|
||||
@@ -51,10 +58,10 @@ namespace OpenRA.Network
|
||||
if (orderManager.LocalClient != null && client != orderManager.LocalClient && client.Team > 0 && client.Team == orderManager.LocalClient.Team)
|
||||
suffix += " (Ally)";
|
||||
|
||||
Game.AddChatLine(client.Color.RGB, client.Name + suffix, order.TargetString);
|
||||
Game.AddChatLine(client.Color.RGB, client.Name + suffix, message);
|
||||
}
|
||||
else
|
||||
Game.AddChatLine(Color.White, "(player {0})".F(clientId), order.TargetString);
|
||||
Game.AddChatLine(Color.White, "(player {0})".F(clientId), message);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<StartArguments>Game.Mod=ra</StartArguments>
|
||||
<Commandlineparameters>Game.Mod=ra</Commandlineparameters>
|
||||
<LangVersion>5</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
@@ -97,10 +98,6 @@
|
||||
<HintPath>..\thirdparty\download\MaxMind.Db.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="SmarIrc4net">
|
||||
<HintPath>..\thirdparty\download\SmarIrc4net.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Activities\Activity.cs" />
|
||||
@@ -175,21 +172,22 @@
|
||||
<Compile Include="Server\ServerOrder.cs" />
|
||||
<Compile Include="Server\TraitInterfaces.cs" />
|
||||
<Compile Include="Support\Arguments.cs" />
|
||||
<Compile Include="Support\ExceptionHandler.cs" />
|
||||
<Compile Include="Support\LaunchArguments.cs" />
|
||||
<Compile Include="Support\PerfHistory.cs" />
|
||||
<Compile Include="Support\Program.cs" />
|
||||
<Compile Include="Sync.cs" />
|
||||
<Compile Include="TraitDictionary.cs" />
|
||||
<Compile Include="Traits\LintAttributes.cs" />
|
||||
<Compile Include="Traits\Player\DeveloperMode.cs" />
|
||||
<Compile Include="Traits\Player\PlayerResources.cs" />
|
||||
<Compile Include="Traits\Player\Shroud.cs" />
|
||||
<Compile Include="Traits\Selectable.cs" />
|
||||
<Compile Include="Traits\Target.cs" />
|
||||
<Compile Include="Traits\TraitsInterfaces.cs" />
|
||||
<Compile Include="Traits\World\Faction.cs" />
|
||||
<Compile Include="Traits\World\ResourceType.cs" />
|
||||
<Compile Include="Traits\World\ScreenShaker.cs" />
|
||||
<Compile Include="Traits\World\Shroud.cs" />
|
||||
<Compile Include="Traits\World\DebugVisualizations.cs" />
|
||||
<Compile Include="World.cs" />
|
||||
<Compile Include="WorldUtils.cs" />
|
||||
<Compile Include="VoiceExts.cs" />
|
||||
@@ -223,10 +221,10 @@
|
||||
<Compile Include="Input\InputHandler.cs" />
|
||||
<Compile Include="Input\Keycode.cs" />
|
||||
<Compile Include="Input\Hotkey.cs" />
|
||||
<Compile Include="Input\HotkeyReference.cs" />
|
||||
<Compile Include="Graphics\PlatformInterfaces.cs" />
|
||||
<Compile Include="Sound\Sound.cs" />
|
||||
<Compile Include="Sound\SoundDevice.cs" />
|
||||
<Compile Include="Graphics\SelectionBarsRenderable.cs" />
|
||||
<Compile Include="Graphics\TargetLineRenderable.cs" />
|
||||
<Compile Include="Graphics\UISpriteRenderable.cs" />
|
||||
<Compile Include="Graphics\SoftwareCursor.cs" />
|
||||
@@ -245,7 +243,6 @@
|
||||
<Compile Include="Renderer.cs" />
|
||||
<Compile Include="Platform.cs" />
|
||||
<Compile Include="GameSpeed.cs" />
|
||||
<Compile Include="GlobalChat.cs" />
|
||||
<Compile Include="Primitives\ObservableList.cs" />
|
||||
<Compile Include="Graphics\RgbaColorRenderer.cs" />
|
||||
<Compile Include="Traits\Player\IndexedPlayerPalette.cs" />
|
||||
@@ -259,6 +256,9 @@
|
||||
<Compile Include="UtilityCommands\RegisterModCommand.cs" />
|
||||
<Compile Include="UtilityCommands\UnregisterModCommand.cs" />
|
||||
<Compile Include="UtilityCommands\ClearInvalidModRegistrationsCommand.cs" />
|
||||
<Compile Include="HotkeyManager.cs" />
|
||||
<Compile Include="HotkeyDefinition.cs" />
|
||||
<Compile Include="Traits\Interactable.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="FileSystem\Folder.cs" />
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Orders
|
||||
{
|
||||
@@ -49,8 +51,10 @@ namespace OpenRA.Orders
|
||||
if (mi.Button == ExpectedButton && world.Map.Contains(cell))
|
||||
{
|
||||
world.CancelInputMode();
|
||||
|
||||
var queued = mi.Modifiers.HasModifier(Modifiers.Shift);
|
||||
foreach (var subject in Subjects)
|
||||
yield return new Order(OrderName, subject, false) { TargetLocation = cell };
|
||||
yield return new Order(OrderName, subject, Target.FromCell(world, cell), queued);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +63,7 @@ namespace OpenRA.Orders
|
||||
return world.Map.Contains(cell) ? Cursor : "generic-blocked";
|
||||
}
|
||||
|
||||
public override bool InputOverridesSelection(World world, int2 xy, MouseInput mi)
|
||||
public override bool InputOverridesSelection(WorldRenderer wr, World world, int2 xy, MouseInput mi)
|
||||
{
|
||||
// Custom order generators always override selection
|
||||
return true;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Orders
|
||||
@@ -20,14 +21,14 @@ namespace OpenRA.Orders
|
||||
{
|
||||
static Target TargetForInput(World world, CPos cell, int2 worldPixel, MouseInput mi)
|
||||
{
|
||||
var actor = world.ScreenMap.ActorsAt(mi)
|
||||
.Where(a => !world.FogObscures(a) && a.Info.HasTraitInfo<ITargetableInfo>())
|
||||
var actor = world.ScreenMap.ActorsAtMouse(mi)
|
||||
.Where(a => a.Actor.Info.HasTraitInfo<ITargetableInfo>() && !world.FogObscures(a.Actor))
|
||||
.WithHighestSelectionPriority(worldPixel);
|
||||
|
||||
if (actor != null)
|
||||
return Target.FromActor(actor);
|
||||
|
||||
var frozen = world.ScreenMap.FrozenActorsAt(world.RenderPlayer, mi)
|
||||
var frozen = world.ScreenMap.FrozenActorsAtMouse(world.RenderPlayer, mi)
|
||||
.Where(a => a.Info.HasTraitInfo<ITargetableInfo>() && a.Visible && a.HasRenderables)
|
||||
.WithHighestSelectionPriority(worldPixel);
|
||||
|
||||
@@ -50,11 +51,6 @@ namespace OpenRA.Orders
|
||||
if (!actorsInvolved.Any())
|
||||
yield break;
|
||||
|
||||
yield return new Order("CreateGroup", actorsInvolved.First().Owner.PlayerActor, false)
|
||||
{
|
||||
TargetString = actorsInvolved.Select(a => a.ActorID).JoinWith(",")
|
||||
};
|
||||
|
||||
foreach (var o in orders)
|
||||
yield return CheckSameOrder(o.Order, o.Trait.IssueOrder(o.Actor, o.Order, o.Target, mi.Modifiers.HasModifier(Modifiers.Shift)));
|
||||
}
|
||||
@@ -83,16 +79,18 @@ namespace OpenRA.Orders
|
||||
}
|
||||
|
||||
// Used for classic mouse orders, determines whether or not action at xy is move or select
|
||||
public virtual bool InputOverridesSelection(World world, int2 xy, MouseInput mi)
|
||||
public virtual bool InputOverridesSelection(WorldRenderer wr, World world, int2 xy, MouseInput mi)
|
||||
{
|
||||
var actor = world.ScreenMap.ActorsAt(xy).WithHighestSelectionPriority(xy);
|
||||
var actor = world.ScreenMap.ActorsAtMouse(xy).WithHighestSelectionPriority(xy);
|
||||
if (actor == null)
|
||||
return true;
|
||||
|
||||
var target = Target.FromActor(actor);
|
||||
var cell = world.Map.CellContaining(target.CenterPosition);
|
||||
var actorsAt = world.ActorMap.GetActorsAt(cell).ToList();
|
||||
var underCursor = world.Selection.Actors.WithHighestSelectionPriority(xy);
|
||||
var underCursor = world.Selection.Actors
|
||||
.Select(a => new ActorBoundsPair(a, a.MouseBounds(wr)))
|
||||
.WithHighestSelectionPriority(xy);
|
||||
|
||||
var o = OrderForUnit(underCursor, target, actorsAt, cell, mi);
|
||||
if (o != null)
|
||||
|
||||
@@ -93,9 +93,6 @@ namespace OpenRA
|
||||
break;
|
||||
}
|
||||
|
||||
if (!Directory.Exists(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
return dir + Path.DirectorySeparatorChar;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,14 @@ using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public enum PowerState { Normal, Low, Critical }
|
||||
[Flags]
|
||||
public enum PowerState
|
||||
{
|
||||
Normal = 1,
|
||||
Low = 2,
|
||||
Critical = 4
|
||||
}
|
||||
|
||||
public enum WinState { Undefined, Won, Lost }
|
||||
|
||||
public class Player : IScriptBindable, IScriptNotifyBind, ILuaTableBinding, ILuaEqualityBinding, ILuaToStringBinding
|
||||
@@ -61,7 +68,6 @@ namespace OpenRA
|
||||
public Shroud Shroud;
|
||||
public World World { get; private set; }
|
||||
|
||||
readonly IFogVisibilityModifier[] fogVisibilities;
|
||||
readonly StanceColors stanceColors;
|
||||
|
||||
static FactionInfo ChooseFaction(World world, string name, bool requireSelectable = true)
|
||||
@@ -134,8 +140,6 @@ namespace OpenRA
|
||||
PlayerActor = world.CreateActor("Player", new TypeDictionary { new OwnerInit(this) });
|
||||
Shroud = PlayerActor.Trait<Shroud>();
|
||||
|
||||
fogVisibilities = PlayerActor.TraitsImplementing<IFogVisibilityModifier>().ToArray();
|
||||
|
||||
// Enable the bot logic on the host
|
||||
IsBot = BotType != null;
|
||||
if (IsBot && Game.IsHost)
|
||||
@@ -165,35 +169,6 @@ namespace OpenRA
|
||||
return p == null || Stances[p] == Stance.Ally || (p.Spectating && !NonCombatant);
|
||||
}
|
||||
|
||||
public bool CanViewActor(Actor a)
|
||||
{
|
||||
return a.CanBeViewedByPlayer(this);
|
||||
}
|
||||
|
||||
public bool CanTargetActor(Actor a)
|
||||
{
|
||||
// PERF: Avoid LINQ.
|
||||
if (HasFogVisibility)
|
||||
foreach (var fogVisibility in fogVisibilities)
|
||||
if (fogVisibility.IsVisible(a))
|
||||
return true;
|
||||
|
||||
return CanViewActor(a);
|
||||
}
|
||||
|
||||
public bool HasFogVisibility
|
||||
{
|
||||
get
|
||||
{
|
||||
// PERF: Avoid LINQ.
|
||||
foreach (var fogVisibility in fogVisibilities)
|
||||
if (fogVisibility.HasFogVisibility())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Color PlayerStanceColor(Actor a)
|
||||
{
|
||||
var player = a.World.RenderPlayer ?? a.World.LocalPlayer;
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace OpenRA.Primitives
|
||||
{
|
||||
readonly Queue<byte> data = new Queue<byte>(1024);
|
||||
readonly Stream baseStream;
|
||||
bool baseStreamEmpty;
|
||||
|
||||
protected ReadOnlyAdapterStream(Stream stream)
|
||||
{
|
||||
@@ -38,7 +39,7 @@ namespace OpenRA.Primitives
|
||||
public sealed override bool CanRead { get { return true; } }
|
||||
public sealed override bool CanWrite { get { return false; } }
|
||||
|
||||
public sealed override long Length { get { throw new NotSupportedException(); } }
|
||||
public override long Length { get { throw new NotSupportedException(); } }
|
||||
public sealed override long Position
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
@@ -55,10 +56,9 @@ namespace OpenRA.Primitives
|
||||
var copied = 0;
|
||||
ConsumeData(buffer, offset, count, ref copied);
|
||||
|
||||
var finished = false;
|
||||
while (copied < count && !finished)
|
||||
while (copied < count && !baseStreamEmpty)
|
||||
{
|
||||
finished = BufferData(baseStream, data);
|
||||
baseStreamEmpty = BufferData(baseStream, data);
|
||||
ConsumeData(buffer, offset, count, ref copied);
|
||||
}
|
||||
|
||||
|
||||
@@ -51,10 +51,20 @@ namespace OpenRA.Primitives
|
||||
MutateBins(item, itemBounds[item] = bounds, addItem);
|
||||
}
|
||||
|
||||
public void Remove(T item)
|
||||
public bool Remove(T item)
|
||||
{
|
||||
MutateBins(item, itemBounds[item], removeItem);
|
||||
Rectangle bounds;
|
||||
if (!itemBounds.TryGetValue(item, out bounds))
|
||||
return false;
|
||||
|
||||
MutateBins(item, bounds, removeItem);
|
||||
itemBounds.Remove(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Contains(T item)
|
||||
{
|
||||
return itemBounds.ContainsKey(item);
|
||||
}
|
||||
|
||||
Dictionary<T, Rectangle> BinAt(int row, int col)
|
||||
@@ -128,5 +138,7 @@ namespace OpenRA.Primitives
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Rectangle> ItemBounds { get { return itemBounds.Values; } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace OpenRA
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct float2 : IEquatable<float2>
|
||||
{
|
||||
public float X, Y;
|
||||
public readonly float X, Y;
|
||||
|
||||
public float2(float x, float y) { X = x; Y = y; }
|
||||
public float2(PointF p) { X = p.X; Y = p.Y; }
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace OpenRA
|
||||
{
|
||||
if (fontSheetBuilder != null)
|
||||
fontSheetBuilder.Dispose();
|
||||
fontSheetBuilder = new SheetBuilder(SheetType.BGRA);
|
||||
fontSheetBuilder = new SheetBuilder(SheetType.BGRA, 512);
|
||||
Fonts = modData.Manifest.Fonts.ToDictionary(x => x.Key,
|
||||
x => new SpriteFont(x.Value.First, modData.DefaultFileSystem.Open(x.Value.First).ReadAllBytes(),
|
||||
x.Value.Second, Device.WindowScale, fontSheetBuilder)).AsReadOnly();
|
||||
|
||||
@@ -13,6 +13,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
@@ -45,21 +47,29 @@ namespace OpenRA.Traits
|
||||
}
|
||||
}
|
||||
|
||||
public static Actor WithHighestSelectionPriority(this IEnumerable<Actor> actors, int2 selectionPixel)
|
||||
public static Actor WithHighestSelectionPriority(this IEnumerable<ActorBoundsPair> actors, int2 selectionPixel)
|
||||
{
|
||||
return actors.MaxByOrDefault(a => CalculateActorSelectionPriority(a.Info, a.Bounds, selectionPixel));
|
||||
if (!actors.Any())
|
||||
return null;
|
||||
|
||||
return actors.MaxBy(a => CalculateActorSelectionPriority(a.Actor.Info, a.Bounds, selectionPixel)).Actor;
|
||||
}
|
||||
|
||||
public static FrozenActor WithHighestSelectionPriority(this IEnumerable<FrozenActor> actors, int2 selectionPixel)
|
||||
{
|
||||
return actors.MaxByOrDefault(a => CalculateActorSelectionPriority(a.Info, a.Bounds, selectionPixel));
|
||||
return actors.MaxByOrDefault(a => CalculateActorSelectionPriority(a.Info, a.MouseBounds, selectionPixel));
|
||||
}
|
||||
|
||||
static long CalculateActorSelectionPriority(ActorInfo info, Rectangle bounds, int2 selectionPixel)
|
||||
{
|
||||
var centerPixel = new int2(bounds.X, bounds.Y);
|
||||
var pixelDistance = (centerPixel - selectionPixel).Length;
|
||||
if (bounds.IsEmpty)
|
||||
return info.SelectionPriority();
|
||||
|
||||
var centerPixel = new int2(
|
||||
bounds.Left + bounds.Size.Width / 2,
|
||||
bounds.Top + bounds.Size.Height / 2);
|
||||
|
||||
var pixelDistance = (centerPixel - selectionPixel).Length;
|
||||
return ((long)-pixelDistance << 32) + info.SelectionPriority();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace OpenRA.Server
|
||||
public int ExpectLength = 8;
|
||||
public int Frame = 0;
|
||||
public int MostRecentFrame = 0;
|
||||
public bool Validated;
|
||||
|
||||
public long TimeSinceLastResponse { get { return Game.RunTime - lastReceivedTime; } }
|
||||
public bool TimeoutMessageShown = false;
|
||||
|
||||
@@ -134,8 +134,7 @@ namespace OpenRA.Server
|
||||
|
||||
randomSeed = (int)DateTime.Now.ToBinary();
|
||||
|
||||
// UPnP is only supported for servers created by the game client.
|
||||
if (!dedicated && Settings.AllowPortForward)
|
||||
if (UPnP.Status == UPnPStatus.Enabled)
|
||||
UPnP.ForwardPort(Settings.ListenPort, Settings.ExternalPort).Wait();
|
||||
|
||||
foreach (var trait in modData.Manifest.ServerTraits)
|
||||
@@ -206,7 +205,7 @@ namespace OpenRA.Server
|
||||
if (State == ServerState.ShuttingDown)
|
||||
{
|
||||
EndGame();
|
||||
if (!dedicated && Settings.AllowPortForward)
|
||||
if (UPnP.Status == UPnPStatus.Enabled)
|
||||
UPnP.RemovePortForward().Wait();
|
||||
break;
|
||||
}
|
||||
@@ -361,6 +360,8 @@ namespace OpenRA.Server
|
||||
PreConns.Remove(newConn);
|
||||
Conns.Add(newConn);
|
||||
LobbyInfo.Clients.Add(client);
|
||||
newConn.Validated = true;
|
||||
|
||||
var clientPing = new Session.ClientPing { Index = client.Index };
|
||||
LobbyInfo.ClientPings.Add(clientPing);
|
||||
|
||||
@@ -477,6 +478,23 @@ namespace OpenRA.Server
|
||||
|
||||
void InterpretServerOrder(Connection conn, ServerOrder so)
|
||||
{
|
||||
// Only accept handshake responses from unvalidated clients
|
||||
// Anything else may be an attempt to exploit the server
|
||||
if (!conn.Validated)
|
||||
{
|
||||
if (so.Name == "HandshakeResponse")
|
||||
ValidateClient(conn, so.Data);
|
||||
else
|
||||
{
|
||||
Log.Write("server", "Rejected connection from {0}; Order `{1}` is not a `HandshakeResponse`.",
|
||||
conn.Socket.RemoteEndPoint, so.Name);
|
||||
|
||||
DropClient(conn);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch (so.Name)
|
||||
{
|
||||
case "Command":
|
||||
@@ -493,9 +511,6 @@ namespace OpenRA.Server
|
||||
break;
|
||||
}
|
||||
|
||||
case "HandshakeResponse":
|
||||
ValidateClient(conn, so.Data);
|
||||
break;
|
||||
case "Chat":
|
||||
case "TeamChat":
|
||||
case "PauseGame":
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace OpenRA.Server
|
||||
|
||||
public byte[] Serialize()
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
var ms = new MemoryStream(1 + Name.Length + 1 + Data.Length + 1);
|
||||
var bw = new BinaryWriter(ms);
|
||||
|
||||
bw.Write((byte)0xfe);
|
||||
|
||||
@@ -54,9 +54,6 @@ namespace OpenRA
|
||||
[Desc("Allow users to enable NAT discovery for external IP detection and automatic port forwarding.")]
|
||||
public bool DiscoverNatDevices = false;
|
||||
|
||||
[Desc("Set this to false to disable UPnP even if compatible devices are found.")]
|
||||
public bool AllowPortForward = true;
|
||||
|
||||
[Desc("Time in milliseconds to search for UPnP enabled NAT devices.")]
|
||||
public int NatDiscoveryTimeout = 1000;
|
||||
|
||||
@@ -86,14 +83,27 @@ namespace OpenRA
|
||||
public bool LuaDebug = false;
|
||||
public bool PerfText = false;
|
||||
public bool PerfGraph = false;
|
||||
|
||||
[Desc("Amount of time required for triggering perf.log output.")]
|
||||
public float LongTickThresholdMs = 1;
|
||||
|
||||
public bool SanityCheckUnsyncedCode = false;
|
||||
public int Samples = 25;
|
||||
|
||||
[Desc("Show incompatible games in server browser.")]
|
||||
public bool IgnoreVersionMismatch = false;
|
||||
|
||||
public bool StrictActivityChecking = false;
|
||||
|
||||
[Desc("Check whether a newer version is available online.")]
|
||||
public bool CheckVersion = true;
|
||||
|
||||
[Desc("Allow the collection of anonymous data such as Operating System, .NET runtime, OpenGL version and language settings.")]
|
||||
public bool SendSystemInformation = true;
|
||||
|
||||
public int SystemInformationVersionPrompt = 0;
|
||||
public string UUID = System.Guid.NewGuid().ToString();
|
||||
public bool EnableDebugCommandsInReplays = false;
|
||||
}
|
||||
|
||||
public class GraphicSettings
|
||||
@@ -147,6 +157,7 @@ namespace OpenRA
|
||||
|
||||
public class PlayerSettings
|
||||
{
|
||||
[Desc("Sets the player nickname for in-game and IRC chat.")]
|
||||
public string Name = "Newbie";
|
||||
public HSLColor Color = new HSLColor(75, 255, 180);
|
||||
public string LastServer = "localhost:1234";
|
||||
@@ -157,6 +168,8 @@ namespace OpenRA
|
||||
public string Platform = "Default";
|
||||
|
||||
public bool ViewportEdgeScroll = true;
|
||||
public int ViewportEdgeScrollMargin = 5;
|
||||
|
||||
public bool LockMouseWindow = false;
|
||||
public MouseScrollType MiddleMouseScroll = MouseScrollType.Standard;
|
||||
public MouseScrollType RightMouseScroll = MouseScrollType.Disabled;
|
||||
@@ -181,158 +194,6 @@ namespace OpenRA
|
||||
public MPGameFilters MPGameFilters = MPGameFilters.Waiting | MPGameFilters.Empty | MPGameFilters.Protected | MPGameFilters.Started;
|
||||
}
|
||||
|
||||
public class KeySettings
|
||||
{
|
||||
public Hotkey CycleBaseKey = new Hotkey(Keycode.H, Modifiers.None);
|
||||
public Hotkey ToLastEventKey = new Hotkey(Keycode.SPACE, Modifiers.None);
|
||||
public Hotkey ToSelectionKey = new Hotkey(Keycode.HOME, Modifiers.None);
|
||||
public Hotkey SelectAllUnitsKey = new Hotkey(Keycode.Q, Modifiers.None);
|
||||
public Hotkey SelectUnitsByTypeKey = new Hotkey(Keycode.W, Modifiers.None);
|
||||
|
||||
public Hotkey MapScrollUp = new Hotkey(Keycode.UP, Modifiers.None);
|
||||
public Hotkey MapScrollDown = new Hotkey(Keycode.DOWN, Modifiers.None);
|
||||
public Hotkey MapScrollLeft = new Hotkey(Keycode.LEFT, Modifiers.None);
|
||||
public Hotkey MapScrollRight = new Hotkey(Keycode.RIGHT, Modifiers.None);
|
||||
|
||||
public Hotkey MapPushTop = new Hotkey(Keycode.UP, Modifiers.Alt);
|
||||
public Hotkey MapPushBottom = new Hotkey(Keycode.DOWN, Modifiers.Alt);
|
||||
public Hotkey MapPushLeftEdge = new Hotkey(Keycode.LEFT, Modifiers.Alt);
|
||||
public Hotkey MapPushRightEdge = new Hotkey(Keycode.RIGHT, Modifiers.Alt);
|
||||
|
||||
public Hotkey ViewPortBookmarkSaveSlot1 = new Hotkey(Keycode.Q, Modifiers.Ctrl);
|
||||
public Hotkey ViewPortBookmarkSaveSlot2 = new Hotkey(Keycode.W, Modifiers.Ctrl);
|
||||
public Hotkey ViewPortBookmarkSaveSlot3 = new Hotkey(Keycode.E, Modifiers.Ctrl);
|
||||
public Hotkey ViewPortBookmarkSaveSlot4 = new Hotkey(Keycode.R, Modifiers.Ctrl);
|
||||
|
||||
public Hotkey ViewPortBookmarkUseSlot1 = new Hotkey(Keycode.Q, Modifiers.Alt);
|
||||
public Hotkey ViewPortBookmarkUseSlot2 = new Hotkey(Keycode.W, Modifiers.Alt);
|
||||
public Hotkey ViewPortBookmarkUseSlot3 = new Hotkey(Keycode.E, Modifiers.Alt);
|
||||
public Hotkey ViewPortBookmarkUseSlot4 = new Hotkey(Keycode.R, Modifiers.Alt);
|
||||
|
||||
public Hotkey PauseKey = new Hotkey(Keycode.PAUSE, Modifiers.None);
|
||||
public Hotkey PlaceBeaconKey = new Hotkey(Keycode.B, Modifiers.None);
|
||||
public Hotkey SellKey = new Hotkey(Keycode.Z, Modifiers.None);
|
||||
public Hotkey PowerDownKey = new Hotkey(Keycode.X, Modifiers.None);
|
||||
public Hotkey RepairKey = new Hotkey(Keycode.C, Modifiers.None);
|
||||
|
||||
public Hotkey NextProductionTabKey = new Hotkey(Keycode.PAGEDOWN, Modifiers.None);
|
||||
public Hotkey PreviousProductionTabKey = new Hotkey(Keycode.PAGEUP, Modifiers.None);
|
||||
public Hotkey CycleProductionBuildingsKey = new Hotkey(Keycode.TAB, Modifiers.None);
|
||||
|
||||
public Hotkey AttackMoveKey = new Hotkey(Keycode.A, Modifiers.None);
|
||||
public Hotkey StopKey = new Hotkey(Keycode.S, Modifiers.None);
|
||||
public Hotkey ScatterKey = new Hotkey(Keycode.X, Modifiers.Ctrl);
|
||||
public Hotkey DeployKey = new Hotkey(Keycode.F, Modifiers.None);
|
||||
public Hotkey StanceHoldFireKey = new Hotkey(Keycode.F, Modifiers.Alt);
|
||||
public Hotkey StanceReturnFireKey = new Hotkey(Keycode.D, Modifiers.Alt);
|
||||
public Hotkey StanceDefendKey = new Hotkey(Keycode.S, Modifiers.Alt);
|
||||
public Hotkey StanceAttackAnythingKey = new Hotkey(Keycode.A, Modifiers.Alt);
|
||||
public Hotkey GuardKey = new Hotkey(Keycode.D, Modifiers.None);
|
||||
|
||||
public Hotkey ObserverCombinedView = new Hotkey(Keycode.MINUS, Modifiers.None);
|
||||
public Hotkey ObserverWorldView = new Hotkey(Keycode.EQUALS, Modifiers.None);
|
||||
|
||||
public Hotkey CycleStatusBarsKey = new Hotkey(Keycode.COMMA, Modifiers.None);
|
||||
public Hotkey TogglePixelDoubleKey = new Hotkey(Keycode.PERIOD, Modifiers.None);
|
||||
public Hotkey TogglePlayerStanceColorsKey = new Hotkey(Keycode.COMMA, Modifiers.Ctrl);
|
||||
|
||||
public Hotkey DevReloadChromeKey = new Hotkey(Keycode.C, Modifiers.Ctrl | Modifiers.Shift);
|
||||
public Hotkey HideUserInterfaceKey = new Hotkey(Keycode.H, Modifiers.Ctrl | Modifiers.Shift);
|
||||
public Hotkey TakeScreenshotKey = new Hotkey(Keycode.P, Modifiers.Ctrl);
|
||||
public Hotkey ToggleMuteKey = new Hotkey(Keycode.M, Modifiers.None);
|
||||
|
||||
public Hotkey Production01Key = new Hotkey(Keycode.F1, Modifiers.None);
|
||||
public Hotkey Production02Key = new Hotkey(Keycode.F2, Modifiers.None);
|
||||
public Hotkey Production03Key = new Hotkey(Keycode.F3, Modifiers.None);
|
||||
public Hotkey Production04Key = new Hotkey(Keycode.F4, Modifiers.None);
|
||||
public Hotkey Production05Key = new Hotkey(Keycode.F5, Modifiers.None);
|
||||
public Hotkey Production06Key = new Hotkey(Keycode.F6, Modifiers.None);
|
||||
public Hotkey Production07Key = new Hotkey(Keycode.F7, Modifiers.None);
|
||||
public Hotkey Production08Key = new Hotkey(Keycode.F8, Modifiers.None);
|
||||
public Hotkey Production09Key = new Hotkey(Keycode.F9, Modifiers.None);
|
||||
public Hotkey Production10Key = new Hotkey(Keycode.F10, Modifiers.None);
|
||||
public Hotkey Production11Key = new Hotkey(Keycode.F11, Modifiers.None);
|
||||
public Hotkey Production12Key = new Hotkey(Keycode.F12, Modifiers.None);
|
||||
|
||||
public Hotkey Production13Key = new Hotkey(Keycode.F1, Modifiers.Ctrl);
|
||||
public Hotkey Production14Key = new Hotkey(Keycode.F2, Modifiers.Ctrl);
|
||||
public Hotkey Production15Key = new Hotkey(Keycode.F3, Modifiers.Ctrl);
|
||||
public Hotkey Production16Key = new Hotkey(Keycode.F4, Modifiers.Ctrl);
|
||||
public Hotkey Production17Key = new Hotkey(Keycode.F5, Modifiers.Ctrl);
|
||||
public Hotkey Production18Key = new Hotkey(Keycode.F6, Modifiers.Ctrl);
|
||||
public Hotkey Production19Key = new Hotkey(Keycode.F7, Modifiers.Ctrl);
|
||||
public Hotkey Production20Key = new Hotkey(Keycode.F8, Modifiers.Ctrl);
|
||||
public Hotkey Production21Key = new Hotkey(Keycode.F9, Modifiers.Ctrl);
|
||||
public Hotkey Production22Key = new Hotkey(Keycode.F10, Modifiers.Ctrl);
|
||||
public Hotkey Production23Key = new Hotkey(Keycode.F11, Modifiers.Ctrl);
|
||||
public Hotkey Production24Key = new Hotkey(Keycode.F12, Modifiers.Ctrl);
|
||||
|
||||
public Hotkey ProductionTypeBuildingKey = new Hotkey(Keycode.E, Modifiers.None);
|
||||
public Hotkey ProductionTypeDefenseKey = new Hotkey(Keycode.R, Modifiers.None);
|
||||
public Hotkey ProductionTypeInfantryKey = new Hotkey(Keycode.T, Modifiers.None);
|
||||
public Hotkey ProductionTypeVehicleKey = new Hotkey(Keycode.Y, Modifiers.None);
|
||||
public Hotkey ProductionTypeAircraftKey = new Hotkey(Keycode.U, Modifiers.None);
|
||||
public Hotkey ProductionTypeNavalKey = new Hotkey(Keycode.I, Modifiers.None);
|
||||
public Hotkey ProductionTypeTankKey = new Hotkey(Keycode.I, Modifiers.None);
|
||||
public Hotkey ProductionTypeMerchantKey = new Hotkey(Keycode.O, Modifiers.None);
|
||||
public Hotkey ProductionTypeUpgradeKey = new Hotkey(Keycode.R, Modifiers.None);
|
||||
|
||||
public Hotkey SupportPower01Key = new Hotkey(Keycode.UNKNOWN, Modifiers.None);
|
||||
public Hotkey SupportPower02Key = new Hotkey(Keycode.UNKNOWN, Modifiers.None);
|
||||
public Hotkey SupportPower03Key = new Hotkey(Keycode.UNKNOWN, Modifiers.None);
|
||||
public Hotkey SupportPower04Key = new Hotkey(Keycode.UNKNOWN, Modifiers.None);
|
||||
public Hotkey SupportPower05Key = new Hotkey(Keycode.UNKNOWN, Modifiers.None);
|
||||
public Hotkey SupportPower06Key = new Hotkey(Keycode.UNKNOWN, Modifiers.None);
|
||||
|
||||
public Hotkey ReplaySpeedSlowKey = new Hotkey(Keycode.F5, Modifiers.None);
|
||||
public Hotkey ReplaySpeedRegularKey = new Hotkey(Keycode.F6, Modifiers.None);
|
||||
public Hotkey ReplaySpeedFastKey = new Hotkey(Keycode.F7, Modifiers.None);
|
||||
public Hotkey ReplaySpeedMaxKey = new Hotkey(Keycode.F8, Modifiers.None);
|
||||
|
||||
public Hotkey NextTrack = new Hotkey(Keycode.AUDIONEXT, Modifiers.None);
|
||||
public Hotkey PreviousTrack = new Hotkey(Keycode.AUDIOPREV, Modifiers.None);
|
||||
public Hotkey StopMusic = new Hotkey(Keycode.AUDIOSTOP, Modifiers.None);
|
||||
public Hotkey PauseMusic = new Hotkey(Keycode.AUDIOPLAY, Modifiers.None);
|
||||
|
||||
static readonly Func<KeySettings, Hotkey>[] ProductionKeys = GetKeys(24, "Production");
|
||||
static readonly Func<KeySettings, Hotkey>[] SupportPowerKeys = GetKeys(6, "SupportPower");
|
||||
|
||||
static Func<KeySettings, Hotkey>[] GetKeys(int count, string prefix)
|
||||
{
|
||||
var keySettings = Expression.Parameter(typeof(KeySettings), "keySettings");
|
||||
return Exts.MakeArray(count, i => Expression.Lambda<Func<KeySettings, Hotkey>>(
|
||||
Expression.Field(keySettings, "{0}{1:D2}Key".F(prefix, i + 1)), keySettings).Compile());
|
||||
}
|
||||
|
||||
public Hotkey GetProductionHotkey(int index)
|
||||
{
|
||||
return GetKey(ProductionKeys, index);
|
||||
}
|
||||
|
||||
public Hotkey GetSupportPowerHotkey(int index)
|
||||
{
|
||||
return GetKey(SupportPowerKeys, index);
|
||||
}
|
||||
|
||||
Hotkey GetKey(Func<KeySettings, Hotkey>[] keys, int index)
|
||||
{
|
||||
if (index < 0 || index >= keys.Length)
|
||||
return Hotkey.Invalid;
|
||||
|
||||
return keys[index](this);
|
||||
}
|
||||
}
|
||||
|
||||
public class ChatSettings
|
||||
{
|
||||
public string Hostname = "irc.openra.net";
|
||||
public int Port = 6667;
|
||||
public string Channel = "lobby";
|
||||
public string QuitMessage = "Battle control terminated!";
|
||||
public string TimestampFormat = "HH:mm";
|
||||
public bool ConnectAutomatically = false;
|
||||
}
|
||||
|
||||
public class Settings
|
||||
{
|
||||
readonly string settingsFile;
|
||||
@@ -343,8 +204,7 @@ namespace OpenRA
|
||||
public readonly GraphicSettings Graphics = new GraphicSettings();
|
||||
public readonly ServerSettings Server = new ServerSettings();
|
||||
public readonly DebugSettings Debug = new DebugSettings();
|
||||
public readonly KeySettings Keys = new KeySettings();
|
||||
public readonly ChatSettings Chat = new ChatSettings();
|
||||
internal Dictionary<string, Hotkey> Keys = new Dictionary<string, Hotkey>();
|
||||
|
||||
public readonly Dictionary<string, object> Sections;
|
||||
|
||||
@@ -364,8 +224,6 @@ namespace OpenRA
|
||||
{ "Graphics", Graphics },
|
||||
{ "Server", Server },
|
||||
{ "Debug", Debug },
|
||||
{ "Keys", Keys },
|
||||
{ "Chat", Chat }
|
||||
};
|
||||
|
||||
// Override fieldloader to ignore invalid entries
|
||||
@@ -384,6 +242,11 @@ namespace OpenRA
|
||||
if (Sections.TryGetValue(yamlSection.Key, out settingsSection))
|
||||
LoadSectionYaml(yamlSection.Value, settingsSection);
|
||||
}
|
||||
|
||||
var keysNode = yamlCache.FirstOrDefault(n => n.Key == "Keys");
|
||||
if (keysNode != null)
|
||||
foreach (var node in keysNode.Value.Nodes)
|
||||
Keys[node.Key] = FieldLoader.GetValue<Hotkey>(node.Key, node.Value.Value);
|
||||
}
|
||||
|
||||
// Override with commandline args
|
||||
@@ -433,6 +296,17 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
var keysYaml = yamlCache.FirstOrDefault(x => x.Key == "Keys");
|
||||
if (keysYaml == null)
|
||||
{
|
||||
keysYaml = new MiniYamlNode("Keys", new MiniYaml(""));
|
||||
yamlCache.Add(keysYaml);
|
||||
}
|
||||
|
||||
keysYaml.Value.Nodes.Clear();
|
||||
foreach (var kv in Keys)
|
||||
keysYaml.Value.Nodes.Add(new MiniYamlNode(kv.Key, FieldSaver.FormatValue(kv.Value)));
|
||||
|
||||
yamlCache.WriteToFile(settingsFile);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace OpenRA
|
||||
ISoundLoader[] loaders;
|
||||
IReadOnlyFileSystem fileSystem;
|
||||
Cache<string, ISoundSource> sounds;
|
||||
ISoundSource rawSource;
|
||||
ISoundSource videoSource;
|
||||
ISound music;
|
||||
ISound video;
|
||||
MusicInfo currentMusic;
|
||||
@@ -149,8 +149,9 @@ namespace OpenRA
|
||||
|
||||
public void PlayVideo(byte[] raw, int channels, int sampleBits, int sampleRate)
|
||||
{
|
||||
rawSource = soundEngine.AddSoundSourceFromMemory(raw, channels, sampleBits, sampleRate);
|
||||
video = soundEngine.Play2D(rawSource, false, true, WPos.Zero, InternalSoundVolume, false);
|
||||
StopVideo();
|
||||
videoSource = soundEngine.AddSoundSourceFromMemory(raw, channels, sampleBits, sampleRate);
|
||||
video = soundEngine.Play2D(videoSource, false, true, WPos.Zero, InternalSoundVolume, false);
|
||||
}
|
||||
|
||||
public void PlayVideo()
|
||||
@@ -168,7 +169,12 @@ namespace OpenRA
|
||||
public void StopVideo()
|
||||
{
|
||||
if (video != null)
|
||||
{
|
||||
soundEngine.StopSound(video);
|
||||
videoSource.Dispose();
|
||||
videoSource = null;
|
||||
video = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
|
||||
93
OpenRA.Game/Support/ExceptionHandler.cs
Normal file
93
OpenRA.Game/Support/ExceptionHandler.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public static class ExceptionHandler
|
||||
{
|
||||
public static void HandleFatalError(Exception ex)
|
||||
{
|
||||
var exceptionName = "exception-" + DateTime.UtcNow.ToString("yyyy-MM-ddTHHmmssZ", CultureInfo.InvariantCulture) + ".log";
|
||||
Log.AddChannel("exception", exceptionName);
|
||||
|
||||
if (Game.EngineVersion != null)
|
||||
Log.Write("exception", "OpenRA engine version {0}", Game.EngineVersion);
|
||||
|
||||
if (Game.ModData != null)
|
||||
{
|
||||
var mod = Game.ModData.Manifest.Metadata;
|
||||
Log.Write("exception", "{0} mod version {1}", mod.Title, mod.Version);
|
||||
}
|
||||
|
||||
if (Game.OrderManager != null && Game.OrderManager.World != null && Game.OrderManager.World.Map != null)
|
||||
{
|
||||
var map = Game.OrderManager.World.Map;
|
||||
Log.Write("exception", "on map {0} ({1} by {2}).", map.Uid, map.Title, map.Author);
|
||||
}
|
||||
|
||||
Log.Write("exception", "Date: {0:u}", DateTime.UtcNow);
|
||||
Log.Write("exception", "Operating System: {0} ({1})", Platform.CurrentPlatform, Environment.OSVersion);
|
||||
Log.Write("exception", "Runtime Version: {0}", Platform.RuntimeVersion);
|
||||
var rpt = BuildExceptionReport(ex).ToString();
|
||||
Log.Write("exception", "{0}", rpt);
|
||||
Console.Error.WriteLine(rpt);
|
||||
}
|
||||
|
||||
static StringBuilder BuildExceptionReport(Exception ex)
|
||||
{
|
||||
return BuildExceptionReport(ex, new StringBuilder(), 0);
|
||||
}
|
||||
|
||||
static StringBuilder AppendIndentedFormatLine(this StringBuilder sb, int indent, string format, params object[] args)
|
||||
{
|
||||
return sb.Append(new string(' ', indent * 2)).AppendFormat(format, args).AppendLine();
|
||||
}
|
||||
|
||||
static StringBuilder BuildExceptionReport(Exception ex, StringBuilder sb, int indent)
|
||||
{
|
||||
if (ex == null)
|
||||
return sb;
|
||||
|
||||
sb.AppendIndentedFormatLine(indent, "Exception of type `{0}`: {1}", ex.GetType().FullName, ex.Message);
|
||||
|
||||
var tle = ex as TypeLoadException;
|
||||
var oom = ex as OutOfMemoryException;
|
||||
if (tle != null)
|
||||
{
|
||||
sb.AppendIndentedFormatLine(indent, "TypeName=`{0}`", tle.TypeName);
|
||||
}
|
||||
else if (oom != null)
|
||||
{
|
||||
var gcMemoryBeforeCollect = GC.GetTotalMemory(false);
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
sb.AppendIndentedFormatLine(indent, "GC Memory (post-collect)={0:N0}", GC.GetTotalMemory(false));
|
||||
sb.AppendIndentedFormatLine(indent, "GC Memory (pre-collect)={0:N0}", gcMemoryBeforeCollect);
|
||||
|
||||
using (var p = Process.GetCurrentProcess())
|
||||
{
|
||||
sb.AppendIndentedFormatLine(indent, "Working Set={0:N0}", p.WorkingSet64);
|
||||
sb.AppendIndentedFormatLine(indent, "Private Memory={0:N0}", p.PrivateMemorySize64);
|
||||
sb.AppendIndentedFormatLine(indent, "Virtual Memory={0:N0}", p.VirtualMemorySize64);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: more exception types
|
||||
}
|
||||
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
sb.AppendIndentedFormatLine(indent, "Inner");
|
||||
BuildExceptionReport(ex.InnerException, sb, indent + 1);
|
||||
}
|
||||
|
||||
sb.AppendIndentedFormatLine(indent, "{0}", ex.StackTrace);
|
||||
|
||||
return sb;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ namespace OpenRA.Support
|
||||
var label = type == typeof(string) || type.IsGenericType ? item.ToString() : type.Name;
|
||||
Log.Write("perf", FormatString,
|
||||
1000f * (endStopwatchTicks - startStopwatchTicks) / Stopwatch.Frequency,
|
||||
"[{0}] {1}: {2}".F(Game.LocalTick, name, label));
|
||||
"[" + Game.LocalTick + "] " + name + ": " + label);
|
||||
}
|
||||
|
||||
public static long LongTickThresholdInStopwatchTicks
|
||||
|
||||
@@ -11,10 +11,7 @@
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
@@ -31,109 +28,19 @@ namespace OpenRA
|
||||
static int Main(string[] args)
|
||||
{
|
||||
if (Debugger.IsAttached || args.Contains("--just-die"))
|
||||
return (int)Run(args);
|
||||
return (int)Game.InitializeAndRun(args);
|
||||
|
||||
AppDomain.CurrentDomain.UnhandledException += (_, e) => FatalError((Exception)e.ExceptionObject);
|
||||
AppDomain.CurrentDomain.UnhandledException += (_, e) => ExceptionHandler.HandleFatalError((Exception)e.ExceptionObject);
|
||||
|
||||
try
|
||||
{
|
||||
return (int)Run(args);
|
||||
return (int)Game.InitializeAndRun(args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
FatalError(e);
|
||||
ExceptionHandler.HandleFatalError(e);
|
||||
return (int)RunStatus.Error;
|
||||
}
|
||||
}
|
||||
|
||||
static void FatalError(Exception ex)
|
||||
{
|
||||
var exceptionName = "exception-" + DateTime.UtcNow.ToString("yyyy-MM-ddTHHmmssZ", CultureInfo.InvariantCulture) + ".log";
|
||||
Log.AddChannel("exception", exceptionName);
|
||||
|
||||
if (Game.EngineVersion != null)
|
||||
Log.Write("exception", "OpenRA engine version {0}", Game.EngineVersion);
|
||||
|
||||
if (Game.ModData != null)
|
||||
{
|
||||
var mod = Game.ModData.Manifest.Metadata;
|
||||
Log.Write("exception", "{0} mod version {1}", mod.Title, mod.Version);
|
||||
}
|
||||
|
||||
if (Game.OrderManager != null && Game.OrderManager.World != null && Game.OrderManager.World.Map != null)
|
||||
{
|
||||
var map = Game.OrderManager.World.Map;
|
||||
Log.Write("exception", "on map {0} ({1} by {2}).", map.Uid, map.Title, map.Author);
|
||||
}
|
||||
|
||||
Log.Write("exception", "Date: {0:u}", DateTime.UtcNow);
|
||||
Log.Write("exception", "Operating System: {0} ({1})", Platform.CurrentPlatform, Environment.OSVersion);
|
||||
Log.Write("exception", "Runtime Version: {0}", Platform.RuntimeVersion);
|
||||
var rpt = BuildExceptionReport(ex).ToString();
|
||||
Log.Write("exception", "{0}", rpt);
|
||||
Console.Error.WriteLine(rpt);
|
||||
}
|
||||
|
||||
static StringBuilder BuildExceptionReport(Exception ex)
|
||||
{
|
||||
return BuildExceptionReport(ex, new StringBuilder(), 0);
|
||||
}
|
||||
|
||||
static StringBuilder AppendIndentedFormatLine(this StringBuilder sb, int indent, string format, params object[] args)
|
||||
{
|
||||
return sb.Append(new string(' ', indent * 2)).AppendFormat(format, args).AppendLine();
|
||||
}
|
||||
|
||||
static StringBuilder BuildExceptionReport(Exception ex, StringBuilder sb, int indent)
|
||||
{
|
||||
if (ex == null)
|
||||
return sb;
|
||||
|
||||
sb.AppendIndentedFormatLine(indent, "Exception of type `{0}`: {1}", ex.GetType().FullName, ex.Message);
|
||||
|
||||
var tle = ex as TypeLoadException;
|
||||
var oom = ex as OutOfMemoryException;
|
||||
if (tle != null)
|
||||
{
|
||||
sb.AppendIndentedFormatLine(indent, "TypeName=`{0}`", tle.TypeName);
|
||||
}
|
||||
else if (oom != null)
|
||||
{
|
||||
var gcMemoryBeforeCollect = GC.GetTotalMemory(false);
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
sb.AppendIndentedFormatLine(indent, "GC Memory (post-collect)={0:N0}", GC.GetTotalMemory(false));
|
||||
sb.AppendIndentedFormatLine(indent, "GC Memory (pre-collect)={0:N0}", gcMemoryBeforeCollect);
|
||||
|
||||
using (var p = Process.GetCurrentProcess())
|
||||
{
|
||||
sb.AppendIndentedFormatLine(indent, "Working Set={0:N0}", p.WorkingSet64);
|
||||
sb.AppendIndentedFormatLine(indent, "Private Memory={0:N0}", p.PrivateMemorySize64);
|
||||
sb.AppendIndentedFormatLine(indent, "Virtual Memory={0:N0}", p.VirtualMemorySize64);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: more exception types
|
||||
}
|
||||
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
sb.AppendIndentedFormatLine(indent, "Inner");
|
||||
BuildExceptionReport(ex.InnerException, sb, indent + 1);
|
||||
}
|
||||
|
||||
sb.AppendIndentedFormatLine(indent, "{0}", ex.StackTrace);
|
||||
|
||||
return sb;
|
||||
}
|
||||
|
||||
static RunStatus Run(string[] args)
|
||||
{
|
||||
Game.Initialize(new Arguments(args));
|
||||
GC.Collect();
|
||||
return Game.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,8 @@ namespace OpenRA.Support
|
||||
{
|
||||
public abstract class VariableExpression
|
||||
{
|
||||
public static readonly IReadOnlyDictionary<string, int> NoVariables = new ReadOnlyDictionary<string, int>(new Dictionary<string, int>());
|
||||
|
||||
public readonly string Expression;
|
||||
readonly HashSet<string> variables = new HashSet<string>();
|
||||
public IEnumerable<string> Variables { get { return variables; } }
|
||||
|
||||
80
OpenRA.Game/Traits/Interactable.cs
Normal file
80
OpenRA.Game/Traits/Interactable.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2017 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.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
[Desc("Used to enable mouse interaction on actors that are not Selectable.")]
|
||||
public class InteractableInfo : ITraitInfo, IMouseBoundsInfo, IDecorationBoundsInfo
|
||||
{
|
||||
[Desc("Defines a custom rectangle for mouse interaction with the actor.",
|
||||
"If null, the engine will guess an appropriate size based on the With*Body trait.",
|
||||
"The first two numbers define the width and height of the rectangle.",
|
||||
"The (optional) second two numbers define an x and y offset from the actor center.")]
|
||||
public readonly int[] Bounds = null;
|
||||
|
||||
[Desc("Defines a custom rectangle for Decorations (e.g. the selection box).",
|
||||
"If null, Bounds will be used instead")]
|
||||
public readonly int[] DecorationBounds = null;
|
||||
|
||||
public virtual object Create(ActorInitializer init) { return new Interactable(this); }
|
||||
}
|
||||
|
||||
public class Interactable : INotifyCreated, IMouseBounds, IDecorationBounds
|
||||
{
|
||||
readonly InteractableInfo info;
|
||||
IAutoMouseBounds[] autoBounds;
|
||||
|
||||
public Interactable(InteractableInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
void INotifyCreated.Created(Actor self)
|
||||
{
|
||||
autoBounds = self.TraitsImplementing<IAutoMouseBounds>().ToArray();
|
||||
}
|
||||
|
||||
Rectangle AutoBounds(Actor self, WorldRenderer wr)
|
||||
{
|
||||
return autoBounds.Select(s => s.AutoMouseoverBounds(self, wr)).FirstOrDefault(r => !r.IsEmpty);
|
||||
}
|
||||
|
||||
Rectangle Bounds(Actor self, WorldRenderer wr, int[] bounds)
|
||||
{
|
||||
if (bounds == null)
|
||||
return AutoBounds(self, wr);
|
||||
|
||||
var size = new int2(bounds[0], bounds[1]);
|
||||
|
||||
var offset = -size / 2;
|
||||
if (bounds.Length > 2)
|
||||
offset += new int2(bounds[2], bounds[3]);
|
||||
|
||||
var xy = wr.ScreenPxPosition(self.CenterPosition) + offset;
|
||||
return new Rectangle(xy.X, xy.Y, size.X, size.Y);
|
||||
}
|
||||
|
||||
Rectangle IMouseBounds.MouseoverBounds(Actor self, WorldRenderer wr)
|
||||
{
|
||||
return Bounds(self, wr, info.Bounds);
|
||||
}
|
||||
|
||||
Rectangle IDecorationBounds.DecorationBounds(Actor self, WorldRenderer wr)
|
||||
{
|
||||
return Bounds(self, wr, info.DecorationBounds ?? info.Bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,12 +31,12 @@ namespace OpenRA.Traits
|
||||
{
|
||||
public readonly PPos[] Footprint;
|
||||
public readonly WPos CenterPosition;
|
||||
public readonly Rectangle Bounds;
|
||||
public readonly HashSet<string> TargetTypes;
|
||||
readonly Actor actor;
|
||||
readonly Player viewer;
|
||||
readonly Shroud shroud;
|
||||
|
||||
public Player Owner { get; private set; }
|
||||
public HashSet<string> TargetTypes { get; private set; }
|
||||
|
||||
public ITooltipInfo TooltipInfo { get; private set; }
|
||||
public Player TooltipOwner { get; private set; }
|
||||
@@ -50,14 +50,21 @@ namespace OpenRA.Traits
|
||||
public bool Shrouded { get; private set; }
|
||||
public bool NeedRenderables { get; set; }
|
||||
public IRenderable[] Renderables = NoRenderables;
|
||||
public Rectangle[] ScreenBounds = NoBounds;
|
||||
|
||||
// TODO: Replace this with an int2[] polygon
|
||||
public Rectangle MouseBounds = Rectangle.Empty;
|
||||
|
||||
static readonly IRenderable[] NoRenderables = new IRenderable[0];
|
||||
static readonly Rectangle[] NoBounds = new Rectangle[0];
|
||||
|
||||
int flashTicks;
|
||||
|
||||
public FrozenActor(Actor self, PPos[] footprint, Shroud shroud, bool startsRevealed)
|
||||
public FrozenActor(Actor actor, PPos[] footprint, Player viewer, bool startsRevealed)
|
||||
{
|
||||
actor = self;
|
||||
this.shroud = shroud;
|
||||
this.actor = actor;
|
||||
this.viewer = viewer;
|
||||
shroud = viewer.Shroud;
|
||||
NeedRenderables = startsRevealed;
|
||||
|
||||
// Consider all cells inside the map area (ignoring the current map bounds)
|
||||
@@ -76,12 +83,11 @@ namespace OpenRA.Traits
|
||||
footprint.Select(p => p.ToString()).JoinWith("|"),
|
||||
footprint.Select(p => shroud.Contains(p).ToString()).JoinWith("|")));
|
||||
|
||||
CenterPosition = self.CenterPosition;
|
||||
Bounds = self.Bounds;
|
||||
TargetTypes = self.GetEnabledTargetTypes().ToHashSet();
|
||||
CenterPosition = actor.CenterPosition;
|
||||
TargetTypes = new HashSet<string>();
|
||||
|
||||
tooltips = self.TraitsImplementing<ITooltip>().ToArray();
|
||||
health = self.TraitOrDefault<IHealth>();
|
||||
tooltips = actor.TraitsImplementing<ITooltip>().ToArray();
|
||||
health = actor.TraitOrDefault<IHealth>();
|
||||
|
||||
UpdateVisibility();
|
||||
}
|
||||
@@ -90,10 +96,12 @@ namespace OpenRA.Traits
|
||||
public bool IsValid { get { return Owner != null; } }
|
||||
public ActorInfo Info { get { return actor.Info; } }
|
||||
public Actor Actor { get { return !actor.IsDead ? actor : null; } }
|
||||
public Player Viewer { get { return viewer; } }
|
||||
|
||||
public void RefreshState()
|
||||
{
|
||||
Owner = actor.Owner;
|
||||
TargetTypes = actor.GetEnabledTargetTypes().ToHashSet();
|
||||
|
||||
if (health != null)
|
||||
{
|
||||
@@ -101,7 +109,7 @@ namespace OpenRA.Traits
|
||||
DamageState = health.DamageState;
|
||||
}
|
||||
|
||||
var tooltip = tooltips.FirstOrDefault(Exts.IsTraitEnabled);
|
||||
var tooltip = tooltips.FirstEnabledTraitOrDefault();
|
||||
if (tooltip != null)
|
||||
{
|
||||
TooltipInfo = tooltip.TooltipInfo;
|
||||
@@ -178,6 +186,7 @@ namespace OpenRA.Traits
|
||||
readonly SpatiallyPartitioned<uint> partitionedFrozenActorIds;
|
||||
readonly bool[] dirtyBins;
|
||||
readonly HashSet<uint> dirtyFrozenActorIds = new HashSet<uint>();
|
||||
readonly int rows, cols;
|
||||
|
||||
public FrozenActorLayer(Actor self, FrozenActorLayerInfo info)
|
||||
{
|
||||
@@ -191,16 +200,17 @@ namespace OpenRA.Traits
|
||||
// expensive visibility update for frozen actors in these regions.
|
||||
partitionedFrozenActorIds = new SpatiallyPartitioned<uint>(
|
||||
world.Map.MapSize.X, world.Map.MapSize.Y, binSize);
|
||||
var maxX = world.Map.MapSize.X / binSize + 1;
|
||||
var maxY = world.Map.MapSize.Y / binSize + 1;
|
||||
dirtyBins = new bool[maxX * maxY];
|
||||
|
||||
cols = world.Map.MapSize.X / binSize + 1;
|
||||
rows = world.Map.MapSize.Y / binSize + 1;
|
||||
dirtyBins = new bool[cols * rows];
|
||||
self.Trait<Shroud>().CellsChanged += cells =>
|
||||
{
|
||||
foreach (var cell in cells)
|
||||
{
|
||||
var x = cell.U / binSize;
|
||||
var y = cell.V / binSize;
|
||||
dirtyBins[y * maxX + x] = true;
|
||||
dirtyBins[y * cols + x] = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -208,7 +218,7 @@ namespace OpenRA.Traits
|
||||
public void Add(FrozenActor fa)
|
||||
{
|
||||
frozenActorsById.Add(fa.ID, fa);
|
||||
world.ScreenMap.Add(owner, fa);
|
||||
world.ScreenMap.AddOrUpdate(owner, fa);
|
||||
partitionedFrozenActorIds.Add(fa.ID, FootprintBounds(fa));
|
||||
}
|
||||
|
||||
@@ -242,49 +252,53 @@ namespace OpenRA.Traits
|
||||
return Rectangle.FromLTRB(minU, minV, maxU + 1, maxV + 1);
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
void ITick.Tick(Actor self)
|
||||
{
|
||||
UpdateDirtyFrozenActorsFromDirtyBins();
|
||||
|
||||
var frozenActorsToRemove = new List<FrozenActor>();
|
||||
VisibilityHash = 0;
|
||||
FrozenHash = 0;
|
||||
|
||||
foreach (var kvp in frozenActorsById)
|
||||
// Update visibility at the end of the tick to make sure that
|
||||
// the fog/shroud state has been updated for the tick
|
||||
self.World.AddFrameEndTask(w =>
|
||||
{
|
||||
var id = kvp.Key;
|
||||
var hash = (int)id;
|
||||
FrozenHash += hash;
|
||||
UpdateDirtyFrozenActorsFromDirtyBins();
|
||||
|
||||
var frozenActor = kvp.Value;
|
||||
frozenActor.Tick();
|
||||
if (dirtyFrozenActorIds.Contains(id))
|
||||
frozenActor.UpdateVisibility();
|
||||
var frozenActorsToRemove = new List<FrozenActor>();
|
||||
VisibilityHash = 0;
|
||||
FrozenHash = 0;
|
||||
|
||||
if (frozenActor.Visible)
|
||||
VisibilityHash += hash;
|
||||
else if (frozenActor.Actor == null)
|
||||
frozenActorsToRemove.Add(frozenActor);
|
||||
}
|
||||
foreach (var kvp in frozenActorsById)
|
||||
{
|
||||
var id = kvp.Key;
|
||||
var hash = (int)id;
|
||||
FrozenHash += hash;
|
||||
|
||||
dirtyFrozenActorIds.Clear();
|
||||
var frozenActor = kvp.Value;
|
||||
frozenActor.Tick();
|
||||
if (dirtyFrozenActorIds.Contains(id))
|
||||
frozenActor.UpdateVisibility();
|
||||
|
||||
foreach (var fa in frozenActorsToRemove)
|
||||
Remove(fa);
|
||||
if (frozenActor.Visible)
|
||||
VisibilityHash += hash;
|
||||
else if (frozenActor.Actor == null)
|
||||
frozenActorsToRemove.Add(frozenActor);
|
||||
}
|
||||
|
||||
dirtyFrozenActorIds.Clear();
|
||||
|
||||
foreach (var fa in frozenActorsToRemove)
|
||||
Remove(fa);
|
||||
});
|
||||
}
|
||||
|
||||
void UpdateDirtyFrozenActorsFromDirtyBins()
|
||||
{
|
||||
// Check which bins on the map were dirtied due to changes in the shroud and gather the frozen actors whose
|
||||
// footprint overlap with these bins.
|
||||
var maxX = world.Map.MapSize.X / binSize + 1;
|
||||
var maxY = world.Map.MapSize.Y / binSize + 1;
|
||||
for (var y = 0; y < maxY; y++)
|
||||
for (var y = 0; y < rows; y++)
|
||||
{
|
||||
for (var x = 0; x < maxX; x++)
|
||||
for (var x = 0; x < cols; x++)
|
||||
{
|
||||
if (!dirtyBins[y * maxX + x])
|
||||
if (!dirtyBins[y * cols + x])
|
||||
continue;
|
||||
|
||||
var box = new Rectangle(x * binSize, y * binSize, binSize, binSize);
|
||||
dirtyFrozenActorIds.UnionWith(partitionedFrozenActorIds.InBox(box));
|
||||
}
|
||||
@@ -295,11 +309,17 @@ namespace OpenRA.Traits
|
||||
|
||||
public virtual IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr)
|
||||
{
|
||||
return world.ScreenMap.FrozenActorsInBox(owner, wr.Viewport.TopLeft, wr.Viewport.BottomRight)
|
||||
return world.ScreenMap.RenderableFrozenActorsInBox(owner, wr.Viewport.TopLeft, wr.Viewport.BottomRight)
|
||||
.Where(f => f.Visible)
|
||||
.SelectMany(ff => ff.Render(wr));
|
||||
}
|
||||
|
||||
public IEnumerable<Rectangle> ScreenBounds(Actor self, WorldRenderer wr)
|
||||
{
|
||||
// Player-actor render traits don't require screen bounds
|
||||
yield break;
|
||||
}
|
||||
|
||||
public FrozenActor FromID(uint id)
|
||||
{
|
||||
FrozenActor fa;
|
||||
@@ -308,5 +328,12 @@ namespace OpenRA.Traits
|
||||
|
||||
return fa;
|
||||
}
|
||||
|
||||
public IEnumerable<FrozenActor> FrozenActorsInRegion(CellRegion region)
|
||||
{
|
||||
var tl = region.TopLeft;
|
||||
var br = region.BottomRight;
|
||||
return partitionedFrozenActorIds.InBox(Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y)).Select(FromID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,14 @@ namespace OpenRA.Traits
|
||||
{
|
||||
public class PlayerResourcesInfo : ITraitInfo, ILobbyOptions
|
||||
{
|
||||
[Translate]
|
||||
[Desc("Descriptive label for the starting cash option in the lobby.")]
|
||||
public readonly string DefaultCashDropdownLabel = "Starting Cash";
|
||||
|
||||
[Translate]
|
||||
[Desc("Tooltip description for the starting cash option in the lobby.")]
|
||||
public readonly string DefaultCashDropdownDescription = "Change the amount of cash that players start with";
|
||||
|
||||
[Desc("Starting cash options that are available in the lobby options.")]
|
||||
public readonly int[] SelectableCash = { 2500, 5000, 10000, 20000 };
|
||||
|
||||
@@ -24,7 +32,13 @@ namespace OpenRA.Traits
|
||||
public readonly int DefaultCash = 5000;
|
||||
|
||||
[Desc("Force the DefaultCash option by disabling changes in the lobby.")]
|
||||
public readonly bool DefaultCashLocked = false;
|
||||
public readonly bool DefaultCashDropdownLocked = false;
|
||||
|
||||
[Desc("Whether to display the DefaultCash option in the lobby.")]
|
||||
public readonly bool DefaultCashDropdownVisible = true;
|
||||
|
||||
[Desc("Display order for the DefaultCash option.")]
|
||||
public readonly int DefaultCashDropdownDisplayOrder = 0;
|
||||
|
||||
[Desc("Speech notification to play when the player does not have any funds.")]
|
||||
public readonly string InsufficientFundsNotification = null;
|
||||
@@ -37,7 +51,8 @@ namespace OpenRA.Traits
|
||||
var startingCash = SelectableCash.ToDictionary(c => c.ToString(), c => "$" + c.ToString());
|
||||
|
||||
if (startingCash.Any())
|
||||
yield return new LobbyOption("startingcash", "Starting Cash", new ReadOnlyDictionary<string, string>(startingCash), DefaultCash.ToString(), DefaultCashLocked);
|
||||
yield return new LobbyOption("startingcash", DefaultCashDropdownLabel, DefaultCashDropdownDescription, DefaultCashDropdownVisible, DefaultCashDropdownDisplayOrder,
|
||||
new ReadOnlyDictionary<string, string>(startingCash), DefaultCash.ToString(), DefaultCashDropdownLocked);
|
||||
}
|
||||
|
||||
public object Create(ActorInitializer init) { return new PlayerResources(init.Self, this); }
|
||||
@@ -155,11 +170,13 @@ namespace OpenRA.Traits
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
void ITick.Tick(Actor self)
|
||||
{
|
||||
ResourceCapacity = self.World.ActorsWithTrait<IStoreResources>()
|
||||
.Where(a => a.Actor.Owner == owner)
|
||||
.Sum(a => a.Trait.Capacity);
|
||||
// PERF: Avoid LINQ.
|
||||
ResourceCapacity = 0;
|
||||
foreach (var tp in self.World.ActorsWithTrait<IStoreResources>())
|
||||
if (tp.Actor.Owner == owner)
|
||||
ResourceCapacity += tp.Trait.Capacity;
|
||||
|
||||
if (Resources > ResourceCapacity)
|
||||
Resources = ResourceCapacity;
|
||||
|
||||
@@ -17,22 +17,52 @@ namespace OpenRA.Traits
|
||||
[Desc("Required for shroud and fog visibility checks. Add this to the player actor.")]
|
||||
public class ShroudInfo : ITraitInfo, ILobbyOptions
|
||||
{
|
||||
[Translate]
|
||||
[Desc("Descriptive label for the fog checkbox in the lobby.")]
|
||||
public readonly string FogCheckboxLabel = "Fog of War";
|
||||
|
||||
[Translate]
|
||||
[Desc("Tooltip description for the fog checkbox in the lobby.")]
|
||||
public readonly string FogCheckboxDescription = "Line of sight is required to view enemy forces";
|
||||
|
||||
[Desc("Default value of the fog checkbox in the lobby.")]
|
||||
public bool FogEnabled = true;
|
||||
public readonly bool FogCheckboxEnabled = true;
|
||||
|
||||
[Desc("Prevent the fog enabled state from being changed in the lobby.")]
|
||||
public bool FogLocked = false;
|
||||
public readonly bool FogCheckboxLocked = false;
|
||||
|
||||
[Desc("Whether to display the fog checkbox in the lobby.")]
|
||||
public readonly bool FogCheckboxVisible = true;
|
||||
|
||||
[Desc("Display order for the fog checkbox in the lobby.")]
|
||||
public readonly int FogCheckboxDisplayOrder = 0;
|
||||
|
||||
[Translate]
|
||||
[Desc("Descriptive label for the explored map checkbox in the lobby.")]
|
||||
public readonly string ExploredMapCheckboxLabel = "Explored Map";
|
||||
|
||||
[Translate]
|
||||
[Desc("Tooltip description for the explored map checkbox in the lobby.")]
|
||||
public readonly string ExploredMapCheckboxDescription = "Initial map shroud is revealed";
|
||||
|
||||
[Desc("Default value of the explore map checkbox in the lobby.")]
|
||||
public bool ExploredMapEnabled = false;
|
||||
public readonly bool ExploredMapCheckboxEnabled = false;
|
||||
|
||||
[Desc("Prevent the explore map enabled state from being changed in the lobby.")]
|
||||
public bool ExploredMapLocked = false;
|
||||
public readonly bool ExploredMapCheckboxLocked = false;
|
||||
|
||||
[Desc("Whether to display the explore map checkbox in the lobby.")]
|
||||
public readonly bool ExploredMapCheckboxVisible = true;
|
||||
|
||||
[Desc("Display order for the explore map checkbox in the lobby.")]
|
||||
public readonly int ExploredMapCheckboxDisplayOrder = 0;
|
||||
|
||||
IEnumerable<LobbyOption> ILobbyOptions.LobbyOptions(Ruleset rules)
|
||||
{
|
||||
yield return new LobbyBooleanOption("explored", "Explored Map", ExploredMapEnabled, ExploredMapLocked);
|
||||
yield return new LobbyBooleanOption("fog", "Fog of War", FogEnabled, FogLocked);
|
||||
yield return new LobbyBooleanOption("explored", ExploredMapCheckboxLabel, ExploredMapCheckboxDescription,
|
||||
ExploredMapCheckboxVisible, ExploredMapCheckboxDisplayOrder, ExploredMapCheckboxEnabled, ExploredMapCheckboxLocked);
|
||||
yield return new LobbyBooleanOption("fog", FogCheckboxLabel, FogCheckboxDescription,
|
||||
FogCheckboxVisible, FogCheckboxDisplayOrder, FogCheckboxEnabled, FogCheckboxLocked);
|
||||
}
|
||||
|
||||
public object Create(ActorInitializer init) { return new Shroud(init.Self, this); }
|
||||
@@ -118,9 +148,9 @@ namespace OpenRA.Traits
|
||||
void INotifyCreated.Created(Actor self)
|
||||
{
|
||||
var gs = self.World.LobbyInfo.GlobalSettings;
|
||||
fogEnabled = gs.OptionOrDefault("fog", info.FogEnabled);
|
||||
fogEnabled = gs.OptionOrDefault("fog", info.FogCheckboxEnabled);
|
||||
|
||||
ExploreMapEnabled = gs.OptionOrDefault("explored", info.ExploredMapEnabled);
|
||||
ExploreMapEnabled = gs.OptionOrDefault("explored", info.ExploredMapCheckboxEnabled);
|
||||
if (ExploreMapEnabled)
|
||||
self.World.AddFrameEndTask(w => ExploreAll());
|
||||
}
|
||||
@@ -9,32 +9,33 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using OpenRA.Graphics;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
[Desc("This actor is selectable. Defines bounds of selectable area, selection class and selection priority.")]
|
||||
public class SelectableInfo : ITraitInfo
|
||||
public class SelectableInfo : InteractableInfo
|
||||
{
|
||||
public readonly int Priority = 10;
|
||||
|
||||
[Desc("Bounds for the selectable area.")]
|
||||
public readonly int[] Bounds = null;
|
||||
|
||||
[Desc("All units having the same selection class specified will be selected with select-by-type commands (e.g. double-click). "
|
||||
+ "Defaults to the actor name when not defined or inherited.")]
|
||||
public readonly string Class = null;
|
||||
|
||||
[VoiceReference] public readonly string Voice = "Select";
|
||||
|
||||
public object Create(ActorInitializer init) { return new Selectable(init.Self, this); }
|
||||
public override object Create(ActorInitializer init) { return new Selectable(init.Self, this); }
|
||||
}
|
||||
|
||||
public class Selectable
|
||||
public class Selectable : Interactable
|
||||
{
|
||||
public readonly string Class = null;
|
||||
|
||||
public SelectableInfo Info;
|
||||
public readonly SelectableInfo Info;
|
||||
|
||||
public Selectable(Actor self, SelectableInfo info)
|
||||
: base(info)
|
||||
{
|
||||
Class = string.IsNullOrEmpty(info.Class) ? self.Info.Name : info.Class;
|
||||
Info = info;
|
||||
|
||||
@@ -15,7 +15,7 @@ using System.Linq;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
public enum TargetType { Invalid, Actor, Terrain, FrozenActor }
|
||||
public enum TargetType : byte { Invalid, Actor, Terrain, FrozenActor }
|
||||
public struct Target
|
||||
{
|
||||
public static readonly Target[] None = { };
|
||||
@@ -25,14 +25,19 @@ namespace OpenRA.Traits
|
||||
Actor actor;
|
||||
FrozenActor frozen;
|
||||
WPos pos;
|
||||
CPos? cell;
|
||||
int generation;
|
||||
|
||||
public static Target FromPos(WPos p) { return new Target { pos = p, type = TargetType.Terrain }; }
|
||||
public static Target FromCell(World w, CPos c, SubCell subCell = SubCell.FullCell)
|
||||
{
|
||||
return new Target { pos = w.Map.CenterOfSubCell(c, subCell), type = TargetType.Terrain };
|
||||
return new Target { pos = w.Map.CenterOfSubCell(c, subCell), cell = c, type = TargetType.Terrain };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DEPRECATED: Use Order.Target instead.
|
||||
/// This method is kept to maintain compatibility with legacy code that may not understand TargetType.FrozenActor.
|
||||
/// </summary>
|
||||
public static Target FromOrder(World w, Order o)
|
||||
{
|
||||
return o.TargetActor != null
|
||||
@@ -191,5 +196,10 @@ namespace OpenRA.Traits
|
||||
return "Invalid";
|
||||
}
|
||||
}
|
||||
|
||||
// Expose internal state for serialization by the orders code *only*
|
||||
internal TargetType SerializableType { get { return type; } }
|
||||
internal Actor SerializableActor { get { return actor; } }
|
||||
internal CPos? SerializableCell { get { return cell; } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,12 +92,24 @@ namespace OpenRA.Traits
|
||||
}
|
||||
}
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
public interface ITick { void Tick(Actor self); }
|
||||
[RequireExplicitImplementation]
|
||||
public interface ITickRender { void TickRender(WorldRenderer wr, Actor self); }
|
||||
public interface IRender { IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr); }
|
||||
public interface IRender
|
||||
{
|
||||
IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr);
|
||||
IEnumerable<Rectangle> ScreenBounds(Actor self, WorldRenderer wr);
|
||||
}
|
||||
|
||||
public interface IAutoSelectionSizeInfo : ITraitInfoInterface { }
|
||||
public interface IAutoSelectionSize { int2 SelectionSize(Actor self); }
|
||||
// TODO: Replace Rectangle with an int2[] polygon
|
||||
public interface IMouseBounds { Rectangle MouseoverBounds(Actor self, WorldRenderer wr); }
|
||||
public interface IMouseBoundsInfo : ITraitInfoInterface { }
|
||||
public interface IAutoMouseBounds { Rectangle AutoMouseoverBounds(Actor self, WorldRenderer wr); }
|
||||
|
||||
// HACK: This provides a shim for legacy code until it can be rewritten
|
||||
public interface IDecorationBounds { Rectangle DecorationBounds(Actor self, WorldRenderer wr); }
|
||||
public interface IDecorationBoundsInfo : ITraitInfoInterface { }
|
||||
|
||||
public interface IIssueOrder
|
||||
{
|
||||
@@ -128,23 +140,20 @@ namespace OpenRA.Traits
|
||||
public interface IResolveOrder { void ResolveOrder(Actor self, Order order); }
|
||||
public interface IValidateOrder { bool OrderValidation(OrderManager orderManager, World world, int clientId, Order order); }
|
||||
public interface IOrderVoice { string VoicePhraseForOrder(Actor self, Order order); }
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
public interface INotifyCreated { void Created(Actor self); }
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
public interface INotifyAddedToWorld { void AddedToWorld(Actor self); }
|
||||
[RequireExplicitImplementation]
|
||||
public interface INotifyRemovedFromWorld { void RemovedFromWorld(Actor self); }
|
||||
public interface INotifyDamage { void Damaged(Actor self, AttackInfo e); }
|
||||
public interface INotifyKilled { void Killed(Actor self, AttackInfo e); }
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
public interface INotifyActorDisposing { void Disposing(Actor self); }
|
||||
public interface INotifyAppliedDamage { void AppliedDamage(Actor self, Actor damaged, AttackInfo e); }
|
||||
public interface INotifyOwnerChanged { void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner); }
|
||||
public interface INotifyEffectiveOwnerChanged { void OnEffectiveOwnerChanged(Actor self, Player oldEffectiveOwner, Player newEffectiveOwner); }
|
||||
|
||||
public interface ISeedableResource { void Seed(Actor self); }
|
||||
|
||||
public interface ISelectionDecorationsInfo : ITraitInfoInterface
|
||||
{
|
||||
int[] SelectionBoxBounds { get; }
|
||||
}
|
||||
|
||||
public interface IVoiced
|
||||
{
|
||||
string VoiceSet { get; }
|
||||
@@ -180,32 +189,11 @@ namespace OpenRA.Traits
|
||||
}
|
||||
|
||||
public interface IDisabledTrait { bool IsTraitDisabled { get; } }
|
||||
public interface IDisable { bool Disabled { get; } }
|
||||
public interface IExplodeModifier { bool ShouldExplode(Actor self); }
|
||||
public interface IHuskModifier { string HuskActor(Actor self); }
|
||||
|
||||
public interface IRadarSignature
|
||||
{
|
||||
IEnumerable<Pair<CPos, Color>> RadarSignatureCells(Actor self);
|
||||
}
|
||||
|
||||
public interface IDefaultVisibilityInfo : ITraitInfoInterface { }
|
||||
public interface IDefaultVisibility { bool IsVisible(Actor self, Player byPlayer); }
|
||||
public interface IVisibilityModifier { bool IsVisible(Actor self, Player byPlayer); }
|
||||
|
||||
public interface IFogVisibilityModifier
|
||||
{
|
||||
bool IsVisible(Actor actor);
|
||||
bool HasFogVisibility();
|
||||
}
|
||||
|
||||
public interface IRadarColorModifier { Color RadarColorOverride(Actor self, Color color); }
|
||||
|
||||
public interface ITargetableCells
|
||||
{
|
||||
IEnumerable<Pair<CPos, SubCell>> TargetableCells();
|
||||
}
|
||||
|
||||
public interface IOccupySpaceInfo : ITraitInfoInterface
|
||||
{
|
||||
IReadOnlyDictionary<CPos, SubCell> OccupiedCells(ActorInfo info, CPos location, SubCell subCell = SubCell.Any);
|
||||
@@ -216,27 +204,7 @@ namespace OpenRA.Traits
|
||||
{
|
||||
WPos CenterPosition { get; }
|
||||
CPos TopLeft { get; }
|
||||
IEnumerable<Pair<CPos, SubCell>> OccupiedCells();
|
||||
}
|
||||
|
||||
public static class IOccupySpaceExts
|
||||
{
|
||||
public static CPos NearestCellTo(this IOccupySpace ios, CPos other)
|
||||
{
|
||||
var nearest = ios.TopLeft;
|
||||
var nearestDistance = int.MaxValue;
|
||||
foreach (var cell in ios.OccupiedCells())
|
||||
{
|
||||
var dist = (other - cell.First).LengthSquared;
|
||||
if (dist < nearestDistance)
|
||||
{
|
||||
nearest = cell.First;
|
||||
nearestDistance = dist;
|
||||
}
|
||||
}
|
||||
|
||||
return nearest;
|
||||
}
|
||||
Pair<CPos, SubCell>[] OccupiedCells();
|
||||
}
|
||||
|
||||
public enum SubCell { Invalid = int.MinValue, Any = int.MinValue / 2, FullCell = 0, First = 1 }
|
||||
@@ -263,7 +231,15 @@ namespace OpenRA.Traits
|
||||
IEnumerable<Actor> ActorsInBox(WPos a, WPos b);
|
||||
}
|
||||
|
||||
public interface IRenderModifier { IEnumerable<IRenderable> ModifyRender(Actor self, WorldRenderer wr, IEnumerable<IRenderable> r); }
|
||||
public interface IRenderModifier
|
||||
{
|
||||
IEnumerable<IRenderable> ModifyRender(Actor self, WorldRenderer wr, IEnumerable<IRenderable> r);
|
||||
|
||||
// HACK: This is here to support the WithShadow trait.
|
||||
// That trait should be rewritten using standard techniques, and then this interface method removed
|
||||
IEnumerable<Rectangle> ModifyScreenBounds(Actor self, WorldRenderer wr, IEnumerable<Rectangle> r);
|
||||
}
|
||||
|
||||
public interface ILoadsPalettes { void LoadPalettes(WorldRenderer wr); }
|
||||
public interface ILoadsPlayerPalettes { void LoadPlayerPalettes(WorldRenderer wr, string playerName, HSLColor playerColor, bool replaceExisting); }
|
||||
public interface IPaletteModifier { void AdjustPalette(IReadOnlyDictionary<string, MutablePalette> b); }
|
||||
@@ -288,24 +264,6 @@ namespace OpenRA.Traits
|
||||
void SetVisualPosition(Actor self, WPos pos);
|
||||
}
|
||||
|
||||
public interface IMoveInfo : ITraitInfoInterface { }
|
||||
public interface IMove
|
||||
{
|
||||
Activity MoveTo(CPos cell, int nearEnough);
|
||||
Activity MoveTo(CPos cell, Actor ignoreActor);
|
||||
Activity MoveWithinRange(Target target, WDist range);
|
||||
Activity MoveWithinRange(Target target, WDist minRange, WDist maxRange);
|
||||
Activity MoveFollow(Actor self, Target target, WDist minRange, WDist maxRange);
|
||||
Activity MoveIntoWorld(Actor self, CPos cell, SubCell subCell = SubCell.Any);
|
||||
Activity MoveToTarget(Actor self, Target target);
|
||||
Activity MoveIntoTarget(Actor self, Target target);
|
||||
Activity VisualMove(Actor self, WPos fromPos, WPos toPos);
|
||||
CPos NearestMoveableCell(CPos target);
|
||||
bool IsMoving { get; set; }
|
||||
bool IsMovingVertically { get; set; }
|
||||
bool CanEnterTargetNow(Actor self, Target target);
|
||||
}
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
public interface ITemporaryBlocker
|
||||
{
|
||||
@@ -332,9 +290,14 @@ namespace OpenRA.Traits
|
||||
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1302:InterfaceNamesMustBeginWithI", Justification = "Not a real interface, but more like a tag.")]
|
||||
public interface UsesInit<T> : ITraitInfo where T : IActorInit { }
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
public interface INotifySelected { void Selected(Actor self); }
|
||||
[RequireExplicitImplementation]
|
||||
public interface INotifySelection { void SelectionChanged(); }
|
||||
|
||||
public interface IWorldLoaded { void WorldLoaded(World w, WorldRenderer wr); }
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
public interface ICreatePlayers { void CreatePlayers(World w); }
|
||||
|
||||
public interface IBotInfo : ITraitInfoInterface
|
||||
@@ -349,14 +312,28 @@ namespace OpenRA.Traits
|
||||
IBotInfo Info { get; }
|
||||
}
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
public interface IRenderOverlay { void Render(WorldRenderer wr); }
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
public interface INotifyBecomingIdle { void OnBecomingIdle(Actor self); }
|
||||
[RequireExplicitImplementation]
|
||||
public interface INotifyIdle { void TickIdle(Actor self); }
|
||||
|
||||
public interface IRenderAboveWorld { void RenderAboveWorld(Actor self, WorldRenderer wr); }
|
||||
public interface IRenderShroud { void RenderShroud(Shroud shroud, WorldRenderer wr); }
|
||||
public interface IRenderAboveShroud { IEnumerable<IRenderable> RenderAboveShroud(Actor self, WorldRenderer wr); }
|
||||
public interface IRenderAboveShroudWhenSelected { IEnumerable<IRenderable> RenderAboveShroud(Actor self, WorldRenderer wr); }
|
||||
|
||||
public interface IRenderAboveShroud
|
||||
{
|
||||
IEnumerable<IRenderable> RenderAboveShroud(Actor self, WorldRenderer wr);
|
||||
bool SpatiallyPartitionable { get; }
|
||||
}
|
||||
|
||||
public interface IRenderAboveShroudWhenSelected
|
||||
{
|
||||
IEnumerable<IRenderable> RenderAboveShroud(Actor self, WorldRenderer wr);
|
||||
bool SpatiallyPartitionable { get; }
|
||||
}
|
||||
|
||||
public interface ITargetableInfo : ITraitInfoInterface
|
||||
{
|
||||
@@ -371,30 +348,13 @@ namespace OpenRA.Traits
|
||||
bool RequiresForceFire { get; }
|
||||
}
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
public interface ITargetablePositions
|
||||
{
|
||||
IEnumerable<WPos> TargetablePositions(Actor self);
|
||||
}
|
||||
|
||||
public interface ILintPass { void Run(Action<string> emitError, Action<string> emitWarning, ModData modData); }
|
||||
public interface ILintMapPass { void Run(Action<string> emitError, Action<string> emitWarning, Map map); }
|
||||
public interface ILintRulesPass { void Run(Action<string> emitError, Action<string> emitWarning, Ruleset rules); }
|
||||
|
||||
public interface IObjectivesPanel
|
||||
{
|
||||
string PanelName { get; }
|
||||
int ExitDelay { get; }
|
||||
}
|
||||
|
||||
public interface INotifyObjectivesUpdated
|
||||
{
|
||||
void OnPlayerWon(Player winner);
|
||||
void OnPlayerLost(Player loser);
|
||||
void OnObjectiveAdded(Player player, int objectiveID);
|
||||
void OnObjectiveCompleted(Player player, int objectiveID);
|
||||
void OnObjectiveFailed(Player player, int objectiveID);
|
||||
}
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
public interface IGameOver { void GameOver(World world); }
|
||||
|
||||
public interface IWarhead
|
||||
@@ -418,17 +378,24 @@ namespace OpenRA.Traits
|
||||
{
|
||||
public readonly string Id;
|
||||
public readonly string Name;
|
||||
public readonly string Description;
|
||||
public readonly IReadOnlyDictionary<string, string> Values;
|
||||
public readonly string DefaultValue;
|
||||
public readonly bool Locked;
|
||||
public readonly bool IsLocked;
|
||||
public readonly bool IsVisible;
|
||||
public readonly int DisplayOrder;
|
||||
|
||||
public LobbyOption(string id, string name, IReadOnlyDictionary<string, string> values, string defaultValue, bool locked)
|
||||
public LobbyOption(string id, string name, string description, bool visible, int displayorder,
|
||||
IReadOnlyDictionary<string, string> values, string defaultValue, bool locked)
|
||||
{
|
||||
Id = id;
|
||||
Name = name;
|
||||
Description = description;
|
||||
IsVisible = visible;
|
||||
DisplayOrder = displayorder;
|
||||
Values = values;
|
||||
DefaultValue = defaultValue;
|
||||
Locked = locked;
|
||||
IsLocked = locked;
|
||||
}
|
||||
|
||||
public virtual string ValueChangedMessage(string playerName, string newValue)
|
||||
@@ -445,8 +412,8 @@ namespace OpenRA.Traits
|
||||
{ false.ToString(), "disabled" }
|
||||
};
|
||||
|
||||
public LobbyBooleanOption(string id, string name, bool defaultValue, bool locked)
|
||||
: base(id, name, new ReadOnlyDictionary<string, string>(BoolValues), defaultValue.ToString(), locked) { }
|
||||
public LobbyBooleanOption(string id, string name, string description, bool visible, int displayorder, bool defaultValue, bool locked)
|
||||
: base(id, name, description, visible, displayorder, new ReadOnlyDictionary<string, string>(BoolValues), defaultValue.ToString(), locked) { }
|
||||
|
||||
public override string ValueChangedMessage(string playerName, string newValue)
|
||||
{
|
||||
|
||||
@@ -9,28 +9,17 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using OpenRA.Activities;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Activities
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
public class FlyCircleTimed : FlyCircle
|
||||
[Desc("Enables visualization commands. Attach this to the world actor.")]
|
||||
public class DebugVisualizationsInfo : TraitInfo<DebugVisualizations> { }
|
||||
|
||||
public class DebugVisualizations
|
||||
{
|
||||
int remainingTicks;
|
||||
|
||||
public FlyCircleTimed(Actor self, int ticks) : base(self)
|
||||
{
|
||||
remainingTicks = ticks;
|
||||
}
|
||||
|
||||
public override Activity Tick(Actor self)
|
||||
{
|
||||
if (IsCanceled || remainingTicks-- == 0)
|
||||
return NextActivity;
|
||||
|
||||
base.Tick(self);
|
||||
|
||||
return this;
|
||||
}
|
||||
public bool CombatGeometry;
|
||||
public bool RenderGeometry;
|
||||
public bool ScreenMap;
|
||||
public bool DepthBuffer;
|
||||
public bool ActorTags;
|
||||
}
|
||||
}
|
||||
@@ -13,11 +13,32 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Effects;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
public struct ActorBoundsPair : IEquatable<ActorBoundsPair>
|
||||
{
|
||||
public readonly Actor Actor;
|
||||
|
||||
// TODO: Replace this with an int2[] polygon
|
||||
public readonly Rectangle Bounds;
|
||||
|
||||
public ActorBoundsPair(Actor actor, Rectangle bounds) { Actor = actor; Bounds = bounds; }
|
||||
|
||||
public static bool operator ==(ActorBoundsPair me, ActorBoundsPair other) { return me.Actor == other.Actor && Equals(me.Bounds, other.Bounds); }
|
||||
public static bool operator !=(ActorBoundsPair me, ActorBoundsPair other) { return !(me == other); }
|
||||
|
||||
public override int GetHashCode() { return Actor.GetHashCode() ^ Bounds.GetHashCode(); }
|
||||
|
||||
public bool Equals(ActorBoundsPair other) { return this == other; }
|
||||
public override bool Equals(object obj) { return obj is ActorBoundsPair && Equals((ActorBoundsPair)obj); }
|
||||
|
||||
public override string ToString() { return "{0}->{1}".F(Actor.Info.Name, Bounds.GetType().Name); }
|
||||
}
|
||||
|
||||
public class ScreenMapInfo : ITraitInfo
|
||||
{
|
||||
[Desc("Size of partition bins (world pixels)")]
|
||||
@@ -31,8 +52,21 @@ namespace OpenRA.Traits
|
||||
static readonly IEnumerable<FrozenActor> NoFrozenActors = new FrozenActor[0];
|
||||
readonly Func<FrozenActor, bool> frozenActorIsValid = fa => fa.IsValid;
|
||||
readonly Func<Actor, bool> actorIsInWorld = a => a.IsInWorld;
|
||||
readonly Cache<Player, SpatiallyPartitioned<FrozenActor>> partitionedFrozenActors;
|
||||
readonly SpatiallyPartitioned<Actor> partitionedActors;
|
||||
readonly Func<Actor, ActorBoundsPair> selectActorAndBounds;
|
||||
readonly Cache<Player, SpatiallyPartitioned<FrozenActor>> partitionedMouseFrozenActors;
|
||||
readonly SpatiallyPartitioned<Actor> partitionedMouseActors;
|
||||
readonly Dictionary<Actor, ActorBoundsPair> partitionedMouseActorBounds = new Dictionary<Actor, ActorBoundsPair>();
|
||||
|
||||
readonly Cache<Player, SpatiallyPartitioned<FrozenActor>> partitionedRenderableFrozenActors;
|
||||
readonly SpatiallyPartitioned<Actor> partitionedRenderableActors;
|
||||
readonly SpatiallyPartitioned<IEffect> partitionedRenderableEffects;
|
||||
|
||||
// Updates are done in one pass to ensure all bound changes have been applied
|
||||
readonly HashSet<Actor> addOrUpdateActors = new HashSet<Actor>();
|
||||
readonly HashSet<Actor> removeActors = new HashSet<Actor>();
|
||||
readonly Cache<Player, HashSet<FrozenActor>> addOrUpdateFrozenActors;
|
||||
readonly Cache<Player, HashSet<FrozenActor>> removeFrozenActors;
|
||||
|
||||
WorldRenderer worldRenderer;
|
||||
|
||||
public ScreenMap(World world, ScreenMapInfo info)
|
||||
@@ -40,74 +74,114 @@ namespace OpenRA.Traits
|
||||
var size = world.Map.Grid.TileSize;
|
||||
var width = world.Map.MapSize.X * size.Width;
|
||||
var height = world.Map.MapSize.Y * size.Height;
|
||||
partitionedFrozenActors = new Cache<Player, SpatiallyPartitioned<FrozenActor>>(
|
||||
|
||||
partitionedMouseFrozenActors = new Cache<Player, SpatiallyPartitioned<FrozenActor>>(
|
||||
_ => new SpatiallyPartitioned<FrozenActor>(width, height, info.BinSize));
|
||||
partitionedActors = new SpatiallyPartitioned<Actor>(width, height, info.BinSize);
|
||||
partitionedMouseActors = new SpatiallyPartitioned<Actor>(width, height, info.BinSize);
|
||||
selectActorAndBounds = a => partitionedMouseActorBounds[a];
|
||||
|
||||
partitionedRenderableFrozenActors = new Cache<Player, SpatiallyPartitioned<FrozenActor>>(
|
||||
_ => new SpatiallyPartitioned<FrozenActor>(width, height, info.BinSize));
|
||||
partitionedRenderableActors = new SpatiallyPartitioned<Actor>(width, height, info.BinSize);
|
||||
partitionedRenderableEffects = new SpatiallyPartitioned<IEffect>(width, height, info.BinSize);
|
||||
|
||||
addOrUpdateFrozenActors = new Cache<Player, HashSet<FrozenActor>>(_ => new HashSet<FrozenActor>());
|
||||
removeFrozenActors = new Cache<Player, HashSet<FrozenActor>>(_ => new HashSet<FrozenActor>());
|
||||
}
|
||||
|
||||
public void WorldLoaded(World w, WorldRenderer wr) { worldRenderer = wr; }
|
||||
|
||||
Rectangle FrozenActorBounds(FrozenActor fa)
|
||||
public void AddOrUpdate(Player viewer, FrozenActor fa)
|
||||
{
|
||||
var pos = worldRenderer.ScreenPxPosition(fa.CenterPosition);
|
||||
var bounds = fa.Bounds;
|
||||
bounds.Offset(pos.X, pos.Y);
|
||||
return bounds;
|
||||
}
|
||||
if (removeFrozenActors[viewer].Contains(fa))
|
||||
removeFrozenActors[viewer].Remove(fa);
|
||||
|
||||
Rectangle ActorBounds(Actor a)
|
||||
{
|
||||
var pos = worldRenderer.ScreenPxPosition(a.CenterPosition);
|
||||
var bounds = a.Bounds;
|
||||
bounds.Offset(pos.X, pos.Y);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
public void Add(Player viewer, FrozenActor fa)
|
||||
{
|
||||
partitionedFrozenActors[viewer].Add(fa, FrozenActorBounds(fa));
|
||||
addOrUpdateFrozenActors[viewer].Add(fa);
|
||||
}
|
||||
|
||||
public void Remove(Player viewer, FrozenActor fa)
|
||||
{
|
||||
partitionedFrozenActors[viewer].Remove(fa);
|
||||
removeFrozenActors[viewer].Add(fa);
|
||||
}
|
||||
|
||||
public void Add(Actor a)
|
||||
public void AddOrUpdate(Actor a)
|
||||
{
|
||||
partitionedActors.Add(a, ActorBounds(a));
|
||||
}
|
||||
if (removeActors.Contains(a))
|
||||
removeActors.Remove(a);
|
||||
|
||||
public void Update(Actor a)
|
||||
{
|
||||
partitionedActors.Update(a, ActorBounds(a));
|
||||
addOrUpdateActors.Add(a);
|
||||
}
|
||||
|
||||
public void Remove(Actor a)
|
||||
{
|
||||
partitionedActors.Remove(a);
|
||||
removeActors.Add(a);
|
||||
}
|
||||
|
||||
public IEnumerable<FrozenActor> FrozenActorsAt(Player viewer, int2 worldPx)
|
||||
public void Add(IEffect effect, WPos position, Size size)
|
||||
{
|
||||
var screenPos = worldRenderer.ScreenPxPosition(position);
|
||||
var screenWidth = Math.Abs(size.Width);
|
||||
var screenHeight = Math.Abs(size.Height);
|
||||
var screenBounds = new Rectangle(screenPos.X - screenWidth / 2, screenPos.Y - screenHeight / 2, screenWidth, screenHeight);
|
||||
if (ValidBounds(screenBounds))
|
||||
partitionedRenderableEffects.Add(effect, screenBounds);
|
||||
}
|
||||
|
||||
public void Add(IEffect effect, WPos position, Sprite sprite)
|
||||
{
|
||||
var size = new Size((int)sprite.Size.X, (int)sprite.Size.Y);
|
||||
Add(effect, position, size);
|
||||
}
|
||||
|
||||
public void Update(IEffect effect, WPos position, Size size)
|
||||
{
|
||||
Remove(effect);
|
||||
Add(effect, position, size);
|
||||
}
|
||||
|
||||
public void Update(IEffect effect, WPos position, Sprite sprite)
|
||||
{
|
||||
var size = new Size((int)sprite.Size.X, (int)sprite.Size.Y);
|
||||
Update(effect, position, size);
|
||||
}
|
||||
|
||||
public void Remove(IEffect effect)
|
||||
{
|
||||
partitionedRenderableEffects.Remove(effect);
|
||||
}
|
||||
|
||||
static bool ValidBounds(Rectangle bounds)
|
||||
{
|
||||
return bounds.Width > 0 && bounds.Height > 0;
|
||||
}
|
||||
|
||||
public IEnumerable<FrozenActor> FrozenActorsAtMouse(Player viewer, int2 worldPx)
|
||||
{
|
||||
if (viewer == null)
|
||||
return NoFrozenActors;
|
||||
return partitionedFrozenActors[viewer].At(worldPx).Where(frozenActorIsValid);
|
||||
|
||||
return partitionedMouseFrozenActors[viewer]
|
||||
.At(worldPx)
|
||||
.Where(frozenActorIsValid)
|
||||
.Where(x => x.MouseBounds.Contains(worldPx));
|
||||
}
|
||||
|
||||
public IEnumerable<FrozenActor> FrozenActorsAt(Player viewer, MouseInput mi)
|
||||
public IEnumerable<FrozenActor> FrozenActorsAtMouse(Player viewer, MouseInput mi)
|
||||
{
|
||||
return FrozenActorsAt(viewer, worldRenderer.Viewport.ViewToWorldPx(mi.Location));
|
||||
return FrozenActorsAtMouse(viewer, worldRenderer.Viewport.ViewToWorldPx(mi.Location));
|
||||
}
|
||||
|
||||
public IEnumerable<Actor> ActorsAt(int2 worldPx)
|
||||
public IEnumerable<ActorBoundsPair> ActorsAtMouse(int2 worldPx)
|
||||
{
|
||||
return partitionedActors.At(worldPx).Where(actorIsInWorld);
|
||||
return partitionedMouseActors.At(worldPx)
|
||||
.Where(actorIsInWorld)
|
||||
.Select(selectActorAndBounds)
|
||||
.Where(x => x.Bounds.Contains(worldPx));
|
||||
}
|
||||
|
||||
public IEnumerable<Actor> ActorsAt(MouseInput mi)
|
||||
public IEnumerable<ActorBoundsPair> ActorsAtMouse(MouseInput mi)
|
||||
{
|
||||
return ActorsAt(worldRenderer.Viewport.ViewToWorldPx(mi.Location));
|
||||
return ActorsAtMouse(worldRenderer.Viewport.ViewToWorldPx(mi.Location));
|
||||
}
|
||||
|
||||
static Rectangle RectWithCorners(int2 a, int2 b)
|
||||
@@ -115,26 +189,156 @@ namespace OpenRA.Traits
|
||||
return Rectangle.FromLTRB(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Max(a.X, b.X), Math.Max(a.Y, b.Y));
|
||||
}
|
||||
|
||||
public IEnumerable<Actor> ActorsInBox(int2 a, int2 b)
|
||||
public IEnumerable<ActorBoundsPair> ActorsInMouseBox(int2 a, int2 b)
|
||||
{
|
||||
return ActorsInBox(RectWithCorners(a, b));
|
||||
return ActorsInMouseBox(RectWithCorners(a, b));
|
||||
}
|
||||
|
||||
public IEnumerable<Actor> ActorsInBox(Rectangle r)
|
||||
public IEnumerable<ActorBoundsPair> ActorsInMouseBox(Rectangle r)
|
||||
{
|
||||
return partitionedActors.InBox(r).Where(actorIsInWorld);
|
||||
return partitionedMouseActors.InBox(r)
|
||||
.Where(actorIsInWorld)
|
||||
.Select(selectActorAndBounds)
|
||||
.Where(x => r.IntersectsWith(x.Bounds));
|
||||
}
|
||||
|
||||
public IEnumerable<FrozenActor> FrozenActorsInBox(Player p, int2 a, int2 b)
|
||||
public IEnumerable<Actor> RenderableActorsInBox(int2 a, int2 b)
|
||||
{
|
||||
return FrozenActorsInBox(p, RectWithCorners(a, b));
|
||||
return partitionedRenderableActors.InBox(RectWithCorners(a, b)).Where(actorIsInWorld);
|
||||
}
|
||||
|
||||
public IEnumerable<FrozenActor> FrozenActorsInBox(Player p, Rectangle r)
|
||||
public IEnumerable<IEffect> RenderableEffectsInBox(int2 a, int2 b)
|
||||
{
|
||||
return partitionedRenderableEffects.InBox(RectWithCorners(a, b));
|
||||
}
|
||||
|
||||
public IEnumerable<FrozenActor> RenderableFrozenActorsInBox(Player p, int2 a, int2 b)
|
||||
{
|
||||
if (p == null)
|
||||
return NoFrozenActors;
|
||||
return partitionedFrozenActors[p].InBox(r).Where(frozenActorIsValid);
|
||||
|
||||
return partitionedRenderableFrozenActors[p].InBox(RectWithCorners(a, b)).Where(frozenActorIsValid);
|
||||
}
|
||||
|
||||
Rectangle AggregateBounds(IEnumerable<Rectangle> screenBounds)
|
||||
{
|
||||
if (!screenBounds.Any())
|
||||
return Rectangle.Empty;
|
||||
|
||||
var bounds = screenBounds.First();
|
||||
foreach (var b in screenBounds.Skip(1))
|
||||
bounds = Rectangle.Union(bounds, b);
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
Rectangle AggregateBounds(IEnumerable<int2> vertices)
|
||||
{
|
||||
if (!vertices.Any())
|
||||
return Rectangle.Empty;
|
||||
|
||||
var first = vertices.First();
|
||||
var rect = new Rectangle(first.X, first.Y, 0, 0);
|
||||
foreach (var v in vertices.Skip(1))
|
||||
rect = Rectangle.Union(rect, new Rectangle(v.X, v.Y, 0, 0));
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
foreach (var a in addOrUpdateActors)
|
||||
{
|
||||
var mouseBounds = a.MouseBounds(worldRenderer);
|
||||
if (!mouseBounds.Size.IsEmpty)
|
||||
{
|
||||
if (partitionedMouseActors.Contains(a))
|
||||
partitionedMouseActors.Update(a, mouseBounds);
|
||||
else
|
||||
partitionedMouseActors.Add(a, mouseBounds);
|
||||
|
||||
partitionedMouseActorBounds[a] = new ActorBoundsPair(a, mouseBounds);
|
||||
}
|
||||
else
|
||||
partitionedMouseActors.Remove(a);
|
||||
|
||||
var screenBounds = AggregateBounds(a.ScreenBounds(worldRenderer));
|
||||
if (!screenBounds.Size.IsEmpty)
|
||||
{
|
||||
if (partitionedRenderableActors.Contains(a))
|
||||
partitionedRenderableActors.Update(a, screenBounds);
|
||||
else
|
||||
partitionedRenderableActors.Add(a, screenBounds);
|
||||
}
|
||||
else
|
||||
partitionedRenderableActors.Remove(a);
|
||||
}
|
||||
|
||||
foreach (var a in removeActors)
|
||||
{
|
||||
partitionedMouseActors.Remove(a);
|
||||
partitionedMouseActorBounds.Remove(a);
|
||||
partitionedRenderableActors.Remove(a);
|
||||
}
|
||||
|
||||
addOrUpdateActors.Clear();
|
||||
removeActors.Clear();
|
||||
|
||||
foreach (var kv in addOrUpdateFrozenActors)
|
||||
{
|
||||
foreach (var fa in kv.Value)
|
||||
{
|
||||
var mouseBounds = fa.MouseBounds;
|
||||
if (!mouseBounds.Size.IsEmpty)
|
||||
{
|
||||
if (partitionedMouseFrozenActors[kv.Key].Contains(fa))
|
||||
partitionedMouseFrozenActors[kv.Key].Update(fa, mouseBounds);
|
||||
else
|
||||
partitionedMouseFrozenActors[kv.Key].Add(fa, mouseBounds);
|
||||
}
|
||||
else
|
||||
partitionedMouseFrozenActors[kv.Key].Remove(fa);
|
||||
|
||||
var screenBounds = AggregateBounds(fa.ScreenBounds);
|
||||
if (!screenBounds.Size.IsEmpty)
|
||||
{
|
||||
if (partitionedRenderableFrozenActors[kv.Key].Contains(fa))
|
||||
partitionedRenderableFrozenActors[kv.Key].Update(fa, screenBounds);
|
||||
else
|
||||
partitionedRenderableFrozenActors[kv.Key].Add(fa, screenBounds);
|
||||
}
|
||||
else
|
||||
partitionedRenderableFrozenActors[kv.Key].Remove(fa);
|
||||
}
|
||||
|
||||
kv.Value.Clear();
|
||||
}
|
||||
|
||||
foreach (var kv in removeFrozenActors)
|
||||
{
|
||||
foreach (var fa in kv.Value)
|
||||
{
|
||||
partitionedMouseFrozenActors[kv.Key].Remove(fa);
|
||||
partitionedRenderableFrozenActors[kv.Key].Remove(fa);
|
||||
}
|
||||
|
||||
kv.Value.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Rectangle> RenderBounds(Player viewer)
|
||||
{
|
||||
var bounds = partitionedRenderableActors.ItemBounds
|
||||
.Concat(partitionedRenderableEffects.ItemBounds);
|
||||
|
||||
return viewer != null ? bounds.Concat(partitionedRenderableFrozenActors[viewer].ItemBounds) : bounds;
|
||||
}
|
||||
|
||||
public IEnumerable<Rectangle> MouseBounds(Player viewer)
|
||||
{
|
||||
var bounds = partitionedMouseActors.ItemBounds;
|
||||
|
||||
return viewer != null ? bounds.Concat(partitionedMouseFrozenActors[viewer].ItemBounds) : bounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,9 +36,9 @@ namespace OpenRA.Traits
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public void WorldLoaded(World w, WorldRenderer wr) { worldRenderer = wr; }
|
||||
void IWorldLoaded.WorldLoaded(World w, WorldRenderer wr) { worldRenderer = wr; }
|
||||
|
||||
public void Tick(Actor self)
|
||||
void ITick.Tick(Actor self)
|
||||
{
|
||||
if (shakeEffects.Any())
|
||||
{
|
||||
|
||||
@@ -128,29 +128,6 @@ namespace OpenRA.Widgets
|
||||
/// <param name="e">Key input data</param>
|
||||
public static bool HandleKeyPress(KeyInput e)
|
||||
{
|
||||
if (e.Event == KeyInputEvent.Down)
|
||||
{
|
||||
var hk = Hotkey.FromKeyInput(e);
|
||||
|
||||
if (hk == Game.Settings.Keys.DevReloadChromeKey)
|
||||
{
|
||||
ChromeProvider.Initialize(Game.ModData);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hk == Game.Settings.Keys.HideUserInterfaceKey)
|
||||
{
|
||||
Root.Visible ^= true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hk == Game.Settings.Keys.TakeScreenshotKey)
|
||||
{
|
||||
Game.TakeScreenshot = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (KeyboardFocusWidget != null)
|
||||
return KeyboardFocusWidget.HandleKeyPressOuter(e);
|
||||
|
||||
@@ -307,8 +284,15 @@ namespace OpenRA.Widgets
|
||||
// PERF: Avoid LINQ.
|
||||
var bounds = EventBounds;
|
||||
foreach (var child in Children)
|
||||
{
|
||||
if (child.IsVisible())
|
||||
bounds = Rectangle.Union(bounds, child.GetEventBounds());
|
||||
{
|
||||
var childBounds = child.GetEventBounds();
|
||||
if (childBounds != Rectangle.Empty)
|
||||
bounds = Rectangle.Union(bounds, childBounds);
|
||||
}
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
|
||||
@@ -203,29 +203,29 @@ namespace OpenRA.Widgets
|
||||
for (var i = 0; i < lines.Count; i++)
|
||||
{
|
||||
var line = lines[i];
|
||||
var m = font.Measure(line);
|
||||
|
||||
if (m.X <= width)
|
||||
if (font.Measure(line).X <= width)
|
||||
continue;
|
||||
|
||||
var bestSpaceIndex = -1;
|
||||
var start = line.Length - 1;
|
||||
|
||||
while (m.X > width)
|
||||
// Scan forwards until we find the last word that fits
|
||||
// This guarantees a small bound on the amount of string we need to search before a linebreak
|
||||
var start = 0;
|
||||
while (true)
|
||||
{
|
||||
var spaceIndex = line.LastIndexOf(' ', start);
|
||||
var spaceIndex = line.IndexOf(' ', start);
|
||||
if (spaceIndex == -1)
|
||||
break;
|
||||
bestSpaceIndex = spaceIndex;
|
||||
|
||||
start = spaceIndex - 1;
|
||||
m = font.Measure(line.Substring(0, spaceIndex));
|
||||
var fragmentWidth = font.Measure(line.Substring(0, spaceIndex)).X;
|
||||
if (fragmentWidth > width)
|
||||
break;
|
||||
|
||||
start = spaceIndex + 1;
|
||||
}
|
||||
|
||||
if (bestSpaceIndex != -1)
|
||||
if (start > 0)
|
||||
{
|
||||
lines[i] = line.Substring(0, bestSpaceIndex);
|
||||
lines.Insert(i + 1, line.Substring(bestSpaceIndex + 1));
|
||||
lines[i] = line.Substring(0, start - 1);
|
||||
lines.Insert(i + 1, line.Substring(start));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace OpenRA
|
||||
internal readonly TraitDictionary TraitDict = new TraitDictionary();
|
||||
readonly SortedDictionary<uint, Actor> actors = new SortedDictionary<uint, Actor>();
|
||||
readonly List<IEffect> effects = new List<IEffect>();
|
||||
readonly List<IEffect> unpartitionedEffects = new List<IEffect>();
|
||||
readonly List<ISync> syncedEffects = new List<ISync>();
|
||||
|
||||
readonly Queue<Action<World>> frameEndActions = new Queue<Action<World>>();
|
||||
@@ -76,7 +77,7 @@ namespace OpenRA
|
||||
set { renderPlayer = value; }
|
||||
}
|
||||
|
||||
public bool FogObscures(Actor a) { return RenderPlayer != null && !RenderPlayer.CanViewActor(a); }
|
||||
public bool FogObscures(Actor a) { return RenderPlayer != null && !a.CanBeViewedByPlayer(RenderPlayer); }
|
||||
public bool FogObscures(CPos p) { return RenderPlayer != null && !RenderPlayer.Shroud.IsVisible(p); }
|
||||
public bool FogObscures(WPos pos) { return RenderPlayer != null && !RenderPlayer.Shroud.IsVisible(pos); }
|
||||
public bool ShroudObscures(CPos p) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(p); }
|
||||
@@ -130,7 +131,7 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
public Selection Selection = new Selection();
|
||||
public readonly Selection Selection = new Selection();
|
||||
|
||||
public void CancelInputMode() { OrderGenerator = new UnitOrderGenerator(); }
|
||||
|
||||
@@ -190,9 +191,7 @@ namespace OpenRA
|
||||
{
|
||||
ActorMap.AddInfluence(self, ios);
|
||||
ActorMap.AddPosition(self, ios);
|
||||
|
||||
if (!self.Bounds.Size.IsEmpty)
|
||||
ScreenMap.Add(self);
|
||||
ScreenMap.AddOrUpdate(self);
|
||||
}
|
||||
|
||||
public void UpdateMaps(Actor self, IOccupySpace ios)
|
||||
@@ -200,9 +199,7 @@ namespace OpenRA
|
||||
if (!self.IsInWorld)
|
||||
return;
|
||||
|
||||
if (!self.Bounds.Size.IsEmpty)
|
||||
ScreenMap.Update(self);
|
||||
|
||||
ScreenMap.AddOrUpdate(self);
|
||||
ActorMap.UpdatePosition(self, ios);
|
||||
}
|
||||
|
||||
@@ -210,9 +207,7 @@ namespace OpenRA
|
||||
{
|
||||
ActorMap.RemoveInfluence(self, ios);
|
||||
ActorMap.RemovePosition(self, ios);
|
||||
|
||||
if (!self.Bounds.Size.IsEmpty)
|
||||
ScreenMap.Remove(self);
|
||||
ScreenMap.Remove(self);
|
||||
}
|
||||
|
||||
public void LoadComplete(WorldRenderer wr)
|
||||
@@ -285,6 +280,11 @@ namespace OpenRA
|
||||
public void Add(IEffect e)
|
||||
{
|
||||
effects.Add(e);
|
||||
|
||||
var sp = e as ISpatiallyPartitionable;
|
||||
if (sp == null)
|
||||
unpartitionedEffects.Add(e);
|
||||
|
||||
var se = e as ISync;
|
||||
if (se != null)
|
||||
syncedEffects.Add(se);
|
||||
@@ -293,6 +293,11 @@ namespace OpenRA
|
||||
public void Remove(IEffect e)
|
||||
{
|
||||
effects.Remove(e);
|
||||
|
||||
var sp = e as ISpatiallyPartitionable;
|
||||
if (sp == null)
|
||||
unpartitionedEffects.Remove(e);
|
||||
|
||||
var se = e as ISync;
|
||||
if (se != null)
|
||||
syncedEffects.Remove(se);
|
||||
@@ -301,6 +306,7 @@ namespace OpenRA
|
||||
public void RemoveAll(Predicate<IEffect> predicate)
|
||||
{
|
||||
effects.RemoveAll(predicate);
|
||||
unpartitionedEffects.RemoveAll(e => predicate((IEffect)e));
|
||||
syncedEffects.RemoveAll(e => predicate((IEffect)e));
|
||||
}
|
||||
|
||||
@@ -347,6 +353,7 @@ namespace OpenRA
|
||||
ActorsWithTrait<ITick>().DoTimed(x => x.Trait.Tick(x.Actor), "Trait");
|
||||
|
||||
effects.DoTimed(e => e.Tick(this), "Effect");
|
||||
ScreenMap.Tick();
|
||||
}
|
||||
|
||||
while (frameEndActions.Count != 0)
|
||||
@@ -361,6 +368,7 @@ namespace OpenRA
|
||||
|
||||
public IEnumerable<Actor> Actors { get { return actors.Values; } }
|
||||
public IEnumerable<IEffect> Effects { get { return effects; } }
|
||||
public IEnumerable<IEffect> UnpartitionedEffects { get { return unpartitionedEffects; } }
|
||||
public IEnumerable<ISync> SyncedEffects { get { return syncedEffects; } }
|
||||
|
||||
public Actor GetActorById(uint actorId)
|
||||
@@ -391,7 +399,7 @@ namespace OpenRA
|
||||
// Hash fields marked with the ISync interface.
|
||||
foreach (var actor in ActorsHavingTrait<ISync>())
|
||||
foreach (var syncHash in actor.SyncHashes)
|
||||
ret += n++ * (int)(1 + actor.ActorID) * syncHash.Hash;
|
||||
ret += n++ * (int)(1 + actor.ActorID) * syncHash.Hash();
|
||||
|
||||
// Hash game state relevant effects such as projectiles.
|
||||
foreach (var sync in SyncedEffects)
|
||||
@@ -467,6 +475,6 @@ namespace OpenRA
|
||||
public bool Equals(TraitPair<T> other) { return this == other; }
|
||||
public override bool Equals(object obj) { return obj is TraitPair<T> && Equals((TraitPair<T>)obj); }
|
||||
|
||||
public override string ToString() { return "{0}->{1}".F(Actor.Info.Name, Trait.GetType().Name); }
|
||||
public override string ToString() { return Actor.Info.Name + "->" + Trait.GetType().Name; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Linq;
|
||||
using OpenRA.Activities;
|
||||
using OpenRA.Mods.Cnc.Traits;
|
||||
using OpenRA.Mods.Common.Activities;
|
||||
@@ -21,14 +22,14 @@ namespace OpenRA.Mods.Cnc.Activities
|
||||
{
|
||||
readonly Actor target;
|
||||
readonly Infiltrates infiltrates;
|
||||
readonly Cloak cloak;
|
||||
readonly INotifyInfiltration[] notifiers;
|
||||
|
||||
public Infiltrate(Actor self, Actor target, Infiltrates infiltrate)
|
||||
: base(self, target, infiltrate.Info.EnterBehaviour)
|
||||
{
|
||||
this.target = target;
|
||||
infiltrates = infiltrate;
|
||||
cloak = self.TraitOrDefault<Cloak>();
|
||||
notifiers = self.TraitsImplementing<INotifyInfiltration>().ToArray();
|
||||
}
|
||||
|
||||
protected override void OnInside(Actor self)
|
||||
@@ -40,11 +41,11 @@ namespace OpenRA.Mods.Cnc.Activities
|
||||
if (!infiltrates.Info.ValidStances.HasStance(stance))
|
||||
return;
|
||||
|
||||
if (cloak != null && cloak.Info.UncloakOn.HasFlag(UncloakType.Infiltrate))
|
||||
cloak.Uncloak();
|
||||
foreach (var ini in notifiers)
|
||||
ini.Infiltrating(self);
|
||||
|
||||
foreach (var t in target.TraitsImplementing<INotifyInfiltrated>())
|
||||
t.Infiltrated(target, self);
|
||||
t.Infiltrated(target, self, infiltrates.Info.Types);
|
||||
|
||||
var exp = self.Owner.PlayerActor.TraitOrDefault<PlayerExperience>();
|
||||
if (exp != null)
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace OpenRA.Mods.Cnc.Activities
|
||||
var pool = ammoPools.FirstOrDefault(x => x.Info.Name == info.AmmoPoolName);
|
||||
if (pool == null)
|
||||
return;
|
||||
pool.TakeAmmo();
|
||||
pool.TakeAmmo(self, 1);
|
||||
}
|
||||
|
||||
self.World.AddFrameEndTask(
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2017 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Effects;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Cnc.Traits;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.Effects
|
||||
{
|
||||
[Desc("Attach this to actors to render pictograms while hidden.")]
|
||||
class GpsDotInfo : ITraitInfo
|
||||
{
|
||||
[Desc("Sprite collection for symbols.")]
|
||||
public readonly string Image = "gpsdot";
|
||||
|
||||
[Desc("Sprite used for this actor.")]
|
||||
[SequenceReference("Image")] public readonly string String = "Infantry";
|
||||
|
||||
[PaletteReference(true)] public readonly string IndicatorPalettePrefix = "player";
|
||||
|
||||
public object Create(ActorInitializer init)
|
||||
{
|
||||
return new GpsDot(init.Self, this);
|
||||
}
|
||||
}
|
||||
|
||||
class GpsDot : IEffect, IEffectAboveShroud
|
||||
{
|
||||
readonly Actor self;
|
||||
readonly GpsDotInfo info;
|
||||
readonly Animation anim;
|
||||
|
||||
readonly PlayerDictionary<DotState> dotStates;
|
||||
readonly Lazy<HiddenUnderFog> huf;
|
||||
readonly Lazy<FrozenUnderFog> fuf;
|
||||
readonly Lazy<Disguise> disguise;
|
||||
readonly Lazy<Cloak> cloak;
|
||||
readonly Cache<Player, FrozenActorLayer> frozen;
|
||||
|
||||
class DotState
|
||||
{
|
||||
public readonly GpsWatcher Gps;
|
||||
public bool IsTargetable;
|
||||
public bool ShouldRender;
|
||||
public DotState(GpsWatcher gps)
|
||||
{
|
||||
Gps = gps;
|
||||
}
|
||||
}
|
||||
|
||||
public GpsDot(Actor self, GpsDotInfo info)
|
||||
{
|
||||
this.self = self;
|
||||
this.info = info;
|
||||
anim = new Animation(self.World, info.Image);
|
||||
anim.PlayRepeating(info.String);
|
||||
|
||||
self.World.AddFrameEndTask(w => w.Add(this));
|
||||
|
||||
huf = Exts.Lazy(() => self.TraitOrDefault<HiddenUnderFog>());
|
||||
fuf = Exts.Lazy(() => self.TraitOrDefault<FrozenUnderFog>());
|
||||
disguise = Exts.Lazy(() => self.TraitOrDefault<Disguise>());
|
||||
cloak = Exts.Lazy(() => self.TraitOrDefault<Cloak>());
|
||||
|
||||
frozen = new Cache<Player, FrozenActorLayer>(p => p.PlayerActor.Trait<FrozenActorLayer>());
|
||||
dotStates = new PlayerDictionary<DotState>(self.World, player => new DotState(player.PlayerActor.Trait<GpsWatcher>()));
|
||||
}
|
||||
|
||||
public bool IsDotVisible(Player toPlayer)
|
||||
{
|
||||
return dotStates[toPlayer].IsTargetable;
|
||||
}
|
||||
|
||||
bool IsTargetableBy(Player toPlayer, out bool shouldRenderIndicator)
|
||||
{
|
||||
shouldRenderIndicator = false;
|
||||
|
||||
if (cloak.Value != null && cloak.Value.Cloaked)
|
||||
return false;
|
||||
|
||||
if (disguise.Value != null && disguise.Value.Disguised)
|
||||
return false;
|
||||
|
||||
if (huf.Value != null && !huf.Value.IsVisible(self, toPlayer)
|
||||
&& toPlayer.Shroud.IsExplored(self.CenterPosition))
|
||||
{
|
||||
var f1 = FrozenActorForPlayer(toPlayer);
|
||||
shouldRenderIndicator = f1 == null || !f1.HasRenderables;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (fuf.Value == null)
|
||||
return false;
|
||||
|
||||
var f2 = FrozenActorForPlayer(toPlayer);
|
||||
if (f2 == null)
|
||||
return false;
|
||||
|
||||
shouldRenderIndicator = !f2.HasRenderables;
|
||||
|
||||
return f2.Visible && !f2.Shrouded && toPlayer.Shroud.IsExplored(self.CenterPosition);
|
||||
}
|
||||
|
||||
FrozenActor FrozenActorForPlayer(Player player)
|
||||
{
|
||||
return frozen[player].FromID(self.ActorID);
|
||||
}
|
||||
|
||||
void IEffect.Tick(World world)
|
||||
{
|
||||
if (self.Disposed)
|
||||
world.AddFrameEndTask(w => w.Remove(this));
|
||||
|
||||
for (var playerIndex = 0; playerIndex < dotStates.Count; playerIndex++)
|
||||
{
|
||||
var state = dotStates[playerIndex];
|
||||
var shouldRender = false;
|
||||
if (self.IsInWorld && !self.IsDead)
|
||||
state.IsTargetable = (state.Gps.Granted || state.Gps.GrantedAllies) && IsTargetableBy(world.Players[playerIndex], out shouldRender);
|
||||
|
||||
state.ShouldRender = state.IsTargetable && shouldRender;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<IRenderable> IEffect.Render(WorldRenderer wr) { return SpriteRenderable.None; }
|
||||
|
||||
IEnumerable<IRenderable> IEffectAboveShroud.RenderAboveShroud(WorldRenderer wr)
|
||||
{
|
||||
if (self.World.RenderPlayer == null || !dotStates[self.World.RenderPlayer].ShouldRender || self.Disposed)
|
||||
return SpriteRenderable.None;
|
||||
|
||||
var palette = wr.Palette(info.IndicatorPalettePrefix + self.Owner.InternalName);
|
||||
return anim.Render(self.CenterPosition, palette);
|
||||
}
|
||||
}
|
||||
}
|
||||
112
OpenRA.Mods.Cnc/Effects/GpsDotEffect.cs
Normal file
112
OpenRA.Mods.Cnc/Effects/GpsDotEffect.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2017 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Effects;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Cnc.Traits;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.Effects
|
||||
{
|
||||
class GpsDotEffect : IEffect, IEffectAboveShroud
|
||||
{
|
||||
readonly Actor actor;
|
||||
readonly GpsDotInfo info;
|
||||
readonly Animation anim;
|
||||
|
||||
readonly PlayerDictionary<DotState> dotStates;
|
||||
readonly IDefaultVisibility visibility;
|
||||
readonly IVisibilityModifier[] visibilityModifiers;
|
||||
|
||||
class DotState
|
||||
{
|
||||
public readonly GpsWatcher Watcher;
|
||||
public readonly FrozenActor FrozenActor;
|
||||
public bool Visible;
|
||||
public DotState(Actor a, GpsWatcher watcher, FrozenActorLayer frozenLayer)
|
||||
{
|
||||
Watcher = watcher;
|
||||
if (frozenLayer != null)
|
||||
FrozenActor = frozenLayer.FromID(a.ActorID);
|
||||
}
|
||||
}
|
||||
|
||||
public GpsDotEffect(Actor actor, GpsDotInfo info)
|
||||
{
|
||||
this.actor = actor;
|
||||
this.info = info;
|
||||
anim = new Animation(actor.World, info.Image);
|
||||
anim.PlayRepeating(info.String);
|
||||
|
||||
visibility = actor.Trait<IDefaultVisibility>();
|
||||
visibilityModifiers = actor.TraitsImplementing<IVisibilityModifier>().ToArray();
|
||||
|
||||
dotStates = new PlayerDictionary<DotState>(actor.World,
|
||||
p => new DotState(actor, p.PlayerActor.Trait<GpsWatcher>(), p.PlayerActor.TraitOrDefault<FrozenActorLayer>()));
|
||||
}
|
||||
|
||||
bool ShouldRender(DotState state, Player toPlayer)
|
||||
{
|
||||
// Hide the indicator if no watchers are available
|
||||
if (!state.Watcher.Granted && !state.Watcher.GrantedAllies)
|
||||
return false;
|
||||
|
||||
// Hide the indicator if a frozen actor portrait is visible
|
||||
if (state.FrozenActor != null && state.FrozenActor.HasRenderables)
|
||||
return false;
|
||||
|
||||
// Hide the indicator if the unit appears to be owned by an allied player
|
||||
if (actor.EffectiveOwner != null && actor.EffectiveOwner.Owner != null &&
|
||||
toPlayer.IsAlliedWith(actor.EffectiveOwner.Owner))
|
||||
return false;
|
||||
|
||||
// Hide indicator if the actor wouldn't otherwise be visible if there wasn't fog
|
||||
foreach (var visibilityModifier in visibilityModifiers)
|
||||
if (!visibilityModifier.IsVisible(actor, toPlayer))
|
||||
return false;
|
||||
|
||||
// Hide the indicator behind shroud
|
||||
if (!toPlayer.Shroud.IsExplored(actor.CenterPosition))
|
||||
return false;
|
||||
|
||||
return !visibility.IsVisible(actor, toPlayer) && toPlayer.Shroud.IsExplored(actor.CenterPosition);
|
||||
}
|
||||
|
||||
void IEffect.Tick(World world)
|
||||
{
|
||||
for (var playerIndex = 0; playerIndex < dotStates.Count; playerIndex++)
|
||||
{
|
||||
var state = dotStates[playerIndex];
|
||||
state.Visible = ShouldRender(state, world.Players[playerIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<IRenderable> IEffect.Render(WorldRenderer wr)
|
||||
{
|
||||
return SpriteRenderable.None;
|
||||
}
|
||||
|
||||
IEnumerable<IRenderable> IEffectAboveShroud.RenderAboveShroud(WorldRenderer wr)
|
||||
{
|
||||
if (actor.World.RenderPlayer == null || !dotStates[actor.World.RenderPlayer].Visible)
|
||||
return SpriteRenderable.None;
|
||||
|
||||
var effectiveOwner = actor.EffectiveOwner != null && actor.EffectiveOwner.Owner != null ?
|
||||
actor.EffectiveOwner.Owner : actor.Owner;
|
||||
|
||||
var palette = wr.Palette(info.IndicatorPalettePrefix + effectiveOwner.InternalName);
|
||||
return anim.Render(actor.CenterPosition, palette);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ using OpenRA.Mods.Cnc.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.Effects
|
||||
{
|
||||
class GpsSatellite : IEffect
|
||||
class GpsSatellite : IEffect, ISpatiallyPartitionable
|
||||
{
|
||||
readonly Player launcher;
|
||||
readonly Animation anim;
|
||||
@@ -34,6 +34,7 @@ namespace OpenRA.Mods.Cnc.Effects
|
||||
|
||||
anim = new Animation(world, image);
|
||||
anim.PlayRepeating(sequence);
|
||||
world.ScreenMap.Add(this, pos, anim.Image);
|
||||
}
|
||||
|
||||
public void Tick(World world)
|
||||
@@ -45,8 +46,10 @@ namespace OpenRA.Mods.Cnc.Effects
|
||||
{
|
||||
var watcher = launcher.PlayerActor.Trait<GpsWatcher>();
|
||||
watcher.ReachedOrbit(launcher);
|
||||
world.AddFrameEndTask(w => w.Remove(this));
|
||||
world.AddFrameEndTask(w => { w.Remove(this); w.ScreenMap.Remove(this); });
|
||||
}
|
||||
|
||||
world.ScreenMap.Update(this, pos, anim.Image);
|
||||
}
|
||||
|
||||
public IEnumerable<IRenderable> Render(WorldRenderer wr)
|
||||
|
||||
@@ -16,7 +16,7 @@ using OpenRA.Mods.Cnc.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.Effects
|
||||
{
|
||||
class SatelliteLaunch : IEffect
|
||||
class SatelliteLaunch : IEffect, ISpatiallyPartitionable
|
||||
{
|
||||
readonly GpsPowerInfo info;
|
||||
readonly Actor launcher;
|
||||
@@ -31,19 +31,21 @@ namespace OpenRA.Mods.Cnc.Effects
|
||||
|
||||
doors = new Animation(launcher.World, info.DoorImage);
|
||||
doors.PlayThen(info.DoorSequence,
|
||||
() => launcher.World.AddFrameEndTask(w => w.Remove(this)));
|
||||
() => launcher.World.AddFrameEndTask(w => { w.Remove(this); w.ScreenMap.Remove(this); }));
|
||||
|
||||
pos = launcher.CenterPosition;
|
||||
launcher.World.ScreenMap.Add(this, pos, doors.Image);
|
||||
}
|
||||
|
||||
public void Tick(World world)
|
||||
{
|
||||
doors.Tick();
|
||||
world.ScreenMap.Update(this, pos, doors.Image);
|
||||
|
||||
if (++frame == 19)
|
||||
{
|
||||
var palette = info.SatellitePaletteIsPlayerPalette ? info.SatellitePalette + launcher.Owner.InternalName : info.SatellitePalette;
|
||||
world.AddFrameEndTask(w => w.Add(new GpsSatellite(world, pos, info.SatelliteImage, info.SatelliteSequence, palette, info.RevealDelay * 25, launcher.Owner)));
|
||||
world.AddFrameEndTask(w => w.Add(new GpsSatellite(world, pos, info.SatelliteImage, info.SatelliteSequence, palette, info.RevealDelay, launcher.Owner)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,15 +28,16 @@ namespace OpenRA.Mods.Cnc.FileFormats
|
||||
while (s.Peek() > -1)
|
||||
{
|
||||
var count = s.ReadInt32();
|
||||
var chars = new List<char>();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var chars = new List<char>();
|
||||
byte c;
|
||||
|
||||
// Read filename
|
||||
while ((c = s.ReadUInt8()) != 0)
|
||||
chars.Add((char)c);
|
||||
entries.Add(new string(chars.ToArray()));
|
||||
chars.Clear();
|
||||
|
||||
// Skip comment
|
||||
while ((c = s.ReadUInt8()) != 0) { }
|
||||
|
||||
@@ -26,14 +26,15 @@ namespace OpenRA.Mods.Cnc.FileFormats
|
||||
var reader = new BinaryReader(s);
|
||||
var count = reader.ReadInt32();
|
||||
Entries = new string[count];
|
||||
var chars = new List<char>();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var chars = new List<char>();
|
||||
char c;
|
||||
while ((c = reader.ReadChar()) != 0)
|
||||
chars.Add(c);
|
||||
|
||||
Entries[i] = new string(chars.ToArray());
|
||||
chars.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace OpenRA.Mods.Cnc.FileSystem
|
||||
{
|
||||
var decrypted = fish.Decrypt(h);
|
||||
|
||||
var ms = new MemoryStream();
|
||||
var ms = new MemoryStream(decrypted.Length * 4);
|
||||
var writer = new BinaryWriter(ms);
|
||||
foreach (var t in decrypted)
|
||||
writer.Write(t);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Cnc.FileFormats;
|
||||
@@ -125,5 +126,33 @@ namespace OpenRA.Mods.Cnc.Graphics
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Rectangle AggregateBounds
|
||||
{
|
||||
get
|
||||
{
|
||||
// Corner offsets
|
||||
var ix = new uint[] { 0, 0, 0, 0, 3, 3, 3, 3 };
|
||||
var iy = new uint[] { 1, 1, 4, 4, 1, 1, 4, 4 };
|
||||
var iz = new uint[] { 2, 5, 2, 5, 2, 5, 2, 5 };
|
||||
|
||||
// Calculate the smallest sphere that covers the model limbs
|
||||
var rSquared = 0f;
|
||||
for (var f = 0U; f < frames; f++)
|
||||
{
|
||||
var bounds = Bounds(f);
|
||||
for (var i = 0; i < 8; i++)
|
||||
{
|
||||
var x = bounds[ix[i]];
|
||||
var y = bounds[iy[i]];
|
||||
var z = bounds[iz[i]];
|
||||
rSquared = Math.Max(rSquared, x * x + y * y + z * z);
|
||||
}
|
||||
}
|
||||
|
||||
var r = (int)Math.Sqrt(rSquared) + 1;
|
||||
return Rectangle.FromLTRB(-r, -r, r, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<LangVersion>5</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
@@ -64,9 +65,10 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CncLoadScreen.cs" />
|
||||
<Compile Include="Traits\AttackPopupTurreted.cs" />
|
||||
<Compile Include="Traits\Attack\AttackPopupTurreted.cs" />
|
||||
<Compile Include="Traits\Buildings\ProductionAirdrop.cs" />
|
||||
<Compile Include="Traits\Render\WithGunboatBody.cs" />
|
||||
<Compile Include="Traits\Render\WithEmbeddedTurretSpriteBody.cs" />
|
||||
<Compile Include="Traits\Render\WithCargo.cs" />
|
||||
<Compile Include="Traits\Render\WithDeliveryAnimation.cs" />
|
||||
<Compile Include="Traits\Render\WithReloadingSpriteTurret.cs" />
|
||||
@@ -98,7 +100,7 @@
|
||||
<Compile Include="Activities\LayMines.cs" />
|
||||
<Compile Include="Activities\Leap.cs" />
|
||||
<Compile Include="Activities\Teleport.cs" />
|
||||
<Compile Include="Effects\GpsDot.cs" />
|
||||
<Compile Include="Effects\GpsDotEffect.cs" />
|
||||
<Compile Include="Effects\GpsSatellite.cs" />
|
||||
<Compile Include="Effects\SatelliteLaunch.cs" />
|
||||
<Compile Include="Projectiles\TeslaZap.cs" />
|
||||
@@ -154,6 +156,9 @@
|
||||
<Compile Include="FileFormats\HvaReader.cs" />
|
||||
<Compile Include="FileFormats\VxlReader.cs" />
|
||||
<Compile Include="Traits\World\VoxelNormalsPalette.cs" />
|
||||
<Compile Include="Traits\TDGunboat.cs" />
|
||||
<Compile Include="Traits\Attack\AttackTDGunboatTurreted.cs" />
|
||||
<Compile Include="Traits\GpsDot.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj">
|
||||
@@ -210,4 +215,4 @@
|
||||
<Message Text="DEBUG TargetName: $(TargetName)"/>
|
||||
-->
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -10,6 +10,7 @@
|
||||
#endregion
|
||||
|
||||
using System.Linq;
|
||||
using OpenRA.Mods.Cnc.Traits.Render;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Mods.Common.Traits.Render;
|
||||
using OpenRA.Traits;
|
||||
@@ -17,7 +18,7 @@ using OpenRA.Traits;
|
||||
namespace OpenRA.Mods.Cnc.Traits
|
||||
{
|
||||
[Desc("Actor's turret rises from the ground before attacking.")]
|
||||
class AttackPopupTurretedInfo : AttackTurretedInfo, Requires<BuildingInfo>, Requires<WithTurretedSpriteBodyInfo>
|
||||
class AttackPopupTurretedInfo : AttackTurretedInfo, Requires<BuildingInfo>, Requires<WithEmbeddedTurretSpriteBodyInfo>
|
||||
{
|
||||
[Desc("How many game ticks should pass before closing the actor's turret.")]
|
||||
public int CloseDelay = 125;
|
||||
73
OpenRA.Mods.Cnc/Traits/Attack/AttackTDGunboatTurreted.cs
Normal file
73
OpenRA.Mods.Cnc/Traits/Attack/AttackTDGunboatTurreted.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2017 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.Linq;
|
||||
using OpenRA.Activities;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.Traits
|
||||
{
|
||||
[Desc("Actor has a visual turret used to attack.")]
|
||||
public class AttackTDGunboatTurretedInfo : AttackTurretedInfo, Requires<TDGunboatInfo>
|
||||
{
|
||||
public override object Create(ActorInitializer init) { return new AttackTDGunboatTurreted(init.Self, this); }
|
||||
}
|
||||
|
||||
public class AttackTDGunboatTurreted : AttackTurreted
|
||||
{
|
||||
public AttackTDGunboatTurreted(Actor self, AttackTDGunboatTurretedInfo info)
|
||||
: base(self, info) { }
|
||||
|
||||
public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove, bool forceAttack)
|
||||
{
|
||||
return new AttackTDGunboatTurretedActivity(self, newTarget, allowMove, forceAttack);
|
||||
}
|
||||
|
||||
class AttackTDGunboatTurretedActivity : Activity
|
||||
{
|
||||
readonly AttackTDGunboatTurreted attack;
|
||||
readonly Target target;
|
||||
readonly bool forceAttack;
|
||||
bool hasTicked;
|
||||
|
||||
public AttackTDGunboatTurretedActivity(Actor self, Target target, bool allowMove, bool forceAttack)
|
||||
{
|
||||
attack = self.Trait<AttackTDGunboatTurreted>();
|
||||
this.target = target;
|
||||
this.forceAttack = forceAttack;
|
||||
}
|
||||
|
||||
public override Activity Tick(Actor self)
|
||||
{
|
||||
if (IsCanceled || !target.IsValidFor(self))
|
||||
return NextActivity;
|
||||
|
||||
if (attack.IsTraitDisabled)
|
||||
return this;
|
||||
|
||||
var weapon = attack.ChooseArmamentsForTarget(target, forceAttack).FirstOrDefault();
|
||||
if (weapon != null)
|
||||
{
|
||||
// Check that AttackTDGunboatTurreted hasn't cancelled the target by modifying attack.Target
|
||||
// Having both this and AttackTDGunboatTurreted modify that field is a horrible hack.
|
||||
if (hasTicked && attack.Target.Type == TargetType.Invalid)
|
||||
return NextActivity;
|
||||
|
||||
attack.Target = target;
|
||||
hasTicked = true;
|
||||
}
|
||||
|
||||
return NextActivity;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
charges = info.MaxCharges;
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
void ITick.Tick(Actor self)
|
||||
{
|
||||
if (--timeToRecharge <= 0)
|
||||
charges = info.MaxCharges;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Mods.Common;
|
||||
using OpenRA.Mods.Common.Traits;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.Traits
|
||||
@@ -39,7 +40,7 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
faction = init.Contains<FactionInit>() ? init.Get<FactionInit, string>() : init.Self.Owner.Faction.InternalName;
|
||||
}
|
||||
|
||||
public void UnitProducedByOther(Actor self, Actor producer, Actor produced)
|
||||
public void UnitProducedByOther(Actor self, Actor producer, Actor produced, string productionType)
|
||||
{
|
||||
// No recursive cloning!
|
||||
if (producer.Owner != self.Owner || producer.Info.HasTraitInfo<ClonesProducedUnitsInfo>())
|
||||
@@ -49,7 +50,13 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
if (ci == null || !info.CloneableTypes.Overlaps(ci.Types))
|
||||
return;
|
||||
|
||||
production.Produce(self, produced.Info, faction);
|
||||
var inits = new TypeDictionary
|
||||
{
|
||||
new OwnerInit(self.Owner),
|
||||
new FactionInit(BuildableInfo.GetInitialFaction(produced.Info, faction))
|
||||
};
|
||||
|
||||
production.Produce(self, produced.Info, productionType, inits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Activities;
|
||||
using OpenRA.Mods.Common;
|
||||
@@ -38,8 +39,11 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public override bool Produce(Actor self, ActorInfo producee, string factionVariant)
|
||||
public override bool Produce(Actor self, ActorInfo producee, string productionType, TypeDictionary inits)
|
||||
{
|
||||
if (IsTraitDisabled || IsTraitPaused)
|
||||
return false;
|
||||
|
||||
var owner = self.Owner;
|
||||
var aircraftInfo = self.World.Map.Rules.Actors[info.ActorType].TraitInfo<AircraftInfo>();
|
||||
|
||||
@@ -79,7 +83,7 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
foreach (var cargo in self.TraitsImplementing<INotifyDelivery>())
|
||||
cargo.Delivered(self);
|
||||
|
||||
self.World.AddFrameEndTask(ww => DoProduction(self, producee, exit, factionVariant));
|
||||
self.World.AddFrameEndTask(ww => DoProduction(self, producee, exit, productionType, inits));
|
||||
Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", info.ReadyAudio, self.Owner.Faction.InternalName);
|
||||
}));
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
chronosphere = init.Get<ChronoshiftChronosphereInit, Actor>();
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
void ITick.Tick(Actor self)
|
||||
{
|
||||
if (!info.ReturnToOrigin || ReturnTicks <= 0)
|
||||
return;
|
||||
@@ -74,7 +74,7 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
}
|
||||
}
|
||||
|
||||
public void Created(Actor self)
|
||||
void INotifyCreated.Created(Actor self)
|
||||
{
|
||||
iPositionable = self.TraitOrDefault<IPositionable>();
|
||||
}
|
||||
@@ -101,8 +101,14 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
}
|
||||
|
||||
// Set up return-to-origin info
|
||||
Origin = self.Location;
|
||||
ReturnTicks = duration;
|
||||
// If this actor is already counting down to return to
|
||||
// an existing location then we shouldn't override it
|
||||
if (ReturnTicks <= 0)
|
||||
{
|
||||
Origin = self.Location;
|
||||
ReturnTicks = duration;
|
||||
}
|
||||
|
||||
this.duration = duration;
|
||||
this.chronosphere = chronosphere;
|
||||
this.killCargo = killCargo;
|
||||
|
||||
@@ -64,7 +64,11 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
{
|
||||
None = 0,
|
||||
Attack = 1,
|
||||
Damaged = 2
|
||||
Damaged = 2,
|
||||
Unload = 4,
|
||||
Infiltrate = 8,
|
||||
Demolish = 16,
|
||||
Move = 32
|
||||
}
|
||||
|
||||
[Desc("Provides access to the disguise command, which makes the actor appear to be another player's actor.")]
|
||||
@@ -76,16 +80,31 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
[Desc("The condition to grant to self while disguised.")]
|
||||
public readonly string DisguisedCondition = null;
|
||||
|
||||
[Desc("Triggers which cause the actor to drop it's disguise. Possible values: None, Attack, Damaged.")]
|
||||
[Desc("What diplomatic stances can this actor disguise as.")]
|
||||
public readonly Stance ValidStances = Stance.Ally | Stance.Neutral | Stance.Enemy;
|
||||
|
||||
[Desc("Target types of actors that this actor disguise as.")]
|
||||
public readonly HashSet<string> TargetTypes = new HashSet<string> { "Disguise" };
|
||||
|
||||
[Desc("Triggers which cause the actor to drop it's disguise. Possible values: None, Attack, Damaged,",
|
||||
"Unload, Infiltrate, Demolish, Move.")]
|
||||
public readonly RevealDisguiseType RevealDisguiseOn = RevealDisguiseType.Attack;
|
||||
|
||||
[Desc("Conditions to grant when disguised as specified actor.",
|
||||
"A dictionary of [actor id]: [condition].")]
|
||||
public readonly Dictionary<string, string> DisguisedAsConditions = new Dictionary<string, string>();
|
||||
|
||||
[GrantedConditionReference]
|
||||
public IEnumerable<string> LinterConditions { get { return DisguisedAsConditions.Values; } }
|
||||
|
||||
public object Create(ActorInitializer init) { return new Disguise(init.Self, this); }
|
||||
}
|
||||
|
||||
class Disguise : INotifyCreated, IEffectiveOwner, IIssueOrder, IResolveOrder, IOrderVoice, IRadarColorModifier, INotifyAttack, INotifyDamage
|
||||
class Disguise : INotifyCreated, IEffectiveOwner, IIssueOrder, IResolveOrder, IOrderVoice, IRadarColorModifier, INotifyAttack,
|
||||
INotifyDamage, INotifyUnload, INotifyDemolition, INotifyInfiltration, ITick
|
||||
{
|
||||
public ActorInfo AsActor { get; private set; }
|
||||
public Player AsPlayer { get; private set; }
|
||||
public string AsSprite { get; private set; }
|
||||
public ITooltipInfo AsTooltipInfo { get; private set; }
|
||||
|
||||
public bool Disguised { get { return AsPlayer != null; } }
|
||||
@@ -96,11 +115,15 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
|
||||
ConditionManager conditionManager;
|
||||
int disguisedToken = ConditionManager.InvalidConditionToken;
|
||||
int disguisedAsToken = ConditionManager.InvalidConditionToken;
|
||||
CPos? lastPos;
|
||||
|
||||
public Disguise(Actor self, DisguiseInfo info)
|
||||
{
|
||||
this.self = self;
|
||||
this.info = info;
|
||||
|
||||
AsActor = self.Info;
|
||||
}
|
||||
|
||||
void INotifyCreated.Created(Actor self)
|
||||
@@ -108,37 +131,41 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
conditionManager = self.TraitOrDefault<ConditionManager>();
|
||||
}
|
||||
|
||||
public IEnumerable<IOrderTargeter> Orders
|
||||
IEnumerable<IOrderTargeter> IIssueOrder.Orders
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return new TargetTypeOrderTargeter(new HashSet<string> { "Disguise" }, "Disguise", 7, "ability", true, true) { ForceAttack = false };
|
||||
yield return new DisguiseOrderTargeter(info);
|
||||
}
|
||||
}
|
||||
|
||||
public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
|
||||
Order IIssueOrder.IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
|
||||
{
|
||||
if (order.OrderID == "Disguise")
|
||||
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
|
||||
return new Order(order.OrderID, self, target, queued);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void ResolveOrder(Actor self, Order order)
|
||||
void IResolveOrder.ResolveOrder(Actor self, Order order)
|
||||
{
|
||||
if (order.OrderString == "Disguise")
|
||||
{
|
||||
var target = order.TargetActor != self && order.TargetActor.IsInWorld ? order.TargetActor : null;
|
||||
DisguiseAs(target);
|
||||
var target = order.Target;
|
||||
if (target.Type == TargetType.Actor)
|
||||
DisguiseAs((target.Actor != self && target.Actor.IsInWorld) ? target.Actor : null);
|
||||
|
||||
if (target.Type == TargetType.FrozenActor)
|
||||
DisguiseAs(target.FrozenActor.Info, target.FrozenActor.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
public string VoicePhraseForOrder(Actor self, Order order)
|
||||
string IOrderVoice.VoicePhraseForOrder(Actor self, Order order)
|
||||
{
|
||||
return order.OrderString == "Disguise" ? info.Voice : null;
|
||||
}
|
||||
|
||||
public Color RadarColorOverride(Actor self, Color color)
|
||||
Color IRadarColorModifier.RadarColorOverride(Actor self, Color color)
|
||||
{
|
||||
if (!Disguised || self.Owner.IsAlliedWith(self.World.RenderPlayer))
|
||||
return color;
|
||||
@@ -148,8 +175,9 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
|
||||
public void DisguiseAs(Actor target)
|
||||
{
|
||||
var oldDisguiseSetting = Disguised;
|
||||
var oldEffectiveActor = AsActor;
|
||||
var oldEffectiveOwner = AsPlayer;
|
||||
var oldDisguiseSetting = Disguised;
|
||||
|
||||
if (target != null)
|
||||
{
|
||||
@@ -158,15 +186,15 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
var targetDisguise = target.TraitOrDefault<Disguise>();
|
||||
if (targetDisguise != null && targetDisguise.Disguised)
|
||||
{
|
||||
AsSprite = targetDisguise.AsSprite;
|
||||
AsPlayer = targetDisguise.AsPlayer;
|
||||
AsActor = targetDisguise.AsActor;
|
||||
AsTooltipInfo = targetDisguise.AsTooltipInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
AsSprite = target.Trait<RenderSprites>().GetImage(target);
|
||||
var tooltip = target.TraitsImplementing<ITooltip>().FirstOrDefault();
|
||||
AsPlayer = tooltip.Owner;
|
||||
AsActor = target.Info;
|
||||
AsTooltipInfo = tooltip.TooltipInfo;
|
||||
}
|
||||
}
|
||||
@@ -174,36 +202,49 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
{
|
||||
AsTooltipInfo = null;
|
||||
AsPlayer = null;
|
||||
AsSprite = null;
|
||||
AsActor = self.Info;
|
||||
}
|
||||
|
||||
HandleDisguise(oldEffectiveOwner, oldDisguiseSetting);
|
||||
HandleDisguise(oldEffectiveActor, oldEffectiveOwner, oldDisguiseSetting);
|
||||
}
|
||||
|
||||
public void DisguiseAs(ActorInfo actorInfo, Player newOwner)
|
||||
{
|
||||
var oldDisguiseSetting = Disguised;
|
||||
var oldEffectiveActor = AsActor;
|
||||
var oldEffectiveOwner = AsPlayer;
|
||||
var oldDisguiseSetting = Disguised;
|
||||
|
||||
var renderSprites = actorInfo.TraitInfoOrDefault<RenderSpritesInfo>();
|
||||
AsSprite = renderSprites == null ? null : renderSprites.GetImage(actorInfo, self.World.Map.Rules.Sequences, newOwner.Faction.InternalName);
|
||||
AsPlayer = newOwner;
|
||||
AsActor = actorInfo;
|
||||
AsTooltipInfo = actorInfo.TraitInfos<TooltipInfo>().FirstOrDefault();
|
||||
|
||||
HandleDisguise(oldEffectiveOwner, oldDisguiseSetting);
|
||||
HandleDisguise(oldEffectiveActor, oldEffectiveOwner, oldDisguiseSetting);
|
||||
}
|
||||
|
||||
void HandleDisguise(Player oldEffectiveOwner, bool oldDisguiseSetting)
|
||||
void HandleDisguise(ActorInfo oldEffectiveActor, Player oldEffectiveOwner, bool oldDisguiseSetting)
|
||||
{
|
||||
foreach (var t in self.TraitsImplementing<INotifyEffectiveOwnerChanged>())
|
||||
t.OnEffectiveOwnerChanged(self, oldEffectiveOwner, AsPlayer);
|
||||
|
||||
if (Disguised != oldDisguiseSetting && conditionManager != null)
|
||||
if (conditionManager != null)
|
||||
{
|
||||
if (Disguised && disguisedToken == ConditionManager.InvalidConditionToken && !string.IsNullOrEmpty(info.DisguisedCondition))
|
||||
disguisedToken = conditionManager.GrantCondition(self, info.DisguisedCondition);
|
||||
else if (!Disguised && disguisedToken != ConditionManager.InvalidConditionToken)
|
||||
disguisedToken = conditionManager.RevokeCondition(self, disguisedToken);
|
||||
if (Disguised != oldDisguiseSetting)
|
||||
{
|
||||
if (Disguised && disguisedToken == ConditionManager.InvalidConditionToken && !string.IsNullOrEmpty(info.DisguisedCondition))
|
||||
disguisedToken = conditionManager.GrantCondition(self, info.DisguisedCondition);
|
||||
else if (!Disguised && disguisedToken != ConditionManager.InvalidConditionToken)
|
||||
disguisedToken = conditionManager.RevokeCondition(self, disguisedToken);
|
||||
}
|
||||
|
||||
if (AsActor != oldEffectiveActor)
|
||||
{
|
||||
if (disguisedAsToken != ConditionManager.InvalidConditionToken)
|
||||
disguisedAsToken = conditionManager.RevokeCondition(self, disguisedAsToken);
|
||||
|
||||
string disguisedAsCondition;
|
||||
if (info.DisguisedAsConditions.TryGetValue(AsActor.Name, out disguisedAsCondition))
|
||||
disguisedAsToken = conditionManager.GrantCondition(self, disguisedAsCondition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,5 +261,63 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
if (info.RevealDisguiseOn.HasFlag(RevealDisguiseType.Damaged) && e.Damage.Value > 0)
|
||||
DisguiseAs(null);
|
||||
}
|
||||
|
||||
void INotifyUnload.Unloading(Actor self)
|
||||
{
|
||||
if (info.RevealDisguiseOn.HasFlag(RevealDisguiseType.Unload))
|
||||
DisguiseAs(null);
|
||||
}
|
||||
|
||||
void INotifyDemolition.Demolishing(Actor self)
|
||||
{
|
||||
if (info.RevealDisguiseOn.HasFlag(RevealDisguiseType.Demolish))
|
||||
DisguiseAs(null);
|
||||
}
|
||||
|
||||
void INotifyInfiltration.Infiltrating(Actor self)
|
||||
{
|
||||
if (info.RevealDisguiseOn.HasFlag(RevealDisguiseType.Infiltrate))
|
||||
DisguiseAs(null);
|
||||
}
|
||||
|
||||
void ITick.Tick(Actor self)
|
||||
{
|
||||
if (info.RevealDisguiseOn.HasFlag(RevealDisguiseType.Move) && lastPos != null && lastPos.Value != self.Location)
|
||||
DisguiseAs(null);
|
||||
|
||||
lastPos = self.Location;
|
||||
}
|
||||
}
|
||||
|
||||
class DisguiseOrderTargeter : UnitOrderTargeter
|
||||
{
|
||||
readonly DisguiseInfo info;
|
||||
|
||||
public DisguiseOrderTargeter(DisguiseInfo info)
|
||||
: base("Disguise", 7, "ability", true, true)
|
||||
{
|
||||
this.info = info;
|
||||
ForceAttack = false;
|
||||
}
|
||||
|
||||
public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
|
||||
{
|
||||
var stance = self.Owner.Stances[target.Owner];
|
||||
|
||||
if (!info.ValidStances.HasStance(stance))
|
||||
return false;
|
||||
|
||||
return info.TargetTypes.Overlaps(target.GetAllTargetTypes());
|
||||
}
|
||||
|
||||
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
|
||||
{
|
||||
var stance = self.Owner.Stances[target.Owner];
|
||||
|
||||
if (!info.ValidStances.HasStance(stance))
|
||||
return false;
|
||||
|
||||
return info.TargetTypes.Overlaps(target.Info.TraitInfos<ITargetableInfo>().SelectMany(ti => ti.GetTargetTypes()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
return !active;
|
||||
}
|
||||
|
||||
public override void AddedToWorld(Actor self)
|
||||
protected override void AddedToWorld(Actor self)
|
||||
{
|
||||
base.AddedToWorld(self);
|
||||
blockedPositions = info.Tiles(self.Location);
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
ActOnFrozenActorsForAllPlayers(Refresh);
|
||||
}
|
||||
|
||||
public void Disposing(Actor self)
|
||||
void INotifyActorDisposing.Disposing(Actor self)
|
||||
{
|
||||
ActOnFrozenActorsForAllPlayers(Remove);
|
||||
}
|
||||
|
||||
53
OpenRA.Mods.Cnc/Traits/GpsDot.cs
Normal file
53
OpenRA.Mods.Cnc/Traits/GpsDot.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2017 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.Graphics;
|
||||
using OpenRA.Mods.Cnc.Effects;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.Traits
|
||||
{
|
||||
[Desc("Show an indicator revealing the actor underneath the fog when a GPSWatcher is activated.")]
|
||||
class GpsDotInfo : ITraitInfo
|
||||
{
|
||||
[Desc("Sprite collection for symbols.")]
|
||||
public readonly string Image = "gpsdot";
|
||||
|
||||
[Desc("Sprite used for this actor.")]
|
||||
[SequenceReference("Image")] public readonly string String = "Infantry";
|
||||
|
||||
[PaletteReference(true)] public readonly string IndicatorPalettePrefix = "player";
|
||||
|
||||
public object Create(ActorInitializer init) { return new GpsDot(this); }
|
||||
}
|
||||
|
||||
class GpsDot : INotifyAddedToWorld, INotifyRemovedFromWorld
|
||||
{
|
||||
readonly GpsDotInfo info;
|
||||
GpsDotEffect effect;
|
||||
|
||||
public GpsDot(GpsDotInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
void INotifyAddedToWorld.AddedToWorld(Actor self)
|
||||
{
|
||||
effect = new GpsDotEffect(self, info);
|
||||
self.World.AddFrameEndTask(w => w.Add(effect));
|
||||
}
|
||||
|
||||
void INotifyRemovedFromWorld.RemovedFromWorld(Actor self)
|
||||
{
|
||||
self.World.AddFrameEndTask(w => w.Remove(effect));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user