Compare commits
694 Commits
playtest-2
...
playtest-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de5d9abec3 | ||
|
|
3674accd0c | ||
|
|
9ffdce7957 | ||
|
|
38df0a28cd | ||
|
|
f659f55801 | ||
|
|
f3da258763 | ||
|
|
2048900c10 | ||
|
|
7b5a8cf089 | ||
|
|
a4bbce32b8 | ||
|
|
19aed01822 | ||
|
|
ba2ec557bc | ||
|
|
a8b4e640e7 | ||
|
|
d1966ab476 | ||
|
|
d90dec9c8e | ||
|
|
e2b739cd3e | ||
|
|
dffb5293d0 | ||
|
|
cf2ad68a7c | ||
|
|
119b0c063d | ||
|
|
b948b9d2b7 | ||
|
|
d98e09e096 | ||
|
|
cf17bc7e5c | ||
|
|
42096cc5c9 | ||
|
|
1d3b2334d0 | ||
|
|
64304635b7 | ||
|
|
dc9de20553 | ||
|
|
486fa9a978 | ||
|
|
e2d1eec56e | ||
|
|
8f9e32dcc0 | ||
|
|
dc012c0faf | ||
|
|
4f6f3eb80d | ||
|
|
fd49ca75d7 | ||
|
|
cb50182fac | ||
|
|
47bbc3a6de | ||
|
|
39ed6087cb | ||
|
|
428999fc0b | ||
|
|
b7975031bc | ||
|
|
e652a15b01 | ||
|
|
fc6438e311 | ||
|
|
829fe6530a | ||
|
|
58797c0d37 | ||
|
|
f6d9923305 | ||
|
|
c3ff679f3a | ||
|
|
fa36c71023 | ||
|
|
d4033f57bc | ||
|
|
13f6a13ad9 | ||
|
|
44e668e804 | ||
|
|
aecf19ed64 | ||
|
|
2a0c0bb991 | ||
|
|
99a0bd9609 | ||
|
|
c20b3b90f4 | ||
|
|
1cee570207 | ||
|
|
382435e629 | ||
|
|
637dbee32f | ||
|
|
81f6843791 | ||
|
|
cc6d445ef1 | ||
|
|
cc2efdfa4a | ||
|
|
f7b34e1c5e | ||
|
|
4d0f3b1554 | ||
|
|
ae1710896a | ||
|
|
6425fc8a4b | ||
|
|
38d267e439 | ||
|
|
95cf0bb827 | ||
|
|
d42bdfa102 | ||
|
|
5a7c62e24a | ||
|
|
29818ba60c | ||
|
|
1c4f4b7886 | ||
|
|
e127095437 | ||
|
|
d8eaa7c841 | ||
|
|
888fe35f08 | ||
|
|
59fdbe8725 | ||
|
|
24a5b3ba03 | ||
|
|
9d768fa1c1 | ||
|
|
31b98c0eb4 | ||
|
|
3dd52a59c9 | ||
|
|
f3997ba3bd | ||
|
|
4a94cf656b | ||
|
|
439bb7d02f | ||
|
|
9ad23b10f0 | ||
|
|
e2a0134c5b | ||
|
|
c6f1740875 | ||
|
|
50834911fe | ||
|
|
2cce1ce23c | ||
|
|
3d47584cd8 | ||
|
|
3ce055da80 | ||
|
|
51d3743b18 | ||
|
|
ce609195cd | ||
|
|
54cf2e7993 | ||
|
|
b5b6b47e95 | ||
|
|
6ff0e41650 | ||
|
|
fcdb3b536d | ||
|
|
14ad5099a0 | ||
|
|
dc3bf2515a | ||
|
|
275dfc43be | ||
|
|
3149f3efa2 | ||
|
|
e8a85db309 | ||
|
|
c1fb6c2732 | ||
|
|
68e8565561 | ||
|
|
92e6570f50 | ||
|
|
e94e21fd32 | ||
|
|
d320a689a2 | ||
|
|
29fbeb2c5d | ||
|
|
7ef884532d | ||
|
|
54c49dfa15 | ||
|
|
8809cbb4f1 | ||
|
|
112dd3b1f6 | ||
|
|
92b0f77867 | ||
|
|
dcec748911 | ||
|
|
7d61e75862 | ||
|
|
2d2bb58054 | ||
|
|
bd0b3edccb | ||
|
|
d66439aa0c | ||
|
|
c85dbfb53d | ||
|
|
d6c71c6618 | ||
|
|
7802931e54 | ||
|
|
14737cc1dc | ||
|
|
d8d33811e4 | ||
|
|
c99f89c987 | ||
|
|
7c76b25a44 | ||
|
|
4fdc8222e8 | ||
|
|
07d2e54bcc | ||
|
|
f3d0e4b8ed | ||
|
|
2fad6f3bf1 | ||
|
|
9482955a83 | ||
|
|
9d75c8d820 | ||
|
|
e3925b752d | ||
|
|
f990006587 | ||
|
|
0b7bee480c | ||
|
|
05f4aeea9f | ||
|
|
5f37b9e7d2 | ||
|
|
734b464de3 | ||
|
|
4206cf1b09 | ||
|
|
17e3ef131c | ||
|
|
2c13ebc6e7 | ||
|
|
4f3b16cab2 | ||
|
|
25ebdea758 | ||
|
|
8b82df8298 | ||
|
|
0298a9e3a9 | ||
|
|
f2f60fb1f7 | ||
|
|
b07eb9b8a1 | ||
|
|
f5bffc4da9 | ||
|
|
55d60bd466 | ||
|
|
c2a945b7ed | ||
|
|
48b7cdad44 | ||
|
|
c7500084df | ||
|
|
d1850e8b4b | ||
|
|
c465a58976 | ||
|
|
6b05b2c2f0 | ||
|
|
cd10fb1db0 | ||
|
|
172e1eb295 | ||
|
|
9e16eb513f | ||
|
|
24b322053c | ||
|
|
e52771c367 | ||
|
|
76f792bfdf | ||
|
|
c6fad7fe98 | ||
|
|
915cb589b3 | ||
|
|
664692cef9 | ||
|
|
220579ff85 | ||
|
|
1a8e964c04 | ||
|
|
3a309ec916 | ||
|
|
d46c64fad5 | ||
|
|
3f47715c36 | ||
|
|
94715a1561 | ||
|
|
a752c47b5a | ||
|
|
4d0535daa9 | ||
|
|
a3e64d1733 | ||
|
|
2618a7dadc | ||
|
|
5617465294 | ||
|
|
9c20fba4a0 | ||
|
|
05718f9e8e | ||
|
|
81dad0bd34 | ||
|
|
c177cf9f9a | ||
|
|
e1761d8969 | ||
|
|
0ae464d1f7 | ||
|
|
4913d82bce | ||
|
|
4f6027e772 | ||
|
|
fa84cfb26e | ||
|
|
971287e989 | ||
|
|
deed6c7267 | ||
|
|
f58f460355 | ||
|
|
2c897d4454 | ||
|
|
b4101c03ee | ||
|
|
200e90a590 | ||
|
|
be8f042e49 | ||
|
|
4b1eb993e4 | ||
|
|
f6ce673345 | ||
|
|
08eeec4d99 | ||
|
|
564a4598b9 | ||
|
|
7e25b6e58e | ||
|
|
57f74606f0 | ||
|
|
b44cb9ad57 | ||
|
|
dfd5906d7f | ||
|
|
2e7b5e8712 | ||
|
|
eb8682fd0e | ||
|
|
2d224a207c | ||
|
|
5070a81db4 | ||
|
|
e97dd2ee47 | ||
|
|
da74c6ad23 | ||
|
|
c7f1d08748 | ||
|
|
7850acc6fb | ||
|
|
ec5b9a1150 | ||
|
|
83f67a9d48 | ||
|
|
fb799ad436 | ||
|
|
60186efca6 | ||
|
|
20ab3b6caa | ||
|
|
32db66eb7e | ||
|
|
112844a9f7 | ||
|
|
745df591e0 | ||
|
|
9c7759d131 | ||
|
|
4bb70f11e9 | ||
|
|
ce5ca9dbca | ||
|
|
0b9a984b7a | ||
|
|
45cd6caeec | ||
|
|
4c517c0e59 | ||
|
|
7884cb310b | ||
|
|
306bece709 | ||
|
|
748a055a24 | ||
|
|
240b26a265 | ||
|
|
ac82121460 | ||
|
|
3189a4f457 | ||
|
|
291e7588e1 | ||
|
|
b8e274e0f9 | ||
|
|
e81a1f78a4 | ||
|
|
51c3455ffe | ||
|
|
583fa0aa39 | ||
|
|
f768f8d969 | ||
|
|
0e26f0ce26 | ||
|
|
2a8beca0f8 | ||
|
|
b5813ebff8 | ||
|
|
0f6f36411a | ||
|
|
f798639125 | ||
|
|
6014095ab3 | ||
|
|
52e265557c | ||
|
|
f9f9fbf3e4 | ||
|
|
d559b5cdb7 | ||
|
|
d6733e62d6 | ||
|
|
00a0aac7a3 | ||
|
|
840ade5b78 | ||
|
|
13d3137ae4 | ||
|
|
62c652a645 | ||
|
|
76428cbda2 | ||
|
|
434ea26950 | ||
|
|
9655b34e5f | ||
|
|
e91caa4e7a | ||
|
|
3b0810a096 | ||
|
|
1d81e71bcb | ||
|
|
29ac9a594a | ||
|
|
fb0e399ab9 | ||
|
|
7c5c989eb2 | ||
|
|
41fd19c766 | ||
|
|
b423889c06 | ||
|
|
5f43923b80 | ||
|
|
5470264f00 | ||
|
|
06ed722b7a | ||
|
|
8565b7be0c | ||
|
|
a3861823c9 | ||
|
|
84fd45ad69 | ||
|
|
dd6a431af2 | ||
|
|
fa31fb199f | ||
|
|
fbeb638582 | ||
|
|
1aebd59062 | ||
|
|
7182908728 | ||
|
|
58a92de5a1 | ||
|
|
4ea04f461a | ||
|
|
26c084cfb8 | ||
|
|
ac513557a1 | ||
|
|
db5b5698a7 | ||
|
|
1f61d22489 | ||
|
|
520f602f23 | ||
|
|
8c5c63a4b5 | ||
|
|
a0741ba26b | ||
|
|
9f21d944d1 | ||
|
|
10f9e3e787 | ||
|
|
eff5e409c0 | ||
|
|
f108735f74 | ||
|
|
e62149398b | ||
|
|
d6e831eb07 | ||
|
|
dcf3912d24 | ||
|
|
efad699d4b | ||
|
|
77a35fd132 | ||
|
|
2fe7e10750 | ||
|
|
6f66a19b18 | ||
|
|
2fc88e439d | ||
|
|
b4b05c3f4e | ||
|
|
0ee1d39bac | ||
|
|
c3521a2490 | ||
|
|
cb93955cc5 | ||
|
|
666efc94d1 | ||
|
|
d2a52fd529 | ||
|
|
085685a769 | ||
|
|
034196ddd6 | ||
|
|
3dc16bdbb4 | ||
|
|
f42f39f9c9 | ||
|
|
40235db52e | ||
|
|
810b73e1f0 | ||
|
|
82850cf4fb | ||
|
|
21e597ca2d | ||
|
|
ed8203c896 | ||
|
|
de7668e8ff | ||
|
|
7271dd5248 | ||
|
|
16dd07bab3 | ||
|
|
5cbfc45819 | ||
|
|
6a238ab51a | ||
|
|
4d9c2a30df | ||
|
|
8b18927c67 | ||
|
|
d77563bdb0 | ||
|
|
d0466714c8 | ||
|
|
08c9c1a92f | ||
|
|
ea3943daf4 | ||
|
|
6ac92c45f1 | ||
|
|
96cd0e2259 | ||
|
|
fc5830a687 | ||
|
|
1dfe437641 | ||
|
|
b8eda5a152 | ||
|
|
de8603832f | ||
|
|
f11bcd27cc | ||
|
|
00dc91cf49 | ||
|
|
8b0255e2f7 | ||
|
|
3a2279f378 | ||
|
|
8085bcb232 | ||
|
|
5e6f325df1 | ||
|
|
f892eb629e | ||
|
|
1629958970 | ||
|
|
2079627030 | ||
|
|
2072b78489 | ||
|
|
3d1d4a1aff | ||
|
|
41665f7e95 | ||
|
|
604aae5980 | ||
|
|
ac613f7bea | ||
|
|
46d0a6d00d | ||
|
|
402afc82ca | ||
|
|
1cd59a0892 | ||
|
|
7b7bcf1005 | ||
|
|
be701007df | ||
|
|
fef6285436 | ||
|
|
b3c4afa620 | ||
|
|
8df1813afd | ||
|
|
6a28bcb49a | ||
|
|
730ed8c597 | ||
|
|
7b5be4a0ec | ||
|
|
30fb1250b3 | ||
|
|
a363bf841c | ||
|
|
da5830845b | ||
|
|
23e6eada26 | ||
|
|
490b0801a0 | ||
|
|
49ab704a84 | ||
|
|
4a12b78f14 | ||
|
|
656dbdcd28 | ||
|
|
88398afba6 | ||
|
|
2f74207bf6 | ||
|
|
f4f9abe4d4 | ||
|
|
e50b8ec7dc | ||
|
|
70f92494b2 | ||
|
|
c07fee3b3b | ||
|
|
07b635672e | ||
|
|
edc4c7ab2b | ||
|
|
6edcc36f17 | ||
|
|
3a2a6c231e | ||
|
|
cf26e4aa8a | ||
|
|
8ca1da8828 | ||
|
|
5764bc1c1c | ||
|
|
dba7335594 | ||
|
|
1461309dba | ||
|
|
d7e8388600 | ||
|
|
e79d039aa0 | ||
|
|
3d8b3efb9e | ||
|
|
58367fdeac | ||
|
|
63d54952d0 | ||
|
|
5904563653 | ||
|
|
5d0b750a64 | ||
|
|
43e99631a8 | ||
|
|
8cd6d60839 | ||
|
|
047d012fff | ||
|
|
0064543989 | ||
|
|
b61b7b5431 | ||
|
|
0c319e88c3 | ||
|
|
da384af339 | ||
|
|
f98f3d0b39 | ||
|
|
439c366ba2 | ||
|
|
d93c42e89c | ||
|
|
850d26a628 | ||
|
|
c3e79405f7 | ||
|
|
03b23d88d2 | ||
|
|
d5d6cea84c | ||
|
|
fb173af647 | ||
|
|
a1bc31883b | ||
|
|
52854c77c2 | ||
|
|
8f58e41304 | ||
|
|
25525b4155 | ||
|
|
66cc0bd8ff | ||
|
|
01c6127545 | ||
|
|
2794e1f62d | ||
|
|
83c9e9e6dd | ||
|
|
39742d02bb | ||
|
|
f1b68a5207 | ||
|
|
493631cd90 | ||
|
|
9a1ce6945b | ||
|
|
ccf66cde2f | ||
|
|
233e9326f0 | ||
|
|
ba97c99f98 | ||
|
|
37edd072a6 | ||
|
|
00358e9fe8 | ||
|
|
888710cbc5 | ||
|
|
518e00c78a | ||
|
|
f2a20a182e | ||
|
|
a058eb06b2 | ||
|
|
64a7592fed | ||
|
|
bfb076b9bc | ||
|
|
5292272902 | ||
|
|
1db3ce4b59 | ||
|
|
060d544390 | ||
|
|
b0e3364a77 | ||
|
|
d78dde4db1 | ||
|
|
a94b2df865 | ||
|
|
e4bb788fb9 | ||
|
|
6dcc401342 | ||
|
|
26a7e06b40 | ||
|
|
2ba8120d79 | ||
|
|
b9b045098b | ||
|
|
a0682d80c0 | ||
|
|
1ff39761b7 | ||
|
|
bddd058b38 | ||
|
|
8efb717cf4 | ||
|
|
65ac607d90 | ||
|
|
0af653b101 | ||
|
|
175b07c0ff | ||
|
|
585cba8af8 | ||
|
|
2a7525122e | ||
|
|
7f7c293856 | ||
|
|
fbb117705e | ||
|
|
78dea9eecb | ||
|
|
be9f52e029 | ||
|
|
000dd6de7b | ||
|
|
5e8e4dd9d5 | ||
|
|
b31a35d34b | ||
|
|
414b3a03c3 | ||
|
|
a7f42dcf0c | ||
|
|
7bd7f4e56b | ||
|
|
9584c78500 | ||
|
|
bc7a9c14d0 | ||
|
|
c1eacc225d | ||
|
|
e56bbe367d | ||
|
|
6cf9939ab3 | ||
|
|
b2f3b8f2af | ||
|
|
80e897abfb | ||
|
|
d152d21338 | ||
|
|
f4e04ece12 | ||
|
|
e7a07ea9c3 | ||
|
|
f8e6245903 | ||
|
|
7c146a9d5d | ||
|
|
846286c988 | ||
|
|
ba25bc6df4 | ||
|
|
622f9bfe71 | ||
|
|
8df47f5a60 | ||
|
|
d2a0647085 | ||
|
|
0e750a3f25 | ||
|
|
0e6dbe28a8 | ||
|
|
cc0c45bceb | ||
|
|
af3e734561 | ||
|
|
1ac61c5e3e | ||
|
|
5d3622b79d | ||
|
|
b3ddf1ae86 | ||
|
|
17770631a2 | ||
|
|
05f6958286 | ||
|
|
bd9c748b17 | ||
|
|
6b40abb58c | ||
|
|
6d67ab2240 | ||
|
|
8b3512e2f1 | ||
|
|
5b71bee4c8 | ||
|
|
75e7124af0 | ||
|
|
9ad55d5e28 | ||
|
|
9e93edf336 | ||
|
|
5608756a24 | ||
|
|
ebca421856 | ||
|
|
22e61a5700 | ||
|
|
7306de3730 | ||
|
|
10ed3db71d | ||
|
|
04e05d9aed | ||
|
|
caae95f12e | ||
|
|
dfa14f16d3 | ||
|
|
8e007131c9 | ||
|
|
ed4c588701 | ||
|
|
d33806e932 | ||
|
|
e83838e9ff | ||
|
|
b77dcd476c | ||
|
|
836b3a598b | ||
|
|
967b16fc0e | ||
|
|
ee3437d0f6 | ||
|
|
e20c736e3f | ||
|
|
a98d20ea72 | ||
|
|
38486e8184 | ||
|
|
047a09bbbd | ||
|
|
d55e58ea1c | ||
|
|
17afe80a54 | ||
|
|
42bf568d80 | ||
|
|
50157c43de | ||
|
|
41988a1298 | ||
|
|
86058ec19f | ||
|
|
356c750b23 | ||
|
|
26cbb9d9c6 | ||
|
|
4a0b78c1e6 | ||
|
|
fcb7c845ba | ||
|
|
12f9c0bce9 | ||
|
|
47ed79b912 | ||
|
|
a265d11a5f | ||
|
|
8a96c5f7b5 | ||
|
|
5e5456191c | ||
|
|
aae7f56e7d | ||
|
|
3645b3ee13 | ||
|
|
e0f357390c | ||
|
|
9c3cb62838 | ||
|
|
14225b8e79 | ||
|
|
e01bf735d9 | ||
|
|
1904f8ced1 | ||
|
|
3caca8c323 | ||
|
|
dd7ce2d45e | ||
|
|
f793f4548b | ||
|
|
56675a61f5 | ||
|
|
3dd27fcb77 | ||
|
|
13d76f8e9c | ||
|
|
9a2cdcde11 | ||
|
|
155e7320cb | ||
|
|
750fd84494 | ||
|
|
924adc68a9 | ||
|
|
0f8d9d7fb3 | ||
|
|
801aa1156f | ||
|
|
aa0c14c214 | ||
|
|
8fcbb670d8 | ||
|
|
85a26ecdf7 | ||
|
|
480c5edd75 | ||
|
|
fdfa1ddf97 | ||
|
|
80caf1818b | ||
|
|
5c0cd50797 | ||
|
|
8129d5d7dc | ||
|
|
a77b7af5fd | ||
|
|
ecf41722c3 | ||
|
|
39b09780f6 | ||
|
|
98dec6dc8e | ||
|
|
50b1ba3acc | ||
|
|
c3fc7b98f3 | ||
|
|
f5b8b18d86 | ||
|
|
e7c61fac5c | ||
|
|
2640603f6c | ||
|
|
1a011276bf | ||
|
|
bc7cf09287 | ||
|
|
7762241653 | ||
|
|
9489196911 | ||
|
|
e32e060d37 | ||
|
|
5e3fce6820 | ||
|
|
6655b6ba6a | ||
|
|
0b4b003c10 | ||
|
|
380e7e8b20 | ||
|
|
9b3533abc0 | ||
|
|
1409016cbd | ||
|
|
4aa1fb4b86 | ||
|
|
40126c52e8 | ||
|
|
dd600d4590 | ||
|
|
532f420338 | ||
|
|
e25878e78d | ||
|
|
002cc4842a | ||
|
|
eac548ac8b | ||
|
|
3fc4d1b219 | ||
|
|
80e3b8be0d | ||
|
|
fa35f6caa4 | ||
|
|
526cf6059a | ||
|
|
1527e472b6 | ||
|
|
94f7c6db97 | ||
|
|
e3ddb8f757 | ||
|
|
d7d0d371c6 | ||
|
|
527c60daa7 | ||
|
|
c30050396a | ||
|
|
60a8acf4d4 | ||
|
|
5164c3cd7d | ||
|
|
6276e659cd | ||
|
|
298314626e | ||
|
|
0112bc4df7 | ||
|
|
c8dbed938a | ||
|
|
278f35e4aa | ||
|
|
dd38e45f2e | ||
|
|
3e08f9b1b1 | ||
|
|
8392a44314 | ||
|
|
d050d1a4b9 | ||
|
|
56598ce2ff | ||
|
|
2fb72155eb | ||
|
|
030bd4b28d | ||
|
|
b9c40ad3ce | ||
|
|
4490a90332 | ||
|
|
15d6facdb9 | ||
|
|
b80962b365 | ||
|
|
814c6a8713 | ||
|
|
eea89c1d33 | ||
|
|
9ec0367ecb | ||
|
|
f5fe1013ee | ||
|
|
c338d28d35 | ||
|
|
2f962452e5 | ||
|
|
10f5e68f7f | ||
|
|
1cde37f32b | ||
|
|
291899de8a | ||
|
|
cc70669f1a | ||
|
|
c568dfa486 | ||
|
|
ff7daf8727 | ||
|
|
fc72066ed2 | ||
|
|
253ccd6d9b | ||
|
|
01accaeb38 | ||
|
|
46c3bc09a1 | ||
|
|
2f0b7566e5 | ||
|
|
9b8ec714d2 | ||
|
|
5d408fe3c7 | ||
|
|
aa0b7bedf0 | ||
|
|
c5358f7c82 | ||
|
|
158d6e5647 | ||
|
|
e091781104 | ||
|
|
25582cb9f8 | ||
|
|
fece294cc6 | ||
|
|
c3501f68e3 | ||
|
|
c4ee5fbd41 | ||
|
|
9a54074b1b | ||
|
|
10e918c375 | ||
|
|
bbcad99fa7 | ||
|
|
c8198a1a1c | ||
|
|
8d5241f419 | ||
|
|
448b681c5b | ||
|
|
826f7a29b5 | ||
|
|
0629b1e2d2 | ||
|
|
0d9cf63dd2 | ||
|
|
6513bd5fe0 | ||
|
|
f933e3de3f | ||
|
|
88a8d84153 | ||
|
|
8e4f5da791 | ||
|
|
a61d21e501 | ||
|
|
2bcf33661a | ||
|
|
f6df7a06f2 | ||
|
|
de92a2fc0c | ||
|
|
6012f1d592 | ||
|
|
caf676dc33 | ||
|
|
e3ae1bec75 | ||
|
|
b98b517e35 | ||
|
|
5d9f25eef5 | ||
|
|
5bf69eb539 | ||
|
|
09f7778294 | ||
|
|
c8ec5f3579 | ||
|
|
a54c7ecc18 | ||
|
|
0064caf1f4 | ||
|
|
fc2fbd4689 | ||
|
|
0f0793cde5 | ||
|
|
2fed6f61e9 | ||
|
|
3df310df6e | ||
|
|
c2b3a749ca | ||
|
|
2d2220f38f | ||
|
|
7509bf85aa | ||
|
|
64e8088ae4 | ||
|
|
15584b9b34 | ||
|
|
79c1d1b0a6 | ||
|
|
50860614cf | ||
|
|
90d1d3e053 | ||
|
|
3bf83b52fb | ||
|
|
5f57dd7a62 | ||
|
|
8e1185b56f | ||
|
|
26098df2a1 | ||
|
|
9ac9d83745 | ||
|
|
51edd5a3f4 | ||
|
|
fc07f90f91 | ||
|
|
b64dcbf502 | ||
|
|
5694c113be | ||
|
|
01cf5c21a7 | ||
|
|
4206d2e131 | ||
|
|
83968553aa | ||
|
|
aa2aba1250 | ||
|
|
bba9c4b976 | ||
|
|
999eef2ec9 | ||
|
|
f28c8903aa | ||
|
|
f58eabe667 | ||
|
|
86695dfe29 | ||
|
|
9ae452e8d6 | ||
|
|
3e547102d2 | ||
|
|
e883e63c87 | ||
|
|
a904047a16 | ||
|
|
ad1af792e6 | ||
|
|
6c9527d9dc | ||
|
|
da2d461aa8 | ||
|
|
49bfd69707 | ||
|
|
2a02df9411 | ||
|
|
185ba80e99 | ||
|
|
127cbf3f96 | ||
|
|
4bdf675def | ||
|
|
9c944924de | ||
|
|
28f79533eb | ||
|
|
5a834c9500 | ||
|
|
3255218d5c | ||
|
|
9befe377d5 | ||
|
|
f39b4f0750 | ||
|
|
1794625cea | ||
|
|
42092fe6dd | ||
|
|
1a451fe813 | ||
|
|
0e845a9f3a |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -44,6 +44,9 @@ packaging/osx/launcher/OpenRA.xcodeproj/*.mode1v3
|
||||
temp.c
|
||||
temp.o
|
||||
temp.s
|
||||
|
||||
OpenRA.Launcher.Mac/build/
|
||||
OpenRA.Launcher.Mac/OpenRA.xcodeproj/*.pbxuser
|
||||
OpenRA.Launcher.Mac/OpenRA.xcodeproj/*.perspectivev3
|
||||
OpenRA.Launcher.Mac/OpenRA.xcodeproj/*.mode1v3
|
||||
*.config
|
||||
*.resources
|
||||
|
||||
1
AUTHORS
1
AUTHORS
@@ -8,3 +8,4 @@ The OpenRA developers are:
|
||||
* Alli Witheford
|
||||
* Joakim Lindberg
|
||||
* Andrew Riedi
|
||||
* Tim Mylemans
|
||||
|
||||
4
HACKING
4
HACKING
@@ -68,7 +68,7 @@ to flush the renderer (if you want to see anything).
|
||||
UserSettings stores the data loaded from settings.ini (or defaults). Eventually we need to be
|
||||
able to save values changed in game into settings.ini (not yet implemented)
|
||||
|
||||
Bugs: There is a list of known bugs and features at http://red-bull.ijw.co.nz:3690/OpenRA .
|
||||
Bugs: There is a list of known bugs and features at http://bugs.open-ra.org/ .
|
||||
|
||||
We also have a website at http://www.open-ra.org/ .
|
||||
|
||||
@@ -76,7 +76,7 @@ Our IRC channel is #openra on irc.freenode.net .
|
||||
|
||||
As far as using git, get your own repository on github. You probably want to set up the gitbot
|
||||
to spam irc when you make commits (its nice to know). Push your changes into your git
|
||||
repository, and it will/might :P be merged into chrisforbes/OpenRA .
|
||||
repository, and it will/might :P be merged into http://github.com/OpenRA/OpenRA .
|
||||
See http://help.github.com/ for working with GitHub and see http://progit.org/ for working
|
||||
with Git.
|
||||
|
||||
|
||||
12
INSTALL
12
INSTALL
@@ -37,7 +37,11 @@ These need to be copied into the mods/cnc/packages/ directory.
|
||||
If you have a case-sensitive filesystem you must change the filenames to
|
||||
lower case.
|
||||
|
||||
The files can be downloaded from:
|
||||
http://open-ra.org/get-dependency.php?file=ra-packages for the Red Alert files and
|
||||
http://open-ra.org/get-dependency.php?file=cnc-packages for the Command & Conquer files.
|
||||
|
||||
Alternatively:
|
||||
Red Alert and C&C have been released by EA Games as freeware. They can be
|
||||
downloaded from http://www.commandandconquer.com/classic
|
||||
Unfortunately the installer is 16-bit and so won’t run on 64-bit operating
|
||||
@@ -55,8 +59,7 @@ WINDOWS:
|
||||
* .NET Framework >= 3.5-SP1
|
||||
(http://www.microsoft.com/downloads/details.aspx?FamilyID=AB99342F-5D1A-413D-8319-81DA479AB0D7&displaylang=en)
|
||||
* Tao Framework >= 2.1.0
|
||||
(http://sourceforge.net/projects/taoframework/)
|
||||
(required libs: Tao.OpenGL, Tao.Cg, Tao.Platform.Windows)
|
||||
This is now bundled with OpenRA, copy the files in thirdparty/Tao to the game root directory.
|
||||
* OpenAL >= 1.1
|
||||
(http://connect.creativelabs.com/openal/Downloads/oalinst.zip)
|
||||
* Cg Toolkit >= 2.2
|
||||
@@ -83,8 +86,8 @@ UBUNTU (substitute comparable packages for other linux distros):
|
||||
OpenRA is incompatible with Compiz, please disable desktop effects when trying
|
||||
to run OpenRA or the game will crash.
|
||||
|
||||
You will need to copy the Tao dependencies (.dll and .config) from the
|
||||
thirdparty/Tao directory into the game root, or install them permanently into
|
||||
You will need to copy the third party dependencies (.dll and .config) from the
|
||||
thirdparty and thirdparty/Tao directories into the game root, or install them permanently into
|
||||
your GAC with the following script
|
||||
|
||||
#!/bin/sh
|
||||
@@ -93,6 +96,7 @@ gacutil -i thirdparty/Tao/Tao.OpenGl.dll
|
||||
gacutil -i thirdparty/Tao/Tao.OpenAl.dll
|
||||
gacutil -i thirdparty/Tao/Tao.Sdl.dll
|
||||
gacutil -i thirdparty/Tao/Tao.FreeType.dll
|
||||
gacutil -i thirdparty/ICSharpCode.SharpZipLib.dll
|
||||
|
||||
To compile OpenRA, run `make' from the command line.
|
||||
Run the game with `mono OpenRA.Game.exe Game.Mods=ra` for Red Alert
|
||||
|
||||
343
Makefile
343
Makefile
@@ -1,7 +1,202 @@
|
||||
CSC = gmcs
|
||||
CSFLAGS = -nologo -warn:4 -debug:+ -debug:full -optimize- -codepage:utf8 -unsafe
|
||||
DEFINE = DEBUG;TRACE
|
||||
PROGRAMS =fileformats gl game ra cnc seqed editor ralint filex tsbuild
|
||||
CSC = gmcs
|
||||
CSFLAGS = -nologo -warn:4 -debug:+ -debug:full -optimize- -codepage:utf8 -unsafe
|
||||
DEFINE = DEBUG;TRACE
|
||||
COMMON_LIBS = System.dll System.Core.dll System.Drawing.dll System.Xml.dll thirdparty/ICSharpCode.SharpZipLib.dll
|
||||
PHONY = core tools package all mods clean distclean
|
||||
|
||||
.SUFFIXES:
|
||||
core: game renderers mod_ra mod_cnc
|
||||
tools: editor ralint seqed filex tsbuild utility
|
||||
package: fixheader core editor utility winlaunch
|
||||
mods: mod_ra mod_cnc
|
||||
all: core tools winlaunch
|
||||
clean:
|
||||
@-rm *.exe *.dll *.mdb mods/**/*.dll mods/**/*.mdb *.resources
|
||||
distclean: clean
|
||||
|
||||
#
|
||||
# Core binaries
|
||||
#
|
||||
fileformats_SRCS = $(shell find OpenRA.FileFormats/ -iname '*.cs')
|
||||
fileformats_TARGET = OpenRA.FileFormats.dll
|
||||
#fileformats_DEPS = fixheader
|
||||
fileformats_KIND = library
|
||||
fileformats_LIBS = $(COMMON_LIBS) thirdparty/Tao/Tao.Sdl.dll System.Windows.Forms.dll
|
||||
PROGRAMS = fileformats
|
||||
fileformats: $(fileformats_TARGET)
|
||||
|
||||
game_SRCS = $(shell find OpenRA.Game/ -iname '*.cs')
|
||||
game_TARGET = OpenRA.Game.exe
|
||||
game_KIND = winexe
|
||||
game_DEPS = $(fileformats_TARGET)
|
||||
game_LIBS = $(COMMON_LIBS) System.Windows.Forms.dll $(game_DEPS) \
|
||||
thirdparty/Tao/Tao.OpenAl.dll thirdparty/Tao/Tao.FreeType.dll
|
||||
game_FLAGS = -win32icon:OpenRA.Game/OpenRA.ico
|
||||
PROGRAMS += game
|
||||
game: $(game_TARGET)
|
||||
|
||||
#
|
||||
# Renderer dlls
|
||||
#
|
||||
rcg_SRCS = $(shell find OpenRA.Renderer.Cg/ -iname '*.cs')
|
||||
rcg_TARGET = OpenRA.Renderer.Cg.dll
|
||||
rcg_KIND = library
|
||||
rcg_DEPS = $(fileformats_TARGET) $(game_TARGET)
|
||||
rcg_LIBS = $(COMMON_LIBS) System.Windows.Forms.dll \
|
||||
thirdparty/Tao/Tao.Cg.dll thirdparty/Tao/Tao.OpenGl.dll thirdparty/Tao/Tao.Sdl.dll \
|
||||
$(rcg_DEPS) $(game_TARGET)
|
||||
|
||||
rgl_SRCS = $(shell find OpenRA.Renderer.Gl/ -iname '*.cs')
|
||||
rgl_TARGET = OpenRA.Renderer.Gl.dll
|
||||
rgl_KIND = library
|
||||
rgl_DEPS = $(fileformats_TARGET) $(game_TARGET)
|
||||
rgl_LIBS = $(COMMON_LIBS) System.Windows.Forms.dll \
|
||||
thirdparty/Tao/Tao.Cg.dll thirdparty/Tao/Tao.OpenGl.dll thirdparty/Tao/Tao.Sdl.dll \
|
||||
$(rgl_DEPS) $(game_TARGET)
|
||||
|
||||
rnull_SRCS = $(shell find OpenRA.Renderer.Null/ -iname '*.cs')
|
||||
rnull_TARGET = OpenRA.Renderer.Null.dll
|
||||
rnull_KIND = library
|
||||
rnull_DEPS = $(fileformats_TARGET) $(game_TARGET)
|
||||
rnull_LIBS = $(COMMON_LIBS) System.Windows.Forms.dll \
|
||||
$(rnull_DEPS) $(game_TARGET)
|
||||
PROGRAMS += rcg rgl rnull
|
||||
renderers: $(rcg_TARGET) $(rgl_TARGET) $(rnull_TARGET)
|
||||
|
||||
#
|
||||
# Official Mods
|
||||
#
|
||||
# Red Alert
|
||||
mod_ra_SRCS = $(shell find OpenRA.Mods.RA/ -iname '*.cs')
|
||||
mod_ra_TARGET = mods/ra/OpenRA.Mods.RA.dll
|
||||
mod_ra_KIND = library
|
||||
mod_ra_DEPS = $(fileformats_TARGET) $(game_TARGET)
|
||||
mod_ra_LIBS = $(COMMON_LIBS) $(mod_ra_DEPS)
|
||||
PROGRAMS += mod_ra
|
||||
mod_ra: $(mod_ra_TARGET)
|
||||
# mono RALint.exe ra
|
||||
|
||||
# Command and Conquer
|
||||
mod_cnc_SRCS = $(shell find OpenRA.Mods.Cnc/ -iname '*.cs')
|
||||
mod_cnc_TARGET = mods/cnc/OpenRA.Mods.Cnc.dll
|
||||
mod_cnc_KIND = library
|
||||
mod_cnc_DEPS = $(fileformats_TARGET) $(game_TARGET) $(mod_ra_TARGET)
|
||||
mod_cnc_LIBS = $(COMMON_LIBS) $(mod_cnc_DEPS)
|
||||
PROGRAMS += mod_cnc
|
||||
mod_cnc: $(mod_cnc_TARGET)
|
||||
# mono RALint.exe cnc
|
||||
|
||||
#
|
||||
# Tools
|
||||
#
|
||||
# Sequence editor (defunct)
|
||||
seqed_SRCS = $(shell find SequenceEditor/ -iname '*.cs')
|
||||
seqed_TARGET = SequenceEditor.exe
|
||||
seqed_KIND = winexe
|
||||
seqed_DEPS = $(fileformats_TARGET)
|
||||
seqed_LIBS = $(COMMON_LIBS) System.Windows.Forms.dll $(seqed_DEPS)
|
||||
seqed_EXTRA = -resource:SequenceEditor.Form1.resources
|
||||
PROGRAMS += seqed
|
||||
SequenceEditor.Form1.resources:
|
||||
resgen2 SequenceEditor/Form1.resx SequenceEditor.Form1.resources 1> /dev/null
|
||||
seqed: SequenceEditor.Form1.resources $(seqed_TARGET)
|
||||
|
||||
# Map Editor
|
||||
editor_SRCS = $(shell find OpenRA.Editor/ -iname '*.cs')
|
||||
editor_TARGET = OpenRA.Editor.exe
|
||||
editor_KIND = winexe
|
||||
editor_DEPS = $(fileformats_TARGET) $(game_TARGET)
|
||||
editor_LIBS = $(COMMON_LIBS) System.Windows.Forms.dll System.Data.dll $(editor_DEPS)
|
||||
editor_EXTRA = -resource:OpenRA.Editor.Form1.resources -resource:OpenRA.Editor.MapSelect.resources
|
||||
PROGRAMS += editor
|
||||
OpenRA.Editor.MapSelect.resources:
|
||||
resgen2 OpenRA.Editor/MapSelect.resx OpenRA.Editor.MapSelect.resources 1> /dev/null
|
||||
OpenRA.Editor.Form1.resources:
|
||||
resgen2 OpenRA.Editor/Form1.resx OpenRA.Editor.Form1.resources 1> /dev/null
|
||||
editor: OpenRA.Editor.MapSelect.resources OpenRA.Editor.Form1.resources $(editor_TARGET)
|
||||
|
||||
# Analyses mod yaml for easy to detect errors
|
||||
ralint_SRCS = $(shell find RALint/ -iname '*.cs')
|
||||
ralint_TARGET = RALint.exe
|
||||
ralint_KIND = exe
|
||||
ralint_DEPS = $(fileformats_TARGET) $(game_TARGET)
|
||||
ralint_LIBS = $(COMMON_LIBS) $(ralint_DEPS)
|
||||
PROGRAMS += ralint
|
||||
ralint: $(ralint_TARGET)
|
||||
|
||||
# Extracts files from packages (mixfiles, zips, etc)
|
||||
filex_SRCS = $(shell find FileExtractor/ -iname '*.cs')
|
||||
filex_TARGET = FileExtractor.exe
|
||||
filex_KIND = exe
|
||||
filex_DEPS = $(fileformats_TARGET)
|
||||
filex_LIBS = $(COMMON_LIBS) $(filex_DEPS)
|
||||
PROGRAMS += filex
|
||||
filex: $(filex_TARGET)
|
||||
|
||||
# Builds and exports tilesets from a bitmap
|
||||
tsbuild_SRCS = $(shell find OpenRA.TilesetBuilder/ -iname '*.cs')
|
||||
tsbuild_TARGET = TilesetBuilder.exe
|
||||
tsbuild_KIND = winexe
|
||||
tsbuild_DEPS = $(fileformats_TARGET) $(game_TARGET)
|
||||
tsbuild_LIBS = $(COMMON_LIBS) $(tsbuild_DEPS) System.Windows.Forms.dll
|
||||
tsbuild_EXTRA = -resource:OpenRA.TilesetBuilder.Form1.resources
|
||||
PROGRAMS += tsbuild
|
||||
OpenRA.TilesetBuilder.Form1.resources:
|
||||
resgen2 OpenRA.TilesetBuilder/Form1.resx OpenRA.TilesetBuilder.Form1.resources 1> /dev/null
|
||||
tsbuild: OpenRA.TilesetBuilder.Form1.resources $(tsbuild_TARGET)
|
||||
|
||||
#
|
||||
# Launchers / Utilities
|
||||
#
|
||||
# Patches binary headers to work around a mono bug
|
||||
fixheader: packaging/fixheader.cs
|
||||
@$(CSC) packaging/fixheader.cs $(CSFLAGS) -out:fixheader.exe -t:exe $(COMMON_LIBS:%=-r:%)
|
||||
PHONY += fixheader
|
||||
|
||||
# Backend for the launcher apps - queries game/mod info and applies actions to an install
|
||||
utility_SRCS = $(shell find OpenRA.Utility/ -iname '*.cs')
|
||||
utility_TARGET = OpenRA.Utility.exe
|
||||
utility_KIND = exe
|
||||
utility_DEPS = $(fileformats_TARGET) thirdparty/ICSharpCode.SharpZipLib.dll
|
||||
utility_LIBS = $(COMMON_LIBS) $(utility_DEPS)
|
||||
PROGRAMS += utility
|
||||
utility: $(utility_TARGET)
|
||||
|
||||
# Windows launcher
|
||||
winlaunch_SRCS = $(shell find OpenRA.Launcher/ -iname '*.cs')
|
||||
winlaunch_TARGET = OpenRA.Launcher.exe
|
||||
winlaunch_KIND = winexe
|
||||
winlaunch_LIBS = $(COMMON_LIBS) System.Windows.Forms.dll
|
||||
winlaunch_EXTRA = -resource:OpenRA.Launcher.Launcher.resources
|
||||
PROGRAMS += winlaunch
|
||||
OpenRA.Launcher.Launcher.resources:
|
||||
resgen2 OpenRA.Launcher/Launcher.resx OpenRA.Launcher.Launcher.resources 1> /dev/null
|
||||
winlaunch: OpenRA.Launcher.Launcher.resources $(winlaunch_TARGET)
|
||||
|
||||
.PHONY: $(PHONY) $(PROGRAMS)
|
||||
|
||||
#
|
||||
# Generate build rules for each target defined above in PROGRAMS
|
||||
#
|
||||
define BUILD_ASSEMBLY
|
||||
|
||||
$$($(1)_TARGET): $$($(1)_SRCS) Makefile $$($(1)_DEPS)
|
||||
@echo CSC $$(@)
|
||||
@$(CSC) $$($(1)_LIBS:%=-r:%) \
|
||||
-out:$$(@) $(CSFLAGS) $$($(1)_FLAGS) \
|
||||
-define:"$(DEFINE)" \
|
||||
-t:"$$($(1)_KIND)" \
|
||||
$$($(1)_EXTRA) \
|
||||
$$($(1)_SRCS)
|
||||
@test -e fixheader.exe && mono fixheader.exe $$(@) || ``
|
||||
endef
|
||||
|
||||
$(foreach prog,$(PROGRAMS),$(eval $(call BUILD_ASSEMBLY,$(prog))))
|
||||
|
||||
|
||||
#
|
||||
# Install / Uninstall for *nix
|
||||
#
|
||||
prefix = /usr/local
|
||||
datarootdir = $(prefix)/share
|
||||
datadir = $(datarootdir)
|
||||
@@ -10,96 +205,14 @@ BIN_INSTALL_DIR = $(DESTDIR)$(bindir)
|
||||
INSTALL_DIR = $(DESTDIR)$(datadir)/openra
|
||||
INSTALL = install
|
||||
INSTALL_PROGRAM = $(INSTALL)
|
||||
|
||||
COMMON_LIBS = System.dll System.Core.dll System.Drawing.dll System.Xml.dll
|
||||
|
||||
fileformats_SRCS = $(shell find OpenRA.FileFormats/ -iname '*.cs')
|
||||
fileformats_TARGET = OpenRA.FileFormats.dll
|
||||
fileformats_KIND = library
|
||||
fileformats_LIBS = $(COMMON_LIBS) thirdparty/Tao/Tao.Sdl.dll thirdparty/WindowsBase.dll
|
||||
|
||||
gl_SRCS = $(shell find OpenRA.Gl/ -iname '*.cs')
|
||||
gl_TARGET = OpenRA.Gl.dll
|
||||
gl_KIND = library
|
||||
gl_DEPS = $(fileformats_TARGET) $(game_TARGET)
|
||||
gl_LIBS = $(COMMON_LIBS) System.Windows.Forms.dll \
|
||||
thirdparty/Tao/Tao.Cg.dll thirdparty/Tao/Tao.OpenGl.dll thirdparty/Tao/Tao.Sdl.dll \
|
||||
$(gl_DEPS) $(game_TARGET)
|
||||
|
||||
game_SRCS = $(shell find OpenRA.Game/ -iname '*.cs')
|
||||
game_TARGET = OpenRA.Game.exe
|
||||
game_KIND = winexe
|
||||
game_DEPS = $(fileformats_TARGET)
|
||||
game_LIBS = $(COMMON_LIBS) System.Windows.Forms.dll $(game_DEPS) \
|
||||
thirdparty/Tao/Tao.OpenAl.dll thirdparty/Tao/Tao.FreeType.dll
|
||||
game_FLAGS = -win32icon:OpenRA.Game/OpenRA.ico
|
||||
|
||||
ra_SRCS = $(shell find OpenRA.Mods.RA/ -iname '*.cs')
|
||||
ra_TARGET = mods/ra/OpenRA.Mods.RA.dll
|
||||
ra_KIND = library
|
||||
ra_DEPS = $(fileformats_TARGET) $(game_TARGET)
|
||||
ra_LIBS = $(COMMON_LIBS) $(ra_DEPS)
|
||||
|
||||
cnc_SRCS = $(shell find OpenRA.Mods.Cnc/ -iname '*.cs')
|
||||
cnc_TARGET = mods/cnc/OpenRA.Mods.Cnc.dll
|
||||
cnc_KIND = library
|
||||
cnc_DEPS = $(fileformats_TARGET) $(game_TARGET) $(ra_TARGET)
|
||||
cnc_LIBS = $(COMMON_LIBS) $(cnc_DEPS)
|
||||
|
||||
seqed_SRCS = $(shell find SequenceEditor/ -iname '*.cs')
|
||||
seqed_TARGET = SequenceEditor.exe
|
||||
seqed_KIND = winexe
|
||||
seqed_DEPS = $(fileformats_TARGET)
|
||||
seqed_LIBS = $(COMMON_LIBS) System.Windows.Forms.dll $(seqed_DEPS)
|
||||
seqed_EXTRA = -resource:SequenceEditor.Form1.resources
|
||||
|
||||
editor_SRCS = $(shell find OpenRA.Editor/ -iname '*.cs')
|
||||
editor_TARGET = OpenRA.Editor.exe
|
||||
editor_KIND = winexe
|
||||
editor_DEPS = $(fileformats_TARGET) $(game_TARGET)
|
||||
editor_LIBS = $(COMMON_LIBS) System.Windows.Forms.dll System.Data.dll $(editor_DEPS)
|
||||
editor_EXTRA = -resource:OpenRA.Editor.Form1.resources -resource:OpenRA.Editor.MapSelect.resources
|
||||
|
||||
ralint_SRCS = $(shell find RALint/ -iname '*.cs')
|
||||
ralint_TARGET = RALint.exe
|
||||
ralint_KIND = winexe
|
||||
ralint_DEPS = $(fileformats_TARGET) $(game_TARGET)
|
||||
ralint_LIBS = $(COMMON_LIBS) $(ralint_DEPS)
|
||||
|
||||
filex_SRCS = $(shell find FileExtractor/ -iname '*.cs')
|
||||
filex_TARGET = FileExtractor.exe
|
||||
filex_KIND = winexe
|
||||
filex_DEPS = $(fileformats_TARGET)
|
||||
filex_LIBS = $(COMMON_LIBS) $(filex_DEPS)
|
||||
|
||||
tsbuild_SRCS = $(shell find OpenRA.TilesetBuilder/ -iname '*.cs')
|
||||
tsbuild_TARGET = TilesetBuilder.exe
|
||||
tsbuild_KIND = winexe
|
||||
tsbuild_DEPS = $(fileformats_TARGET) $(game_TARGET)
|
||||
tsbuild_LIBS = $(COMMON_LIBS) $(tsbuild_DEPS) System.Windows.Forms.dll
|
||||
tsbuild_EXTRA = -resource:OpenRA.TilesetBuilder.Form1.resources
|
||||
|
||||
# -platform:x86
|
||||
|
||||
.SUFFIXES:
|
||||
.PHONY: clean all game tool default mods mod_ra mod_cnc install uninstall editor_res editor tsbuild ralint seqed filex
|
||||
|
||||
game: $(fileformats_TARGET) $(gl_TARGET) $(game_TARGET) $(ra_TARGET) $(cnc_TARGET)
|
||||
|
||||
clean:
|
||||
@-rm *.exe *.dll *.mdb mods/**/*.dll mods/**/*.mdb *.resources
|
||||
|
||||
distclean: clean
|
||||
|
||||
CORE = fileformats gl game editor
|
||||
|
||||
CORE = fileformats rcg rgl rnull game editor utility winlaunch
|
||||
install: all
|
||||
@-echo "Installing OpenRA to $(INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) -d $(INSTALL_DIR)
|
||||
@$(INSTALL_PROGRAM) $(foreach prog,$(CORE),$($(prog)_TARGET)) $(INSTALL_DIR)
|
||||
|
||||
@$(INSTALL_PROGRAM) -d $(INSTALL_DIR)/mods/cnc
|
||||
@$(INSTALL_PROGRAM) $(cnc_TARGET) $(INSTALL_DIR)/mods/cnc
|
||||
@$(INSTALL_PROGRAM) $(mod_cnc_TARGET) $(INSTALL_DIR)/mods/cnc
|
||||
@-cp $(foreach f,$(shell ls mods/cnc --hide=*.dll),mods/cnc/$(f)) $(INSTALL_DIR)/mods/cnc
|
||||
@cp -r mods/cnc/maps $(INSTALL_DIR)/mods/cnc
|
||||
@cp -r mods/cnc/chrome $(INSTALL_DIR)/mods/cnc
|
||||
@@ -110,7 +223,7 @@ install: all
|
||||
@cp -r mods/cnc/uibits $(INSTALL_DIR)/mods/cnc
|
||||
|
||||
@$(INSTALL_PROGRAM) -d $(INSTALL_DIR)/mods/ra
|
||||
@$(INSTALL_PROGRAM) $(ra_TARGET) $(INSTALL_DIR)/mods/ra
|
||||
@$(INSTALL_PROGRAM) $(mod_ra_TARGET) $(INSTALL_DIR)/mods/ra
|
||||
@-cp $(foreach f,$(shell ls mods/ra --hide=*.dll),mods/ra/$(f)) $(INSTALL_DIR)/mods/ra
|
||||
@cp -r mods/ra/maps $(INSTALL_DIR)/mods/ra
|
||||
@cp -r mods/ra/bits $(INSTALL_DIR)/mods/ra
|
||||
@@ -119,10 +232,11 @@ install: all
|
||||
@cp -r mods/ra/tilesets $(INSTALL_DIR)/mods/ra
|
||||
@cp -r mods/ra/uibits $(INSTALL_DIR)/mods/ra
|
||||
|
||||
@cp -r shaders $(INSTALL_DIR)
|
||||
@cp -r glsl $(INSTALL_DIR)
|
||||
@cp -r cg $(INSTALL_DIR)
|
||||
@cp *.ttf $(INSTALL_DIR)
|
||||
@cp --parents -r thirdparty/Tao $(INSTALL_DIR)
|
||||
@$(INSTALL_PROGRAM) thirdparty/WindowsBase.dll $(INSTALL_DIR)
|
||||
@$(INSTALL_PROGRAM) thirdparty/ICSharpCode.SharpZipLib.dll $(INSTALL_DIR)
|
||||
@-$(INSTALL_PROGRAM) VERSION $(INSTALL_DIR)
|
||||
|
||||
@echo "#!/bin/sh" > openra
|
||||
@@ -130,10 +244,10 @@ install: all
|
||||
@echo "mono "$(datadir)"/openra/OpenRA.Game.exe SupportDir=~/.openra \"$$""@\"" >> openra
|
||||
@$(INSTALL_PROGRAM) -d $(BIN_INSTALL_DIR)
|
||||
@$(INSTALL_PROGRAM) -m +rx openra $(BIN_INSTALL_DIR)
|
||||
|
||||
|
||||
@echo "OpenRA is now installed. You will now want to download"
|
||||
@echo "http://open-ra.org/get-dependency.php?ra-packages and"
|
||||
@echo "http://open-ra.org/get-dependency.php?cnc-packages"
|
||||
@echo "http://open-ra.org/get-dependency.php?file=ra-packages and"
|
||||
@echo "http://open-ra.org/get-dependency.php?file=cnc-packages"
|
||||
@echo "and extract their contents to"
|
||||
@echo "$(INSTALL_DIR)/mods/ra/packages and "
|
||||
@echo "$(INSTALL_DIR)/mods/cnc/packages respectively."
|
||||
@@ -143,44 +257,3 @@ install: all
|
||||
uninstall:
|
||||
@-rm -r $(INSTALL_DIR)
|
||||
@-rm $(DESTDIR)$(bindir)/openra
|
||||
|
||||
mod_ra: $(ra_TARGET) $(ralint_TARGET)
|
||||
mono RALint.exe ra
|
||||
mod_cnc: $(cnc_TARGET) $(ralint_TARGET)
|
||||
mono RALint.exe cnc
|
||||
mods: mod_ra mod_cnc
|
||||
|
||||
OpenRA.Editor.MapSelect.resources:
|
||||
resgen2 OpenRA.Editor/MapSelect.resx OpenRA.Editor.MapSelect.resources 1> /dev/null
|
||||
|
||||
OpenRA.Editor.Form1.resources:
|
||||
resgen2 OpenRA.Editor/Form1.resx OpenRA.Editor.Form1.resources 1> /dev/null
|
||||
editor: OpenRA.Editor.MapSelect.resources OpenRA.Editor.Form1.resources $(editor_TARGET)
|
||||
ralint: $(ralint_TARGET)
|
||||
seqed: SequenceEditor.Form1.resources $(seqed_TARGET)
|
||||
SequenceEditor.Form1.resources:
|
||||
resgen2 SequenceEditor/Form1.resx SequenceEditor.Form1.resources 1> /dev/null
|
||||
filex: $(filex_TARGET)
|
||||
tsbuild: OpenRA.TilesetBuilder.Form1.resources $(tsbuild_TARGET)
|
||||
OpenRA.TilesetBuilder.Form1.resources:
|
||||
resgen2 OpenRA.TilesetBuilder/Form1.resx OpenRA.TilesetBuilder.Form1.resources 1> /dev/null
|
||||
tools: editor ralint seqed filex tsbuild
|
||||
all: game tools
|
||||
|
||||
fixheader: packaging/fixheader.cs
|
||||
@$(CSC) packaging/fixheader.cs $(CSFLAGS) -out:fixheader.exe -t:exe $(COMMON_LIBS:%=-r:%)
|
||||
|
||||
define BUILD_ASSEMBLY
|
||||
|
||||
$$($(1)_TARGET): $$($(1)_SRCS) Makefile $$($(1)_DEPS) fixheader
|
||||
@echo CSC $$(@)
|
||||
@$(CSC) $$($(1)_LIBS:%=-r:%) \
|
||||
-out:$$(@) $(CSFLAGS) $$($(1)_FLAGS) \
|
||||
-define:"$(DEFINE)" \
|
||||
-t:"$$($(1)_KIND)" \
|
||||
$$($(1)_EXTRA) \
|
||||
$$($(1)_SRCS)
|
||||
@mono fixheader.exe $$(@)
|
||||
endef
|
||||
|
||||
$(foreach prog,$(PROGRAMS),$(eval $(call BUILD_ASSEMBLY,$(prog))))
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace OpenRA.Editor
|
||||
{
|
||||
public Bitmap Bitmap;
|
||||
public ActorInfo Info;
|
||||
public bool Centered;
|
||||
public EditorAppearanceInfo Appearance;
|
||||
}
|
||||
|
||||
class BrushTemplate
|
||||
|
||||
53
OpenRA.Editor/Form1.Designer.cs
generated
Normal file → Executable file
53
OpenRA.Editor/Form1.Designer.cs
generated
Normal file → Executable file
@@ -65,6 +65,9 @@
|
||||
this.tt = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.saveFileDialog = new System.Windows.Forms.SaveFileDialog();
|
||||
this.splitContainer3 = new System.Windows.Forms.SplitContainer();
|
||||
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
|
||||
this.toolStripStatusLabelFiller = new System.Windows.Forms.ToolStripStatusLabel();
|
||||
this.toolStripStatusLabelMousePosition = new System.Windows.Forms.ToolStripStatusLabel();
|
||||
this.surface1 = new OpenRA.Editor.Surface();
|
||||
this.splitContainer1.Panel1.SuspendLayout();
|
||||
this.splitContainer1.Panel2.SuspendLayout();
|
||||
@@ -81,6 +84,7 @@
|
||||
this.splitContainer3.Panel1.SuspendLayout();
|
||||
this.splitContainer3.Panel2.SuspendLayout();
|
||||
this.splitContainer3.SuspendLayout();
|
||||
this.statusStrip1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// splitContainer1
|
||||
@@ -115,7 +119,7 @@
|
||||
//
|
||||
this.splitContainer2.Panel2.Controls.Add(this.tabControl1);
|
||||
this.splitContainer2.Size = new System.Drawing.Size(198, 744);
|
||||
this.splitContainer2.SplitterDistance = 182;
|
||||
this.splitContainer2.SplitterDistance = 164;
|
||||
this.splitContainer2.TabIndex = 1;
|
||||
//
|
||||
// pmMiniMap
|
||||
@@ -125,7 +129,7 @@
|
||||
this.pmMiniMap.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.pmMiniMap.Location = new System.Drawing.Point(0, 0);
|
||||
this.pmMiniMap.Name = "pmMiniMap";
|
||||
this.pmMiniMap.Size = new System.Drawing.Size(198, 182);
|
||||
this.pmMiniMap.Size = new System.Drawing.Size(198, 164);
|
||||
this.pmMiniMap.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
this.pmMiniMap.TabIndex = 1;
|
||||
this.pmMiniMap.TabStop = false;
|
||||
@@ -142,7 +146,7 @@
|
||||
this.tabControl1.Name = "tabControl1";
|
||||
this.tabControl1.Padding = new System.Drawing.Point(6, 0);
|
||||
this.tabControl1.SelectedIndex = 0;
|
||||
this.tabControl1.Size = new System.Drawing.Size(198, 558);
|
||||
this.tabControl1.Size = new System.Drawing.Size(198, 576);
|
||||
this.tabControl1.TabIndex = 0;
|
||||
//
|
||||
// tabPage1
|
||||
@@ -151,7 +155,7 @@
|
||||
this.tabPage1.Location = new System.Drawing.Point(4, 20);
|
||||
this.tabPage1.Name = "tabPage1";
|
||||
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
|
||||
this.tabPage1.Size = new System.Drawing.Size(190, 534);
|
||||
this.tabPage1.Size = new System.Drawing.Size(190, 552);
|
||||
this.tabPage1.TabIndex = 0;
|
||||
this.tabPage1.Text = "Templates";
|
||||
this.tabPage1.UseVisualStyleBackColor = true;
|
||||
@@ -163,7 +167,7 @@
|
||||
this.tilePalette.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tilePalette.Location = new System.Drawing.Point(3, 3);
|
||||
this.tilePalette.Name = "tilePalette";
|
||||
this.tilePalette.Size = new System.Drawing.Size(184, 528);
|
||||
this.tilePalette.Size = new System.Drawing.Size(184, 546);
|
||||
this.tilePalette.TabIndex = 1;
|
||||
//
|
||||
// tabPage2
|
||||
@@ -172,7 +176,7 @@
|
||||
this.tabPage2.Location = new System.Drawing.Point(4, 20);
|
||||
this.tabPage2.Name = "tabPage2";
|
||||
this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
|
||||
this.tabPage2.Size = new System.Drawing.Size(190, 534);
|
||||
this.tabPage2.Size = new System.Drawing.Size(190, 552);
|
||||
this.tabPage2.TabIndex = 1;
|
||||
this.tabPage2.Text = "Actors";
|
||||
this.tabPage2.UseVisualStyleBackColor = true;
|
||||
@@ -184,7 +188,7 @@
|
||||
this.actorPalette.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.actorPalette.Location = new System.Drawing.Point(3, 3);
|
||||
this.actorPalette.Name = "actorPalette";
|
||||
this.actorPalette.Size = new System.Drawing.Size(184, 528);
|
||||
this.actorPalette.Size = new System.Drawing.Size(184, 546);
|
||||
this.actorPalette.TabIndex = 2;
|
||||
//
|
||||
// tabPage3
|
||||
@@ -192,7 +196,7 @@
|
||||
this.tabPage3.Controls.Add(this.resourcePalette);
|
||||
this.tabPage3.Location = new System.Drawing.Point(4, 20);
|
||||
this.tabPage3.Name = "tabPage3";
|
||||
this.tabPage3.Size = new System.Drawing.Size(190, 534);
|
||||
this.tabPage3.Size = new System.Drawing.Size(190, 552);
|
||||
this.tabPage3.TabIndex = 2;
|
||||
this.tabPage3.Text = "Resources";
|
||||
this.tabPage3.UseVisualStyleBackColor = true;
|
||||
@@ -204,7 +208,7 @@
|
||||
this.resourcePalette.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.resourcePalette.Location = new System.Drawing.Point(0, 0);
|
||||
this.resourcePalette.Name = "resourcePalette";
|
||||
this.resourcePalette.Size = new System.Drawing.Size(190, 534);
|
||||
this.resourcePalette.Size = new System.Drawing.Size(190, 552);
|
||||
this.resourcePalette.TabIndex = 3;
|
||||
//
|
||||
// menuStrip1
|
||||
@@ -417,6 +421,30 @@
|
||||
this.splitContainer3.SplitterDistance = 25;
|
||||
this.splitContainer3.TabIndex = 6;
|
||||
//
|
||||
// statusStrip1
|
||||
//
|
||||
this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.toolStripStatusLabelFiller,
|
||||
this.toolStripStatusLabelMousePosition});
|
||||
this.statusStrip1.Location = new System.Drawing.Point(0, 751);
|
||||
this.statusStrip1.Name = "statusStrip1";
|
||||
this.statusStrip1.Size = new System.Drawing.Size(985, 22);
|
||||
this.statusStrip1.TabIndex = 7;
|
||||
this.statusStrip1.Text = "statusStrip1";
|
||||
//
|
||||
// toolStripStatusLabelFiller
|
||||
//
|
||||
this.toolStripStatusLabelFiller.Name = "toolStripStatusLabelFiller";
|
||||
this.toolStripStatusLabelFiller.Size = new System.Drawing.Size(948, 17);
|
||||
this.toolStripStatusLabelFiller.Spring = true;
|
||||
//
|
||||
// toolStripStatusLabelMousePosition
|
||||
//
|
||||
this.toolStripStatusLabelMousePosition.Name = "toolStripStatusLabelMousePosition";
|
||||
this.toolStripStatusLabelMousePosition.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
|
||||
this.toolStripStatusLabelMousePosition.Size = new System.Drawing.Size(22, 17);
|
||||
this.toolStripStatusLabelMousePosition.Text = "0,0";
|
||||
//
|
||||
// surface1
|
||||
//
|
||||
this.surface1.BackColor = System.Drawing.Color.Black;
|
||||
@@ -432,6 +460,7 @@
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(985, 773);
|
||||
this.Controls.Add(this.statusStrip1);
|
||||
this.Controls.Add(this.splitContainer3);
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.KeyPreview = true;
|
||||
@@ -459,7 +488,10 @@
|
||||
this.splitContainer3.Panel1.PerformLayout();
|
||||
this.splitContainer3.Panel2.ResumeLayout(false);
|
||||
this.splitContainer3.ResumeLayout(false);
|
||||
this.statusStrip1.ResumeLayout(false);
|
||||
this.statusStrip1.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
@@ -501,6 +533,9 @@
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuMinimapToPNG;
|
||||
private System.Windows.Forms.SaveFileDialog saveFileDialog;
|
||||
private System.Windows.Forms.SplitContainer splitContainer3;
|
||||
private System.Windows.Forms.StatusStrip statusStrip1;
|
||||
private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabelMousePosition;
|
||||
private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabelFiller;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
64
OpenRA.Editor/Form1.cs
Normal file → Executable file
64
OpenRA.Editor/Form1.cs
Normal file → Executable file
@@ -36,6 +36,7 @@ namespace OpenRA.Editor
|
||||
Rules.LoadRules(Game.modData.Manifest, new Map());
|
||||
|
||||
surface1.AfterChange += OnMapChanged;
|
||||
surface1.MousePositionChanged += s => toolStripStatusLabelMousePosition.Text = s;
|
||||
}
|
||||
|
||||
void OnMapChanged()
|
||||
@@ -59,9 +60,9 @@ namespace OpenRA.Editor
|
||||
loadedMapName = mapname;
|
||||
|
||||
Game.modData = new ModData(currentMod);
|
||||
|
||||
|
||||
// load the map
|
||||
var map = new Map(new Folder(mapname));
|
||||
var map = new Map(mapname);
|
||||
|
||||
// upgrade maps that have no player definitions. editor doesnt care,
|
||||
// but this breaks the game pretty badly.
|
||||
@@ -93,7 +94,7 @@ namespace OpenRA.Editor
|
||||
void PrepareMapResources(Manifest manifest, Map map)
|
||||
{
|
||||
Rules.LoadRules(manifest, map);
|
||||
tileset = Rules.TileSets[map.Theater];
|
||||
tileset = Rules.TileSets[map.Tileset];
|
||||
tileset.LoadTiles();
|
||||
var palette = new Palette(FileSystem.Open(tileset.Palette), true);
|
||||
|
||||
@@ -203,6 +204,7 @@ namespace OpenRA.Editor
|
||||
p.Visible = true;
|
||||
p.ResumeLayout();
|
||||
}
|
||||
|
||||
pmMiniMap.Image = Minimap.AddStaticResources(surface1.Map, Minimap.TerrainBitmap(surface1.Map, true));
|
||||
}
|
||||
|
||||
@@ -212,16 +214,18 @@ namespace OpenRA.Editor
|
||||
{
|
||||
rd.width.Value = surface1.Map.MapSize.X;
|
||||
rd.height.Value = surface1.Map.MapSize.Y;
|
||||
rd.cordonLeft.Value = surface1.Map.TopLeft.X;
|
||||
rd.cordonTop.Value = surface1.Map.TopLeft.Y;
|
||||
rd.cordonRight.Value = surface1.Map.BottomRight.X;
|
||||
rd.cordonBottom.Value = surface1.Map.BottomRight.Y;
|
||||
rd.cordonLeft.Value = surface1.Map.Bounds.Left;
|
||||
rd.cordonTop.Value = surface1.Map.Bounds.Top;
|
||||
rd.cordonRight.Value = surface1.Map.Bounds.Right;
|
||||
rd.cordonBottom.Value = surface1.Map.Bounds.Bottom;
|
||||
|
||||
if (DialogResult.OK != rd.ShowDialog())
|
||||
return;
|
||||
|
||||
surface1.Map.TopLeft = new int2((int)rd.cordonLeft.Value, (int)rd.cordonTop.Value);
|
||||
surface1.Map.BottomRight = new int2((int)rd.cordonRight.Value, (int)rd.cordonBottom.Value);
|
||||
surface1.Map.ResizeCordon((int)rd.cordonLeft.Value,
|
||||
(int)rd.cordonTop.Value,
|
||||
(int)rd.cordonRight.Value,
|
||||
(int)rd.cordonBottom.Value);
|
||||
|
||||
if ((int)rd.width.Value != surface1.Map.MapSize.X || (int)rd.height.Value != surface1.Map.MapSize.Y)
|
||||
{
|
||||
@@ -239,8 +243,7 @@ namespace OpenRA.Editor
|
||||
SaveAsClicked(sender, e);
|
||||
else
|
||||
{
|
||||
surface1.Map.PlayerCount = surface1.Map.Waypoints.Count;
|
||||
surface1.Map.Package = new Folder(loadedMapName);
|
||||
surface1.Map.PlayerCount = surface1.Map.Waypoints.Count;
|
||||
surface1.Map.Save(loadedMapName);
|
||||
dirty = false;
|
||||
}
|
||||
@@ -249,11 +252,8 @@ namespace OpenRA.Editor
|
||||
|
||||
void SaveAsClicked(object sender, EventArgs e)
|
||||
{
|
||||
using (var nms = new MapSelect())
|
||||
using (var nms = new MapSelect(currentMod))
|
||||
{
|
||||
nms.MapFolderPath = new string[] { Environment.CurrentDirectory, "mods", currentMod, "maps" }
|
||||
.Aggregate(Path.Combine);
|
||||
|
||||
nms.txtNew.ReadOnly = false;
|
||||
nms.btnOk.Text = "Save";
|
||||
nms.txtNew.Text = "unnamed";
|
||||
@@ -264,18 +264,8 @@ namespace OpenRA.Editor
|
||||
if (nms.txtNew.Text == "")
|
||||
nms.txtNew.Text = "unnamed";
|
||||
|
||||
string mapfoldername = Path.Combine(nms.MapFolderPath, nms.txtNew.Text);
|
||||
loadedMapName = mapfoldername;
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(mapfoldername);
|
||||
}
|
||||
catch (Exception ed)
|
||||
{
|
||||
MessageBox.Show("Directory creation failed: {0}", ed.ToString());
|
||||
}
|
||||
|
||||
// TODO: Allow the user to choose map format (directory vs oramap)
|
||||
loadedMapName = Path.Combine(nms.MapFolderPath, nms.txtNew.Text + ".oramap");
|
||||
SaveClicked(sender, e);
|
||||
}
|
||||
}
|
||||
@@ -283,20 +273,14 @@ namespace OpenRA.Editor
|
||||
|
||||
void OpenClicked(object sender, EventArgs e)
|
||||
{
|
||||
using (var nms = new MapSelect())
|
||||
using (var nms = new MapSelect(currentMod))
|
||||
{
|
||||
nms.MapFolderPath = new string[] { Environment.CurrentDirectory, "mods", currentMod, "maps" }
|
||||
.Aggregate(Path.Combine);
|
||||
|
||||
nms.txtNew.ReadOnly = true;
|
||||
nms.txtPathOut.ReadOnly = true;
|
||||
nms.btnOk.Text = "Open";
|
||||
|
||||
if (DialogResult.OK == nms.ShowDialog())
|
||||
{
|
||||
string mapfoldername = Path.Combine(nms.MapFolderPath, nms.txtNew.Text);
|
||||
LoadMap(mapfoldername);
|
||||
}
|
||||
LoadMap(nms.txtNew.Tag as string);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,12 +294,11 @@ namespace OpenRA.Editor
|
||||
|
||||
if (DialogResult.OK == nmd.ShowDialog())
|
||||
{
|
||||
var map = new Map(nmd.theater.SelectedItem as string);
|
||||
var map = Map.FromTileset(nmd.theater.SelectedItem as string);
|
||||
|
||||
map.Resize((int)nmd.width.Value, (int)nmd.height.Value);
|
||||
|
||||
map.TopLeft = new int2((int)nmd.cordonLeft.Value, (int)nmd.cordonTop.Value);
|
||||
map.BottomRight = new int2((int)nmd.cordonRight.Value, (int)nmd.cordonBottom.Value);
|
||||
map.ResizeCordon((int)nmd.cordonLeft.Value, (int)nmd.cordonTop.Value,
|
||||
(int)nmd.cordonRight.Value, (int)nmd.cordonBottom.Value);
|
||||
map.Players.Add("Neutral", new PlayerReference("Neutral", Rules.Info["world"].Traits.WithInterface<CountryInfo>().First().Race, true, true));
|
||||
NewMap(map);
|
||||
}
|
||||
@@ -330,6 +313,7 @@ namespace OpenRA.Editor
|
||||
pd.desc.Text = surface1.Map.Description;
|
||||
pd.author.Text = surface1.Map.Author;
|
||||
pd.selectable.Checked = surface1.Map.Selectable;
|
||||
pd.useAsShellmap.Checked = surface1.Map.UseAsShellmap;
|
||||
|
||||
if (DialogResult.OK != pd.ShowDialog())
|
||||
return;
|
||||
@@ -338,6 +322,7 @@ namespace OpenRA.Editor
|
||||
surface1.Map.Description = pd.desc.Text;
|
||||
surface1.Map.Author = pd.author.Text;
|
||||
surface1.Map.Selectable = pd.selectable.Checked;
|
||||
surface1.Map.UseAsShellmap = pd.useAsShellmap.Checked;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,7 +349,6 @@ namespace OpenRA.Editor
|
||||
Directory.CreateDirectory(savePath);
|
||||
|
||||
var map = LegacyMapImporter.Import(ofd.FileName);
|
||||
map.Package = new Folder(savePath);
|
||||
map.Players.Add("Neutral", new PlayerReference("Neutral",
|
||||
Rules.Info["world"].Traits.WithInterface<CountryInfo>().First().Race, true, true));
|
||||
|
||||
|
||||
3
OpenRA.Editor/Form1.resx
Normal file → Executable file
3
OpenRA.Editor/Form1.resx
Normal file → Executable file
@@ -311,6 +311,9 @@
|
||||
<metadata name="saveFileDialog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>76, 17</value>
|
||||
</metadata>
|
||||
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>313, 17</value>
|
||||
</metadata>
|
||||
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>45</value>
|
||||
</metadata>
|
||||
|
||||
2
OpenRA.Editor/MapSelect.Designer.cs
generated
2
OpenRA.Editor/MapSelect.Designer.cs
generated
@@ -256,7 +256,7 @@
|
||||
this.lblTheater.Name = "lblTheater";
|
||||
this.lblTheater.Size = new System.Drawing.Size(47, 13);
|
||||
this.lblTheater.TabIndex = 11;
|
||||
this.lblTheater.Text = "Theater:";
|
||||
this.lblTheater.Text = "Tileset:";
|
||||
//
|
||||
// txtAuthor
|
||||
//
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Graphics;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.Editor
|
||||
{
|
||||
@@ -10,21 +11,25 @@ namespace OpenRA.Editor
|
||||
{
|
||||
public string MapFolderPath;
|
||||
|
||||
public MapSelect()
|
||||
public MapSelect(string currentMod)
|
||||
{
|
||||
InitializeComponent();
|
||||
MapFolderPath = new string[] { Environment.CurrentDirectory, "mods", currentMod, "maps" }
|
||||
.Aggregate(Path.Combine);
|
||||
|
||||
InitializeComponent();
|
||||
MapIconsList.Images.Add(pictureBox1.Image);
|
||||
}
|
||||
|
||||
void MapSelect_Load(object sender, EventArgs e)
|
||||
{
|
||||
DirectoryInfo directory = new DirectoryInfo(MapFolderPath);
|
||||
DirectoryInfo[] directories = directory.GetDirectories();
|
||||
MapList.Items.Clear();
|
||||
txtPathOut.Text = MapFolderPath;
|
||||
foreach (DirectoryInfo subDirectory in directories)
|
||||
|
||||
foreach (var map in ModData.FindMapsIn(MapFolderPath))
|
||||
{
|
||||
ListViewItem map1 = new ListViewItem(subDirectory.Name);
|
||||
ListViewItem map1 = new ListViewItem();
|
||||
map1.Tag = map;
|
||||
map1.Text = Path.GetFileNameWithoutExtension(map);
|
||||
map1.ImageIndex = 0;
|
||||
MapList.Items.Add(map1);
|
||||
}
|
||||
@@ -39,12 +44,15 @@ namespace OpenRA.Editor
|
||||
if (MapList.SelectedItems.Count == 1)
|
||||
{
|
||||
txtNew.Text = MapList.SelectedItems[0].Text;
|
||||
var map = new Map(new Folder(Path.Combine(MapFolderPath, MapList.SelectedItems[0].Text)));
|
||||
txtNew.Tag = MapList.SelectedItems[0].Tag;
|
||||
|
||||
var map = new Map(txtNew.Tag as string);
|
||||
txtTitle.Text = map.Title;
|
||||
txtAuthor.Text = map.Author;
|
||||
txtTheater.Text = map.Theater;
|
||||
txtTheater.Text = map.Tileset;
|
||||
txtDesc.Text = map.Description;
|
||||
pbMinimap.Image = null;
|
||||
|
||||
try
|
||||
{
|
||||
pbMinimap.Image = Minimap.AddStaticResources(map, Minimap.TerrainBitmap(map, true));
|
||||
|
||||
2
OpenRA.Editor/NewMapDialog.Designer.cs
generated
2
OpenRA.Editor/NewMapDialog.Designer.cs
generated
@@ -221,7 +221,7 @@
|
||||
this.label4.Name = "label4";
|
||||
this.label4.Size = new System.Drawing.Size(44, 13);
|
||||
this.label4.TabIndex = 14;
|
||||
this.label4.Text = "Theater";
|
||||
this.label4.Text = "Tileset";
|
||||
//
|
||||
// theater
|
||||
//
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
@@ -13,8 +13,6 @@
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ApplicationIcon>OpenRA.Editor.Icon.ico</ApplicationIcon>
|
||||
<StartupObject>
|
||||
</StartupObject>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Forms;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
|
||||
namespace OpenRA.Editor
|
||||
{
|
||||
@@ -19,11 +21,32 @@ namespace OpenRA.Editor
|
||||
[STAThread]
|
||||
static void Main( string[] args )
|
||||
{
|
||||
if (args.Length >= 2 && args[0] == "--convert")
|
||||
{
|
||||
Game.modData = new ModData(args[1]);
|
||||
Rules.LoadRules(Game.modData.Manifest, new Map());
|
||||
UpgradeMaps(args[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
Application.CurrentCulture = CultureInfo.InvariantCulture;
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
|
||||
Application.Run(new Form1(args));
|
||||
}
|
||||
|
||||
static void UpgradeMaps(string mod)
|
||||
{
|
||||
var MapFolderPath = new string[] { Environment.CurrentDirectory, "mods", mod, "maps" }
|
||||
.Aggregate(Path.Combine);
|
||||
|
||||
foreach (var path in ModData.FindMapsIn(MapFolderPath))
|
||||
{
|
||||
var map = new Map(path);
|
||||
map.Save(path+".oramap");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
239
OpenRA.Editor/PropertiesDialog.Designer.cs
generated
239
OpenRA.Editor/PropertiesDialog.Designer.cs
generated
@@ -28,119 +28,131 @@
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.button2 = new System.Windows.Forms.Button();
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.title = new System.Windows.Forms.TextBox();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.desc = new System.Windows.Forms.TextBox();
|
||||
this.selectable = new System.Windows.Forms.CheckBox();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.author = new System.Windows.Forms.TextBox();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// button2
|
||||
//
|
||||
this.button2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.button2.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.button2.Location = new System.Drawing.Point(196, 193);
|
||||
this.button2.Name = "button2";
|
||||
this.button2.Size = new System.Drawing.Size(75, 23);
|
||||
this.button2.TabIndex = 14;
|
||||
this.button2.Text = "OK";
|
||||
this.button2.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// button1
|
||||
//
|
||||
this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.button1.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.button1.Location = new System.Drawing.Point(277, 193);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(75, 23);
|
||||
this.button1.TabIndex = 15;
|
||||
this.button1.Text = "Cancel";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(12, 50);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(27, 13);
|
||||
this.label1.TabIndex = 16;
|
||||
this.label1.Text = "Title";
|
||||
//
|
||||
// title
|
||||
//
|
||||
this.title.Location = new System.Drawing.Point(66, 47);
|
||||
this.title.Name = "title";
|
||||
this.title.Size = new System.Drawing.Size(286, 20);
|
||||
this.title.TabIndex = 17;
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Location = new System.Drawing.Point(12, 76);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(32, 13);
|
||||
this.label2.TabIndex = 16;
|
||||
this.label2.Text = "Desc";
|
||||
//
|
||||
// desc
|
||||
//
|
||||
this.desc.Location = new System.Drawing.Point(66, 73);
|
||||
this.desc.Name = "desc";
|
||||
this.desc.Size = new System.Drawing.Size(286, 20);
|
||||
this.desc.TabIndex = 17;
|
||||
//
|
||||
// selectable
|
||||
//
|
||||
this.selectable.AutoSize = true;
|
||||
this.selectable.Location = new System.Drawing.Point(118, 138);
|
||||
this.selectable.Name = "selectable";
|
||||
this.selectable.Size = new System.Drawing.Size(130, 17);
|
||||
this.selectable.TabIndex = 18;
|
||||
this.selectable.Text = "Show in Map Chooser";
|
||||
this.selectable.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.Location = new System.Drawing.Point(12, 102);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(38, 13);
|
||||
this.label3.TabIndex = 16;
|
||||
this.label3.Text = "Author";
|
||||
//
|
||||
// author
|
||||
//
|
||||
this.author.Location = new System.Drawing.Point(66, 99);
|
||||
this.author.Name = "author";
|
||||
this.author.Size = new System.Drawing.Size(286, 20);
|
||||
this.author.TabIndex = 17;
|
||||
//
|
||||
// PropertiesDialog
|
||||
//
|
||||
this.AcceptButton = this.button2;
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.CancelButton = this.button1;
|
||||
this.ClientSize = new System.Drawing.Size(370, 228);
|
||||
this.Controls.Add(this.selectable);
|
||||
this.Controls.Add(this.author);
|
||||
this.Controls.Add(this.label3);
|
||||
this.Controls.Add(this.desc);
|
||||
this.Controls.Add(this.label2);
|
||||
this.Controls.Add(this.title);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.button2);
|
||||
this.Controls.Add(this.button1);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||
this.Name = "PropertiesDialog";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "PropertiesDialog";
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
this.button2 = new System.Windows.Forms.Button();
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.title = new System.Windows.Forms.TextBox();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.desc = new System.Windows.Forms.TextBox();
|
||||
this.selectable = new System.Windows.Forms.CheckBox();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.author = new System.Windows.Forms.TextBox();
|
||||
this.useAsShellmap = new System.Windows.Forms.CheckBox();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// button2
|
||||
//
|
||||
this.button2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.button2.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.button2.Location = new System.Drawing.Point(196, 193);
|
||||
this.button2.Name = "button2";
|
||||
this.button2.Size = new System.Drawing.Size(75, 23);
|
||||
this.button2.TabIndex = 14;
|
||||
this.button2.Text = "OK";
|
||||
this.button2.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// button1
|
||||
//
|
||||
this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.button1.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.button1.Location = new System.Drawing.Point(277, 193);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(75, 23);
|
||||
this.button1.TabIndex = 15;
|
||||
this.button1.Text = "Cancel";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(12, 50);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(27, 13);
|
||||
this.label1.TabIndex = 16;
|
||||
this.label1.Text = "Title";
|
||||
//
|
||||
// title
|
||||
//
|
||||
this.title.Location = new System.Drawing.Point(66, 47);
|
||||
this.title.Name = "title";
|
||||
this.title.Size = new System.Drawing.Size(286, 20);
|
||||
this.title.TabIndex = 17;
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Location = new System.Drawing.Point(12, 76);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(32, 13);
|
||||
this.label2.TabIndex = 16;
|
||||
this.label2.Text = "Desc";
|
||||
//
|
||||
// desc
|
||||
//
|
||||
this.desc.Location = new System.Drawing.Point(66, 73);
|
||||
this.desc.Name = "desc";
|
||||
this.desc.Size = new System.Drawing.Size(286, 20);
|
||||
this.desc.TabIndex = 17;
|
||||
//
|
||||
// selectable
|
||||
//
|
||||
this.selectable.AutoSize = true;
|
||||
this.selectable.Location = new System.Drawing.Point(118, 138);
|
||||
this.selectable.Name = "selectable";
|
||||
this.selectable.Size = new System.Drawing.Size(130, 17);
|
||||
this.selectable.TabIndex = 18;
|
||||
this.selectable.Text = "Show in Map Chooser";
|
||||
this.selectable.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.Location = new System.Drawing.Point(12, 102);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(38, 13);
|
||||
this.label3.TabIndex = 16;
|
||||
this.label3.Text = "Author";
|
||||
//
|
||||
// author
|
||||
//
|
||||
this.author.Location = new System.Drawing.Point(66, 99);
|
||||
this.author.Name = "author";
|
||||
this.author.Size = new System.Drawing.Size(286, 20);
|
||||
this.author.TabIndex = 17;
|
||||
//
|
||||
// checkBox1
|
||||
//
|
||||
this.useAsShellmap.AutoSize = true;
|
||||
this.useAsShellmap.Location = new System.Drawing.Point(118, 161);
|
||||
this.useAsShellmap.Name = "checkBox1";
|
||||
this.useAsShellmap.Size = new System.Drawing.Size(105, 17);
|
||||
this.useAsShellmap.TabIndex = 18;
|
||||
this.useAsShellmap.Text = "Use as Shellmap";
|
||||
this.useAsShellmap.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// PropertiesDialog
|
||||
//
|
||||
this.AcceptButton = this.button2;
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.CancelButton = this.button1;
|
||||
this.ClientSize = new System.Drawing.Size(370, 228);
|
||||
this.Controls.Add(this.useAsShellmap);
|
||||
this.Controls.Add(this.selectable);
|
||||
this.Controls.Add(this.author);
|
||||
this.Controls.Add(this.label3);
|
||||
this.Controls.Add(this.desc);
|
||||
this.Controls.Add(this.label2);
|
||||
this.Controls.Add(this.title);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.button2);
|
||||
this.Controls.Add(this.button1);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||
this.Name = "PropertiesDialog";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "PropertiesDialog";
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
@@ -155,5 +167,6 @@
|
||||
public System.Windows.Forms.CheckBox selectable;
|
||||
private System.Windows.Forms.Label label3;
|
||||
public System.Windows.Forms.TextBox author;
|
||||
public System.Windows.Forms.CheckBox useAsShellmap;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using OpenRA.FileFormats;
|
||||
@@ -17,19 +18,34 @@ namespace OpenRA.Editor
|
||||
{
|
||||
static class RenderUtils
|
||||
{
|
||||
public static ColorPalette MakeSystemPalette(Palette p)
|
||||
{
|
||||
ColorPalette pal;
|
||||
using (var b = new Bitmap(1, 1, PixelFormat.Format8bppIndexed))
|
||||
pal = b.Palette;
|
||||
|
||||
for (var i = 0; i < 256; i++)
|
||||
pal.Entries[i] = p.GetColor(i);
|
||||
return pal;
|
||||
}
|
||||
|
||||
public static Bitmap RenderTemplate(TileSet ts, ushort n, Palette p)
|
||||
{
|
||||
var template = ts.Templates[n];
|
||||
var tile = ts.Tiles[n];
|
||||
|
||||
var bitmap = new Bitmap(ts.TileSize * template.Size.X, ts.TileSize * template.Size.Y);
|
||||
var bitmap = new Bitmap(ts.TileSize * template.Size.X, ts.TileSize * template.Size.Y,
|
||||
PixelFormat.Format8bppIndexed);
|
||||
|
||||
bitmap.Palette = MakeSystemPalette(p);
|
||||
|
||||
var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
|
||||
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
|
||||
|
||||
unsafe
|
||||
{
|
||||
int* q = (int*)data.Scan0.ToPointer();
|
||||
var stride = data.Stride >> 2;
|
||||
byte* q = (byte*)data.Scan0.ToPointer();
|
||||
var stride = data.Stride;
|
||||
|
||||
for (var u = 0; u < template.Size.X; u++)
|
||||
for (var v = 0; v < template.Size.Y; v++)
|
||||
@@ -38,13 +54,13 @@ namespace OpenRA.Editor
|
||||
var rawImage = tile.TileBitmapBytes[u + v * template.Size.X];
|
||||
for (var i = 0; i < ts.TileSize; i++)
|
||||
for (var j = 0; j < ts.TileSize; j++)
|
||||
q[(v * ts.TileSize + j) * stride + u * ts.TileSize + i] = p.GetColor(rawImage[i + ts.TileSize * j]).ToArgb();
|
||||
q[(v * ts.TileSize + j) * stride + u * ts.TileSize + i] = rawImage[i + ts.TileSize * j];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < ts.TileSize; i++)
|
||||
for (var j = 0; j < ts.TileSize; j++)
|
||||
q[(v * ts.TileSize + j) * stride + u * ts.TileSize + i] = Color.Transparent.ToArgb();
|
||||
q[(v * ts.TileSize + j) * stride + u * ts.TileSize + i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,18 +72,21 @@ namespace OpenRA.Editor
|
||||
{
|
||||
var frame = shp[0];
|
||||
|
||||
var bitmap = new Bitmap(shp.Width, shp.Height);
|
||||
var bitmap = new Bitmap(shp.Width, shp.Height, PixelFormat.Format8bppIndexed);
|
||||
|
||||
bitmap.Palette = MakeSystemPalette(p);
|
||||
|
||||
var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
|
||||
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
|
||||
|
||||
unsafe
|
||||
{
|
||||
int* q = (int*)data.Scan0.ToPointer();
|
||||
var stride2 = data.Stride >> 2;
|
||||
byte* q = (byte*)data.Scan0.ToPointer();
|
||||
var stride2 = data.Stride;
|
||||
|
||||
for (var i = 0; i < shp.Width; i++)
|
||||
for (var j = 0; j < shp.Height; j++)
|
||||
q[j * stride2 + i] = p.GetColor(frame.Image[i + shp.Width * j]).ToArgb();
|
||||
q[j * stride2 + i] = frame.Image[i + shp.Width * j];
|
||||
}
|
||||
|
||||
bitmap.UnlockBits(data);
|
||||
@@ -78,9 +97,9 @@ namespace OpenRA.Editor
|
||||
{
|
||||
var ri = info.Traits.Get<RenderSimpleInfo>();
|
||||
string image = null;
|
||||
if (ri.OverrideTheater != null)
|
||||
for (int i = 0; i < ri.OverrideTheater.Length; i++)
|
||||
if (ri.OverrideTheater[i] == tileset.Id)
|
||||
if (ri.OverrideTileset != null)
|
||||
for (int i = 0; i < ri.OverrideTileset.Length; i++)
|
||||
if (ri.OverrideTileset[i] == tileset.Id)
|
||||
image = ri.OverrideImage[i];
|
||||
|
||||
image = image ?? ri.Image ?? info.Name;
|
||||
@@ -102,7 +121,12 @@ namespace OpenRA.Editor
|
||||
}
|
||||
catch { }
|
||||
|
||||
return new ActorTemplate { Bitmap = bitmap, Info = info, Centered = !info.Traits.Contains<BuildingInfo>() };
|
||||
return new ActorTemplate
|
||||
{
|
||||
Bitmap = bitmap,
|
||||
Info = info,
|
||||
Appearance = info.Traits.GetOrDefault<EditorAppearanceInfo>()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,18 +138,19 @@ namespace OpenRA.Editor
|
||||
var shp = new ShpReader(s);
|
||||
var frame = shp[shp.ImageCount - 1];
|
||||
|
||||
var bitmap = new Bitmap(shp.Width, shp.Height);
|
||||
var bitmap = new Bitmap(shp.Width, shp.Height, PixelFormat.Format8bppIndexed);
|
||||
bitmap.Palette = MakeSystemPalette(p);
|
||||
var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
|
||||
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
|
||||
|
||||
unsafe
|
||||
{
|
||||
int* q = (int*)data.Scan0.ToPointer();
|
||||
var stride = data.Stride >> 2;
|
||||
byte* q = (byte*)data.Scan0.ToPointer();
|
||||
var stride = data.Stride;
|
||||
|
||||
for (var i = 0; i < shp.Width; i++)
|
||||
for (var j = 0; j < shp.Height; j++)
|
||||
q[j * stride + i] = p.GetColor(frame.Image[i + shp.Width * j]).ToArgb();
|
||||
q[j * stride + i] = frame.Image[i + shp.Width * j];
|
||||
}
|
||||
|
||||
bitmap.UnlockBits(data);
|
||||
|
||||
@@ -15,6 +15,7 @@ using System.Drawing.Imaging;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Editor
|
||||
{
|
||||
@@ -34,6 +35,7 @@ namespace OpenRA.Editor
|
||||
|
||||
public bool IsPanning;
|
||||
public event Action AfterChange = () => { };
|
||||
public event Action<string> MousePositionChanged = _ => { };
|
||||
|
||||
Dictionary<string, ActorTemplate> ActorTemplates = new Dictionary<string, ActorTemplate>();
|
||||
Dictionary<int, ResourceTemplate> ResourceTemplates = new Dictionary<int, ResourceTemplate>();
|
||||
@@ -44,6 +46,7 @@ namespace OpenRA.Editor
|
||||
TileSet = ts;
|
||||
Palette = p;
|
||||
Brush = null;
|
||||
PlayerPalettes = null;
|
||||
Chunks.Clear();
|
||||
}
|
||||
|
||||
@@ -120,6 +123,7 @@ namespace OpenRA.Editor
|
||||
|
||||
var oldMousePos = MousePos;
|
||||
MousePos = new int2(e.Location);
|
||||
MousePositionChanged(GetBrushLocation().ToString());
|
||||
|
||||
if (e.Button == MouseButtons.Middle || (e.Button != MouseButtons.None && IsPanning))
|
||||
Scroll(oldMousePos - MousePos);
|
||||
@@ -139,6 +143,16 @@ namespace OpenRA.Editor
|
||||
{
|
||||
var queue = new Queue<int2>();
|
||||
var replace = Map.MapTiles[pos.X, pos.Y];
|
||||
var touched = new bool[Map.MapSize.X, Map.MapSize.Y];
|
||||
|
||||
Action<int, int> MaybeEnqueue = (x, y) =>
|
||||
{
|
||||
if (Map.IsInMap(x, y) && !touched[x, y])
|
||||
{
|
||||
queue.Enqueue(new int2(x, y));
|
||||
touched[x, y] = true;
|
||||
}
|
||||
};
|
||||
|
||||
queue.Enqueue(pos);
|
||||
while (queue.Count > 0)
|
||||
@@ -153,10 +167,10 @@ namespace OpenRA.Editor
|
||||
for (var x = a.X; x <= b.X; x++)
|
||||
{
|
||||
Map.MapTiles[x, p.Y] = new TileReference<ushort, byte> { type = Brush.N, image = (byte)0, index = (byte)0 };
|
||||
if (Map.MapTiles[x, p.Y - 1].Equals(replace) && Map.IsInMap(x, p.Y - 1))
|
||||
queue.Enqueue(new int2(x, p.Y - 1));
|
||||
if (Map.MapTiles[x, p.Y + 1].Equals(replace) && Map.IsInMap(x, p.Y + 1))
|
||||
queue.Enqueue(new int2(x, p.Y + 1));
|
||||
if (Map.MapTiles[x, p.Y - 1].Equals(replace))
|
||||
MaybeEnqueue(x, p.Y - 1);
|
||||
if (Map.MapTiles[x, p.Y + 1].Equals(replace))
|
||||
MaybeEnqueue(x, p.Y + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +185,7 @@ namespace OpenRA.Editor
|
||||
{
|
||||
for (; ; )
|
||||
{
|
||||
var q = p+d;
|
||||
var q = p + d;
|
||||
if (!Map.IsInMap(q)) return p;
|
||||
if (!Map.MapTiles[q.X, q.Y].Equals(replace)) return p;
|
||||
p = q;
|
||||
@@ -203,7 +217,7 @@ namespace OpenRA.Editor
|
||||
{
|
||||
type = Brush.N,
|
||||
index = template.PickAny ? byte.MaxValue : (byte)z,
|
||||
image = template.PickAny ? (byte)((u + pos.X) % 4 + ((v + pos.Y) % 4)*4) : (byte)z,
|
||||
image = template.PickAny ? (byte)((u + pos.X) % 4 + ((v + pos.Y) % 4) * 4) : (byte)z,
|
||||
};
|
||||
|
||||
var ch = new int2((pos.X + u) / ChunkSize, (pos.Y + v) / ChunkSize);
|
||||
@@ -407,19 +421,25 @@ namespace OpenRA.Editor
|
||||
return new int2(vX / TileSet.TileSize, vY / TileSet.TileSize);
|
||||
}
|
||||
|
||||
void DrawActor(System.Drawing.Graphics g, int2 p, ActorTemplate t)
|
||||
void DrawActor(System.Drawing.Graphics g, int2 p, ActorTemplate t, ColorPalette cp)
|
||||
{
|
||||
float OffsetX = t.Centered ? t.Bitmap.Width / 2 - TileSet.TileSize/2 : 0;
|
||||
var centered = t.Appearance == null || !t.Appearance.RelativeToTopLeft;
|
||||
|
||||
float OffsetX = centered ? t.Bitmap.Width / 2 - TileSet.TileSize / 2 : 0;
|
||||
float DrawX = TileSet.TileSize * p.X * Zoom + Offset.X - OffsetX;
|
||||
|
||||
float OffsetY = t.Centered ? t.Bitmap.Height / 2 - TileSet.TileSize/2 : 0;
|
||||
float OffsetY = centered ? t.Bitmap.Height / 2 - TileSet.TileSize / 2 : 0;
|
||||
float DrawY = TileSet.TileSize * p.Y * Zoom + Offset.Y - OffsetY;
|
||||
|
||||
float width = t.Bitmap.Width * Zoom;
|
||||
float height = t.Bitmap.Height * Zoom;
|
||||
RectangleF sourceRect = new RectangleF(0, 0, t.Bitmap.Width, t.Bitmap.Height);
|
||||
RectangleF destRect = new RectangleF(DrawX, DrawY, width, height);
|
||||
|
||||
var restorePalette = t.Bitmap.Palette;
|
||||
if (cp != null) t.Bitmap.Palette = cp;
|
||||
g.DrawImage(t.Bitmap, destRect, sourceRect, GraphicsUnit.Pixel);
|
||||
if (cp != null) t.Bitmap.Palette = restorePalette;
|
||||
}
|
||||
|
||||
void DrawImage(System.Drawing.Graphics g, Bitmap bmp, int2 location)
|
||||
@@ -439,10 +459,12 @@ namespace OpenRA.Editor
|
||||
|
||||
void DrawActorBorder(System.Drawing.Graphics g, int2 p, ActorTemplate t)
|
||||
{
|
||||
float OffsetX = t.Centered ? t.Bitmap.Width / 2 - TileSet.TileSize / 2 : 0;
|
||||
var centered = t.Appearance == null || !t.Appearance.RelativeToTopLeft;
|
||||
|
||||
float OffsetX = centered ? t.Bitmap.Width / 2 - TileSet.TileSize / 2 : 0;
|
||||
float DrawX = TileSet.TileSize * p.X * Zoom + Offset.X - OffsetX;
|
||||
|
||||
float OffsetY = t.Centered ? t.Bitmap.Height / 2 - TileSet.TileSize / 2 : 0;
|
||||
float OffsetY = centered ? t.Bitmap.Height / 2 - TileSet.TileSize / 2 : 0;
|
||||
float DrawY = TileSet.TileSize * p.Y * Zoom + Offset.Y - OffsetY;
|
||||
|
||||
g.DrawRectangle(CordonPen,
|
||||
@@ -450,20 +472,42 @@ namespace OpenRA.Editor
|
||||
t.Bitmap.Width * Zoom, t.Bitmap.Height * Zoom);
|
||||
}
|
||||
|
||||
ColorPalette GetPaletteForPlayer(string name)
|
||||
{
|
||||
var pr = Map.Players[name];
|
||||
var pcpi = Rules.Info["player"].Traits.Get<PlayerColorPaletteInfo>();
|
||||
var remap = new PlayerColorRemap(pr.Color, pr.Color2, pcpi.PaletteFormat);
|
||||
return RenderUtils.MakeSystemPalette(new Palette(Palette, remap));
|
||||
}
|
||||
|
||||
Cache<string, ColorPalette> PlayerPalettes;
|
||||
|
||||
ColorPalette GetPaletteForActor(ActorReference ar)
|
||||
{
|
||||
if (PlayerPalettes == null)
|
||||
PlayerPalettes = new Cache<string, ColorPalette>(GetPaletteForPlayer);
|
||||
|
||||
var ownerInit = ar.InitDict.GetOrDefault<OwnerInit>();
|
||||
if (ownerInit == null)
|
||||
return null;
|
||||
|
||||
return PlayerPalettes[ownerInit.PlayerName];
|
||||
}
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e)
|
||||
{
|
||||
if (Map == null) return;
|
||||
if (TileSet == null) return;
|
||||
|
||||
for( var u = 0; u < Map.MapSize.X; u += ChunkSize )
|
||||
for (var u = 0; u < Map.MapSize.X; u += ChunkSize)
|
||||
for (var v = 0; v < Map.MapSize.Y; v += ChunkSize)
|
||||
{
|
||||
var x = new int2(u/ChunkSize,v/ChunkSize);
|
||||
var x = new int2(u / ChunkSize, v / ChunkSize);
|
||||
if (!Chunks.ContainsKey(x)) Chunks[x] = RenderChunk(u / ChunkSize, v / ChunkSize);
|
||||
|
||||
Bitmap bmp = Chunks[x];
|
||||
|
||||
float DrawX = TileSet.TileSize* 1f * (float)ChunkSize * (float)x.X * Zoom + Offset.X;
|
||||
float DrawX = TileSet.TileSize * 1f * (float)ChunkSize * (float)x.X * Zoom + Offset.X;
|
||||
float DrawY = TileSet.TileSize * 1f * (float)ChunkSize * (float)x.Y * Zoom + Offset.Y;
|
||||
RectangleF sourceRect = new RectangleF(0, 0, bmp.Width, bmp.Height);
|
||||
RectangleF destRect = new RectangleF(DrawX, DrawY, bmp.Width * Zoom, bmp.Height * Zoom);
|
||||
@@ -471,13 +515,14 @@ namespace OpenRA.Editor
|
||||
}
|
||||
|
||||
e.Graphics.DrawRectangle(CordonPen,
|
||||
Map.XOffset * TileSet.TileSize * Zoom + Offset.X,
|
||||
Map.YOffset * TileSet.TileSize * Zoom + Offset.Y,
|
||||
Map.Width * TileSet.TileSize * Zoom,
|
||||
Map.Height * TileSet.TileSize * Zoom);
|
||||
Map.Bounds.Left * TileSet.TileSize * Zoom + Offset.X,
|
||||
Map.Bounds.Top * TileSet.TileSize * Zoom + Offset.Y,
|
||||
Map.Bounds.Width * TileSet.TileSize * Zoom,
|
||||
Map.Bounds.Height * TileSet.TileSize * Zoom);
|
||||
|
||||
foreach (var ar in Map.Actors)
|
||||
DrawActor(e.Graphics, ar.Value.Location(), ActorTemplates[ar.Value.Type]);
|
||||
DrawActor(e.Graphics, ar.Value.Location(), ActorTemplates[ar.Value.Type],
|
||||
GetPaletteForActor(ar.Value));
|
||||
|
||||
foreach (var wp in Map.Waypoints)
|
||||
e.Graphics.DrawRectangle(Pens.LimeGreen,
|
||||
@@ -493,7 +538,8 @@ namespace OpenRA.Editor
|
||||
Brush.Bitmap.Height * Zoom);
|
||||
|
||||
if (Actor != null)
|
||||
DrawActor(e.Graphics, GetBrushLocation(), Actor);
|
||||
DrawActor(e.Graphics, GetBrushLocation(), Actor, null); /* todo: include the player
|
||||
* in the brush so we can color new buildings too */
|
||||
|
||||
if (Resource != null)
|
||||
DrawImage(e.Graphics, Resource.Bitmap, GetBrushLocation());
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 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. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Packaging;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
public class CompressedPackage : IFolder
|
||||
{
|
||||
readonly uint[] hashes;
|
||||
readonly Stream s;
|
||||
readonly ZipPackage pkg;
|
||||
|
||||
public CompressedPackage(string filename)
|
||||
{
|
||||
s = FileSystem.Open(filename);
|
||||
pkg = (ZipPackage)ZipPackage.Open(s, FileMode.Open);
|
||||
hashes = pkg.GetParts()
|
||||
.Select(p => PackageEntry.HashFilename(p.Uri.LocalPath)).ToArray();
|
||||
}
|
||||
|
||||
public Stream GetContent(string filename)
|
||||
{
|
||||
return pkg.GetPart(new Uri(filename)).GetStream(FileMode.Open);
|
||||
}
|
||||
|
||||
public IEnumerable<uint> AllFileHashes() { return hashes; }
|
||||
|
||||
public bool Exists(string filename)
|
||||
{
|
||||
return hashes.Contains(PackageEntry.HashFilename(filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,6 +105,14 @@ namespace OpenRA.FileFormats
|
||||
return InvalidValueAction(x,fieldType, field);
|
||||
}
|
||||
|
||||
else if (fieldType == typeof(decimal))
|
||||
{
|
||||
decimal res;
|
||||
if (decimal.TryParse(x.Replace("%",""), System.Globalization.NumberStyles.Any, NumberFormatInfo.InvariantInfo, out res))
|
||||
return res * (x.Contains( '%' ) ? 0.01m : 1m);
|
||||
return InvalidValueAction(x,fieldType, field);
|
||||
}
|
||||
|
||||
else if (fieldType == typeof(string))
|
||||
return x;
|
||||
|
||||
@@ -254,6 +262,11 @@ namespace OpenRA.FileFormats
|
||||
FormatValue( o, f ) ) ).ToList() );
|
||||
}
|
||||
|
||||
public static MiniYamlNode SaveField(object o, string field)
|
||||
{
|
||||
return new MiniYamlNode(field, FieldSaver.FormatValue( o, o.GetType().GetField(field) ));
|
||||
}
|
||||
|
||||
public static string FormatValue(object o, FieldInfo f)
|
||||
{
|
||||
var v = f.GetValue(o);
|
||||
|
||||
274
OpenRA.FileFormats/FileFormats/Blast.cs
Normal file
274
OpenRA.FileFormats/FileFormats/Blast.cs
Normal file
@@ -0,0 +1,274 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 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. For more information,
|
||||
* see LICENSE.
|
||||
*
|
||||
* This file is based on the blast routines (version 1.1 by Mark Adler)
|
||||
* included in zlib/contrib
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
public static class Blast
|
||||
{
|
||||
public static readonly int MAXBITS = 13; // maximum code length
|
||||
public static readonly int MAXWIN = 4096; // maximum window size
|
||||
|
||||
static byte[] litlen = new byte[] {
|
||||
11, 124, 8, 7, 28, 7, 188, 13, 76, 4,
|
||||
10, 8, 12, 10, 12, 10, 8, 23, 8, 9,
|
||||
7, 6, 7, 8, 7, 6, 55, 8, 23, 24,
|
||||
12, 11, 7, 9, 11, 12, 6, 7, 22, 5,
|
||||
7, 24, 6, 11, 9, 6, 7, 22, 7, 11,
|
||||
38, 7, 9, 8, 25, 11, 8, 11, 9, 12,
|
||||
8, 12, 5, 38, 5, 38, 5, 11, 7, 5,
|
||||
6, 21, 6, 10, 53, 8, 7, 24, 10, 27,
|
||||
44, 253, 253, 253, 252, 252, 252, 13, 12, 45,
|
||||
12, 45, 12, 61, 12, 45, 44, 173
|
||||
};
|
||||
|
||||
// bit lengths of length codes 0..15
|
||||
static byte[] lenlen = new byte[] { 2, 35, 36, 53, 38, 23 };
|
||||
|
||||
// bit lengths of distance codes 0..63
|
||||
static byte[] distlen = new byte[] { 2, 20, 53, 230, 247, 151, 248 };
|
||||
|
||||
// base for length codes
|
||||
static short[] lengthbase = new short[] {
|
||||
3, 2, 4, 5, 6, 7, 8, 9, 10, 12,
|
||||
16, 24, 40, 72, 136, 264
|
||||
};
|
||||
|
||||
// extra bits for length codes
|
||||
static byte[] extra = new byte[] {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 2,
|
||||
3, 4, 5, 6, 7, 8
|
||||
};
|
||||
|
||||
static Huffman litcode = new Huffman(litlen, litlen.Length, 256);
|
||||
static Huffman lencode = new Huffman(lenlen, lenlen.Length, 16);
|
||||
static Huffman distcode = new Huffman(distlen, distlen.Length, 64);
|
||||
|
||||
// Decode PKWare Compression Library stream.
|
||||
public static byte[] Decompress(byte[] src)
|
||||
{
|
||||
BitReader br = new BitReader(src);
|
||||
|
||||
// Are literals coded?
|
||||
int coded = br.ReadBits(8);
|
||||
|
||||
if (coded < 0 || coded > 1)
|
||||
throw new NotImplementedException("Invalid datastream");
|
||||
bool EncodedLiterals = (coded == 1);
|
||||
|
||||
// log2(dictionary size) - 6
|
||||
int dict = br.ReadBits(8);
|
||||
if (dict < 4 || dict > 6)
|
||||
throw new InvalidDataException("Invalid dictionary size");
|
||||
|
||||
// output state
|
||||
ushort next = 0; // index of next write location in out[]
|
||||
bool first = true; // true to check distances (for first 4K)
|
||||
byte[] outBuffer = new byte[MAXWIN]; // output buffer and sliding window
|
||||
var ms = new MemoryStream();
|
||||
|
||||
// decode literals and length/distance pairs
|
||||
do
|
||||
{
|
||||
// length/distance pair
|
||||
if (br.ReadBits(1) == 1)
|
||||
{
|
||||
// Length
|
||||
int symbol = Decode(lencode, br);
|
||||
int len = lengthbase[symbol] + br.ReadBits(extra[symbol]);
|
||||
if (len == 519) // Magic number for "done"
|
||||
{
|
||||
for (int i = 0; i < next; i++)
|
||||
ms.WriteByte(outBuffer[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
// Distance
|
||||
symbol = len == 2 ? 2 : dict;
|
||||
int dist = Decode(distcode, br) << symbol;
|
||||
dist += br.ReadBits(symbol);
|
||||
dist++;
|
||||
|
||||
if (first && dist > next)
|
||||
throw new InvalidDataException("Attempt to jump before data");
|
||||
|
||||
// copy length bytes from distance bytes back
|
||||
do
|
||||
{
|
||||
int dest = next;
|
||||
int source = dest - dist;
|
||||
|
||||
int copy = MAXWIN;
|
||||
if (next < dist)
|
||||
{
|
||||
source += copy;
|
||||
copy = dist;
|
||||
}
|
||||
|
||||
copy -= next;
|
||||
if (copy > len)
|
||||
copy = len;
|
||||
|
||||
len -= copy;
|
||||
next += (ushort)copy;
|
||||
Array.Copy(outBuffer, source, outBuffer, dest, copy);
|
||||
|
||||
// Flush window to outstream
|
||||
if (next == MAXWIN)
|
||||
{
|
||||
for (int i = 0; i < next; i++)
|
||||
ms.WriteByte(outBuffer[i]);
|
||||
next = 0;
|
||||
first = false;
|
||||
}
|
||||
} while (len != 0);
|
||||
}
|
||||
else // literal value
|
||||
{
|
||||
int symbol = EncodedLiterals ? Decode(litcode, br) : br.ReadBits(8);
|
||||
outBuffer[next++] = (byte)symbol;
|
||||
if (next == MAXWIN)
|
||||
{
|
||||
for (int i = 0; i < next; i++)
|
||||
ms.WriteByte(outBuffer[i]);
|
||||
next = 0;
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
} while (true);
|
||||
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
// Decode a code using huffman table h.
|
||||
private static int Decode(Huffman h, BitReader br)
|
||||
{
|
||||
int code = 0; // len bits being decoded
|
||||
int first = 0; // first code of length len
|
||||
int index = 0; // index of first code of length len in symbol table
|
||||
short next = 1;
|
||||
while (true)
|
||||
{
|
||||
code |= br.ReadBits(1) ^ 1; // invert code
|
||||
int count = h.Count[next++];
|
||||
if (code < first + count)
|
||||
return h.Symbol[index + (code - first)];
|
||||
|
||||
index += count;
|
||||
first += count;
|
||||
first <<= 1;
|
||||
code <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BitReader
|
||||
{
|
||||
readonly byte[] src;
|
||||
int offset = 0;
|
||||
int bitBuffer = 0;
|
||||
int bitCount = 0;
|
||||
|
||||
public BitReader(byte[] src)
|
||||
{
|
||||
this.src = src;
|
||||
}
|
||||
|
||||
public int ReadBits(int count)
|
||||
{
|
||||
int ret = 0;
|
||||
int filled = 0;
|
||||
while (filled < count)
|
||||
{
|
||||
if (bitCount == 0)
|
||||
{
|
||||
bitBuffer = src[offset++];
|
||||
bitCount = 8;
|
||||
}
|
||||
|
||||
ret |= (bitBuffer & 1) << filled;
|
||||
bitBuffer >>= 1;
|
||||
bitCount--;
|
||||
filled++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a list of repeated code lengths rep[0..n-1], where each byte is a
|
||||
* count (high four bits + 1) and a code length (low four bits), generate the
|
||||
* list of code lengths. This compaction reduces the size of the object code.
|
||||
* Then given the list of code lengths length[0..n-1] representing a canonical
|
||||
* Huffman code for n symbols, construct the tables required to decode those
|
||||
* codes. Those tables are the number of codes of each length, and the symbols
|
||||
* sorted by length, retaining their original order within each length.
|
||||
*/
|
||||
class Huffman
|
||||
{
|
||||
public short[] Count; // number of symbols of each length
|
||||
public short[] Symbol; // canonically ordered symbols
|
||||
|
||||
public Huffman(byte[] rep, int n, short SymbolCount)
|
||||
{
|
||||
short[] length = new short[256]; // code lengths
|
||||
int s = 0; // current symbol
|
||||
|
||||
// convert compact repeat counts into symbol bit length list
|
||||
foreach (byte code in rep)
|
||||
{
|
||||
int num = (code >> 4) + 1; // Number of codes (top four bits plus 1)
|
||||
byte len = (byte)(code & 15); // Code length (low four bits)
|
||||
do
|
||||
{
|
||||
length[s++] = len;
|
||||
} while (--num > 0);
|
||||
}
|
||||
n = s;
|
||||
|
||||
// count number of codes of each length
|
||||
Count = new short[Blast.MAXBITS + 1];
|
||||
for (int i = 0; i < n; i++)
|
||||
Count[length[i]]++;
|
||||
|
||||
// no codes!
|
||||
if (Count[0] == n)
|
||||
return;
|
||||
|
||||
// check for an over-subscribed or incomplete set of lengths
|
||||
int left = 1; // one possible code of zero length
|
||||
for (int len = 1; len <= Blast.MAXBITS; len++)
|
||||
{
|
||||
left <<= 1;
|
||||
// one more bit, double codes left
|
||||
left -= Count[len];
|
||||
// deduct count from possible codes
|
||||
if (left < 0)
|
||||
throw new InvalidDataException ("over subscribed code set");
|
||||
}
|
||||
|
||||
// generate offsets into symbol table for each length for sorting
|
||||
short[] offs = new short[Blast.MAXBITS + 1];
|
||||
for (int len = 1; len < Blast.MAXBITS; len++)
|
||||
offs[len + 1] = (short)(offs[len] + Count[len]);
|
||||
|
||||
// put symbols in table sorted by length, by symbol order within each length
|
||||
Symbol = new short[SymbolCount];
|
||||
for (short i = 0; i < n; i++)
|
||||
if (length[i] != 0)
|
||||
Symbol[offs[length[i]]++] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,8 @@ namespace OpenRA.FileFormats
|
||||
Array.Copy(src, this.offset, dest, offset, count);
|
||||
this.offset += count;
|
||||
}
|
||||
|
||||
public int Remaining() { return src.Length - offset; }
|
||||
}
|
||||
|
||||
public static class Format80
|
||||
@@ -121,5 +123,25 @@ namespace OpenRA.FileFormats
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int EncodeInto(byte[] src, byte[] dest)
|
||||
{
|
||||
/* quick & dirty format80 encoder -- only uses raw copy operator, terminated with a zero-run. */
|
||||
/* this does not produce good compression, but it's valid format80 */
|
||||
|
||||
var destIndex = 0;
|
||||
var ctx = new FastByteReader(src);
|
||||
|
||||
do
|
||||
{
|
||||
var len = Math.Min(ctx.Remaining(), 0x3F);
|
||||
dest[destIndex++] = (byte)(0x80 | len);
|
||||
if (len > 0)
|
||||
ctx.CopyTo(dest, destIndex, len);
|
||||
}
|
||||
while (!ctx.Done());
|
||||
|
||||
return destIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
@@ -33,19 +34,43 @@ namespace OpenRA.FileFormats
|
||||
}
|
||||
}
|
||||
|
||||
static int order = 0;
|
||||
|
||||
static IFolder OpenPackage(string filename)
|
||||
{
|
||||
if (filename.EndsWith(".mix"))
|
||||
return new Package(filename);
|
||||
else if (filename.EndsWith(".zip"))
|
||||
return new CompressedPackage(filename);
|
||||
else
|
||||
return new Folder(filename);
|
||||
return OpenPackage(filename, order++);
|
||||
}
|
||||
|
||||
public static IFolder CreatePackage(string filename, int order, Dictionary<string, byte[]> content)
|
||||
{
|
||||
if (filename.EndsWith(".mix", StringComparison.InvariantCultureIgnoreCase))
|
||||
return new MixFile(filename, order, content);
|
||||
else if (filename.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase))
|
||||
return new ZipFile(filename, order, content);
|
||||
else if (filename.EndsWith(".oramap", StringComparison.InvariantCultureIgnoreCase))
|
||||
return new ZipFile(filename, order, content);
|
||||
else if (filename.EndsWith(".Z", StringComparison.InvariantCultureIgnoreCase))
|
||||
throw new NotImplementedException("Creating .Z archives is unsupported");
|
||||
else
|
||||
return new Folder(filename, order, content);
|
||||
}
|
||||
|
||||
public static IFolder OpenPackage(string filename, int order)
|
||||
{
|
||||
if (filename.EndsWith(".mix", StringComparison.InvariantCultureIgnoreCase))
|
||||
return new MixFile(filename, order);
|
||||
else if (filename.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase))
|
||||
return new ZipFile(filename, order);
|
||||
else if (filename.EndsWith(".oramap", StringComparison.InvariantCultureIgnoreCase))
|
||||
return new ZipFile(filename, order);
|
||||
else if (filename.EndsWith(".Z", StringComparison.InvariantCultureIgnoreCase))
|
||||
return new InstallShieldPackage(filename, order);
|
||||
else
|
||||
return new Folder(filename, order);
|
||||
}
|
||||
|
||||
public static void Mount(string name)
|
||||
{
|
||||
name = name.ToLowerInvariant();
|
||||
var optional = name.StartsWith("~");
|
||||
if (optional) name = name.Substring(1);
|
||||
|
||||
@@ -64,6 +89,16 @@ namespace OpenRA.FileFormats
|
||||
allFiles = new Cache<uint, List<IFolder>>( _ => new List<IFolder>() );
|
||||
}
|
||||
|
||||
public static bool Unmount(IFolder mount)
|
||||
{
|
||||
return (mountedFolders.RemoveAll(f => f == mount) > 0);
|
||||
}
|
||||
|
||||
public static void Mount(IFolder mount)
|
||||
{
|
||||
if (!mountedFolders.Contains(mount)) mountedFolders.Add(mount);
|
||||
}
|
||||
|
||||
public static void LoadFromManifest( Manifest manifest )
|
||||
{
|
||||
UnmountAll();
|
||||
@@ -73,9 +108,14 @@ namespace OpenRA.FileFormats
|
||||
|
||||
static Stream GetFromCache( Cache<uint, List<IFolder>> index, string filename )
|
||||
{
|
||||
foreach( var folder in index[ PackageEntry.HashFilename( filename ) ] )
|
||||
if (folder.Exists(filename))
|
||||
return folder.GetContent(filename);
|
||||
var folder = index[PackageEntry.HashFilename(filename)]
|
||||
.Where(x => x.Exists(filename))
|
||||
.OrderBy(x => x.Priority)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (folder != null)
|
||||
return folder.GetContent(filename);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -88,11 +128,13 @@ namespace OpenRA.FileFormats
|
||||
return ret;
|
||||
}
|
||||
|
||||
foreach( IFolder folder in mountedFolders )
|
||||
{
|
||||
if (folder.Exists(filename))
|
||||
return folder.GetContent(filename);
|
||||
}
|
||||
var folder = mountedFolders
|
||||
.Where(x => x.Exists(filename))
|
||||
.OrderByDescending(x => x.Priority)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (folder != null)
|
||||
return folder.GetContent(filename);
|
||||
|
||||
throw new FileNotFoundException( string.Format( "File not found: {0}", filename ), filename );
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
@@ -17,7 +18,24 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
readonly string path;
|
||||
|
||||
public Folder(string path) { this.path = path; }
|
||||
int priority;
|
||||
|
||||
// Create a new folder package
|
||||
public Folder(string path, int priority, Dictionary<string, byte[]> contents)
|
||||
{
|
||||
this.path = path;
|
||||
this.priority = priority;
|
||||
if (Directory.Exists(path))
|
||||
Directory.Delete(path);
|
||||
|
||||
Write(contents);
|
||||
}
|
||||
|
||||
public Folder(string path, int priority)
|
||||
{
|
||||
this.path = path;
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public Stream GetContent(string filename)
|
||||
{
|
||||
@@ -28,12 +46,26 @@ namespace OpenRA.FileFormats
|
||||
public IEnumerable<uint> AllFileHashes()
|
||||
{
|
||||
foreach( var filename in Directory.GetFiles( path, "*", SearchOption.TopDirectoryOnly ) )
|
||||
yield return PackageEntry.HashFilename( filename );
|
||||
yield return PackageEntry.HashFilename( Path.GetFileName(filename) );
|
||||
}
|
||||
|
||||
public bool Exists(string filename)
|
||||
{
|
||||
return File.Exists(Path.Combine(path,filename));
|
||||
}
|
||||
|
||||
|
||||
public int Priority
|
||||
{
|
||||
get { return priority; }
|
||||
}
|
||||
|
||||
public void Write(Dictionary<string, byte[]> contents)
|
||||
{
|
||||
foreach (var file in contents)
|
||||
using (var dataStream = File.Create(Path.Combine(path, file.Key)))
|
||||
using (var writer = new BinaryWriter(dataStream))
|
||||
writer.Write(file.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
126
OpenRA.FileFormats/Filesystem/InstallShieldPackage.cs
Normal file
126
OpenRA.FileFormats/Filesystem/InstallShieldPackage.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 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. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
public class InstallShieldPackage : IFolder
|
||||
{
|
||||
readonly Dictionary<uint, PackageEntry> index = new Dictionary<uint, PackageEntry>();
|
||||
readonly Stream s;
|
||||
readonly long dataStart = 255;
|
||||
int priority;
|
||||
|
||||
public InstallShieldPackage(string filename, int priority)
|
||||
{
|
||||
this.priority = priority;
|
||||
s = FileSystem.Open(filename);
|
||||
|
||||
// Parse package header
|
||||
BinaryReader reader = new BinaryReader(s);
|
||||
uint signature = reader.ReadUInt32();
|
||||
if (signature != 0x8C655D13)
|
||||
throw new InvalidDataException("Not an Installshield package");
|
||||
|
||||
reader.ReadBytes(8);
|
||||
/*var FileCount = */reader.ReadUInt16();
|
||||
reader.ReadBytes(4);
|
||||
/*var ArchiveSize = */reader.ReadUInt32();
|
||||
reader.ReadBytes(19);
|
||||
var TOCAddress = reader.ReadInt32();
|
||||
reader.ReadBytes(4);
|
||||
var DirCount = reader.ReadUInt16();
|
||||
|
||||
// Parse the directory list
|
||||
s.Seek(TOCAddress, SeekOrigin.Begin);
|
||||
BinaryReader TOCreader = new BinaryReader(s);
|
||||
for (var i = 0; i < DirCount; i++)
|
||||
ParseDirectory(TOCreader);
|
||||
}
|
||||
|
||||
void ParseDirectory(BinaryReader reader)
|
||||
{
|
||||
// Parse directory header
|
||||
var FileCount = reader.ReadUInt16();
|
||||
var ChunkSize = reader.ReadUInt16();
|
||||
var NameLength = reader.ReadUInt16();
|
||||
reader.ReadChars(NameLength); //var DirName = new String(reader.ReadChars(NameLength));
|
||||
|
||||
// Skip to the end of the chunk
|
||||
reader.ReadBytes(ChunkSize - NameLength - 6);
|
||||
|
||||
// Parse files
|
||||
for (var i = 0; i < FileCount; i++)
|
||||
ParseFile(reader);
|
||||
}
|
||||
|
||||
uint AccumulatedData = 0;
|
||||
void ParseFile(BinaryReader reader)
|
||||
{
|
||||
reader.ReadBytes(7);
|
||||
var CompressedSize = reader.ReadUInt32();
|
||||
reader.ReadBytes(12);
|
||||
var ChunkSize = reader.ReadUInt16();
|
||||
reader.ReadBytes(4);
|
||||
var NameLength = reader.ReadByte();
|
||||
var FileName = new String(reader.ReadChars(NameLength));
|
||||
|
||||
var hash = PackageEntry.HashFilename(FileName);
|
||||
index.Add(hash, new PackageEntry(hash,AccumulatedData, CompressedSize));
|
||||
AccumulatedData += CompressedSize;
|
||||
|
||||
// Skip to the end of the chunk
|
||||
reader.ReadBytes(ChunkSize - NameLength - 30);
|
||||
}
|
||||
|
||||
public Stream GetContent(uint hash)
|
||||
{
|
||||
PackageEntry e;
|
||||
if (!index.TryGetValue(hash, out e))
|
||||
return null;
|
||||
|
||||
s.Seek( dataStart + e.Offset, SeekOrigin.Begin );
|
||||
byte[] data = new byte[ e.Length ];
|
||||
s.Read( data, 0, (int)e.Length );
|
||||
|
||||
return new MemoryStream(Blast.Decompress(data));
|
||||
}
|
||||
|
||||
public Stream GetContent(string filename)
|
||||
{
|
||||
return GetContent(PackageEntry.HashFilename(filename));
|
||||
}
|
||||
|
||||
public IEnumerable<uint> AllFileHashes()
|
||||
{
|
||||
return index.Keys;
|
||||
}
|
||||
|
||||
public bool Exists(string filename)
|
||||
{
|
||||
return index.ContainsKey(PackageEntry.HashFilename(filename));
|
||||
}
|
||||
|
||||
|
||||
public int Priority
|
||||
{
|
||||
get { return 2000 + priority; }
|
||||
}
|
||||
|
||||
public void Write(Dictionary<string, byte[]> contents)
|
||||
{
|
||||
throw new NotImplementedException("Cannot save InstallShieldPackages.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,17 +20,32 @@ namespace OpenRA.FileFormats
|
||||
Stream GetContent(string filename);
|
||||
bool Exists(string filename);
|
||||
IEnumerable<uint> AllFileHashes();
|
||||
void Write(Dictionary<string, byte[]> contents);
|
||||
int Priority { get; }
|
||||
}
|
||||
|
||||
public class Package : IFolder
|
||||
public class MixFile : IFolder
|
||||
{
|
||||
readonly Dictionary<uint, PackageEntry> index;
|
||||
readonly bool isRmix, isEncrypted;
|
||||
readonly long dataStart;
|
||||
readonly Stream s;
|
||||
|
||||
public Package(string filename)
|
||||
int priority;
|
||||
|
||||
// Create a new MixFile
|
||||
public MixFile(string filename, int priority, Dictionary<string, byte[]> contents)
|
||||
{
|
||||
this.priority = priority;
|
||||
if (File.Exists(filename))
|
||||
File.Delete(filename);
|
||||
|
||||
s = File.Create(filename);
|
||||
Write(contents);
|
||||
}
|
||||
|
||||
public MixFile(string filename, int priority)
|
||||
{
|
||||
this.priority = priority;
|
||||
s = FileSystem.Open(filename);
|
||||
|
||||
BinaryReader reader = new BinaryReader(s);
|
||||
@@ -149,6 +164,51 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
return index.ContainsKey(PackageEntry.HashFilename(filename));
|
||||
}
|
||||
|
||||
|
||||
public int Priority
|
||||
{
|
||||
get { return 1000 + priority; }
|
||||
}
|
||||
|
||||
public void Write(Dictionary<string, byte[]> contents)
|
||||
{
|
||||
// Cannot modify existing mixfile - rename existing file and
|
||||
// create a new one with original content plus modifications
|
||||
FileSystem.Unmount(this);
|
||||
|
||||
// TODO: Add existing data to the contents list
|
||||
if (index.Count > 0)
|
||||
throw new NotImplementedException("Updating mix files unfinished");
|
||||
|
||||
// Construct a list of entries for the file header
|
||||
uint dataSize = 0;
|
||||
var items = new List<PackageEntry>();
|
||||
foreach (var kv in contents)
|
||||
{
|
||||
uint length = (uint)kv.Value.Length;
|
||||
uint hash = PackageEntry.HashFilename(Path.GetFileName(kv.Key));
|
||||
items.Add(new PackageEntry(hash, dataSize, length));
|
||||
dataSize += length;
|
||||
}
|
||||
|
||||
// Write the new file
|
||||
s.Seek(0,SeekOrigin.Begin);
|
||||
using (var writer = new BinaryWriter(s))
|
||||
{
|
||||
// Write file header
|
||||
writer.Write((ushort)items.Count);
|
||||
writer.Write(dataSize);
|
||||
foreach (var item in items)
|
||||
item.Write(writer);
|
||||
|
||||
writer.Flush();
|
||||
|
||||
// Copy file data
|
||||
foreach (var file in contents)
|
||||
s.Write(file.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
117
OpenRA.FileFormats/Filesystem/ZipFile.cs
Normal file
117
OpenRA.FileFormats/Filesystem/ZipFile.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 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. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using SZipFile = ICSharpCode.SharpZipLib.Zip.ZipFile;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
public class ZipFile : IFolder
|
||||
{
|
||||
string filename;
|
||||
SZipFile pkg;
|
||||
int priority;
|
||||
|
||||
public ZipFile(string filename, int priority)
|
||||
{
|
||||
this.filename = filename;
|
||||
this.priority = priority;
|
||||
try
|
||||
{
|
||||
// pull the file into memory, dont keep it open.
|
||||
pkg = new SZipFile(new MemoryStream(File.ReadAllBytes(filename)));
|
||||
}
|
||||
catch (ZipException e)
|
||||
{
|
||||
Log.Write("debug", "Couldn't load zip file: {0}", e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new zip with the specified contents
|
||||
public ZipFile(string filename, int priority, Dictionary<string, byte[]> contents)
|
||||
{
|
||||
this.priority = priority;
|
||||
this.filename = filename;
|
||||
|
||||
if (File.Exists(filename))
|
||||
File.Delete(filename);
|
||||
|
||||
pkg = SZipFile.Create(filename);
|
||||
Write(contents);
|
||||
}
|
||||
|
||||
public Stream GetContent(string filename)
|
||||
{
|
||||
|
||||
using (var z = pkg.GetInputStream(pkg.GetEntry(filename)))
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
int bufSize = 2048;
|
||||
byte[] buf = new byte[bufSize];
|
||||
while ((bufSize = z.Read(buf, 0, buf.Length)) > 0)
|
||||
ms.Write(buf, 0, bufSize);
|
||||
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
return ms;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<uint> AllFileHashes()
|
||||
{
|
||||
foreach(ZipEntry entry in pkg)
|
||||
yield return PackageEntry.HashFilename(entry.Name);
|
||||
}
|
||||
|
||||
public bool Exists(string filename)
|
||||
{
|
||||
return pkg.GetEntry(filename) != null;
|
||||
}
|
||||
|
||||
public int Priority
|
||||
{
|
||||
get { return 500 + priority; }
|
||||
}
|
||||
|
||||
public void Write(Dictionary<string, byte[]> contents)
|
||||
{
|
||||
pkg.Close();
|
||||
|
||||
pkg = SZipFile.Create(filename);
|
||||
|
||||
pkg.BeginUpdate();
|
||||
// TODO: Clear existing content?
|
||||
|
||||
foreach (var kvp in contents)
|
||||
pkg.Add(new StaticMemoryDataSource(kvp.Value), kvp.Key);
|
||||
|
||||
pkg.CommitUpdate();
|
||||
|
||||
pkg.Close();
|
||||
|
||||
pkg = new SZipFile(new MemoryStream(File.ReadAllBytes(filename)));
|
||||
}
|
||||
}
|
||||
|
||||
class StaticMemoryDataSource : IStaticDataSource
|
||||
{
|
||||
byte[] data;
|
||||
public StaticMemoryDataSource (byte[] data)
|
||||
{
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public Stream GetSource()
|
||||
{
|
||||
return new MemoryStream(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,14 +33,12 @@ namespace OpenRA.FileFormats.Graphics
|
||||
IIndexBuffer CreateIndexBuffer( int length );
|
||||
ITexture CreateTexture( Bitmap bitmap );
|
||||
ITexture CreateTexture();
|
||||
IShader CreateShader( Stream stream );
|
||||
IShader CreateShader( string name );
|
||||
|
||||
Size WindowSize { get; }
|
||||
|
||||
void Begin();
|
||||
void End();
|
||||
void Clear( Color color );
|
||||
void Present();
|
||||
void Present( IInputHandler inputHandler );
|
||||
|
||||
void DrawIndexedPrimitives( PrimitiveType type, Range<int> vertexRange, Range<int> indexRange );
|
||||
void DrawIndexedPrimitives( PrimitiveType type, int vertexPool, int numPrimitives );
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#region Copyright & License Information
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
@@ -9,16 +9,33 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public interface IInputHandler
|
||||
{
|
||||
void ModifierKeys( Modifiers mods );
|
||||
void OnKeyInput( KeyInput input );
|
||||
void OnMouseInput( MouseInput input );
|
||||
}
|
||||
|
||||
public struct MouseInput
|
||||
{
|
||||
public MouseInputEvent Event;
|
||||
public int2 Location;
|
||||
public MouseButton Button;
|
||||
public int2 Location;
|
||||
public Modifiers Modifiers;
|
||||
|
||||
public MouseInput( MouseInputEvent ev, MouseButton button, int2 location, Modifiers mods )
|
||||
{
|
||||
this.Event = ev;
|
||||
this.Button = button;
|
||||
this.Location = location;
|
||||
this.Modifiers = mods;
|
||||
}
|
||||
}
|
||||
|
||||
public enum MouseInputEvent { Down, Move, Up };
|
||||
@@ -18,11 +18,10 @@ namespace OpenRA.FileFormats
|
||||
public class Manifest
|
||||
{
|
||||
public readonly string[]
|
||||
Mods, Folders, Packages, Rules,
|
||||
Mods, Folders, Packages, Rules, ServerTraits,
|
||||
Sequences, Cursors, Chrome, Assemblies, ChromeLayout,
|
||||
Weapons, Voices, Music, Movies, TileSets;
|
||||
|
||||
public readonly string ShellmapUid, LoadScreen;
|
||||
public readonly string LoadScreen;
|
||||
public readonly int TileSize = 24;
|
||||
|
||||
public Manifest(string[] mods)
|
||||
@@ -36,6 +35,7 @@ namespace OpenRA.FileFormats
|
||||
Folders = YamlList(yaml, "Folders");
|
||||
Packages = YamlList(yaml, "Packages");
|
||||
Rules = YamlList(yaml, "Rules");
|
||||
ServerTraits = YamlList(yaml, "ServerTraits");
|
||||
Sequences = YamlList(yaml, "Sequences");
|
||||
Cursors = YamlList(yaml, "Cursors");
|
||||
Chrome = YamlList(yaml, "Chrome");
|
||||
@@ -47,7 +47,6 @@ namespace OpenRA.FileFormats
|
||||
Movies = YamlList(yaml, "Movies");
|
||||
TileSets = YamlList(yaml, "TileSets");
|
||||
|
||||
ShellmapUid = yaml.First( x => x.Key == "ShellmapUid" ).Value.Value;
|
||||
LoadScreen = yaml.First( x => x.Key == "LoadScreen" ).Value.Value;
|
||||
|
||||
if (yaml.FirstOrDefault( x => x.Key == "TileSize" ) != null)
|
||||
|
||||
@@ -12,18 +12,22 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
public class MapStub
|
||||
{
|
||||
public readonly IFolder Package;
|
||||
protected IFolder Container;
|
||||
public string Path {get; protected set;}
|
||||
|
||||
// Yaml map data
|
||||
public readonly string Uid;
|
||||
[FieldLoader.Load] public bool Selectable;
|
||||
public string Uid { get; protected set; }
|
||||
[FieldLoader.Load] public bool Selectable;
|
||||
[FieldLoader.Load] public bool UseAsShellmap;
|
||||
|
||||
[FieldLoader.Load] public string Title;
|
||||
[FieldLoader.Load] public string Type = "Conquest";
|
||||
[FieldLoader.Load] public string Description;
|
||||
[FieldLoader.Load] public string Author;
|
||||
[FieldLoader.Load] public int PlayerCount;
|
||||
@@ -35,18 +39,33 @@ namespace OpenRA.FileFormats
|
||||
|
||||
[FieldLoader.Load] public int2 TopLeft;
|
||||
[FieldLoader.Load] public int2 BottomRight;
|
||||
public int Width { get { return BottomRight.X - TopLeft.X; } }
|
||||
public int Height { get { return BottomRight.Y - TopLeft.Y; } }
|
||||
|
||||
public MapStub(IFolder package)
|
||||
public Rectangle Bounds;
|
||||
|
||||
public MapStub() {} // Hack for the editor - not used for anything important
|
||||
|
||||
public MapStub(string path)
|
||||
{
|
||||
Package = package;
|
||||
var yaml = MiniYaml.FromStream(Package.GetContent("map.yaml"));
|
||||
Path = path;
|
||||
Container = FileSystem.OpenPackage(path, int.MaxValue);
|
||||
var yaml = MiniYaml.FromStream(Container.GetContent("map.yaml"));
|
||||
FieldLoader.Load( this, new MiniYaml( null, yaml ) );
|
||||
|
||||
Uid = Package.GetContent("map.uid").ReadAllText();
|
||||
|
||||
Bounds = Rectangle.FromLTRB(TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y);
|
||||
Uid = ComputeHash();
|
||||
}
|
||||
|
||||
string ComputeHash()
|
||||
{
|
||||
// UID is calculated by taking an SHA1 of the yaml and binary data
|
||||
// Read the relevant data into a buffer
|
||||
var data = Container.GetContent("map.yaml").ReadAllBytes()
|
||||
.Concat(Container.GetContent("map.bin").ReadAllBytes()).ToArray();
|
||||
|
||||
// Take the SHA1
|
||||
using (var csp = SHA1.Create())
|
||||
return new string(csp.ComputeHash(data).SelectMany(a => a.ToString("x2")).ToArray());
|
||||
}
|
||||
|
||||
static object LoadWaypoints( MiniYaml y )
|
||||
{
|
||||
var ret = new Dictionary<string, int2>();
|
||||
|
||||
@@ -16,11 +16,16 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
public string Name;
|
||||
public string Palette;
|
||||
public string Race;
|
||||
public bool OwnsWorld = false;
|
||||
public bool NonCombatant = false;
|
||||
public bool Playable = false;
|
||||
public bool DefaultStartingUnits = false;
|
||||
public bool AllowBots = true;
|
||||
|
||||
public bool LockRace = false;
|
||||
public string Race;
|
||||
|
||||
public bool LockColor = false;
|
||||
public Color Color = Color.FromArgb(238,238,238);
|
||||
public Color Color2 = Color.FromArgb(44,28,24);
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
return string.Join("\n", y.ToLines(true).Select(x => x.TrimEnd()).ToArray());
|
||||
}
|
||||
|
||||
|
||||
public static IEnumerable<string> ToLines(this MiniYamlNodes y, bool lowest)
|
||||
{
|
||||
foreach (var kv in y)
|
||||
|
||||
39
OpenRA.FileFormats/Mod.cs
Normal file
39
OpenRA.FileFormats/Mod.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
public class Mod
|
||||
{
|
||||
public string Title;
|
||||
public string Description;
|
||||
public string Version;
|
||||
public string Author;
|
||||
public string[] RequiresMods;
|
||||
public bool Standalone = false;
|
||||
|
||||
public static readonly Dictionary<string, Mod> AllMods = ValidateMods(Directory.GetDirectories("mods").Select(x => x.Substring(5)).ToArray());
|
||||
|
||||
public static Dictionary<string, Mod> ValidateMods(string[] mods)
|
||||
{
|
||||
var ret = new Dictionary<string, Mod>();
|
||||
foreach (var m in mods)
|
||||
{
|
||||
if (!File.Exists("mods" + Path.DirectorySeparatorChar + m + Path.DirectorySeparatorChar + "mod.yaml"))
|
||||
continue;
|
||||
|
||||
var yaml = new MiniYaml(null, MiniYaml.FromFile("mods" + Path.DirectorySeparatorChar + m + Path.DirectorySeparatorChar + "mod.yaml"));
|
||||
if (!yaml.NodesDict.ContainsKey("Metadata"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ret.Add(m, FieldLoader.Load<Mod>(yaml.NodesDict["Metadata"]));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.21022</ProductVersion>
|
||||
<ProductVersion>9.0.30729</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{BDAEAB25-991E-46A7-AF1E-4F0E03358DAA}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
@@ -45,6 +45,7 @@
|
||||
</Reference>
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Tao.Sdl, Version=1.2.13.0, Culture=neutral, PublicKeyToken=9c7a200e36c0094e">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
@@ -53,20 +54,22 @@
|
||||
<Reference Include="WindowsBase">
|
||||
<RequiredTargetFramework>3.0</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\thirdparty\ICSharpCode.SharpZipLib.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Evaluator.cs" />
|
||||
<Compile Include="Exts.cs" />
|
||||
<Compile Include="FieldLoader.cs" />
|
||||
<Compile Include="FileSystem.cs" />
|
||||
<Compile Include="Folder.cs" />
|
||||
<Compile Include="Graphics\IGraphicsDevice.cs" />
|
||||
<Compile Include="Graphics\IInputHandler.cs" />
|
||||
<Compile Include="Graphics\Vertex.cs" />
|
||||
<Compile Include="Manifest.cs" />
|
||||
<Compile Include="MiniYaml.cs" />
|
||||
<Compile Include="Mod.cs" />
|
||||
<Compile Include="PackageEntry.cs" />
|
||||
<Compile Include="Package.cs" />
|
||||
<Compile Include="PackageWriter.cs" />
|
||||
<Compile Include="Palette.cs" />
|
||||
<Compile Include="PlayerColorRemap.cs" />
|
||||
<Compile Include="Primitives\DisposableAction.cs" />
|
||||
@@ -98,8 +101,13 @@
|
||||
<Compile Include="Map\MapStub.cs" />
|
||||
<Compile Include="Map\SmudgeReference.cs" />
|
||||
<Compile Include="Map\PlayerReference.cs" />
|
||||
<Compile Include="CompressedPackage.cs" />
|
||||
<Compile Include="Graphics\VqaReader.cs" />
|
||||
<Compile Include="Filesystem\MixFile.cs" />
|
||||
<Compile Include="Filesystem\FileSystem.cs" />
|
||||
<Compile Include="Filesystem\Folder.cs" />
|
||||
<Compile Include="Filesystem\InstallShieldPackage.cs" />
|
||||
<Compile Include="FileFormats\Blast.cs" />
|
||||
<Compile Include="Filesystem\ZipFile.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
@@ -109,4 +117,7 @@
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<ItemGroup>
|
||||
<Folder Include="Filesystem\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,48 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 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. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
public static class PackageWriter
|
||||
{
|
||||
public static void CreateMix(string filename, List<string> contents)
|
||||
{
|
||||
// Construct a list of entries for the file header
|
||||
uint dataSize = 0;
|
||||
var items = new List<PackageEntry>();
|
||||
foreach (var file in contents)
|
||||
{
|
||||
uint length = (uint)new FileInfo(file).Length;
|
||||
uint hash = PackageEntry.HashFilename(Path.GetFileName(file));
|
||||
items.Add(new PackageEntry(hash, dataSize, length));
|
||||
dataSize += length;
|
||||
}
|
||||
|
||||
using (var s = File.Create(filename))
|
||||
using (var writer = new BinaryWriter(s))
|
||||
{
|
||||
// Write file header
|
||||
writer.Write((ushort)items.Count);
|
||||
writer.Write(dataSize);
|
||||
foreach (var item in items)
|
||||
item.Write(writer);
|
||||
|
||||
writer.Flush();
|
||||
|
||||
// Copy file data
|
||||
foreach (var file in contents)
|
||||
s.Write(File.ReadAllBytes(file));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
OpenRA.FileFormats/PlayerColorRemap.cs
Normal file → Executable file
2
OpenRA.FileFormats/PlayerColorRemap.cs
Normal file → Executable file
@@ -32,7 +32,7 @@ namespace OpenRA.FileFormats
|
||||
.ToDictionary(u => u.First, u => u.Second);
|
||||
}
|
||||
|
||||
static Color ColorLerp(float t, Color c1, Color c2)
|
||||
public static Color ColorLerp(float t, Color c1, Color c2)
|
||||
{
|
||||
return Color.FromArgb(255,
|
||||
(int)(t * c2.R + (1 - t) * c1.R),
|
||||
|
||||
@@ -62,7 +62,8 @@ namespace OpenRA
|
||||
Constrain(Y, min.Y, max.Y));
|
||||
}
|
||||
|
||||
public static float2 operator *(float a, float2 b) { return new float2(a * b.X, a * b.Y); }
|
||||
public static float2 operator *(float a, float2 b) { return new float2(a * b.X, a * b.Y); }
|
||||
public static float2 operator *(float2 b, float a) { return new float2(a * b.X, a * b.Y); }
|
||||
public static float2 operator *( float2 a, float2 b ) { return new float2( a.X * b.X, a.Y * b.Y ); }
|
||||
public static float2 operator /( float2 a, float2 b ) { return new float2( a.X / b.X, a.Y / b.Y ); }
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ namespace OpenRA
|
||||
public static int2 operator *(int2 b, int a) { return new int2(a * b.X, a * b.Y); }
|
||||
public static int2 operator /(int2 a, int b) { return new int2(a.X / b, a.Y / b); }
|
||||
|
||||
public static int2 operator -(int2 a) { return new int2(-a.X, -a.Y); }
|
||||
|
||||
public static bool operator ==(int2 me, int2 other) { return (me.X == other.X && me.Y == other.Y); }
|
||||
public static bool operator !=(int2 me, int2 other) { return !(me == other); }
|
||||
|
||||
@@ -70,5 +72,11 @@ namespace OpenRA
|
||||
{
|
||||
return a + ( b - a ) * mul / div;
|
||||
}
|
||||
|
||||
public int2 Clamp(Rectangle r)
|
||||
{
|
||||
return new int2(Math.Min(r.Right, Math.Max(X, r.Left)),
|
||||
Math.Min(r.Bottom, Math.Max(Y, r.Top)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace OpenRA
|
||||
[Sync]
|
||||
public Player Owner;
|
||||
|
||||
IActivity currentActivity;
|
||||
private IActivity currentActivity;
|
||||
public Group Group;
|
||||
|
||||
internal Actor(World world, string name, TypeDictionary initDict )
|
||||
@@ -61,40 +61,23 @@ namespace OpenRA
|
||||
|
||||
// auto size from render
|
||||
var firstSprite = TraitsImplementing<IRender>().SelectMany(x => x.Render(this)).FirstOrDefault();
|
||||
if (firstSprite.Sprite == null) return float2.Zero;
|
||||
return firstSprite.Sprite.size;
|
||||
if (firstSprite.Sprite == null) return float2.Zero;
|
||||
return firstSprite.Sprite.size * firstSprite.Scale;
|
||||
});
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
var wasIdle = currentActivity is Idle;
|
||||
while (currentActivity != null)
|
||||
{
|
||||
var a = currentActivity;
|
||||
|
||||
var sw = new Stopwatch();
|
||||
currentActivity = a.Tick(this) ?? new Idle();
|
||||
var dt = sw.ElapsedTime();
|
||||
if(dt > Game.Settings.Debug.LongTickThreshold)
|
||||
Log.Write("perf", "[{2}] Activity: {0} ({1:0.000} ms)", a, dt * 1000, Game.LocalTick);
|
||||
|
||||
if (a == currentActivity) break;
|
||||
if (currentActivity == null)
|
||||
foreach (var ni in TraitsImplementing<INotifyIdle>())
|
||||
ni.TickIdle(this);
|
||||
|
||||
if (currentActivity is Idle)
|
||||
{
|
||||
if (!wasIdle)
|
||||
foreach (var ni in TraitsImplementing<INotifyIdle>())
|
||||
ni.Idle(this);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
currentActivity = Util.RunActivity( this, currentActivity );
|
||||
}
|
||||
|
||||
public bool IsIdle
|
||||
{
|
||||
get { return currentActivity == null || currentActivity is Idle; }
|
||||
get { return currentActivity == null; }
|
||||
}
|
||||
|
||||
OpenRA.FileFormats.Lazy<float2> Size;
|
||||
@@ -108,11 +91,10 @@ namespace OpenRA
|
||||
|
||||
public RectangleF GetBounds(bool useAltitude)
|
||||
{
|
||||
var si = Info.Traits.GetOrDefault<SelectableInfo>();
|
||||
|
||||
var size = Size.Value;
|
||||
var loc = CenterLocation - 0.5f * size;
|
||||
var loc = CenterLocation - 0.5f * size;
|
||||
|
||||
var si = Info.Traits.GetOrDefault<SelectableInfo>();
|
||||
if (si != null && si.Bounds != null && si.Bounds.Length > 2)
|
||||
loc += new float2(si.Bounds[2], si.Bounds[3]);
|
||||
|
||||
@@ -125,7 +107,14 @@ namespace OpenRA
|
||||
return new RectangleF(loc.X, loc.Y, size.X, size.Y);
|
||||
}
|
||||
|
||||
public bool IsInWorld { get; internal set; }
|
||||
public bool IsInWorld { get; internal set; }
|
||||
|
||||
public void QueueActivity( bool queued, IActivity nextActivity )
|
||||
{
|
||||
if( !queued )
|
||||
CancelActivity();
|
||||
QueueActivity( nextActivity );
|
||||
}
|
||||
|
||||
public void QueueActivity( IActivity nextActivity )
|
||||
{
|
||||
@@ -141,7 +130,6 @@ namespace OpenRA
|
||||
currentActivity.Cancel( this );
|
||||
}
|
||||
|
||||
// For pathdebug, et al
|
||||
public IActivity GetCurrentActivity()
|
||||
{
|
||||
return currentActivity;
|
||||
@@ -194,10 +182,24 @@ namespace OpenRA
|
||||
{
|
||||
World.AddFrameEndTask( w =>
|
||||
{
|
||||
if (Destroyed) return;
|
||||
|
||||
World.Remove( this );
|
||||
World.traitDict.RemoveActor( this );
|
||||
Destroyed = true;
|
||||
} );
|
||||
}
|
||||
|
||||
// todo: move elsewhere.
|
||||
public void ChangeOwner(Player newOwner)
|
||||
{
|
||||
World.AddFrameEndTask(w =>
|
||||
{
|
||||
// momentarily remove from world so the ownership queries don't get confused
|
||||
w.Remove(this);
|
||||
Owner = newOwner;
|
||||
w.Add(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 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. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Effects
|
||||
{
|
||||
public class MoveFlash : IEffect
|
||||
{
|
||||
Animation anim = new Animation("moveflsh");
|
||||
float2 pos;
|
||||
|
||||
|
||||
public MoveFlash( World world, int2 cell )
|
||||
{
|
||||
this.pos = Game.CellSize * (cell + new float2(0.5f, 0.5f));
|
||||
anim.PlayThen( "idle",
|
||||
() => world.AddFrameEndTask(
|
||||
w => w.Remove( this ) ) );
|
||||
}
|
||||
|
||||
public MoveFlash( World world, float2 pos )
|
||||
{
|
||||
this.pos = pos;
|
||||
anim.PlayThen( "idle",
|
||||
() => world.AddFrameEndTask(
|
||||
w => w.Remove( this ) ) );
|
||||
}
|
||||
|
||||
public void Tick( World world ) { anim.Tick(); }
|
||||
|
||||
public IEnumerable<Renderable> Render()
|
||||
{
|
||||
yield return new Renderable(anim.Image, pos - .5f * anim.Image.size, "shadow", (int)pos.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
|
||||
172
OpenRA.Game/Game.cs
Normal file → Executable file
172
OpenRA.Game/Game.cs
Normal file → Executable file
@@ -13,6 +13,7 @@ using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Windows.Forms;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.GameRules;
|
||||
@@ -37,49 +38,58 @@ namespace OpenRA
|
||||
public static Settings Settings;
|
||||
|
||||
internal static OrderManager orderManager;
|
||||
static Server.Server server;
|
||||
|
||||
public static XRandom CosmeticRandom = new XRandom(); // not synced
|
||||
|
||||
public static Renderer Renderer;
|
||||
public static bool HasInputFocus = false;
|
||||
|
||||
public static void MoveViewport(int2 loc)
|
||||
public static void MoveViewport(float2 loc)
|
||||
{
|
||||
viewport.Center(loc);
|
||||
}
|
||||
|
||||
internal static void JoinServer(string host, int port)
|
||||
public static void JoinServer(string host, int port)
|
||||
{
|
||||
if (orderManager != null) orderManager.Dispose();
|
||||
|
||||
var replayFilename = ChooseReplayFilename();
|
||||
string path = Path.Combine( Game.SupportDir, "Replays" );
|
||||
if( !Directory.Exists( path ) ) Directory.CreateDirectory( path );
|
||||
var replayFile = File.Create( Path.Combine( path, replayFilename ) );
|
||||
|
||||
orderManager = new OrderManager( host, port, new ReplayRecorderConnection( new NetworkConnection( host, port ), replayFile ) );
|
||||
JoinInner(new OrderManager(host, port,
|
||||
new ReplayRecorderConnection(new NetworkConnection(host, port), replayFile)));
|
||||
}
|
||||
|
||||
static string ChooseReplayFilename()
|
||||
{
|
||||
return DateTime.UtcNow.ToString("OpenRA-yyyy-MM-ddTHHmmssZ.rep");
|
||||
}
|
||||
|
||||
static void JoinInner(OrderManager om)
|
||||
{
|
||||
if (orderManager != null) orderManager.Dispose();
|
||||
orderManager = om;
|
||||
lastConnectionState = ConnectionState.PreConnecting;
|
||||
ConnectionStateChanged(orderManager);
|
||||
}
|
||||
|
||||
static string ChooseReplayFilename()
|
||||
public static void JoinReplay(string replayFile)
|
||||
{
|
||||
return DateTime.UtcNow.ToString("OpenRA-yyyy-MM-ddThhmmssZ.rep");
|
||||
JoinInner(new OrderManager("<no server>", -1, new ReplayConnection(replayFile)));
|
||||
}
|
||||
|
||||
static void JoinLocal()
|
||||
{
|
||||
if (orderManager != null) orderManager.Dispose();
|
||||
orderManager = new OrderManager("<no server>", -1, new EchoConnection());
|
||||
lastConnectionState = ConnectionState.PreConnecting;
|
||||
ConnectionStateChanged( orderManager );
|
||||
JoinInner(new OrderManager("<no server>", -1, new EchoConnection()));
|
||||
}
|
||||
|
||||
internal static int RenderFrame = 0;
|
||||
internal static int LocalTick { get { return orderManager.LocalFrameNumber; } }
|
||||
public static int RenderFrame = 0;
|
||||
public static int NetFrameNumber { get { return orderManager.NetFrameNumber; } }
|
||||
public static int LocalTick { get { return orderManager.LocalFrameNumber; } }
|
||||
const int NetTickScale = 3; // 120ms net tick for 40ms local tick
|
||||
|
||||
internal static event Action<OrderManager> ConnectionStateChanged = _ => { };
|
||||
public static event Action<OrderManager> ConnectionStateChanged = _ => { };
|
||||
static ConnectionState lastConnectionState = ConnectionState.PreConnecting;
|
||||
public static int LocalClientId { get { return orderManager.Connection.LocalClientId; } }
|
||||
|
||||
@@ -98,14 +108,12 @@ namespace OpenRA
|
||||
using (new PerfSample("render"))
|
||||
{
|
||||
++RenderFrame;
|
||||
viewport.DrawRegions(worldRenderer);
|
||||
viewport.DrawRegions(worldRenderer, new DefaultInputHandler( orderManager.world ));
|
||||
Sound.SetListenerPosition(viewport.Location + .5f * new float2(viewport.Width, viewport.Height));
|
||||
}
|
||||
|
||||
PerfHistory.items["render"].Tick();
|
||||
PerfHistory.items["batches"].Tick();
|
||||
PerfHistory.items["text"].Tick();
|
||||
PerfHistory.items["cursor"].Tick();
|
||||
|
||||
MasterServerQuery.Tick();
|
||||
}
|
||||
@@ -125,30 +133,37 @@ namespace OpenRA
|
||||
Sound.Tick();
|
||||
Sync.CheckSyncUnchanged( world, () => { orderManager.TickImmediate(); } );
|
||||
|
||||
var isNetTick = LocalTick % NetTickScale == 0;
|
||||
|
||||
if( !isNetTick || orderManager.IsReadyForNextFrame )
|
||||
if (world != null)
|
||||
{
|
||||
++orderManager.LocalFrameNumber;
|
||||
var isNetTick = LocalTick % NetTickScale == 0;
|
||||
|
||||
Log.Write( "debug", "--Tick: {0} ({1})", LocalTick, isNetTick ? "net" : "local" );
|
||||
if (!isNetTick || orderManager.IsReadyForNextFrame)
|
||||
{
|
||||
++orderManager.LocalFrameNumber;
|
||||
|
||||
if( isNetTick ) orderManager.Tick();
|
||||
Log.Write("debug", "--Tick: {0} ({1})", LocalTick, isNetTick ? "net" : "local");
|
||||
|
||||
world.OrderGenerator.Tick( world );
|
||||
world.Selection.Tick( world );
|
||||
world.Tick();
|
||||
worldRenderer.Tick();
|
||||
if (isNetTick) orderManager.Tick();
|
||||
|
||||
PerfHistory.Tick();
|
||||
|
||||
Sync.CheckSyncUnchanged(world, () =>
|
||||
{
|
||||
world.OrderGenerator.Tick(world);
|
||||
world.Selection.Tick(world);
|
||||
});
|
||||
|
||||
world.Tick();
|
||||
|
||||
PerfHistory.Tick();
|
||||
}
|
||||
else
|
||||
if (orderManager.NetFrameNumber == 0)
|
||||
orderManager.LastTickTime = Environment.TickCount;
|
||||
}
|
||||
else
|
||||
if( orderManager.NetFrameNumber == 0 )
|
||||
orderManager.LastTickTime = Environment.TickCount;
|
||||
}
|
||||
}
|
||||
|
||||
internal static event Action LobbyInfoChanged = () => { };
|
||||
public static event Action LobbyInfoChanged = () => { };
|
||||
|
||||
internal static void SyncLobbyInfo()
|
||||
{
|
||||
@@ -162,7 +177,7 @@ namespace OpenRA
|
||||
BeforeGameStart();
|
||||
|
||||
var map = modData.PrepareMap(mapUID);
|
||||
viewport = new Viewport(new float2(Renderer.Resolution), map.TopLeft, map.BottomRight, Renderer);
|
||||
viewport = new Viewport(new int2(Renderer.Resolution), map.Bounds, Renderer);
|
||||
orderManager.world = new World(modData.Manifest, map, orderManager);
|
||||
worldRenderer = new WorldRenderer(orderManager.world);
|
||||
|
||||
@@ -170,43 +185,19 @@ namespace OpenRA
|
||||
Widget.SelectedWidget = null;
|
||||
|
||||
orderManager.LocalFrameNumber = 0;
|
||||
|
||||
orderManager.StartGame();
|
||||
worldRenderer.RefreshPalette();
|
||||
AfterGameStart( orderManager.world );
|
||||
}
|
||||
|
||||
public static void DispatchMouseInput(MouseInputEvent ev, MouseEventArgs e, Modifiers modifierKeys)
|
||||
{
|
||||
Sync.CheckSyncUnchanged( orderManager.world, () =>
|
||||
{
|
||||
var mi = new MouseInput
|
||||
{
|
||||
Button = (MouseButton)(int)e.Button,
|
||||
Event = ev,
|
||||
Location = new int2( e.Location ),
|
||||
Modifiers = modifierKeys,
|
||||
};
|
||||
Widget.HandleInput( mi );
|
||||
} );
|
||||
}
|
||||
|
||||
public static bool IsHost
|
||||
{
|
||||
get { return orderManager.Connection.LocalClientId == 0; }
|
||||
}
|
||||
|
||||
public static void HandleKeyEvent(KeyInput e)
|
||||
{
|
||||
Sync.CheckSyncUnchanged( orderManager.world, () =>
|
||||
{
|
||||
Widget.HandleKeyPress( e );
|
||||
} );
|
||||
}
|
||||
|
||||
static Modifiers modifiers;
|
||||
public static Modifiers GetModifierKeys() { return modifiers; }
|
||||
public static void HandleModifierKeys(Modifiers mods) { modifiers = mods; }
|
||||
internal static void HandleModifierKeys(Modifiers mods) { modifiers = mods; }
|
||||
|
||||
internal static void Initialize(Arguments args)
|
||||
{
|
||||
@@ -218,10 +209,6 @@ namespace OpenRA
|
||||
SupportDir = args.GetValue("SupportDir", defaultSupport);
|
||||
Settings = new Settings(SupportDir + "settings.yaml", args);
|
||||
|
||||
// force master server upgrade -- remove once everyone is switched over.
|
||||
if (Settings.Server.MasterServer == "http://open-ra.org/master/")
|
||||
Settings.Server.MasterServer = "http://master.open-ra.org/";
|
||||
|
||||
Settings.Save();
|
||||
|
||||
Log.LogPath = SupportDir + "Logs" + Path.DirectorySeparatorChar;
|
||||
@@ -235,60 +222,40 @@ namespace OpenRA
|
||||
Renderer = new Renderer();
|
||||
|
||||
Console.WriteLine("Available mods:");
|
||||
foreach(var mod in ModData.AllMods)
|
||||
foreach(var mod in Mod.AllMods)
|
||||
Console.WriteLine("\t{0}: {1} ({2})", mod.Key, mod.Value.Title, mod.Value.Version);
|
||||
|
||||
// Discard any invalid mods
|
||||
var mods = Settings.Game.Mods.Where( m => ModData.AllMods.ContainsKey( m ) ).ToArray();
|
||||
var mods = Settings.Game.Mods.Where( m => Mod.AllMods.ContainsKey( m ) ).ToArray();
|
||||
Console.WriteLine("Loading mods: {0}",string.Join(",",mods));
|
||||
|
||||
modData = new ModData( mods );
|
||||
|
||||
Sound.Initialize();
|
||||
PerfHistory.items["render"].hasNormalTick = false;
|
||||
PerfHistory.items["batches"].hasNormalTick = false;
|
||||
PerfHistory.items["text"].hasNormalTick = false;
|
||||
PerfHistory.items["cursor"].hasNormalTick = false;
|
||||
|
||||
|
||||
JoinLocal();
|
||||
StartGame(modData.Manifest.ShellmapUid);
|
||||
|
||||
Game.AfterGameStart += world => Widget.OpenWindow("INGAME_ROOT", new Dictionary<string,object>{{"world", world},{"orderManager",orderManager}});
|
||||
|
||||
Game.ConnectionStateChanged += orderManager =>
|
||||
{
|
||||
Widget.CloseWindow();
|
||||
switch( orderManager.Connection.ConnectionState )
|
||||
{
|
||||
case ConnectionState.PreConnecting:
|
||||
Widget.OpenWindow("MAINMENU_BG");
|
||||
break;
|
||||
case ConnectionState.Connecting:
|
||||
Widget.OpenWindow( "CONNECTING_BG",
|
||||
new Dictionary<string, object> { { "host", orderManager.Host }, { "port", orderManager.Port } } );
|
||||
break;
|
||||
case ConnectionState.NotConnected:
|
||||
Widget.OpenWindow( "CONNECTION_FAILED_BG",
|
||||
new Dictionary<string, object> { { "host", orderManager.Host }, { "port", orderManager.Port } } );
|
||||
break;
|
||||
case ConnectionState.Connected:
|
||||
var lobby = Widget.OpenWindow( "SERVER_LOBBY", new Dictionary<string, object> { { "orderManager", orderManager } } );
|
||||
lobby.GetWidget<ChatDisplayWidget>("CHAT_DISPLAY").ClearChat();
|
||||
lobby.GetWidget("CHANGEMAP_BUTTON").Visible = true;
|
||||
lobby.GetWidget("LOCKTEAMS_CHECKBOX").Visible = true;
|
||||
lobby.GetWidget("DISCONNECT_BUTTON").Visible = true;
|
||||
//r.GetWidget("INGAME_ROOT").GetWidget<ChatDisplayWidget>("CHAT_DISPLAY").ClearChat();
|
||||
break;
|
||||
}
|
||||
};
|
||||
StartGame(ChooseShellmap());
|
||||
|
||||
// TODO: unhardcode this
|
||||
modData.WidgetLoader.LoadWidget( new Dictionary<string,object>(), Widget.RootWidget, "PERF_BG" );
|
||||
Widget.OpenWindow("MAINMENU_BG");
|
||||
|
||||
Game.orderManager.LastTickTime = Environment.TickCount;
|
||||
}
|
||||
|
||||
static string ChooseShellmap()
|
||||
{
|
||||
var shellmaps = modData.AvailableMaps
|
||||
.Where(m => m.Value.UseAsShellmap);
|
||||
|
||||
if (shellmaps.Count() == 0)
|
||||
throw new InvalidDataException("No valid shellmaps available");
|
||||
|
||||
return shellmaps.Random(CosmeticRandom).Key;
|
||||
}
|
||||
|
||||
static bool quit;
|
||||
internal static void Run()
|
||||
{
|
||||
@@ -310,8 +277,11 @@ namespace OpenRA
|
||||
|
||||
public static void Disconnect()
|
||||
{
|
||||
if (IsHost && server != null)
|
||||
server.Shutdown();
|
||||
|
||||
orderManager.Dispose();
|
||||
var shellmap = modData.Manifest.ShellmapUid;
|
||||
var shellmap = ChooseShellmap();
|
||||
JoinLocal();
|
||||
StartGame(shellmap);
|
||||
|
||||
@@ -342,5 +312,11 @@ namespace OpenRA
|
||||
{
|
||||
return modData.ObjectCreator.CreateObject<T>( name );
|
||||
}
|
||||
|
||||
public static void CreateAndJoinServer(Settings settings, string map)
|
||||
{
|
||||
server = new Server.Server(modData, settings, map);
|
||||
JoinServer(IPAddress.Loopback.ToString(), settings.Server.ListenPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,21 +66,21 @@ namespace OpenRA
|
||||
var ret = new List<ITraitInfo>();
|
||||
var t = Traits.WithInterface<ITraitInfo>().ToList();
|
||||
int index = 0;
|
||||
while( t.Count != 0 )
|
||||
while (t.Count != 0)
|
||||
{
|
||||
if( index >= t.Count )
|
||||
throw new InvalidOperationException( "Trait prerequisites not satisfied (or prerequisite loop) Actor={0} Unresolved={1}".F(
|
||||
Name, string.Join( ",", t.Select( x => x.GetType().Name ).ToArray())));
|
||||
|
||||
var prereqs = PrerequisitesOf( t[ index ] );
|
||||
if( prereqs.Count == 0 || prereqs.All( n => ret.Any( x => x.GetType() == n || x.GetType().IsSubclassOf( n ) ) ) )
|
||||
var prereqs = PrerequisitesOf(t[index]);
|
||||
var unsatisfied = prereqs.Where(n => !ret.Any(x => x.GetType() == n || x.GetType().IsSubclassOf(n)));
|
||||
if (!unsatisfied.Any())
|
||||
{
|
||||
ret.Add( t[ index ] );
|
||||
t.RemoveAt( index );
|
||||
ret.Add(t[index]);
|
||||
t.RemoveAt(index);
|
||||
index = 0;
|
||||
}
|
||||
else
|
||||
++index;
|
||||
else if (++index >= t.Count)
|
||||
throw new InvalidOperationException("Trait prerequisites not satisfied (or prerequisite loop) Actor={0} Unresolved={1} Missing={2}".F(
|
||||
Name,
|
||||
string.Join(",", t.Select(x => x.GetType().Name).ToArray()),
|
||||
string.Join(",", unsatisfied.Select(x => x.Name).ToArray())));
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -27,9 +27,10 @@ namespace OpenRA
|
||||
|
||||
public static void LoadRules(Manifest m, Map map)
|
||||
{
|
||||
// Added support to extend the list of rules (add it to m.LocalRules)
|
||||
Info = LoadYamlRules(m.Rules, map.Rules, (k, y) => new ActorInfo(k.Key.ToLowerInvariant(), k.Value, y));
|
||||
Weapons = LoadYamlRules(m.Weapons, new List<MiniYamlNode>(), (k, _) => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value));
|
||||
Voices = LoadYamlRules(m.Voices, new List<MiniYamlNode>(), (k, _) => new VoiceInfo(k.Value));
|
||||
Weapons = LoadYamlRules(m.Weapons, map.Weapons, (k, _) => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value));
|
||||
Voices = LoadYamlRules(m.Voices, map.Voices, (k, _) => new VoiceInfo(k.Value));
|
||||
Music = LoadYamlRules(m.Music, new List<MiniYamlNode>(), (k, _) => new MusicInfo(k.Key, k.Value));
|
||||
Movies = LoadYamlRules(m.Movies, new List<MiniYamlNode>(), (k, v) => k.Value.Value);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.FileFormats.Graphics;
|
||||
using OpenRA.Server;
|
||||
|
||||
namespace OpenRA.GameRules
|
||||
{
|
||||
@@ -30,17 +31,17 @@ namespace OpenRA.GameRules
|
||||
|
||||
public class DebugSettings
|
||||
{
|
||||
public bool BotDebug = false;
|
||||
public bool PerfGraph = false;
|
||||
public bool RecordSyncReports = true;
|
||||
public bool ShowCollisions = false;
|
||||
public float LongTickThreshold = 0.001f;
|
||||
}
|
||||
|
||||
|
||||
public class GraphicSettings
|
||||
{
|
||||
public string Renderer = "Gl";
|
||||
public WindowMode Mode = WindowMode.PseudoFullscreen;
|
||||
public int2 FullscreenSize = new int2(Screen.PrimaryScreen.Bounds.Width,Screen.PrimaryScreen.Bounds.Height);
|
||||
public int2 WindowedSize = new int2(1024,768);
|
||||
public int2 FullscreenSize = new int2(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
|
||||
public int2 WindowedSize = new int2(1024, 768);
|
||||
public readonly int2 MinResolution = new int2(800, 600);
|
||||
}
|
||||
|
||||
@@ -66,6 +67,9 @@ namespace OpenRA.GameRules
|
||||
public string[] Mods = { "ra" };
|
||||
public bool MatchTimer = true;
|
||||
|
||||
// Chat settings
|
||||
public bool TeamChatToggle = false;
|
||||
|
||||
// Behaviour settings
|
||||
public bool ViewportEdgeScroll = true;
|
||||
public bool InverseDragScroll = false;
|
||||
|
||||
@@ -56,13 +56,14 @@ namespace OpenRA.Graphics
|
||||
PlayThen( sequenceName, () => PlayRepeating( CurrentSequence.Name ) );
|
||||
}
|
||||
|
||||
public void ReplaceAnim(string sequenceName)
|
||||
public bool ReplaceAnim(string sequenceName)
|
||||
{
|
||||
if (!HasSequence(sequenceName))
|
||||
return;
|
||||
return false;
|
||||
|
||||
CurrentSequence = SequenceProvider.GetSequence(name, sequenceName);
|
||||
frame %= CurrentSequence.Length;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void PlayThen( string sequenceName, Action after )
|
||||
@@ -124,14 +125,15 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeImage(string newImage)
|
||||
public void ChangeImage(string newImage, string newAnimIfMissing)
|
||||
{
|
||||
newImage = newImage.ToLowerInvariant();
|
||||
|
||||
if (name != newImage)
|
||||
{
|
||||
name = newImage.ToLowerInvariant();
|
||||
ReplaceAnim(CurrentSequence.Name);
|
||||
if (!ReplaceAnim(CurrentSequence.Name))
|
||||
ReplaceAnim(newAnimIfMissing);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
|
||||
Sprite[] LoadCursors(string filename)
|
||||
{
|
||||
{
|
||||
try
|
||||
{
|
||||
var shp = new Dune2ShpReader(FileSystem.OpenWithExts(filename, exts));
|
||||
|
||||
@@ -28,12 +28,12 @@ namespace OpenRA.Graphics
|
||||
public static Bitmap TerrainBitmap(Map map, bool actualSize)
|
||||
{
|
||||
var tileset = Rules.TileSets[map.Tileset];
|
||||
var width = map.Width;
|
||||
var height = map.Height;
|
||||
var width = map.Bounds.Width;
|
||||
var height = map.Bounds.Height;
|
||||
|
||||
if (!actualSize)
|
||||
{
|
||||
width = height = Util.NextPowerOf2(Math.Max(map.Width, map.Height));
|
||||
width = height = Util.NextPowerOf2(Math.Max(map.Bounds.Width, map.Bounds.Height));
|
||||
}
|
||||
|
||||
Bitmap terrain = new Bitmap(width, height);
|
||||
@@ -45,11 +45,11 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
int* c = (int*)bitmapData.Scan0;
|
||||
|
||||
for (var x = 0; x < map.Width; x++)
|
||||
for (var y = 0; y < map.Height; y++)
|
||||
for (var x = 0; x < map.Bounds.Width; x++)
|
||||
for (var y = 0; y < map.Bounds.Height; y++)
|
||||
{
|
||||
var mapX = x + map.TopLeft.X;
|
||||
var mapY = y + map.TopLeft.Y;
|
||||
var mapX = x + map.Bounds.Left;
|
||||
var mapY = y + map.Bounds.Top;
|
||||
var type = tileset.GetTerrainType(map.MapTiles[mapX, mapY]);
|
||||
if (!tileset.Terrain.ContainsKey(type))
|
||||
throw new InvalidDataException("Tileset {0} lacks terraintype {1}".F(tileset.Id, type));
|
||||
@@ -75,11 +75,11 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
int* c = (int*)bitmapData.Scan0;
|
||||
|
||||
for (var x = 0; x < map.Width; x++)
|
||||
for (var y = 0; y < map.Height; y++)
|
||||
for (var x = 0; x < map.Bounds.Width; x++)
|
||||
for (var y = 0; y < map.Bounds.Height; y++)
|
||||
{
|
||||
var mapX = x + map.TopLeft.X;
|
||||
var mapY = y + map.TopLeft.Y;
|
||||
var mapX = x + map.Bounds.Left;
|
||||
var mapY = y + map.Bounds.Top;
|
||||
if (map.MapResources[mapX, mapY].type == 0)
|
||||
continue;
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace OpenRA.Graphics
|
||||
public static Bitmap CustomTerrainBitmap(World world)
|
||||
{
|
||||
var map = world.Map;
|
||||
var size = Util.NextPowerOf2(Math.Max(map.Width, map.Height));
|
||||
var size = Util.NextPowerOf2(Math.Max(map.Bounds.Width, map.Bounds.Height));
|
||||
Bitmap bitmap = new Bitmap(size, size);
|
||||
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
|
||||
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
|
||||
@@ -109,11 +109,11 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
int* c = (int*)bitmapData.Scan0;
|
||||
|
||||
for (var x = 0; x < map.Width; x++)
|
||||
for (var y = 0; y < map.Height; y++)
|
||||
for (var x = 0; x < map.Bounds.Width; x++)
|
||||
for (var y = 0; y < map.Bounds.Height; y++)
|
||||
{
|
||||
var mapX = x + map.TopLeft.X;
|
||||
var mapY = y + map.TopLeft.Y;
|
||||
var mapX = x + map.Bounds.Left;
|
||||
var mapY = y + map.Bounds.Top;
|
||||
var custom = map.CustomTerrain[mapX,mapY];
|
||||
if (custom == null)
|
||||
continue;
|
||||
@@ -127,7 +127,7 @@ namespace OpenRA.Graphics
|
||||
public static Bitmap ActorsBitmap(World world)
|
||||
{
|
||||
var map = world.Map;
|
||||
var size = Util.NextPowerOf2(Math.Max(map.Width, map.Height));
|
||||
var size = Util.NextPowerOf2(Math.Max(map.Bounds.Width, map.Bounds.Height));
|
||||
Bitmap bitmap = new Bitmap(size, size);
|
||||
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
|
||||
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
|
||||
@@ -135,16 +135,16 @@ namespace OpenRA.Graphics
|
||||
unsafe
|
||||
{
|
||||
int* c = (int*)bitmapData.Scan0;
|
||||
|
||||
foreach (var t in world.Queries.WithTraitMultiple<IRadarSignature>())
|
||||
|
||||
foreach (var t in world.Queries.WithTrait<IRadarSignature>())
|
||||
{
|
||||
if (!t.Actor.IsVisible(world.LocalPlayer))
|
||||
if (!world.LocalShroud.IsVisible(t.Actor))
|
||||
continue;
|
||||
|
||||
var color = t.Trait.RadarSignatureColor(t.Actor);
|
||||
foreach (var cell in t.Trait.RadarSignatureCells(t.Actor))
|
||||
if (world.Map.IsInMap(cell))
|
||||
*(c + ((cell.Y - world.Map.TopLeft.Y) * bitmapData.Stride >> 2) + cell.X - world.Map.TopLeft.X) = color.ToArgb();
|
||||
*(c + ((cell.Y - world.Map.Bounds.Top) * bitmapData.Stride >> 2) + cell.X - world.Map.Bounds.Left) = color.ToArgb();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,26 +155,29 @@ namespace OpenRA.Graphics
|
||||
public static Bitmap ShroudBitmap(World world)
|
||||
{
|
||||
var map = world.Map;
|
||||
var size = Util.NextPowerOf2(Math.Max(map.Width, map.Height));
|
||||
var size = Util.NextPowerOf2(Math.Max(map.Bounds.Width, map.Bounds.Height));
|
||||
Bitmap bitmap = new Bitmap(size, size);
|
||||
if (world.LocalShroud.Disabled)
|
||||
return bitmap;
|
||||
|
||||
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
|
||||
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
|
||||
|
||||
var shroud = Color.Black.ToArgb();
|
||||
var fog = Color.FromArgb(128, Color.Black).ToArgb();
|
||||
|
||||
|
||||
unsafe
|
||||
{
|
||||
int* c = (int*)bitmapData.Scan0;
|
||||
|
||||
for (var x = 0; x < map.Width; x++)
|
||||
for (var y = 0; y < map.Height; y++)
|
||||
for (var x = 0; x < map.Bounds.Width; x++)
|
||||
for (var y = 0; y < map.Bounds.Height; y++)
|
||||
{
|
||||
var mapX = x + map.TopLeft.X;
|
||||
var mapY = y + map.TopLeft.Y;
|
||||
if (!world.LocalPlayer.Shroud.IsExplored(mapX, mapY))
|
||||
var mapX = x + map.Bounds.Left;
|
||||
var mapY = y + map.Bounds.Top;
|
||||
if (!world.LocalShroud.IsExplored(mapX, mapY))
|
||||
*(c + (y * bitmapData.Stride >> 2) + x) = shroud;
|
||||
else if (!world.LocalPlayer.Shroud.IsVisible(mapX,mapY))
|
||||
else if (!world.LocalShroud.IsVisible(mapX,mapY))
|
||||
*(c + (y * bitmapData.Stride >> 2) + x) = fog;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.FileFormats.Graphics;
|
||||
using OpenRA.Support;
|
||||
using System.Windows.Forms;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
@@ -36,7 +36,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public ITexture PaletteTexture;
|
||||
|
||||
public readonly SpriteFont RegularFont, BoldFont, TitleFont;
|
||||
public readonly SpriteFont RegularFont, BoldFont, TitleFont, TinyFont;
|
||||
|
||||
internal const int TempBufferSize = 8192;
|
||||
const int TempBufferCount = 8;
|
||||
@@ -46,10 +46,10 @@ namespace OpenRA.Graphics
|
||||
|
||||
public Renderer()
|
||||
{
|
||||
SpriteShader = device.CreateShader(FileSystem.Open("shaders/world-shp.fx"));
|
||||
LineShader = device.CreateShader(FileSystem.Open("shaders/line.fx"));
|
||||
RgbaSpriteShader = device.CreateShader(FileSystem.Open("shaders/chrome-rgba.fx"));
|
||||
WorldSpriteShader = device.CreateShader(FileSystem.Open("shaders/chrome-shp.fx"));
|
||||
SpriteShader = device.CreateShader("world-shp");
|
||||
LineShader = device.CreateShader("world-line");
|
||||
RgbaSpriteShader = device.CreateShader("chrome-rgba");
|
||||
WorldSpriteShader = device.CreateShader("chrome-shp");
|
||||
|
||||
SpriteRenderer = new SpriteRenderer( this, SpriteShader );
|
||||
RgbaSpriteRenderer = new SpriteRenderer( this, RgbaSpriteShader );
|
||||
@@ -59,6 +59,7 @@ namespace OpenRA.Graphics
|
||||
RegularFont = new SpriteFont("FreeSans.ttf", 14);
|
||||
BoldFont = new SpriteFont("FreeSansBold.ttf", 14);
|
||||
TitleFont = new SpriteFont("titles.ttf", 48);
|
||||
TinyFont = new SpriteFont("FreeSans.ttf", 10);
|
||||
|
||||
for( int i = 0 ; i < TempBufferCount ; i++ )
|
||||
{
|
||||
@@ -71,7 +72,6 @@ namespace OpenRA.Graphics
|
||||
|
||||
public void BeginFrame(float2 scroll)
|
||||
{
|
||||
device.Begin();
|
||||
device.Clear(Color.Black);
|
||||
|
||||
float2 r1 = new float2(2f/Resolution.Width, -2f/Resolution.Height);
|
||||
@@ -86,17 +86,16 @@ namespace OpenRA.Graphics
|
||||
private void SetShaderParams( IShader s, float2 r1, float2 r2, float2 scroll )
|
||||
{
|
||||
s.SetValue( "Palette", PaletteTexture );
|
||||
s.SetValue( "Scroll", scroll.X, scroll.Y );
|
||||
s.SetValue( "Scroll", (int) scroll.X, (int) scroll.Y );
|
||||
s.SetValue( "r1", r1.X, r1.Y );
|
||||
s.SetValue( "r2", r2.X, r2.Y );
|
||||
s.Commit();
|
||||
}
|
||||
|
||||
public void EndFrame()
|
||||
public void EndFrame( IInputHandler inputHandler )
|
||||
{
|
||||
Flush();
|
||||
device.End();
|
||||
device.Present();
|
||||
device.Present( inputHandler );
|
||||
}
|
||||
|
||||
public void DrawBatch<T>(IVertexBuffer<T> vertices, IIndexBuffer indices,
|
||||
@@ -135,14 +134,14 @@ namespace OpenRA.Graphics
|
||||
internal static void Initialize( OpenRA.FileFormats.Graphics.WindowMode windowMode )
|
||||
{
|
||||
var resolution = GetResolution( windowMode );
|
||||
device = CreateDevice( Assembly.LoadFile( Path.GetFullPath( "OpenRA.Gl.dll" ) ), resolution.Width, resolution.Height, windowMode, false );
|
||||
device = CreateDevice( Assembly.LoadFile( Path.GetFullPath( "OpenRA.Renderer.{0}.dll".F(Game.Settings.Graphics.Renderer) ) ), resolution.Width, resolution.Height, windowMode, false );
|
||||
}
|
||||
|
||||
static Size GetResolution(WindowMode windowmode)
|
||||
{
|
||||
var desktopResolution = Screen.PrimaryScreen.Bounds.Size;
|
||||
var desktopResolution = Screen.PrimaryScreen.Bounds.Size;
|
||||
var customSize = (windowmode == WindowMode.Windowed) ? Game.Settings.Graphics.WindowedSize : Game.Settings.Graphics.FullscreenSize;
|
||||
|
||||
|
||||
if (customSize.X > 0 && customSize.Y > 0)
|
||||
{
|
||||
desktopResolution.Width = customSize.X;
|
||||
|
||||
@@ -21,15 +21,13 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
static Dictionary<string, Dictionary<string, Sequence>> units;
|
||||
|
||||
public static void Initialize(string[] sequenceFiles)
|
||||
public static void Initialize(string[] sequenceFiles, List<MiniYamlNode> sequenceNodes)
|
||||
{
|
||||
units = new Dictionary<string, Dictionary<string, Sequence>>();
|
||||
if (sequenceFiles.Length == 0)
|
||||
return;
|
||||
|
||||
var sequences = sequenceFiles
|
||||
.Select(s => MiniYaml.FromFile(s))
|
||||
.Aggregate(MiniYaml.Merge);
|
||||
var sequences = sequenceFiles.Select(s => MiniYaml.FromFile(s)).Aggregate(sequenceNodes, MiniYaml.Merge);
|
||||
|
||||
foreach (var s in sequences)
|
||||
LoadSequencesForUnit(s.Key, s.Value);
|
||||
|
||||
161
OpenRA.Game/Graphics/ShroudRenderer.cs
Normal file
161
OpenRA.Game/Graphics/ShroudRenderer.cs
Normal file
@@ -0,0 +1,161 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 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. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Drawing;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
public class ShroudRenderer
|
||||
{
|
||||
Traits.Shroud shroud;
|
||||
Sprite[] shadowBits = SpriteSheetBuilder.LoadAllSprites("shadow");
|
||||
Sprite[,] sprites, fogSprites;
|
||||
|
||||
bool dirty = true;
|
||||
Map map;
|
||||
|
||||
public ShroudRenderer(World world)
|
||||
{
|
||||
this.shroud = world.LocalShroud;
|
||||
this.map = world.Map;
|
||||
|
||||
sprites = new Sprite[map.MapSize.X, map.MapSize.Y];
|
||||
fogSprites = new Sprite[map.MapSize.X, map.MapSize.Y];
|
||||
shroud.Dirty += () => dirty = true;
|
||||
}
|
||||
|
||||
static readonly byte[][] SpecialShroudTiles =
|
||||
{
|
||||
new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
|
||||
new byte[] { 32, 32, 25, 25, 19, 19, 20, 20 },
|
||||
new byte[] { 33, 33, 33, 33, 26, 26, 26, 26, 21, 21, 21, 21, 23, 23, 23, 23 },
|
||||
new byte[] { 36, 36, 36, 36, 30, 30, 30, 30 },
|
||||
new byte[] { 34, 16, 34, 16, 34, 16, 34, 16, 27, 22, 27, 22, 27, 22, 27, 22 },
|
||||
new byte[] { 44 },
|
||||
new byte[] { 37, 37, 37, 37, 37, 37, 37, 37, 31, 31, 31, 31, 31, 31, 31, 31 },
|
||||
new byte[] { 40 },
|
||||
new byte[] { 35, 24, 17, 18 },
|
||||
new byte[] { 39, 39, 29, 29 },
|
||||
new byte[] { 45 },
|
||||
new byte[] { 43 },
|
||||
new byte[] { 38, 28 },
|
||||
new byte[] { 42 },
|
||||
new byte[] { 41 },
|
||||
new byte[] { 46 },
|
||||
};
|
||||
|
||||
Sprite ChooseShroud(int i, int j)
|
||||
{
|
||||
if( !shroud.IsExplored( i, j ) ) return shadowBits[ 0xf ];
|
||||
|
||||
// bits are for unexploredness: up, right, down, left
|
||||
var v = 0;
|
||||
// bits are for unexploredness: TL, TR, BR, BL
|
||||
var u = 0;
|
||||
|
||||
if( !shroud.IsExplored( i, j - 1 ) ) { v |= 1; u |= 3; }
|
||||
if( !shroud.IsExplored( i + 1, j ) ) { v |= 2; u |= 6; }
|
||||
if( !shroud.IsExplored( i, j + 1 ) ) { v |= 4; u |= 12; }
|
||||
if( !shroud.IsExplored( i - 1, j ) ) { v |= 8; u |= 9; }
|
||||
|
||||
var uSides = u;
|
||||
|
||||
if( !shroud.IsExplored( i - 1, j - 1 ) ) u |= 1;
|
||||
if( !shroud.IsExplored( i + 1, j - 1 ) ) u |= 2;
|
||||
if( !shroud.IsExplored( i + 1, j + 1 ) ) u |= 4;
|
||||
if( !shroud.IsExplored( i - 1, j + 1 ) ) u |= 8;
|
||||
|
||||
return shadowBits[ SpecialShroudTiles[ u ^ uSides ][ v ] ];
|
||||
}
|
||||
|
||||
Sprite ChooseFog(int i, int j)
|
||||
{
|
||||
if (!shroud.IsVisible(i,j)) return shadowBits[0xf];
|
||||
if (!shroud.IsExplored(i, j)) return shadowBits[0xf];
|
||||
|
||||
// bits are for unexploredness: up, right, down, left
|
||||
var v = 0;
|
||||
// bits are for unexploredness: TL, TR, BR, BL
|
||||
var u = 0;
|
||||
|
||||
if (!shroud.IsVisible(i, j - 1)) { v |= 1; u |= 3; }
|
||||
if (!shroud.IsVisible(i + 1, j)) { v |= 2; u |= 6; }
|
||||
if (!shroud.IsVisible(i, j + 1)) { v |= 4; u |= 12; }
|
||||
if (!shroud.IsVisible(i - 1, j)) { v |= 8; u |= 9; }
|
||||
|
||||
var uSides = u;
|
||||
|
||||
if (!shroud.IsVisible(i - 1, j - 1)) u |= 1;
|
||||
if (!shroud.IsVisible(i + 1, j - 1)) u |= 2;
|
||||
if (!shroud.IsVisible(i + 1, j + 1)) u |= 4;
|
||||
if (!shroud.IsVisible(i - 1, j + 1)) u |= 8;
|
||||
|
||||
return shadowBits[SpecialShroudTiles[u ^ uSides][v]];
|
||||
}
|
||||
|
||||
internal void Draw( WorldRenderer wr )
|
||||
{
|
||||
if (dirty)
|
||||
{
|
||||
dirty = false;
|
||||
for (int i = map.Bounds.Left; i < map.Bounds.Right; i++)
|
||||
for (int j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
|
||||
sprites[i, j] = ChooseShroud(i, j);
|
||||
|
||||
for (int i = map.Bounds.Left; i < map.Bounds.Right; i++)
|
||||
for (int j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
|
||||
fogSprites[i, j] = ChooseFog(i, j);
|
||||
}
|
||||
|
||||
var clipRect = Game.viewport.WorldBounds(wr.world);
|
||||
DrawShroud( wr, clipRect, fogSprites, "fog" );
|
||||
DrawShroud( wr, clipRect, sprites, "shroud" );
|
||||
}
|
||||
|
||||
void DrawShroud( WorldRenderer wr, Rectangle clip, Sprite[,] s, string pal )
|
||||
{
|
||||
var shroudPalette = wr.GetPaletteIndex(pal);
|
||||
|
||||
for (var j = clip.Top; j < clip.Bottom; j++)
|
||||
{
|
||||
var starti = clip.Left;
|
||||
var last = shadowBits[0x0f];
|
||||
for (var i = clip.Left; i < clip.Right; i++)
|
||||
{
|
||||
if ((s[i, j] == shadowBits[0x0f] && last == shadowBits[0x0f])
|
||||
|| (s[i, j] == shadowBits[0] && last == shadowBits[0]))
|
||||
continue;
|
||||
|
||||
if (starti != i)
|
||||
{
|
||||
s[starti, j].DrawAt(
|
||||
Game.CellSize * new float2(starti, j),
|
||||
shroudPalette,
|
||||
new float2(Game.CellSize * (i - starti), Game.CellSize));
|
||||
starti = i + 1;
|
||||
}
|
||||
|
||||
s[i, j].DrawAt(
|
||||
Game.CellSize * new float2(i, j),
|
||||
shroudPalette);
|
||||
starti = i + 1;
|
||||
last = s[i, j];
|
||||
}
|
||||
|
||||
if (starti < clip.Right)
|
||||
s[starti, j].DrawAt(
|
||||
Game.CellSize * new float2(starti, j),
|
||||
shroudPalette,
|
||||
new float2(Game.CellSize * (clip.Right - starti), Game.CellSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,11 @@ namespace OpenRA.Graphics
|
||||
Game.Renderer.SpriteRenderer.DrawSprite( this, location, paletteIndex, this.size );
|
||||
}
|
||||
|
||||
public void DrawAt(float2 location, int paletteIndex, float scale)
|
||||
{
|
||||
Game.Renderer.SpriteRenderer.DrawSprite(this, location, paletteIndex, this.size * scale);
|
||||
}
|
||||
|
||||
public void DrawAt( float2 location, int paletteIndex, float2 size )
|
||||
{
|
||||
Game.Renderer.SpriteRenderer.DrawSprite( this, location, paletteIndex, size );
|
||||
|
||||
@@ -34,15 +34,16 @@ namespace OpenRA.Graphics
|
||||
var tileMapping = new Cache<TileReference<ushort,byte>, Sprite>(
|
||||
x => Game.modData.SheetBuilder.Add(world.TileSet.GetBytes(x), tileSize));
|
||||
|
||||
Vertex[] vertices = new Vertex[4 * map.Height * map.Width];
|
||||
ushort[] indices = new ushort[6 * map.Height * map.Width];
|
||||
Vertex[] vertices = new Vertex[4 * map.Bounds.Height * map.Bounds.Width];
|
||||
ushort[] indices = new ushort[6 * map.Bounds.Height * map.Bounds.Width];
|
||||
|
||||
terrainSheet = tileMapping[map.MapTiles[map.TopLeft.X, map.TopLeft.Y]].sheet;
|
||||
terrainSheet = tileMapping[map.MapTiles[map.Bounds.Left, map.Bounds.Top]].sheet;
|
||||
|
||||
int nv = 0;
|
||||
int ni = 0;
|
||||
for( int j = map.TopLeft.Y ; j < map.BottomRight.Y; j++ )
|
||||
for( int i = map.TopLeft.X ; i < map.BottomRight.X; i++ )
|
||||
|
||||
for( int j = map.Bounds.Top; j < map.Bounds.Bottom; j++ )
|
||||
for( int i = map.Bounds.Left; i < map.Bounds.Right; i++ )
|
||||
{
|
||||
Sprite tile = tileMapping[map.MapTiles[i, j]];
|
||||
// TODO: The zero below should explicitly refer to the terrain palette, but this code is called
|
||||
@@ -64,28 +65,28 @@ namespace OpenRA.Graphics
|
||||
|
||||
public void Draw( WorldRenderer wr, Viewport viewport )
|
||||
{
|
||||
int indicesPerRow = map.Width * 6;
|
||||
int verticesPerRow = map.Width * 4;
|
||||
int indicesPerRow = map.Bounds.Width * 6;
|
||||
int verticesPerRow = map.Bounds.Width * 4;
|
||||
|
||||
int visibleRows = (int)(viewport.Height * 1f / Game.CellSize + 2);
|
||||
|
||||
int firstRow = (int)(viewport.Location.Y * 1f / Game.CellSize - map.YOffset);
|
||||
int firstRow = (int)(viewport.Location.Y * 1f / Game.CellSize - map.Bounds.Top);
|
||||
int lastRow = firstRow + visibleRows;
|
||||
|
||||
if (lastRow < 0 || firstRow > map.Height)
|
||||
if (lastRow < 0 || firstRow > map.Bounds.Height)
|
||||
return;
|
||||
|
||||
if (firstRow < 0) firstRow = 0;
|
||||
if (lastRow > map.Height) lastRow = map.Height;
|
||||
if (lastRow > map.Bounds.Height) lastRow = map.Bounds.Height;
|
||||
|
||||
if (world.LocalPlayer != null && !world.LocalPlayer.Shroud.Disabled && world.LocalPlayer.Shroud.Bounds.HasValue)
|
||||
if (world.LocalPlayer != null && !world.LocalShroud.Disabled && world.LocalShroud.Bounds.HasValue)
|
||||
{
|
||||
var r = world.LocalPlayer.Shroud.Bounds.Value;
|
||||
if (firstRow < r.Top - map.YOffset)
|
||||
firstRow = r.Top - map.YOffset;
|
||||
var r = world.LocalShroud.Bounds.Value;
|
||||
if (firstRow < r.Top - map.Bounds.Top)
|
||||
firstRow = r.Top - map.Bounds.Top;
|
||||
|
||||
if (firstRow > r.Bottom - map.YOffset)
|
||||
firstRow = r.Bottom - map.YOffset;
|
||||
if (firstRow > r.Bottom - map.Bounds.Top)
|
||||
firstRow = r.Bottom - map.Bounds.Top;
|
||||
}
|
||||
|
||||
if( lastRow < firstRow ) lastRow = firstRow;
|
||||
|
||||
114
OpenRA.Game/Graphics/Viewport.cs
Normal file → Executable file
114
OpenRA.Game/Graphics/Viewport.cs
Normal file → Executable file
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
@@ -18,16 +19,15 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
public class Viewport
|
||||
{
|
||||
readonly float2 screenSize;
|
||||
float2 scrollPosition;
|
||||
readonly int2 screenSize;
|
||||
int2 scrollPosition;
|
||||
readonly Renderer renderer;
|
||||
readonly int2 mapStart;
|
||||
readonly int2 mapEnd;
|
||||
readonly Rectangle adjustedMapBounds;
|
||||
|
||||
public float2 Location { get { return scrollPosition; } }
|
||||
|
||||
public int Width { get { return (int)screenSize.X; } }
|
||||
public int Height { get { return (int)screenSize.Y; } }
|
||||
public int Width { get { return screenSize.X; } }
|
||||
public int Height { get { return screenSize.Y; } }
|
||||
|
||||
float cursorFrame = 0f;
|
||||
|
||||
@@ -41,7 +41,8 @@ namespace OpenRA.Graphics
|
||||
|
||||
public void Scroll(float2 delta, bool ignoreBorders)
|
||||
{
|
||||
float2 newScrollPosition = scrollPosition + delta;
|
||||
var d = delta.ToInt2();
|
||||
var newScrollPosition = scrollPosition + d;
|
||||
|
||||
if(!ignoreBorders)
|
||||
newScrollPosition = this.NormalizeScrollPosition(newScrollPosition);
|
||||
@@ -49,64 +50,47 @@ namespace OpenRA.Graphics
|
||||
scrollPosition = newScrollPosition;
|
||||
}
|
||||
|
||||
private float2 NormalizeScrollPosition(float2 newScrollPosition)
|
||||
private int2 NormalizeScrollPosition(int2 newScrollPosition)
|
||||
{
|
||||
float2 topLeftBorder = (Game.CellSize* mapStart).ToFloat2();
|
||||
float2 bottomRightBorder = (Game.CellSize* mapEnd).ToFloat2();
|
||||
|
||||
if(newScrollPosition.Y < topLeftBorder.Y - screenSize.Y/2)
|
||||
newScrollPosition.Y = topLeftBorder.Y - screenSize.Y/2;
|
||||
if(newScrollPosition.X < topLeftBorder.X - screenSize.X/2)
|
||||
newScrollPosition.X = topLeftBorder.X - screenSize.X/2;
|
||||
if(newScrollPosition.Y > bottomRightBorder.Y - screenSize.Y/2)
|
||||
newScrollPosition.Y = bottomRightBorder.Y - screenSize.Y/2;
|
||||
if(newScrollPosition.X > bottomRightBorder.X - screenSize.X/2)
|
||||
newScrollPosition.X = bottomRightBorder.X - screenSize.X/2;
|
||||
|
||||
return newScrollPosition;
|
||||
return newScrollPosition.Clamp(adjustedMapBounds);
|
||||
}
|
||||
|
||||
public ScrollDirection GetBlockedDirections()
|
||||
{
|
||||
int2 topLeftBorder = (Game.CellSize* mapStart);
|
||||
int2 bottomRightBorder = (Game.CellSize* mapEnd);
|
||||
|
||||
ScrollDirection blockedDirections = ScrollDirection.None;
|
||||
|
||||
if(scrollPosition.Y <= topLeftBorder.Y - screenSize.Y/2)
|
||||
if(scrollPosition.Y <= adjustedMapBounds.Top)
|
||||
blockedDirections = blockedDirections.Set(ScrollDirection.Up, true);
|
||||
if(scrollPosition.X <= topLeftBorder.X - screenSize.X/2)
|
||||
if(scrollPosition.X <= adjustedMapBounds.Left)
|
||||
blockedDirections = blockedDirections.Set(ScrollDirection.Left, true);
|
||||
if(scrollPosition.Y >= bottomRightBorder.Y - screenSize.Y/2)
|
||||
if(scrollPosition.Y >= adjustedMapBounds.Bottom)
|
||||
blockedDirections = blockedDirections.Set(ScrollDirection.Down, true);
|
||||
if(scrollPosition.X >= bottomRightBorder.X - screenSize.X/2)
|
||||
if(scrollPosition.X >= adjustedMapBounds.Right)
|
||||
blockedDirections = blockedDirections.Set(ScrollDirection.Right, true);
|
||||
|
||||
return blockedDirections;
|
||||
}
|
||||
|
||||
public Viewport(float2 screenSize, int2 mapStart, int2 mapEnd, Renderer renderer)
|
||||
public Viewport(int2 screenSize, Rectangle mapBounds, Renderer renderer)
|
||||
{
|
||||
this.screenSize = screenSize;
|
||||
this.renderer = renderer;
|
||||
this.mapStart = mapStart;
|
||||
this.mapEnd = mapEnd;
|
||||
|
||||
this.scrollPosition = Game.CellSize* mapStart;
|
||||
this.adjustedMapBounds = new Rectangle(Game.CellSize*mapBounds.X - screenSize.X/2,
|
||||
Game.CellSize*mapBounds.Y - screenSize.Y/2,
|
||||
Game.CellSize*mapBounds.Width,
|
||||
Game.CellSize*mapBounds.Height);
|
||||
this.scrollPosition = new int2(adjustedMapBounds.Location) + new int2(adjustedMapBounds.Size)/2;
|
||||
}
|
||||
|
||||
public void DrawRegions( WorldRenderer wr )
|
||||
public void DrawRegions( WorldRenderer wr, IInputHandler inputHandler )
|
||||
{
|
||||
renderer.BeginFrame(scrollPosition);
|
||||
|
||||
wr.Draw();
|
||||
|
||||
Widget.DoDraw( wr );
|
||||
|
||||
var cursorName = Widget.RootWidget.GetCursorOuter(Viewport.LastMousePos) ?? "default";
|
||||
var c = new Cursor(cursorName);
|
||||
c.Draw(wr, (int)cursorFrame, Viewport.LastMousePos + Location);
|
||||
new Cursor(cursorName).Draw(wr, (int)cursorFrame, Viewport.LastMousePos + Location);
|
||||
|
||||
renderer.EndFrame();
|
||||
renderer.EndFrame( inputHandler );
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
@@ -123,9 +107,9 @@ namespace OpenRA.Graphics
|
||||
return ViewToWorld(mi.Location);
|
||||
}
|
||||
|
||||
public void Center(int2 loc)
|
||||
public void Center(float2 loc)
|
||||
{
|
||||
scrollPosition = this.NormalizeScrollPosition(Game.CellSize*loc - .5f * new float2(Width, Height));
|
||||
scrollPosition = this.NormalizeScrollPosition((Game.CellSize*loc - screenSize / 2).ToInt2());
|
||||
}
|
||||
|
||||
public void Center(IEnumerable<Actor> actors)
|
||||
@@ -136,24 +120,40 @@ namespace OpenRA.Graphics
|
||||
.Select(a => a.CenterLocation)
|
||||
.Aggregate((a, b) => a + b);
|
||||
|
||||
scrollPosition = this.NormalizeScrollPosition((avgPos - .5f * new float2(Width, Height)));
|
||||
}
|
||||
|
||||
public Rectangle ShroudBounds( World world )
|
||||
{
|
||||
var localPlayer = world.LocalPlayer;
|
||||
if( localPlayer == null ) return world.Map.Bounds;
|
||||
if( localPlayer.Shroud.Disabled ) return world.Map.Bounds;
|
||||
if( !localPlayer.Shroud.Bounds.HasValue ) return world.Map.Bounds;
|
||||
return Rectangle.Intersect( localPlayer.Shroud.Bounds.Value, world.Map.Bounds );
|
||||
scrollPosition = this.NormalizeScrollPosition((avgPos.ToInt2() - screenSize / 2));
|
||||
}
|
||||
|
||||
public Rectangle ViewBounds()
|
||||
public Rectangle ViewBounds(World world)
|
||||
{
|
||||
int2 boundary = new int2(1,1); // Add a curtain of cells around the viewport to account for rounding errors
|
||||
var tl = ViewToWorld(int2.Zero).ToInt2() - boundary;
|
||||
var br = ViewToWorld(new int2(Width, Height)).ToInt2() + boundary;
|
||||
return Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y);
|
||||
var r = WorldBounds(world);
|
||||
var left = (int)(Game.CellSize * r.Left - Game.viewport.Location.X);
|
||||
var top = (int)(Game.CellSize * r.Top - Game.viewport.Location.Y);
|
||||
var right = left + (int)(Game.CellSize * r.Width);
|
||||
var bottom = top + (int)(Game.CellSize * r.Height);
|
||||
|
||||
if (left < 0) left = 0;
|
||||
if (top < 0) top = 0;
|
||||
if (right > Game.viewport.Width) right = Game.viewport.Width;
|
||||
if (bottom > Game.viewport.Height) bottom = Game.viewport.Height;
|
||||
return new Rectangle(left, top, right - left, bottom - top);
|
||||
}
|
||||
|
||||
int2 cachedScroll = new int2(int.MaxValue, int.MaxValue);
|
||||
Rectangle cachedRect;
|
||||
|
||||
public Rectangle WorldBounds(World world)
|
||||
{
|
||||
if (cachedScroll != scrollPosition)
|
||||
{
|
||||
int2 boundary = new int2(1,1); // Add a curtain of cells around the viewport to account for rounding errors
|
||||
var tl = ViewToWorld(int2.Zero).ToInt2() - boundary;
|
||||
var br = ViewToWorld(new int2(Width, Height)).ToInt2() + boundary;
|
||||
cachedRect = Rectangle.Intersect(Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y), world.Map.Bounds);
|
||||
cachedScroll = scrollPosition;
|
||||
}
|
||||
|
||||
var b = world.LocalShroud.Bounds;
|
||||
return (b.HasValue) ? Rectangle.Intersect(cachedRect, b.Value) : cachedRect;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
public readonly World world;
|
||||
internal readonly TerrainRenderer terrainRenderer;
|
||||
internal readonly ShroudRenderer shroudRenderer;
|
||||
|
||||
public readonly UiOverlay uiOverlay;
|
||||
internal readonly HardwarePalette palette;
|
||||
|
||||
@@ -29,6 +31,7 @@ namespace OpenRA.Graphics
|
||||
this.world = world;
|
||||
|
||||
terrainRenderer = new TerrainRenderer(world, this);
|
||||
shroudRenderer = new ShroudRenderer(world);
|
||||
uiOverlay = new UiOverlay();
|
||||
palette = new HardwarePalette(world.Map);
|
||||
|
||||
@@ -48,32 +51,9 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle GetBoundsRect()
|
||||
IEnumerable<Renderable> SpritesToRender()
|
||||
{
|
||||
if (world.LocalPlayer != null && !world.LocalPlayer.Shroud.Disabled && world.LocalPlayer.Shroud.Bounds.HasValue)
|
||||
{
|
||||
var r = world.LocalPlayer.Shroud.Bounds.Value;
|
||||
|
||||
var left = (int)(Game.CellSize * r.Left - Game.viewport.Location.X);
|
||||
var top = (int)(Game.CellSize * r.Top - Game.viewport.Location.Y);
|
||||
var right = left + (int)(Game.CellSize * r.Width);
|
||||
var bottom = top + (int)(Game.CellSize * r.Height);
|
||||
|
||||
if (left < 0) left = 0;
|
||||
if (top < 0) top = 0;
|
||||
if (right > Game.viewport.Width) right = Game.viewport.Width;
|
||||
if (bottom > Game.viewport.Height) bottom = Game.viewport.Height;
|
||||
|
||||
return new Rectangle(left, top, right - left, bottom - top);
|
||||
}
|
||||
else
|
||||
return new Rectangle(0, 0, Game.viewport.Width, Game.viewport.Height);
|
||||
}
|
||||
|
||||
Renderable[] worldSprites = { };
|
||||
public void Tick()
|
||||
{
|
||||
var bounds = GetBoundsRect();
|
||||
var bounds = Game.viewport.ViewBounds(world);
|
||||
var comparer = new SpriteComparer();
|
||||
|
||||
bounds.Offset((int)Game.viewport.Location.X, (int)Game.viewport.Location.Y);
|
||||
@@ -87,31 +67,52 @@ namespace OpenRA.Graphics
|
||||
|
||||
var effects = world.Effects.SelectMany(e => e.Render());
|
||||
|
||||
worldSprites = renderables.Concat(effects).ToArray();
|
||||
return renderables.Concat(effects);
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
RefreshPalette();
|
||||
var bounds = GetBoundsRect();
|
||||
var bounds = Game.viewport.ViewBounds(world);
|
||||
Game.Renderer.EnableScissor(bounds.Left, bounds.Top, bounds.Width, bounds.Height);
|
||||
|
||||
terrainRenderer.Draw(this, Game.viewport);
|
||||
foreach (var a in world.traitDict.ActorsWithTraitMultiple<IRenderAsTerrain>(world))
|
||||
foreach (var r in a.Trait.RenderAsTerrain(a.Actor))
|
||||
r.Sprite.DrawAt(r.Pos, this.GetPaletteIndex(r.Palette), r.Scale);
|
||||
|
||||
foreach (var a in world.Selection.Actors)
|
||||
if (!a.Destroyed)
|
||||
foreach (var t in a.TraitsImplementing<IPreRenderSelection>())
|
||||
t.RenderBeforeWorld(this, a);
|
||||
|
||||
Game.Renderer.Flush();
|
||||
|
||||
if (world.OrderGenerator != null)
|
||||
world.OrderGenerator.RenderBeforeWorld(this, world);
|
||||
|
||||
foreach (var image in SpritesToRender() )
|
||||
image.Sprite.DrawAt(image.Pos, this.GetPaletteIndex(image.Palette), image.Scale);
|
||||
uiOverlay.Draw(this, world);
|
||||
|
||||
foreach( var image in worldSprites )
|
||||
image.Sprite.DrawAt( image.Pos, this.GetPaletteIndex( image.Palette ) );
|
||||
uiOverlay.Draw(this, world);
|
||||
// added for contrails
|
||||
foreach (var a in world.Actors)
|
||||
if (!a.Destroyed)
|
||||
foreach (var t in a.TraitsImplementing<IPostRender>())
|
||||
t.RenderAfterWorld(this, a);
|
||||
|
||||
if (world.OrderGenerator != null)
|
||||
world.OrderGenerator.RenderAfterWorld(this, world);
|
||||
|
||||
if (world.LocalPlayer != null)
|
||||
world.LocalPlayer.Shroud.Draw( this );
|
||||
|
||||
shroudRenderer.Draw( this );
|
||||
Game.Renderer.DisableScissor();
|
||||
|
||||
foreach (var a in world.Selection.Actors)
|
||||
if (!a.Destroyed)
|
||||
foreach (var t in a.TraitsImplementing<IPostRenderSelection>())
|
||||
t.RenderAfterWorld(this, a);
|
||||
|
||||
Game.Renderer.Flush();
|
||||
}
|
||||
|
||||
void DrawBox(RectangleF r, Color color)
|
||||
@@ -125,20 +126,6 @@ namespace OpenRA.Graphics
|
||||
Game.Renderer.LineRenderer.DrawLine(a, a + c, color, color);
|
||||
}
|
||||
|
||||
void DrawBins(RectangleF bounds)
|
||||
{
|
||||
DrawBox(bounds, Color.Red);
|
||||
if (world.LocalPlayer != null)
|
||||
DrawBox(world.LocalPlayer.Shroud.Bounds.Value, Color.Blue);
|
||||
|
||||
for (var j = 0; j < world.Map.MapSize.Y;
|
||||
j += world.WorldActor.Info.Traits.Get<SpatialBinsInfo>().BinSize)
|
||||
{
|
||||
Game.Renderer.LineRenderer.DrawLine(new float2(0, j * Game.CellSize), new float2(world.Map.MapSize.X * Game.CellSize, j * Game.CellSize), Color.Black, Color.Black);
|
||||
Game.Renderer.LineRenderer.DrawLine(new float2(j * Game.CellSize, 0), new float2(j * Game.CellSize, world.Map.MapSize.Y * Game.CellSize), Color.Black, Color.Black);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawSelectionBox(Actor selectedUnit, Color c)
|
||||
{
|
||||
var bounds = selectedUnit.GetBounds(true);
|
||||
@@ -179,7 +166,7 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawRangeCircle(Color c, float2 location, int range)
|
||||
public void DrawRangeCircle(Color c, float2 location, float range)
|
||||
{
|
||||
var prev = location + Game.CellSize * range * float2.FromAngle(0);
|
||||
for (var i = 1; i <= 32; i++)
|
||||
|
||||
52
OpenRA.Game/InputHandler.cs
Executable file
52
OpenRA.Game/InputHandler.cs
Executable file
@@ -0,0 +1,52 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 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. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public class NullInputHandler : IInputHandler
|
||||
{
|
||||
// ignore all input
|
||||
public void ModifierKeys( Modifiers mods ) { }
|
||||
public void OnKeyInput( KeyInput input ) { }
|
||||
public void OnMouseInput( MouseInput input ) { }
|
||||
}
|
||||
|
||||
public class DefaultInputHandler : IInputHandler
|
||||
{
|
||||
readonly World world;
|
||||
public DefaultInputHandler( World world )
|
||||
{
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public void ModifierKeys( Modifiers mods )
|
||||
{
|
||||
Game.HandleModifierKeys( mods );
|
||||
}
|
||||
|
||||
public void OnKeyInput( KeyInput input )
|
||||
{
|
||||
Sync.CheckSyncUnchanged( world, () =>
|
||||
{
|
||||
Widget.HandleKeyPress( input );
|
||||
} );
|
||||
}
|
||||
|
||||
public void OnMouseInput( MouseInput input )
|
||||
{
|
||||
Sync.CheckSyncUnchanged( world, () =>
|
||||
{
|
||||
Widget.HandleInput( input );
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
173
OpenRA.Game/Map.cs
Executable file → Normal file
173
OpenRA.Game/Map.cs
Executable file → Normal file
@@ -16,76 +16,65 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using OpenRA.FileFormats;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public class Map
|
||||
public class Map : MapStub
|
||||
{
|
||||
public IFolder Package;
|
||||
public string Uid;
|
||||
|
||||
// Yaml map data
|
||||
[FieldLoader.Load] public bool Selectable = true;
|
||||
[FieldLoader.Load] public int MapFormat;
|
||||
[FieldLoader.Load] public string Title;
|
||||
[FieldLoader.Load] public string Description;
|
||||
[FieldLoader.Load] public string Author;
|
||||
[FieldLoader.Load] public int PlayerCount;
|
||||
[FieldLoader.Load] public string Tileset;
|
||||
|
||||
public Dictionary<string, PlayerReference> Players = new Dictionary<string, PlayerReference>();
|
||||
public Dictionary<string, ActorReference> Actors = new Dictionary<string, ActorReference>();
|
||||
public List<SmudgeReference> Smudges = new List<SmudgeReference>();
|
||||
public Dictionary<string, int2> Waypoints = new Dictionary<string, int2>();
|
||||
|
||||
// Rules overrides
|
||||
|
||||
// Rules overrides
|
||||
public List<MiniYamlNode> Rules = new List<MiniYamlNode>();
|
||||
|
||||
// Sequences overrides
|
||||
public List<MiniYamlNode> Sequences = new List<MiniYamlNode>();
|
||||
|
||||
// Weapon overrides
|
||||
public List<MiniYamlNode> Weapons = new List<MiniYamlNode>();
|
||||
|
||||
// Voices overrides
|
||||
public List<MiniYamlNode> Voices = new List<MiniYamlNode>();
|
||||
|
||||
// Binary map data
|
||||
public byte TileFormat = 1;
|
||||
[FieldLoader.Load] public int2 MapSize;
|
||||
|
||||
[FieldLoader.Load] public int2 TopLeft;
|
||||
[FieldLoader.Load] public int2 BottomRight;
|
||||
|
||||
public TileReference<ushort, byte>[,] MapTiles;
|
||||
public TileReference<byte, byte>[,] MapResources;
|
||||
public string [,] CustomTerrain;
|
||||
|
||||
// Temporary compat hacks
|
||||
public int XOffset { get { return TopLeft.X; } }
|
||||
public int YOffset { get { return TopLeft.Y; } }
|
||||
public int Width { get { return BottomRight.X - TopLeft.X; } }
|
||||
public int Height { get { return BottomRight.Y - TopLeft.Y; } }
|
||||
public string Theater { get { return Tileset; } }
|
||||
public IEnumerable<int2> SpawnPoints { get { return Waypoints.Select(kv => kv.Value); } }
|
||||
public Rectangle Bounds { get { return Rectangle.FromLTRB(TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y); } }
|
||||
|
||||
public Map()
|
||||
{
|
||||
// Do nothing; not a valid map (editor hack)
|
||||
}
|
||||
|
||||
public Map(string tileset)
|
||||
public static Map FromTileset(string tileset)
|
||||
{
|
||||
MapSize = new int2(1, 1);
|
||||
Tileset = tileset;
|
||||
MapResources = new TileReference<byte, byte>[1, 1];
|
||||
|
||||
var tile = OpenRA.Rules.TileSets[Tileset].Templates.First();
|
||||
MapTiles = new TileReference<ushort, byte>[1, 1]
|
||||
var tile = OpenRA.Rules.TileSets[tileset].Templates.First();
|
||||
Map map = new Map()
|
||||
{
|
||||
Title = "Name your map here",
|
||||
Description = "Describe your map here",
|
||||
Author = "Your name here",
|
||||
MapSize = new int2(1, 1),
|
||||
PlayerCount = 0,
|
||||
Tileset = tileset,
|
||||
MapResources = new TileReference<byte, byte>[1, 1],
|
||||
MapTiles = new TileReference<ushort, byte>[1, 1]
|
||||
{ { new TileReference<ushort, byte> {
|
||||
type = tile.Key,
|
||||
image = (byte)(tile.Value.PickAny ? 0xffu : 0),
|
||||
index = (byte)(tile.Value.PickAny ? 0xffu : 0) } } };
|
||||
|
||||
PlayerCount = 0;
|
||||
TopLeft = new int2(0, 0);
|
||||
BottomRight = new int2(0, 0);
|
||||
|
||||
Title = "Name your map here";
|
||||
Description = "Describe your map here";
|
||||
Author = "Your name here";
|
||||
index = (byte)(tile.Value.PickAny ? 0xffu : 0) }
|
||||
} },
|
||||
};
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
class Format2ActorReference
|
||||
@@ -95,21 +84,15 @@ namespace OpenRA
|
||||
public int2 Location = int2.Zero;
|
||||
public string Owner = null;
|
||||
}
|
||||
|
||||
public Map(IFolder package)
|
||||
|
||||
public Map(string path)
|
||||
: base(path)
|
||||
{
|
||||
Package = package;
|
||||
var yaml = new MiniYaml( null, MiniYaml.FromStream( Package.GetContent( "map.yaml" ) ) );
|
||||
var yaml = new MiniYaml( null, MiniYaml.FromStream( Container.GetContent( "map.yaml" ) ) );
|
||||
|
||||
// 'Simple' metadata
|
||||
FieldLoader.Load( this, yaml );
|
||||
|
||||
// Waypoints
|
||||
foreach (var wp in yaml.NodesDict["Waypoints"].NodesDict)
|
||||
{
|
||||
string[] loc = wp.Value.Value.Split(',');
|
||||
Waypoints.Add(wp.Key, new int2(int.Parse(loc[0]), int.Parse(loc[1])));
|
||||
}
|
||||
|
||||
// Players & Actors -- this has changed several times.
|
||||
// - Be backwards compatible wherever possible.
|
||||
@@ -198,19 +181,29 @@ namespace OpenRA
|
||||
// Rules
|
||||
Rules = yaml.NodesDict["Rules"].Nodes;
|
||||
|
||||
// Sequences
|
||||
Sequences = (yaml.NodesDict.ContainsKey("Sequences")) ? yaml.NodesDict["Sequences"].Nodes : new List<MiniYamlNode>();
|
||||
|
||||
// Weapons
|
||||
Weapons = (yaml.NodesDict.ContainsKey("Weapons")) ? yaml.NodesDict["Weapons"].Nodes : new List<MiniYamlNode>();
|
||||
|
||||
// Voices
|
||||
Voices = (yaml.NodesDict.ContainsKey("Voices")) ? yaml.NodesDict["Voices"].Nodes : new List<MiniYamlNode>();
|
||||
|
||||
CustomTerrain = new string[MapSize.X, MapSize.Y];
|
||||
LoadUid();
|
||||
LoadBinaryData();
|
||||
}
|
||||
|
||||
public void Save(string filepath)
|
||||
{
|
||||
public void Save(string toPath)
|
||||
{
|
||||
Console.WriteLine("Saving map to path {0}",toPath);
|
||||
// Todo: save to a zip file in the support dir by default
|
||||
MapFormat = 3;
|
||||
|
||||
var root = new List<MiniYamlNode>();
|
||||
foreach (var field in new string[] {"Selectable", "MapFormat", "Title", "Description", "Author", "PlayerCount", "Tileset", "MapSize", "TopLeft", "BottomRight"})
|
||||
foreach (var field in new string[] {"Selectable", "MapFormat", "Title", "Description", "Author", "PlayerCount", "Tileset", "MapSize", "TopLeft", "BottomRight", "UseAsShellmap", "Type"})
|
||||
{
|
||||
FieldInfo f = this.GetType().GetField(field);
|
||||
var f = this.GetType().GetField(field);
|
||||
if (f.GetValue(this) == null) continue;
|
||||
root.Add( new MiniYamlNode( field, FieldSaver.FormatValue( this, f ) ) );
|
||||
}
|
||||
@@ -225,13 +218,30 @@ namespace OpenRA
|
||||
x.Key,
|
||||
x.Value.Save() ) ).ToList() ) );
|
||||
|
||||
root.Add( new MiniYamlNode( "Waypoints", MiniYaml.FromDictionary<string, int2>( Waypoints ) ) );
|
||||
root.Add( new MiniYamlNode( "Smudges", MiniYaml.FromList<SmudgeReference>( Smudges ) ) );
|
||||
root.Add( new MiniYamlNode( "Rules", null, Rules ) );
|
||||
|
||||
SaveBinaryData(Path.Combine(filepath, "map.bin"));
|
||||
root.WriteToFile(Path.Combine(filepath, "map.yaml"));
|
||||
SaveUid(Path.Combine(filepath, "map.uid"));
|
||||
root.Add(new MiniYamlNode("Waypoints", MiniYaml.FromDictionary<string, int2>( Waypoints )));
|
||||
root.Add(new MiniYamlNode("Smudges", MiniYaml.FromList<SmudgeReference>( Smudges )));
|
||||
root.Add(new MiniYamlNode("Rules", null, Rules));
|
||||
root.Add(new MiniYamlNode("Sequences", null, Sequences));
|
||||
root.Add(new MiniYamlNode("Weapons", null, Weapons));
|
||||
root.Add(new MiniYamlNode("Voices", null, Voices));
|
||||
|
||||
Dictionary<string, byte[]> entries = new Dictionary<string, byte[]>();
|
||||
entries.Add("map.bin", SaveBinaryData());
|
||||
var s = root.WriteToString();
|
||||
entries.Add("map.yaml", Encoding.UTF8.GetBytes(s));
|
||||
|
||||
// Saving the map to a new location
|
||||
if (toPath != Path)
|
||||
{
|
||||
Path = toPath;
|
||||
|
||||
// Create a new map package
|
||||
// TODO: Add other files (resources, rules) to the entries list
|
||||
Container = FileSystem.CreatePackage(Path, int.MaxValue, entries);
|
||||
}
|
||||
|
||||
// Update existing package
|
||||
Container.Write(entries);
|
||||
}
|
||||
|
||||
static byte ReadByte(Stream s)
|
||||
@@ -252,7 +262,7 @@ namespace OpenRA
|
||||
|
||||
public void LoadBinaryData()
|
||||
{
|
||||
using (var dataStream = Package.GetContent("map.bin"))
|
||||
using (var dataStream = Container.GetContent("map.bin"))
|
||||
{
|
||||
if (ReadByte(dataStream) != 1)
|
||||
throw new InvalidDataException("Unknown binary map format");
|
||||
@@ -284,9 +294,9 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveBinaryData(string filepath)
|
||||
public byte[] SaveBinaryData()
|
||||
{
|
||||
using (var dataStream = File.Create(filepath + ".tmp"))
|
||||
MemoryStream dataStream = new MemoryStream();
|
||||
using (var writer = new BinaryWriter(dataStream))
|
||||
{
|
||||
// File header consists of a version byte, followed by 2 ushorts for width and height
|
||||
@@ -310,27 +320,7 @@ namespace OpenRA
|
||||
writer.Write(MapResources[i, j].index);
|
||||
}
|
||||
}
|
||||
File.Delete(filepath);
|
||||
File.Move(filepath + ".tmp", filepath);
|
||||
}
|
||||
|
||||
public void LoadUid()
|
||||
{
|
||||
Uid = Package.GetContent("map.uid").ReadAllText();
|
||||
}
|
||||
|
||||
public void SaveUid(string filename)
|
||||
{
|
||||
// UID is calculated by taking an SHA1 of the yaml and binary data
|
||||
// Read the relevant data into a buffer
|
||||
var data = Package.GetContent("map.yaml").ReadAllBytes()
|
||||
.Concat(Package.GetContent("map.bin").ReadAllBytes()).ToArray();
|
||||
|
||||
// Take the SHA1
|
||||
using (var csp = SHA1.Create())
|
||||
Uid = new string(csp.ComputeHash(data).SelectMany(a => a.ToString("x2")).ToArray());
|
||||
|
||||
File.WriteAllText(filename, Uid);
|
||||
return dataStream.ToArray();
|
||||
}
|
||||
|
||||
public bool IsInMap(int2 xy)
|
||||
@@ -340,7 +330,7 @@ namespace OpenRA
|
||||
|
||||
public bool IsInMap(int x, int y)
|
||||
{
|
||||
return (x >= TopLeft.X && y >= TopLeft.Y && x < BottomRight.X && y < BottomRight.Y);
|
||||
return Bounds.Contains(x,y);
|
||||
}
|
||||
|
||||
static T[,] ResizeArray<T>(T[,] ts, T t, int width, int height)
|
||||
@@ -359,5 +349,12 @@ namespace OpenRA
|
||||
MapResources = ResizeArray(MapResources, MapResources[0, 0], width, height);
|
||||
MapSize = new int2(width, height);
|
||||
}
|
||||
|
||||
public void ResizeCordon(int left, int top, int right, int bottom)
|
||||
{
|
||||
TopLeft = new int2(left, top);
|
||||
BottomRight = new int2(right, bottom);
|
||||
Bounds = Rectangle.FromLTRB(TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,52 +13,19 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Support;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public class ModData
|
||||
{
|
||||
public static readonly Dictionary<string,Mod> AllMods = ValidateMods(Directory.GetDirectories("mods").Select(x => x.Substring(5)).ToArray());
|
||||
|
||||
public static Dictionary<string,Mod> ValidateMods(string[] mods)
|
||||
{
|
||||
var ret = new Dictionary<string,Mod>();
|
||||
foreach (var m in mods)
|
||||
{
|
||||
if (!File.Exists("mods" + Path.DirectorySeparatorChar + m + Path.DirectorySeparatorChar + "mod.yaml"))
|
||||
continue;
|
||||
|
||||
var yaml = new MiniYaml( null, MiniYaml.FromFile("mods" + Path.DirectorySeparatorChar + m + Path.DirectorySeparatorChar + "mod.yaml"));
|
||||
if (!yaml.NodesDict.ContainsKey("Metadata"))
|
||||
{
|
||||
System.Console.WriteLine("Invalid mod: "+m);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret.Add(m,FieldLoader.Load<Mod>(yaml.NodesDict["Metadata"]));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public class Mod
|
||||
{
|
||||
public string Title;
|
||||
public string Description;
|
||||
public string Version;
|
||||
public string Author;
|
||||
public string[] RequiresMods;
|
||||
public bool Standalone = false;
|
||||
}
|
||||
|
||||
public readonly Manifest Manifest;
|
||||
public readonly ObjectCreator ObjectCreator;
|
||||
public readonly SheetBuilder SheetBuilder;
|
||||
public readonly CursorSheetBuilder CursorSheetBuilder;
|
||||
public readonly Dictionary<string, MapStub> AvailableMaps;
|
||||
public readonly WidgetLoader WidgetLoader;
|
||||
public ILoadScreen LoadScreen = null;
|
||||
|
||||
public SheetBuilder SheetBuilder;
|
||||
public CursorSheetBuilder CursorSheetBuilder;
|
||||
|
||||
public ModData( params string[] mods )
|
||||
{
|
||||
Manifest = new Manifest( mods );
|
||||
@@ -67,6 +34,9 @@ namespace OpenRA
|
||||
LoadScreen.Init();
|
||||
LoadScreen.Display();
|
||||
|
||||
// all this manipulation of static crap here is nasty and breaks
|
||||
// horribly when you use ModData in unexpected ways.
|
||||
|
||||
FileSystem.LoadFromManifest( Manifest );
|
||||
ChromeProvider.Initialize( Manifest.Chrome );
|
||||
SheetBuilder = new SheetBuilder( TextureChannel.Red );
|
||||
@@ -75,17 +45,39 @@ namespace OpenRA
|
||||
WidgetLoader = new WidgetLoader( this );
|
||||
}
|
||||
|
||||
// TODO: Do this nicer
|
||||
public static IEnumerable<string> FindMapsIn(string dir)
|
||||
{
|
||||
string[] NoMaps = { };
|
||||
|
||||
if (!Directory.Exists(dir))
|
||||
return NoMaps;
|
||||
|
||||
// todo: look for compressed maps too.
|
||||
return Directory.GetDirectories(dir)
|
||||
.Concat(Directory.GetFiles(dir, "*.zip"))
|
||||
.Concat(Directory.GetFiles(dir, "*.oramap"));
|
||||
}
|
||||
|
||||
Dictionary<string, MapStub> FindMaps(string[] mods)
|
||||
{
|
||||
var paths = new[] { "maps/" }.Concat(mods.Select(m => "mods/" + m + "/maps/"))
|
||||
.Where(p => Directory.Exists(p))
|
||||
.SelectMany(p => Directory.GetDirectories(p)).ToList();
|
||||
var paths = mods.SelectMany(p => FindMapsIn("mods/" + p + "/maps/"));
|
||||
|
||||
return paths.Select(p => new MapStub(new Folder(p))).ToDictionary(m => m.Uid);
|
||||
Dictionary<string, MapStub> ret = new Dictionary<string, MapStub>();
|
||||
foreach (var path in paths)
|
||||
{
|
||||
var map = new MapStub(path);
|
||||
if (ret.ContainsKey(map.Uid))
|
||||
System.Console.WriteLine("Ignoring duplicate map: {0}", path);
|
||||
else
|
||||
ret.Add(map.Uid, map);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
string cachedTheatre = null;
|
||||
string cachedTileset = null;
|
||||
bool previousMapHadSequences = true;
|
||||
IFolder previousMapMount = null;
|
||||
|
||||
public Map PrepareMap(string uid)
|
||||
{
|
||||
LoadScreen.Display();
|
||||
@@ -93,16 +85,32 @@ namespace OpenRA
|
||||
if (!AvailableMaps.ContainsKey(uid))
|
||||
throw new InvalidDataException("Invalid map uid: {0}".F(uid));
|
||||
|
||||
var map = new Map(AvailableMaps[uid].Package);
|
||||
|
||||
var map = new Map(AvailableMaps[uid].Path);
|
||||
|
||||
// unload the previous map mount if we have one
|
||||
if (previousMapMount != null) FileSystem.Unmount(previousMapMount);
|
||||
|
||||
// Adds the map its container to the FileSystem
|
||||
// allowing the map to use custom assets
|
||||
// Container should have the lowest priority of all (ie int max)
|
||||
// Store a reference so we can unload it next time
|
||||
previousMapMount = FileSystem.OpenPackage(map.Path, int.MaxValue);
|
||||
FileSystem.Mount(previousMapMount);
|
||||
Rules.LoadRules(Manifest, map);
|
||||
if (map.Theater != cachedTheatre)
|
||||
|
||||
if (map.Tileset != cachedTileset
|
||||
|| previousMapHadSequences || map.Sequences.Count > 0)
|
||||
{
|
||||
SheetBuilder = new SheetBuilder( TextureChannel.Red );
|
||||
SpriteSheetBuilder.Initialize( Rules.TileSets[map.Tileset] );
|
||||
SequenceProvider.Initialize(Manifest.Sequences);
|
||||
CursorSheetBuilder = new CursorSheetBuilder( this );
|
||||
CursorProvider.Initialize(Manifest.Cursors);
|
||||
cachedTheatre = map.Theater;
|
||||
SequenceProvider.Initialize(Manifest.Sequences, map.Sequences);
|
||||
cachedTileset = map.Tileset;
|
||||
}
|
||||
|
||||
previousMapHadSequences = map.Sequences.Count > 0;
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ using OpenRA.Support;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
enum ConnectionState
|
||||
public enum ConnectionState
|
||||
{
|
||||
PreConnecting,
|
||||
NotConnected,
|
||||
@@ -26,11 +26,13 @@ namespace OpenRA.Network
|
||||
Connected,
|
||||
}
|
||||
|
||||
interface IConnection : IDisposable
|
||||
public interface IConnection : IDisposable
|
||||
{
|
||||
int LocalClientId { get; }
|
||||
ConnectionState ConnectionState { get; }
|
||||
void Send( byte[] packet );
|
||||
void Send( int frame, List<byte[]> orders );
|
||||
void SendImmediate( List<byte[]> orders );
|
||||
void SendSync( int frame, byte[] syncData );
|
||||
void Receive( Action<int, byte[]> packetFn );
|
||||
}
|
||||
|
||||
@@ -53,7 +55,33 @@ namespace OpenRA.Network
|
||||
get { return ConnectionState.PreConnecting; }
|
||||
}
|
||||
|
||||
public virtual void Send( byte[] packet )
|
||||
public virtual void Send( int frame, List<byte[]> orders )
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
ms.Write( BitConverter.GetBytes( frame ) );
|
||||
foreach( var o in orders )
|
||||
ms.Write( o );
|
||||
Send( ms.ToArray() );
|
||||
}
|
||||
|
||||
public virtual void SendImmediate( List<byte[]> orders )
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
ms.Write( BitConverter.GetBytes( (int)0 ) );
|
||||
foreach( var o in orders )
|
||||
ms.Write( o );
|
||||
Send( ms.ToArray() );
|
||||
}
|
||||
|
||||
public virtual void SendSync( int frame, byte[] syncData )
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
ms.Write( BitConverter.GetBytes( frame ) );
|
||||
ms.Write( syncData );
|
||||
Send( ms.ToArray() );
|
||||
}
|
||||
|
||||
protected virtual void Send( byte[] packet )
|
||||
{
|
||||
if( packet.Length == 0 )
|
||||
throw new NotImplementedException();
|
||||
@@ -129,7 +157,17 @@ namespace OpenRA.Network
|
||||
public override int LocalClientId { get { return clientId; } }
|
||||
public override ConnectionState ConnectionState { get { return connectionState; } }
|
||||
|
||||
public override void Send( byte[] packet )
|
||||
List<byte[]> queuedSyncPackets = new List<byte[]>();
|
||||
|
||||
public override void SendSync( int frame, byte[] syncData )
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
ms.Write( BitConverter.GetBytes( frame ) );
|
||||
ms.Write( syncData );
|
||||
queuedSyncPackets.Add( ms.ToArray() );
|
||||
}
|
||||
|
||||
protected override void Send( byte[] packet )
|
||||
{
|
||||
base.Send( packet );
|
||||
|
||||
@@ -138,10 +176,18 @@ namespace OpenRA.Network
|
||||
var ms = new MemoryStream();
|
||||
ms.Write(BitConverter.GetBytes((int)packet.Length));
|
||||
ms.Write(packet);
|
||||
foreach( var q in queuedSyncPackets )
|
||||
{
|
||||
ms.Write( BitConverter.GetBytes( (int)q.Length ) );
|
||||
ms.Write( q );
|
||||
base.Send( q );
|
||||
}
|
||||
queuedSyncPackets.Clear();
|
||||
ms.WriteTo(socket.GetStream());
|
||||
}
|
||||
catch (SocketException) { /* drop this on the floor; we'll pick up the disconnect from the reader thread */ }
|
||||
catch (ObjectDisposedException) { /* ditto */ }
|
||||
catch (InvalidOperationException) { /* ditto */ }
|
||||
}
|
||||
|
||||
bool disposed = false;
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace OpenRA.Network
|
||||
|
||||
public void ClientQuit( int clientId, int lastClientFrame )
|
||||
{
|
||||
clientQuitTimes.Add( clientId, lastClientFrame );
|
||||
clientQuitTimes[clientId] = lastClientFrame;
|
||||
}
|
||||
|
||||
public void AddFrameOrders( int clientId, int frame, byte[] orders )
|
||||
|
||||
73
OpenRA.Game/Network/Handshake.cs
Normal file
73
OpenRA.Game/Network/Handshake.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 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. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
public class HandshakeRequest
|
||||
{
|
||||
[FieldLoader.Load] public string[] Mods;
|
||||
[FieldLoader.Load] public string Map;
|
||||
|
||||
public string Serialize()
|
||||
{
|
||||
var data = new List<MiniYamlNode>();
|
||||
data.Add(new MiniYamlNode("Handshake", FieldSaver.Save(this)));
|
||||
return data.WriteToString();
|
||||
}
|
||||
|
||||
public static HandshakeRequest Deserialize(string data)
|
||||
{
|
||||
var handshake = new HandshakeRequest();
|
||||
FieldLoader.Load(handshake, MiniYaml.FromString(data).First().Value);
|
||||
return handshake;
|
||||
}
|
||||
}
|
||||
|
||||
public class HandshakeResponse
|
||||
{
|
||||
[FieldLoader.Load] public string[] Mods;
|
||||
[FieldLoader.Load] public string Password;
|
||||
public Session.Client Client;
|
||||
|
||||
public string Serialize()
|
||||
{
|
||||
var data = new List<MiniYamlNode>();
|
||||
data.Add( new MiniYamlNode( "Handshake", null,
|
||||
new string[]{ "Mods", "Password" }.Select( p => FieldSaver.SaveField(this, p) ).ToList() ) );
|
||||
data.Add(new MiniYamlNode("Client", FieldSaver.Save(Client)));
|
||||
|
||||
return data.WriteToString();
|
||||
}
|
||||
|
||||
public static HandshakeResponse Deserialize(string data)
|
||||
{
|
||||
var handshake = new HandshakeResponse();
|
||||
handshake.Client = new Session.Client();
|
||||
|
||||
var ys = MiniYaml.FromString(data);
|
||||
foreach (var y in ys)
|
||||
switch (y.Key)
|
||||
{
|
||||
case "Handshake":
|
||||
FieldLoader.Load(handshake, y.Value);
|
||||
break;
|
||||
case "Client":
|
||||
FieldLoader.Load(handshake.Client, y.Value);
|
||||
break;
|
||||
}
|
||||
return handshake;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,23 +11,43 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.Network;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
[Flags]
|
||||
enum OrderFields : byte
|
||||
{
|
||||
TargetActor = 0x01,
|
||||
TargetLocation = 0x02,
|
||||
TargetString = 0x04,
|
||||
Queued = 0x08,
|
||||
ExtraLocation = 0x10,
|
||||
}
|
||||
|
||||
static class OrderFieldsExts
|
||||
{
|
||||
public static bool HasField(this OrderFields of, OrderFields f)
|
||||
{
|
||||
return (of & f) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Order
|
||||
{
|
||||
public readonly string OrderString;
|
||||
public readonly Actor Subject;
|
||||
public readonly Actor TargetActor;
|
||||
public readonly int2 TargetLocation;
|
||||
public readonly string TargetString;
|
||||
public readonly bool Queued;
|
||||
public Actor TargetActor;
|
||||
public int2 TargetLocation;
|
||||
public string TargetString;
|
||||
public int2 ExtraLocation;
|
||||
public bool IsImmediate;
|
||||
|
||||
public Player Player { get { return Subject.Owner; } }
|
||||
|
||||
public Order(string orderString, Actor subject,
|
||||
Actor targetActor, int2 targetLocation, string targetString, bool queued)
|
||||
Order(string orderString, Actor subject,
|
||||
Actor targetActor, int2 targetLocation, string targetString, bool queued, int2 extraLocation)
|
||||
{
|
||||
this.OrderString = orderString;
|
||||
this.Subject = subject;
|
||||
@@ -35,24 +55,15 @@ namespace OpenRA
|
||||
this.TargetLocation = targetLocation;
|
||||
this.TargetString = targetString;
|
||||
this.Queued = queued;
|
||||
this.ExtraLocation = extraLocation;
|
||||
}
|
||||
|
||||
public Order(string orderString, Actor subject)
|
||||
: this(orderString, subject, null, int2.Zero, null, false) { }
|
||||
public Order(string orderString, Actor subject, Actor targetActor)
|
||||
: this(orderString, subject, targetActor, int2.Zero, null, false) { }
|
||||
public Order(string orderString, Actor subject, int2 targetLocation)
|
||||
: this(orderString, subject, null, targetLocation, null, false) { }
|
||||
public Order(string orderString, Actor subject, int2 targetLocation, bool queued)
|
||||
: this(orderString, subject, null, targetLocation, null, queued) { }
|
||||
public Order(string orderString, Actor subject, string targetString)
|
||||
: this(orderString, subject, null, int2.Zero, targetString, false) { }
|
||||
public Order(string orderString, Actor subject, Actor targetActor, int2 targetLocation)
|
||||
: this(orderString, subject, targetActor, targetLocation, null, false) { }
|
||||
public Order(string orderString, Actor subject, Actor targetActor, string targetString)
|
||||
: this(orderString, subject, targetActor, int2.Zero, targetString, false) { }
|
||||
public Order(string orderString, Actor subject, int2 targetLocation, string targetString)
|
||||
: this(orderString, subject, null, targetLocation, targetString, false) { }
|
||||
// For scripting special powers
|
||||
public Order()
|
||||
: this(null, null, null, int2.Zero, null, false, int2.Zero) { }
|
||||
|
||||
public Order(string orderString, Actor subject, bool queued)
|
||||
: this(orderString, subject, null, int2.Zero, null, queued, int2.Zero) { }
|
||||
|
||||
public byte[] Serialize()
|
||||
{
|
||||
@@ -80,13 +91,25 @@ namespace OpenRA
|
||||
w.Write( (byte)0xFF );
|
||||
w.Write(OrderString);
|
||||
w.Write(UIntFromActor(Subject));
|
||||
w.Write(UIntFromActor(TargetActor));
|
||||
w.Write(TargetLocation.X);
|
||||
w.Write(TargetLocation.Y);
|
||||
w.Write(TargetString != null);
|
||||
|
||||
OrderFields fields = 0;
|
||||
if (TargetActor != null) fields |= OrderFields.TargetActor;
|
||||
if (TargetLocation != int2.Zero) fields |= OrderFields.TargetLocation;
|
||||
if (TargetString != null) fields |= OrderFields.TargetString;
|
||||
if (Queued) fields |= OrderFields.Queued;
|
||||
if (ExtraLocation != int2.Zero) fields |= OrderFields.ExtraLocation;
|
||||
|
||||
w.Write((byte)fields);
|
||||
|
||||
if (TargetActor != null)
|
||||
w.Write(UIntFromActor(TargetActor));
|
||||
if (TargetLocation != int2.Zero)
|
||||
w.Write(TargetLocation);
|
||||
if (TargetString != null)
|
||||
w.Write(TargetString);
|
||||
w.Write(Queued);
|
||||
if (ExtraLocation != int2.Zero)
|
||||
w.Write(ExtraLocation);
|
||||
|
||||
return ret.ToArray();
|
||||
}
|
||||
}
|
||||
@@ -100,19 +123,19 @@ namespace OpenRA
|
||||
{
|
||||
var order = r.ReadString();
|
||||
var subjectId = r.ReadUInt32();
|
||||
var targetActorId = r.ReadUInt32();
|
||||
var targetLocation = new int2(r.ReadInt32(), 0);
|
||||
targetLocation.Y = r.ReadInt32();
|
||||
var targetString = null as string;
|
||||
if (r.ReadBoolean())
|
||||
targetString = r.ReadString();
|
||||
var queued = r.ReadBoolean();
|
||||
var flags = (OrderFields)r.ReadByte();
|
||||
|
||||
var targetActorId = flags.HasField(OrderFields.TargetActor) ? r.ReadUInt32() : 0xffffffff;
|
||||
var targetLocation = flags.HasField(OrderFields.TargetLocation) ? r.ReadInt2() : int2.Zero;
|
||||
var targetString = flags.HasField(OrderFields.TargetString) ? r.ReadString() : null;
|
||||
var queued = flags.HasField(OrderFields.Queued);
|
||||
var extraLocation = flags.HasField(OrderFields.ExtraLocation) ? r.ReadInt2() : int2.Zero;
|
||||
|
||||
Actor subject, targetActor;
|
||||
if( !TryGetActorFromUInt( world, subjectId, out subject ) || !TryGetActorFromUInt( world, targetActorId, out targetActor ) )
|
||||
return null;
|
||||
|
||||
return new Order( order, subject, targetActor, targetLocation, targetString, queued);
|
||||
return new Order( order, subject, targetActor, targetLocation, targetString, queued, extraLocation);
|
||||
}
|
||||
|
||||
case 0xfe:
|
||||
@@ -120,7 +143,7 @@ namespace OpenRA
|
||||
var name = r.ReadString();
|
||||
var data = r.ReadString();
|
||||
|
||||
return new Order( name, null, data ) { IsImmediate = true };
|
||||
return new Order( name, null, false ) { IsImmediate = true, TargetString = data };
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -130,9 +153,9 @@ namespace OpenRA
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "OrderString: \"{0}\" \n\t Subject: \"{1}\". \n\t TargetActor: \"{2}\" \n\t TargetLocation: {3}." +
|
||||
"\n\t TargetString: \"{4}\".\n\t IsImmediate: {5}.\n\t Player(PlayerName): {6}\n".F(
|
||||
OrderString, Subject, TargetActor.Info.Name , TargetLocation, TargetString, IsImmediate, Player.PlayerName);
|
||||
return ("OrderString: \"{0}\" \n\t Subject: \"{1}\". \n\t TargetActor: \"{2}\" \n\t TargetLocation: {3}." +
|
||||
"\n\t TargetString: \"{4}\".\n\t IsImmediate: {5}.\n\t Player(PlayerName): {6}\n").F(
|
||||
OrderString, Subject, TargetActor != null ? TargetActor.Info.Name : null , TargetLocation, TargetString, IsImmediate, Player != null ? Player.PlayerName : null);
|
||||
}
|
||||
|
||||
static uint UIntFromActor(Actor a)
|
||||
@@ -164,32 +187,37 @@ namespace OpenRA
|
||||
// Now that Orders are resolved by individual Actors, these are weird; you unpack orders manually, but not pack them.
|
||||
public static Order Chat(string text)
|
||||
{
|
||||
return new Order("Chat", null, text) { IsImmediate = true };
|
||||
return new Order("Chat", null, false) { IsImmediate = true, TargetString = text};
|
||||
}
|
||||
|
||||
public static Order TeamChat(string text)
|
||||
{
|
||||
return new Order("TeamChat", null, text) { IsImmediate = true };
|
||||
return new Order("TeamChat", null, false) { IsImmediate = true, TargetString = text };
|
||||
}
|
||||
|
||||
public static Order HandshakeResponse(string text)
|
||||
{
|
||||
return new Order("HandshakeResponse", null, false) { IsImmediate = true, TargetString = text };
|
||||
}
|
||||
|
||||
public static Order Command(string text)
|
||||
{
|
||||
return new Order("Command", null, text) { IsImmediate = true };
|
||||
return new Order("Command", null, false) { IsImmediate = true, TargetString = text };
|
||||
}
|
||||
|
||||
public static Order StartProduction(Actor subject, string item, int count)
|
||||
{
|
||||
return new Order("StartProduction", subject, new int2( count, 0 ), item );
|
||||
return new Order("StartProduction", subject, false) { TargetLocation = new int2(count, 0), TargetString = item };
|
||||
}
|
||||
|
||||
public static Order PauseProduction(Actor subject, string item, bool pause)
|
||||
{
|
||||
return new Order("PauseProduction", subject, new int2( pause ? 1 : 0, 0 ), item);
|
||||
return new Order("PauseProduction", subject, false) { TargetLocation = new int2(pause ? 1 : 0, 0), TargetString = item };
|
||||
}
|
||||
|
||||
public static Order CancelProduction(Actor subject, string item, int count)
|
||||
{
|
||||
return new Order("CancelProduction", subject, new int2( count, 0 ), item);
|
||||
return new Order("CancelProduction", subject, false) { TargetLocation = new int2(count, 0), TargetString = item };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,36 +8,18 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
static class OrderIO
|
||||
public static class OrderIO
|
||||
{
|
||||
public static void Write(this Stream s, byte[] buf)
|
||||
{
|
||||
s.Write(buf, 0, buf.Length);
|
||||
}
|
||||
|
||||
public static void WriteFrameData(this Stream s, IEnumerable<Order> orders, int frameNumber)
|
||||
{
|
||||
var bytes = Serialize( orders, frameNumber );
|
||||
s.Write( BitConverter.GetBytes( (int)bytes.Length ) );
|
||||
s.Write( bytes );
|
||||
}
|
||||
|
||||
public static byte[] Serialize( this IEnumerable<Order> orders, int frameNumber )
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
ms.Write( BitConverter.GetBytes( frameNumber ) );
|
||||
foreach( var o in orders.Select( o => o.Serialize() ) )
|
||||
ms.Write( o );
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
public static List<Order> ToOrderList(this byte[] bytes, World world)
|
||||
{
|
||||
var ms = new MemoryStream(bytes, 4, bytes.Length - 4);
|
||||
@@ -52,17 +34,29 @@ namespace OpenRA.Network
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static byte[] SerializeSync( this List<int> sync, int frameNumber )
|
||||
public static byte[] SerializeSync( this List<int> sync )
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
using( var writer = new BinaryWriter( ms ) )
|
||||
{
|
||||
writer.Write( frameNumber );
|
||||
writer.Write( (byte)0x65 );
|
||||
foreach( var s in sync )
|
||||
writer.Write( s );
|
||||
}
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
public static int2 ReadInt2(this BinaryReader r)
|
||||
{
|
||||
var x = r.ReadInt32();
|
||||
var y = r.ReadInt32();
|
||||
return new int2(x, y);
|
||||
}
|
||||
|
||||
public static void Write(this BinaryWriter w, int2 p)
|
||||
{
|
||||
w.Write(p.X);
|
||||
w.Write(p.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ using OpenRA.FileFormats;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
class OrderManager : IDisposable
|
||||
public class OrderManager : IDisposable
|
||||
{
|
||||
readonly SyncReport syncReport;
|
||||
readonly FrameData frameData = new FrameData();
|
||||
@@ -47,7 +47,7 @@ namespace OpenRA.Network
|
||||
|
||||
NetFrameNumber = 1;
|
||||
for( int i = NetFrameNumber ; i <= FramesAhead ; i++ )
|
||||
Connection.Send( new List<Order>().Serialize( i ) );
|
||||
Connection.Send( i, new List<byte[]>() );
|
||||
}
|
||||
|
||||
public OrderManager( string host, int port, IConnection conn )
|
||||
@@ -73,7 +73,7 @@ namespace OpenRA.Network
|
||||
{
|
||||
var immediateOrders = localOrders.Where( o => o.IsImmediate ).ToList();
|
||||
if( immediateOrders.Count != 0 )
|
||||
Connection.Send( immediateOrders.Serialize( 0 ) );
|
||||
Connection.SendImmediate( immediateOrders.Select( o => o.Serialize() ).ToList() );
|
||||
localOrders.RemoveAll( o => o.IsImmediate );
|
||||
|
||||
var immediatePackets = new List<Pair<int, byte[]>>();
|
||||
@@ -99,7 +99,7 @@ namespace OpenRA.Network
|
||||
|
||||
Dictionary<int, byte[]> syncForFrame = new Dictionary<int, byte[]>();
|
||||
|
||||
void CheckSync(byte[] packet)
|
||||
void CheckSync( byte[] packet )
|
||||
{
|
||||
var frame = BitConverter.ToInt32(packet, 0);
|
||||
byte[] existingSync;
|
||||
@@ -156,7 +156,7 @@ namespace OpenRA.Network
|
||||
if( !IsReadyForNextFrame )
|
||||
throw new InvalidOperationException();
|
||||
|
||||
Connection.Send( localOrders.Serialize( NetFrameNumber + FramesAhead ) );
|
||||
Connection.Send( NetFrameNumber + FramesAhead, localOrders.Select( o => o.Serialize() ).ToList() );
|
||||
localOrders.Clear();
|
||||
|
||||
var sync = new List<int>();
|
||||
@@ -168,13 +168,11 @@ namespace OpenRA.Network
|
||||
sync.Add( world.SyncHash() );
|
||||
}
|
||||
|
||||
var ss = sync.SerializeSync( NetFrameNumber );
|
||||
Connection.Send( ss );
|
||||
var ss = sync.SerializeSync();
|
||||
Connection.SendSync( NetFrameNumber, ss );
|
||||
|
||||
syncReport.UpdateSyncReport();
|
||||
|
||||
CheckSync( ss );
|
||||
|
||||
++NetFrameNumber;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.IO;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
class ReplayConnection : IConnection
|
||||
public class ReplayConnection : IConnection
|
||||
{
|
||||
//uint nextFrame = 1;
|
||||
FileStream replayStream;
|
||||
@@ -26,13 +26,26 @@ namespace OpenRA.Network
|
||||
get { return ConnectionState.Connected; }
|
||||
}
|
||||
|
||||
public void Send( byte[] packet )
|
||||
// do nothing; ignore locally generated orders
|
||||
public void Send( int frame, List<byte[]> orders ) { }
|
||||
public void SendImmediate( List<byte[]> orders ) { }
|
||||
public void SendSync( int frame, byte[] syncData )
|
||||
{
|
||||
// do nothing; ignore locally generated orders
|
||||
var ms = new MemoryStream();
|
||||
ms.Write( BitConverter.GetBytes( frame ) );
|
||||
ms.Write( syncData );
|
||||
sync.Add( ms.ToArray() );
|
||||
}
|
||||
|
||||
List<byte[]> sync = new List<byte[]>();
|
||||
|
||||
public void Receive( Action<int, byte[]> packetFn )
|
||||
{
|
||||
while( sync.Count != 0 )
|
||||
{
|
||||
packetFn( LocalClientId, sync[ 0 ] );
|
||||
sync.RemoveAt( 0 );
|
||||
}
|
||||
if( replayStream == null ) return;
|
||||
|
||||
var reader = new BinaryReader( replayStream );
|
||||
@@ -52,23 +65,20 @@ namespace OpenRA.Network
|
||||
class ReplayRecorderConnection : IConnection
|
||||
{
|
||||
IConnection inner;
|
||||
FileStream replayFile;
|
||||
BinaryWriter writer;
|
||||
|
||||
public ReplayRecorderConnection( IConnection inner, FileStream replayFile )
|
||||
{
|
||||
this.inner = inner;
|
||||
this.replayFile = replayFile;
|
||||
this.writer = new BinaryWriter( replayFile );
|
||||
}
|
||||
|
||||
public int LocalClientId { get { return inner.LocalClientId; } }
|
||||
public ConnectionState ConnectionState { get { return inner.ConnectionState; } }
|
||||
|
||||
public void Send( byte[] packet )
|
||||
{
|
||||
inner.Send( packet );
|
||||
}
|
||||
public void Send( int frame, List<byte[]> orders ) { inner.Send( frame, orders ); }
|
||||
public void SendImmediate( List<byte[]> orders ) { inner.SendImmediate( orders ); }
|
||||
public void SendSync( int frame, byte[] syncData ) { inner.SendSync( frame, syncData ); }
|
||||
|
||||
public void Receive( Action<int, byte[]> packetFn )
|
||||
{
|
||||
|
||||
@@ -21,20 +21,21 @@ namespace OpenRA.Network
|
||||
public List<Slot> Slots = new List<Slot>();
|
||||
public Global GlobalSettings = new Global();
|
||||
|
||||
public Client ClientWithIndex( int clientID )
|
||||
public Client ClientWithIndex(int clientID)
|
||||
{
|
||||
return Clients.SingleOrDefault( c => c.Index == clientID );
|
||||
return Clients.SingleOrDefault(c => c.Index == clientID);
|
||||
}
|
||||
|
||||
public Client ClientInSlot( Slot slot )
|
||||
public Client ClientInSlot(Slot slot)
|
||||
{
|
||||
return Clients.SingleOrDefault( c => c.Slot == slot.Index );
|
||||
return Clients.SingleOrDefault(c => c.Slot == slot.Index);
|
||||
}
|
||||
|
||||
public enum ClientState
|
||||
{
|
||||
NotReady,
|
||||
Ready
|
||||
Ready,
|
||||
Disconnected = 1000
|
||||
}
|
||||
|
||||
public class Client
|
||||
@@ -55,8 +56,8 @@ namespace OpenRA.Network
|
||||
public int Index;
|
||||
public string Bot; // trait name of the bot to initialize in this slot, or null otherwise.
|
||||
public bool Closed; // host has explicitly closed this slot.
|
||||
public string MapPlayer; // playerReference to bind against.
|
||||
|
||||
public string MapPlayer; // playerReference to bind against.
|
||||
public bool Spectator = false; // Spectating or not
|
||||
// todo: more stuff?
|
||||
}
|
||||
|
||||
@@ -67,11 +68,11 @@ namespace OpenRA.Network
|
||||
public string[] Mods = { "ra" }; // mod names
|
||||
public int OrderLatency = 3;
|
||||
public int RandomSeed = 0;
|
||||
public bool LockTeams = false; // don't allow team changes after game start.
|
||||
public bool LockTeams = true; // don't allow team changes after game start.
|
||||
public bool AllowCheats = false;
|
||||
}
|
||||
|
||||
public Session( string[] mods )
|
||||
public Session(string[] mods)
|
||||
{
|
||||
this.GlobalSettings.Mods = mods.ToArray();
|
||||
}
|
||||
@@ -80,27 +81,27 @@ namespace OpenRA.Network
|
||||
{
|
||||
var clientData = new List<MiniYamlNode>();
|
||||
|
||||
foreach( var client in Clients )
|
||||
clientData.Add( new MiniYamlNode( "Client@{0}".F( client.Index ), FieldSaver.Save( client ) ) );
|
||||
foreach (var client in Clients)
|
||||
clientData.Add(new MiniYamlNode("Client@{0}".F(client.Index), FieldSaver.Save(client)));
|
||||
|
||||
foreach( var slot in Slots )
|
||||
clientData.Add( new MiniYamlNode( "Slot@{0}".F( slot.Index ), FieldSaver.Save( slot ) ) );
|
||||
foreach (var slot in Slots)
|
||||
clientData.Add(new MiniYamlNode("Slot@{0}".F(slot.Index), FieldSaver.Save(slot)));
|
||||
|
||||
clientData.Add( new MiniYamlNode( "GlobalSettings", FieldSaver.Save( GlobalSettings ) ) );
|
||||
clientData.Add(new MiniYamlNode("GlobalSettings", FieldSaver.Save(GlobalSettings)));
|
||||
|
||||
return clientData.WriteToString();
|
||||
}
|
||||
|
||||
public static Session Deserialize(string data)
|
||||
{
|
||||
var session = new Session( Game.Settings.Game.Mods );
|
||||
var session = new Session(Game.Settings.Game.Mods);
|
||||
|
||||
var ys = MiniYaml.FromString(data);
|
||||
foreach (var y in ys)
|
||||
{
|
||||
var yy = y.Key.Split('@');
|
||||
|
||||
switch( yy[0] )
|
||||
switch (yy[0])
|
||||
{
|
||||
case "GlobalSettings":
|
||||
FieldLoader.Load(session.GlobalSettings, y.Value);
|
||||
@@ -111,7 +112,7 @@ namespace OpenRA.Network
|
||||
break;
|
||||
|
||||
case "Slot":
|
||||
session.Slots.Add(FieldLoader.Load<Session.Slot>(y.Value ));
|
||||
session.Slots.Add(FieldLoader.Load<Session.Slot>(y.Value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
using System;
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 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. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Support;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
@@ -23,9 +28,6 @@ namespace OpenRA.Network
|
||||
|
||||
internal void UpdateSyncReport()
|
||||
{
|
||||
if (!Game.Settings.Debug.RecordSyncReports)
|
||||
return;
|
||||
|
||||
GenerateSyncReport(syncReports[curIndex]);
|
||||
curIndex = ++curIndex % numSyncReports;
|
||||
}
|
||||
@@ -35,7 +37,7 @@ namespace OpenRA.Network
|
||||
report.Frame = orderManager.NetFrameNumber;
|
||||
report.SyncedRandom = orderManager.world.SharedRandom.Last;
|
||||
report.Traits.Clear();
|
||||
foreach (var a in orderManager.world.Queries.WithTraitMultiple<object>())
|
||||
foreach (var a in orderManager.world.Queries.WithTrait<object>())
|
||||
{
|
||||
var sync = Sync.CalculateSyncHash(a.Trait);
|
||||
if (sync != 0)
|
||||
@@ -43,7 +45,7 @@ namespace OpenRA.Network
|
||||
{
|
||||
ActorID = a.Actor.ActorID,
|
||||
Type = a.Actor.Info.Name,
|
||||
Owner = (a.Actor.Owner == null) ? "null" : a.Actor.Owner.InternalName,
|
||||
Owner = (a.Actor.Owner == null) ? "null" : a.Actor.Owner.PlayerName,
|
||||
Trait = a.Trait.GetType().Name,
|
||||
Hash = sync
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#region Copyright & License Information
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
@@ -11,12 +11,14 @@
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Traits;
|
||||
using System;
|
||||
using OpenRA.FileFormats;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
static class UnitOrders
|
||||
{
|
||||
static Player FindPlayerByClient( this World world, Session.Client c)
|
||||
static Player FindPlayerByClient(this World world, Session.Client c)
|
||||
{
|
||||
/* todo: this is still a hack.
|
||||
* the cases we're trying to avoid are the extra players on the host's client -- Neutral, other MapPlayers,
|
||||
@@ -25,115 +27,194 @@ namespace OpenRA.Network
|
||||
p => p.ClientIndex == c.Index && p.PlayerName == c.Name);
|
||||
}
|
||||
|
||||
public static void ProcessOrder( OrderManager orderManager, World world, int clientId, Order order )
|
||||
public static void ProcessOrder(OrderManager orderManager, World world, int clientId, Order order)
|
||||
{
|
||||
// Drop exploiting orders
|
||||
if (order.Subject != null && order.Subject.Owner.ClientIndex != clientId)
|
||||
if (world != null)
|
||||
{
|
||||
Game.Debug("Detected exploit order from {0}: {1}".F(clientId, order.OrderString));
|
||||
return;
|
||||
if (!world.WorldActor.TraitsImplementing<IValidateOrder>().All(vo =>
|
||||
vo.OrderValidation(orderManager, world, clientId, order)))
|
||||
return;
|
||||
}
|
||||
|
||||
switch( order.OrderString )
|
||||
|
||||
switch (order.OrderString)
|
||||
{
|
||||
case "Chat":
|
||||
{
|
||||
var client = orderManager.LobbyInfo.ClientWithIndex( clientId );
|
||||
if (client != null)
|
||||
{
|
||||
var player = world != null ? world.FindPlayerByClient(client) : null;
|
||||
var suffix = (player != null && player.WinState == WinState.Lost) ? " (Dead)" : "";
|
||||
Game.AddChatLine(client.Color1, client.Name, order.TargetString);
|
||||
}
|
||||
else
|
||||
Game.AddChatLine(Color.White, "(player {0})".F(clientId), order.TargetString);
|
||||
break;
|
||||
}
|
||||
case "TeamChat":
|
||||
{
|
||||
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
|
||||
if (client != null)
|
||||
{
|
||||
if (world == null)
|
||||
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
|
||||
if (client != null)
|
||||
{
|
||||
if (client.Team == orderManager.LocalClient.Team)
|
||||
Game.AddChatLine(client.Color1, client.Name + " (Team)",
|
||||
order.TargetString);
|
||||
var player = world != null ? world.FindPlayerByClient(client) : null;
|
||||
var suffix = (player != null && player.WinState == WinState.Lost) ? " (Dead)" : "";
|
||||
Game.AddChatLine(client.Color1, client.Name + suffix, order.TargetString);
|
||||
}
|
||||
else
|
||||
Game.AddChatLine(Color.White, "(player {0})".F(clientId), order.TargetString);
|
||||
break;
|
||||
}
|
||||
case "Disconnected": /* reports that the target player disconnected */
|
||||
{
|
||||
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
|
||||
if (client != null)
|
||||
{
|
||||
var player = world.FindPlayerByClient(client);
|
||||
var display = player != null
|
||||
&& (world.LocalPlayer != null && player.Stances[world.LocalPlayer] == Stance.Ally
|
||||
|| player.WinState == WinState.Lost);
|
||||
client.State = Session.ClientState.Disconnected;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "TeamChat":
|
||||
{
|
||||
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
|
||||
|
||||
if (display)
|
||||
if (client != null)
|
||||
{
|
||||
if (world == null)
|
||||
{
|
||||
var suffix = (player != null && player.WinState == WinState.Lost) ? " (Dead)" : " (Team)";
|
||||
Game.AddChatLine(client.Color1, client.Name + suffix, order.TargetString);
|
||||
if (client.Team == orderManager.LocalClient.Team)
|
||||
Game.AddChatLine(client.Color1, client.Name + " (Team)",
|
||||
order.TargetString);
|
||||
}
|
||||
else
|
||||
{
|
||||
var player = world.FindPlayerByClient(client);
|
||||
var display = player != null
|
||||
&&
|
||||
(world.LocalPlayer != null &&
|
||||
player.Stances[world.LocalPlayer] == Stance.Ally
|
||||
|| player.WinState == WinState.Lost);
|
||||
|
||||
if (display)
|
||||
{
|
||||
var suffix = (player != null && player.WinState == WinState.Lost)
|
||||
? " (Dead)"
|
||||
: " (Team)";
|
||||
Game.AddChatLine(client.Color1, client.Name + suffix, order.TargetString);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "StartGame":
|
||||
{
|
||||
Game.AddChatLine(Color.White, "Server", "The game has started.");
|
||||
Game.StartGame(orderManager.LobbyInfo.GlobalSettings.Map);
|
||||
break;
|
||||
}
|
||||
case "SyncInfo":
|
||||
{
|
||||
orderManager.LobbyInfo = Session.Deserialize( order.TargetString );
|
||||
|
||||
//if( !world.GameHasStarted )
|
||||
// world.SharedRandom = new XRandom( LobbyInfo.GlobalSettings.RandomSeed );
|
||||
|
||||
//if (orderManager.Connection.ConnectionState == ConnectionState.Connected)
|
||||
// world.SetLocalPlayer(orderManager.Connection.LocalClientId);
|
||||
|
||||
if( orderManager.FramesAhead != orderManager.LobbyInfo.GlobalSettings.OrderLatency
|
||||
&& !orderManager.GameStarted )
|
||||
case "StartGame":
|
||||
{
|
||||
orderManager.FramesAhead = orderManager.LobbyInfo.GlobalSettings.OrderLatency;
|
||||
Game.Debug( "Order lag is now {0} frames.".F( orderManager.LobbyInfo.GlobalSettings.OrderLatency ) );
|
||||
Game.AddChatLine(Color.White, "Server", "The game has started.");
|
||||
Game.StartGame(orderManager.LobbyInfo.GlobalSettings.Map);
|
||||
break;
|
||||
}
|
||||
|
||||
case "HandshakeRequest":
|
||||
{
|
||||
var request = HandshakeRequest.Deserialize(order.TargetString);
|
||||
|
||||
// Check valid mods/versions
|
||||
var serverMods = request.Mods;
|
||||
var localMods = orderManager.LobbyInfo.GlobalSettings.Mods;
|
||||
|
||||
bool valid = true;
|
||||
if (localMods.Length != serverMods.Length)
|
||||
valid = false;
|
||||
else
|
||||
foreach (var m in serverMods)
|
||||
{
|
||||
var parts = m.Split('@');
|
||||
if (!localMods.Contains(parts[0]))
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
if (parts[1] == "{DEV_VERSION}" || Mod.AllMods[parts[0]].Version == "{DEV_VERSION}")
|
||||
continue;
|
||||
|
||||
if (parts[1] != Mod.AllMods[parts[0]].Version)
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
throw new InvalidOperationException("Mod/Version mismatch. Client: `{0}`, Server: `{1}`".F(
|
||||
string.Join(",",localMods.Select(m => "{0}@{1}".F(m, Mod.AllMods[m].Version)).ToArray()),
|
||||
string.Join(",",serverMods)));
|
||||
|
||||
// Check that the map exists on the client
|
||||
if (!Game.modData.AvailableMaps.ContainsKey(request.Map))
|
||||
throw new InvalidOperationException("Missing map {0}".F(request.Map));
|
||||
|
||||
var info = new Session.Client()
|
||||
{
|
||||
Name = Game.Settings.Player.Name,
|
||||
Color1 = Game.Settings.Player.Color1,
|
||||
Color2 = Game.Settings.Player.Color2,
|
||||
Country = "random",
|
||||
SpawnPoint = 0,
|
||||
Team = 0,
|
||||
State = Session.ClientState.NotReady
|
||||
};
|
||||
|
||||
var response = new HandshakeResponse()
|
||||
{
|
||||
Client = info,
|
||||
Mods = localMods,
|
||||
Password = "Foo"
|
||||
};
|
||||
|
||||
orderManager.IssueOrder(Order.HandshakeResponse(response.Serialize()));
|
||||
break;
|
||||
}
|
||||
|
||||
case "SyncInfo":
|
||||
{
|
||||
orderManager.LobbyInfo = Session.Deserialize(order.TargetString);
|
||||
|
||||
if (orderManager.FramesAhead != orderManager.LobbyInfo.GlobalSettings.OrderLatency
|
||||
&& !orderManager.GameStarted)
|
||||
{
|
||||
orderManager.FramesAhead = orderManager.LobbyInfo.GlobalSettings.OrderLatency;
|
||||
Game.Debug(
|
||||
"Order lag is now {0} frames.".F(orderManager.LobbyInfo.GlobalSettings.OrderLatency));
|
||||
}
|
||||
Game.SyncLobbyInfo();
|
||||
break;
|
||||
}
|
||||
|
||||
Game.SyncLobbyInfo();
|
||||
break;
|
||||
}
|
||||
case "SetStance":
|
||||
{
|
||||
var targetPlayer = order.Player.World.players[order.TargetLocation.X];
|
||||
var oldStance = order.Player.Stances[targetPlayer];
|
||||
var newStance = (Stance)order.TargetLocation.Y;
|
||||
case "SetStance":
|
||||
{
|
||||
var targetPlayer = order.Player.World.players[order.TargetLocation.X];
|
||||
var newStance = (Stance)order.TargetLocation.Y;
|
||||
|
||||
SetPlayerStance(world, order.Player, targetPlayer, newStance);
|
||||
SetPlayerStance(world, order.Player, targetPlayer, newStance);
|
||||
|
||||
// automatically declare war reciprocally
|
||||
if (newStance == Stance.Enemy)
|
||||
SetPlayerStance(world, targetPlayer, order.Player, newStance);
|
||||
Game.Debug("{0} has set diplomatic stance vs {1} to {2}".F(
|
||||
order.Player.PlayerName, targetPlayer.PlayerName, newStance));
|
||||
|
||||
// automatically declare war reciprocally
|
||||
if (newStance == Stance.Enemy && targetPlayer.Stances[order.Player] == Stance.Ally)
|
||||
{
|
||||
SetPlayerStance(world, targetPlayer, order.Player, newStance);
|
||||
Game.Debug("{0} has reciprocated",targetPlayer.PlayerName);
|
||||
}
|
||||
|
||||
Game.Debug("{0} has set diplomatic stance vs {1} to {2}".F(
|
||||
order.Player.PlayerName, targetPlayer.PlayerName, newStance));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if( !order.IsImmediate )
|
||||
foreach (var t in order.Subject.TraitsImplementing<IResolveOrder>())
|
||||
t.ResolveOrder(order.Subject, order);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if( !order.IsImmediate )
|
||||
{
|
||||
var self = order.Subject;
|
||||
var health = self.TraitOrDefault<Health>();
|
||||
if( health == null || !health.IsDead )
|
||||
foreach( var t in self.TraitsImplementing<IResolveOrder>() )
|
||||
t.ResolveOrder( self, order );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SetPlayerStance(World w, Player a, Player b, Stance s)
|
||||
static void SetPlayerStance(World w, Player p, Player target, Stance s)
|
||||
{
|
||||
var oldStance = a.Stances[b];
|
||||
a.Stances[b] = s;
|
||||
if (b == w.LocalPlayer)
|
||||
w.WorldActor.Trait<Shroud>().UpdatePlayerStance(w, b, oldStance, s);
|
||||
var oldStance = p.Stances[target];
|
||||
p.Stances[target] = s;
|
||||
if (target == w.LocalPlayer)
|
||||
w.WorldActor.Trait<Shroud>().UpdatePlayerStance(w, p, oldStance, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
using System;
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 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. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using OpenRA.FileFormats;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
@@ -79,22 +79,14 @@
|
||||
<Compile Include="Group.cs" />
|
||||
<Compile Include="Orders\GenericSelectTarget.cs" />
|
||||
<Compile Include="Server\ProtocolVersion.cs" />
|
||||
<Compile Include="Traits\BaseBuilding.cs" />
|
||||
<Compile Include="Traits\LintAttributes.cs" />
|
||||
<Compile Include="Traits\Player\PlayerResources.cs" />
|
||||
<Compile Include="Traits\World\Shroud.cs" />
|
||||
<Compile Include="Widgets\ChatEntryWidget.cs" />
|
||||
<Compile Include="Widgets\Delegates\ConnectionDialogsDelegate.cs" />
|
||||
<Compile Include="Widgets\Delegates\CreateServerMenuDelegate.cs" />
|
||||
<Compile Include="Widgets\Delegates\DiplomacyDelegate.cs" />
|
||||
<Compile Include="Widgets\Delegates\MainMenuButtonsDelegate.cs" />
|
||||
<Compile Include="Widgets\Delegates\ServerBrowserDelegate.cs" />
|
||||
<Compile Include="Widgets\Delegates\SettingsMenuDelegate.cs" />
|
||||
<Compile Include="Widgets\MapPreviewWidget.cs" />
|
||||
<Compile Include="Widgets\WidgetUtils.cs" />
|
||||
<Compile Include="Effects\DelayedAction.cs" />
|
||||
<Compile Include="Effects\FlashTarget.cs" />
|
||||
<Compile Include="Effects\MoveFlash.cs" />
|
||||
<Compile Include="Exts.cs" />
|
||||
<Compile Include="GameRules\ActorInfo.cs" />
|
||||
<Compile Include="GameRules\VoiceInfo.cs" />
|
||||
@@ -106,23 +98,19 @@
|
||||
<Compile Include="Network\Connection.cs" />
|
||||
<Compile Include="Network\OrderIO.cs" />
|
||||
<Compile Include="Network\OrderManager.cs" />
|
||||
<Compile Include="PathSearch.cs" />
|
||||
<Compile Include="Selection.cs" />
|
||||
<Compile Include="Server\Connection.cs" />
|
||||
<Compile Include="Server\Exts.cs" />
|
||||
<Compile Include="Server\MasterServerQuery.cs" />
|
||||
<Compile Include="Server\Server.cs" />
|
||||
<Compile Include="Server\ServerOrder.cs" />
|
||||
<Compile Include="ShroudRenderer.cs" />
|
||||
<Compile Include="Sound.cs" />
|
||||
<Compile Include="Support\PerfHistory.cs" />
|
||||
<Compile Include="Sync.cs" />
|
||||
<Compile Include="Traits\CustomSellValue.cs" />
|
||||
<Compile Include="Traits\World\SpatialBins.cs" />
|
||||
<Compile Include="Traits\World\Country.cs" />
|
||||
<Compile Include="Actor.cs" />
|
||||
<Compile Include="Cursor.cs" />
|
||||
<Compile Include="GameRules\Footprint.cs" />
|
||||
<Compile Include="GameRules\Rules.cs" />
|
||||
<Compile Include="Graphics\Animation.cs" />
|
||||
<Compile Include="Game.cs" />
|
||||
@@ -130,20 +118,14 @@
|
||||
<Compile Include="Graphics\CursorSheetBuilder.cs" />
|
||||
<Compile Include="Graphics\LineRenderer.cs" />
|
||||
<Compile Include="Graphics\WorldRenderer.cs" />
|
||||
<Compile Include="Traits\Activities\Idle.cs" />
|
||||
<Compile Include="Traits\Activities\RemoveSelf.cs" />
|
||||
<Compile Include="Traits\Activities\Sell.cs" />
|
||||
<Compile Include="Orders\IOrderGenerator.cs" />
|
||||
<Compile Include="Player.cs" />
|
||||
<Compile Include="Graphics\Sheet.cs" />
|
||||
<Compile Include="PathFinder.cs" />
|
||||
<Compile Include="Graphics\Sequence.cs" />
|
||||
<Compile Include="Network\Order.cs" />
|
||||
<Compile Include="Graphics\SequenceProvider.cs" />
|
||||
<Compile Include="Graphics\SheetBuilder.cs" />
|
||||
<Compile Include="Graphics\HardwarePalette.cs" />
|
||||
<Compile Include="MainWindow.cs">
|
||||
</Compile>
|
||||
<Compile Include="Support\Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Graphics\Renderer.cs" />
|
||||
@@ -151,15 +133,10 @@
|
||||
<Compile Include="Graphics\SpriteRenderer.cs" />
|
||||
<Compile Include="Graphics\SpriteSheetBuilder.cs" />
|
||||
<Compile Include="Graphics\TerrainRenderer.cs" />
|
||||
<Compile Include="Traits\Activities\Move.cs" />
|
||||
<Compile Include="Traits\Activities\Turn.cs" />
|
||||
<Compile Include="Traits\Building.cs" />
|
||||
<Compile Include="Traits\World\BuildingInfluence.cs" />
|
||||
<Compile Include="Traits\World\PlayerColorPalette.cs" />
|
||||
<Compile Include="Traits\World\ResourceLayer.cs" />
|
||||
<Compile Include="Traits\World\ResourceType.cs" />
|
||||
<Compile Include="Traits\Selectable.cs" />
|
||||
<Compile Include="Traits\Mobile.cs" />
|
||||
<Compile Include="Traits\Render\RenderSimple.cs" />
|
||||
<Compile Include="Traits\TraitsInterfaces.cs" />
|
||||
<Compile Include="Traits\World\UnitInfluence.cs" />
|
||||
@@ -169,29 +146,22 @@
|
||||
<Compile Include="Graphics\Util.cs" />
|
||||
<Compile Include="Graphics\Viewport.cs" />
|
||||
<Compile Include="Orders\UnitOrderGenerator.cs" />
|
||||
<Compile Include="Widgets\WorldTooltipWidget.cs" />
|
||||
<Compile Include="World.cs" />
|
||||
<Compile Include="WorldUtils.cs" />
|
||||
<Compile Include="Traits\Player\EvaAlerts.cs" />
|
||||
<Compile Include="Traits\World\ScreenShaker.cs" />
|
||||
<Compile Include="Traits\LineBuild.cs" />
|
||||
<Compile Include="Widgets\WidgetLoader.cs" />
|
||||
<Compile Include="Widgets\ButtonWidget.cs" />
|
||||
<Compile Include="Widgets\Widget.cs" />
|
||||
<Compile Include="Widgets\BackgroundWidget.cs" />
|
||||
<Compile Include="Widgets\LabelWidget.cs" />
|
||||
<Compile Include="Widgets\CheckboxWidget.cs" />
|
||||
<Compile Include="Widgets\Delegates\MusicPlayerDelegate.cs" />
|
||||
<Compile Include="Widgets\PerfGraphWidget.cs" />
|
||||
<Compile Include="Widgets\Delegates\PerfDebugDelegate.cs" />
|
||||
<Compile Include="Widgets\Delegates\LobbyDelegate.cs" />
|
||||
<Compile Include="Widgets\ColorBlockWidget.cs" />
|
||||
<Compile Include="GameRules\MusicInfo.cs" />
|
||||
<Compile Include="Widgets\ImageWidget.cs" />
|
||||
<Compile Include="Widgets\TextFieldWidget.cs" />
|
||||
<Compile Include="Widgets\ChatDisplayWidget.cs" />
|
||||
<Compile Include="Widgets\Delegates\MapChooserDelegate.cs" />
|
||||
<Compile Include="Widgets\ListBoxWidget.cs" />
|
||||
<Compile Include="Widgets\SliderWidget.cs" />
|
||||
<Compile Include="Widgets\TimerWidget.cs" />
|
||||
<Compile Include="Widgets\ShpImageWidget.cs" />
|
||||
@@ -200,18 +170,17 @@
|
||||
<Compile Include="Widgets\ViewportScrollControllerWidget.cs" />
|
||||
<Compile Include="Traits\Player\DeveloperMode.cs" />
|
||||
<Compile Include="Traits\RevealsShroud.cs" />
|
||||
<Compile Include="Traits\Targetable.cs" />
|
||||
<Compile Include="Traits\Health.cs" />
|
||||
<Compile Include="Traits\Activities\Drag.cs" />
|
||||
<Compile Include="Widgets\VqaPlayerWidget.cs" />
|
||||
<Compile Include="Widgets\Delegates\VideoPlayerDelegate.cs" />
|
||||
<Compile Include="GameRules\Settings.cs" />
|
||||
<Compile Include="Support\Arguments.cs" />
|
||||
<Compile Include="Traits\ActorStance.cs" />
|
||||
<Compile Include="Traits\Armor.cs" />
|
||||
<Compile Include="Graphics\CursorProvider.cs" />
|
||||
<Compile Include="Traits\Player\TechTree.cs" />
|
||||
<Compile Include="Traits\Player\PowerManager.cs" />
|
||||
<Compile Include="Server\TraitInterfaces.cs" />
|
||||
<Compile Include="Widgets\ScrollPanelWidget.cs" />
|
||||
<Compile Include="Graphics\ShroudRenderer.cs" />
|
||||
<Compile Include="Network\Handshake.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
|
||||
@@ -220,6 +189,7 @@
|
||||
</ProjectReference>
|
||||
<Compile Include="ActorInitializer.cs" />
|
||||
<Compile Include="ActorReference.cs" />
|
||||
<Compile Include="InputHandler.cs" />
|
||||
<Compile Include="ModData.cs" />
|
||||
<Compile Include="Map.cs" />
|
||||
<Compile Include="Network\FrameData.cs" />
|
||||
@@ -227,11 +197,12 @@
|
||||
<Compile Include="Network\Session.cs" />
|
||||
<Compile Include="ObjectCreator.cs" />
|
||||
<Compile Include="Network\SyncReport.cs" />
|
||||
<Compile Include="Traits\EditorAppearance.cs" />
|
||||
<Compile Include="Traits\ValidateOrder.cs" />
|
||||
<Compile Include="TraitDictionary.cs" />
|
||||
<Compile Include="Traits\Activities\CancelableActivity.cs" />
|
||||
<Compile Include="Traits\SharesCell.cs" />
|
||||
<Compile Include="Traits\Valued.cs" />
|
||||
<Compile Include="Traits\World\BibLayer.cs" />
|
||||
<Compile Include="Widgets\Delegates\DeveloperModeDelegate.cs" />
|
||||
<Compile Include="Widgets\PasswordFieldWidget.cs" />
|
||||
<Compile Include="Widgets\ScrollingTextWidget.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -254,6 +225,9 @@
|
||||
<ItemGroup>
|
||||
<Content Include="OpenRA.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Widgets\Delegates\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
||||
@@ -20,60 +20,54 @@ namespace OpenRA.Orders
|
||||
readonly IEnumerable<Actor> subjects;
|
||||
readonly string order;
|
||||
readonly string cursor;
|
||||
readonly MouseButton expectedButton;
|
||||
|
||||
public GenericSelectTarget(IEnumerable<Actor> subjects, string order, string cursor)
|
||||
public GenericSelectTarget(IEnumerable<Actor> subjects, string order, string cursor, MouseButton button)
|
||||
{
|
||||
this.subjects = subjects;
|
||||
this.order = order;
|
||||
this.cursor = cursor;
|
||||
expectedButton = button;
|
||||
}
|
||||
|
||||
public GenericSelectTarget(IEnumerable<Actor> subjects, string order, string cursor)
|
||||
: this(subjects, order, cursor, MouseButton.Left)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public GenericSelectTarget(Actor subject, string order, string cursor)
|
||||
: this(new Actor[] { subject }, order, cursor)
|
||||
{
|
||||
this.subjects = new Actor[] { subject };
|
||||
this.order = order;
|
||||
this.cursor = cursor;
|
||||
|
||||
}
|
||||
|
||||
public GenericSelectTarget(Actor subject, string order, string cursor, MouseButton button)
|
||||
: this(new Actor[] { subject }, order, cursor, button)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public IEnumerable<Order> Order(World world, int2 xy, MouseInput mi)
|
||||
{
|
||||
if (mi.Button == MouseButton.Right)
|
||||
if (mi.Button != expectedButton)
|
||||
world.CancelInputMode();
|
||||
return OrderInner(world, xy, mi);
|
||||
}
|
||||
|
||||
IEnumerable<Order> OrderInner(World world, int2 xy, MouseInput mi)
|
||||
{
|
||||
if (mi.Button == MouseButton.Left && world.Map.IsInMap(xy))
|
||||
if (mi.Button == expectedButton && world.Map.IsInMap(xy))
|
||||
{
|
||||
world.CancelInputMode();
|
||||
foreach (var subject in subjects)
|
||||
yield return new Order(order, subject, xy);
|
||||
yield return new Order(order, subject, false) { TargetLocation = xy };
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Tick(World world) { }
|
||||
|
||||
public void RenderBeforeWorld(WorldRenderer wr, World world)
|
||||
{
|
||||
foreach (var a in world.Selection.Actors)
|
||||
if (!a.Destroyed)
|
||||
foreach (var t in a.TraitsImplementing<IPreRenderSelection>())
|
||||
t.RenderBeforeWorld(wr, a);
|
||||
|
||||
Game.Renderer.Flush();
|
||||
}
|
||||
|
||||
public void RenderAfterWorld(WorldRenderer wr, World world)
|
||||
{
|
||||
foreach (var a in world.Selection.Actors)
|
||||
if (!a.Destroyed)
|
||||
foreach (var t in a.TraitsImplementing<IPostRenderSelection>())
|
||||
t.RenderAfterWorld(wr, a);
|
||||
|
||||
Game.Renderer.Flush();
|
||||
}
|
||||
|
||||
public virtual void Tick(World world) { }
|
||||
public void RenderBeforeWorld(WorldRenderer wr, World world) { }
|
||||
public void RenderAfterWorld(WorldRenderer wr, World world) { }
|
||||
public string GetCursor(World world, int2 xy, MouseInput mi) { return world.Map.IsInMap(xy) ? cursor : "generic-blocked"; }
|
||||
}
|
||||
|
||||
@@ -84,6 +78,9 @@ namespace OpenRA.Orders
|
||||
public GenericSelectTargetWithBuilding(Actor subject, string order, string cursor)
|
||||
: base(subject, order, cursor) { }
|
||||
|
||||
public GenericSelectTargetWithBuilding(Actor subject, string order, string cursor, MouseButton button)
|
||||
: base(subject, order, cursor, button) { }
|
||||
|
||||
public override void Tick(World world)
|
||||
{
|
||||
var hasStructure = world.Queries.OwnedBy[world.LocalPlayer]
|
||||
|
||||
@@ -17,68 +17,106 @@ namespace OpenRA.Orders
|
||||
{
|
||||
class UnitOrderGenerator : IOrderGenerator
|
||||
{
|
||||
public IEnumerable<Order> Order( World world, int2 xy, MouseInput mi )
|
||||
{
|
||||
var underCursor = world.FindUnitsAtMouse(mi.Location)
|
||||
.Where(a => a.Info.Traits.Contains<TargetableInfo>())
|
||||
.OrderByDescending(a => a.Info.Traits.Contains<SelectableInfo>() ? a.Info.Traits.Get<SelectableInfo>().Priority : int.MinValue)
|
||||
.FirstOrDefault();
|
||||
|
||||
var orders = world.Selection.Actors
|
||||
.Select(a => OrderForUnit(a, xy, mi, underCursor))
|
||||
.Where(o => o != null)
|
||||
.ToArray();
|
||||
|
||||
var actorsInvolved = orders.Select(o => o.self).Distinct();
|
||||
if (actorsInvolved.Any())
|
||||
yield return new Order("CreateGroup", actorsInvolved.First().Owner.PlayerActor,
|
||||
string.Join(",", actorsInvolved.Select(a => a.ActorID.ToString()).ToArray()));
|
||||
public IEnumerable<Order> Order( World world, int2 xy, MouseInput mi )
|
||||
{
|
||||
var custom = world.WorldActor.TraitOrDefault<ICustomUnitOrderGenerator>();
|
||||
if (custom != null)
|
||||
{
|
||||
var customOrders = custom.Order(world, xy, mi);
|
||||
|
||||
foreach( var o in orders )
|
||||
yield return CheckSameOrder( o.iot, o.trait.IssueOrder( o.self, o.iot, o.target ) );
|
||||
foreach (var o in customOrders)
|
||||
yield return o;
|
||||
}
|
||||
else
|
||||
{
|
||||
var underCursor = world.FindUnitsAtMouse(mi.Location)
|
||||
.Where(a => a.HasTrait<ITargetable>())
|
||||
.OrderByDescending(
|
||||
a =>
|
||||
a.Info.Traits.Contains<SelectableInfo>()
|
||||
? a.Info.Traits.Get<SelectableInfo>().Priority
|
||||
: int.MinValue)
|
||||
.FirstOrDefault();
|
||||
|
||||
var orders = world.Selection.Actors
|
||||
.Select(a => OrderForUnit(a, xy, mi, underCursor))
|
||||
.Where(o => o != null)
|
||||
.ToArray();
|
||||
|
||||
var actorsInvolved = orders.Select(o => o.self).Distinct();
|
||||
if (actorsInvolved.Any())
|
||||
yield return new Order("CreateGroup", actorsInvolved.First().Owner.PlayerActor, false)
|
||||
{
|
||||
TargetString = string.Join(",", actorsInvolved.Select(a => a.ActorID.ToString()).ToArray())
|
||||
};
|
||||
|
||||
|
||||
foreach (var o in orders)
|
||||
yield return CheckSameOrder(o.iot, o.trait.IssueOrder(o.self, o.iot, o.target, mi.Modifiers.HasModifier(Modifiers.Shift)));
|
||||
}
|
||||
}
|
||||
|
||||
public void Tick( World world ) {}
|
||||
public void Tick( World world )
|
||||
{
|
||||
var custom = world.WorldActor.TraitOrDefault<ICustomUnitOrderGenerator>();
|
||||
if (custom != null)
|
||||
{
|
||||
custom.Tick(world);
|
||||
}
|
||||
}
|
||||
|
||||
public void RenderBeforeWorld( WorldRenderer wr, World world )
|
||||
{
|
||||
foreach (var a in world.Selection.Actors)
|
||||
if (!a.Destroyed)
|
||||
foreach (var t in a.TraitsImplementing<IPreRenderSelection>())
|
||||
t.RenderBeforeWorld( wr, a );
|
||||
public void RenderBeforeWorld( WorldRenderer wr, World world )
|
||||
{
|
||||
var custom = world.WorldActor.TraitOrDefault<ICustomUnitOrderGenerator>();
|
||||
if (custom != null)
|
||||
{
|
||||
custom.RenderBeforeWorld(wr, world);
|
||||
return;
|
||||
}
|
||||
|
||||
Game.Renderer.Flush();
|
||||
}
|
||||
|
||||
public void RenderAfterWorld( WorldRenderer wr, World world )
|
||||
{
|
||||
foreach (var a in world.Selection.Actors)
|
||||
if (!a.Destroyed)
|
||||
foreach (var t in a.TraitsImplementing<IPostRenderSelection>())
|
||||
t.RenderAfterWorld( wr, a );
|
||||
|
||||
public void RenderAfterWorld( WorldRenderer wr, World world )
|
||||
{
|
||||
var custom = world.WorldActor.TraitOrDefault<ICustomUnitOrderGenerator>();
|
||||
if (custom != null)
|
||||
{
|
||||
custom.RenderAfterWorld(wr, world);
|
||||
return;
|
||||
}
|
||||
|
||||
Game.Renderer.Flush();
|
||||
}
|
||||
|
||||
public string GetCursor( World world, int2 xy, MouseInput mi )
|
||||
{
|
||||
public string GetCursor( World world, int2 xy, MouseInput mi )
|
||||
{
|
||||
bool useSelect = false;
|
||||
|
||||
var custom = world.WorldActor.TraitOrDefault<ICustomUnitOrderGenerator>();
|
||||
if (custom != null)
|
||||
{
|
||||
return custom.GetCursor(world, xy, mi);
|
||||
}
|
||||
|
||||
var underCursor = world.FindUnitsAtMouse(mi.Location)
|
||||
.Where(a => a.Info.Traits.Contains<TargetableInfo>())
|
||||
.Where(a => a.HasTrait<ITargetable>())
|
||||
.OrderByDescending(a => a.Info.Traits.Contains<SelectableInfo>() ? a.Info.Traits.Get<SelectableInfo>().Priority : int.MinValue)
|
||||
.FirstOrDefault();
|
||||
|
||||
if( mi.Modifiers.HasModifier( Modifiers.Shift ) || !world.Selection.Actors.Any() )
|
||||
if( underCursor != null )
|
||||
return "select";
|
||||
|
||||
.FirstOrDefault();
|
||||
|
||||
|
||||
if (mi.Modifiers.HasModifier(Modifiers.Shift) || !world.Selection.Actors.Any())
|
||||
if (underCursor != null)
|
||||
useSelect = true;
|
||||
|
||||
var orders = world.Selection.Actors
|
||||
.Select(a => OrderForUnit(a, xy, mi, underCursor))
|
||||
.Where(o => o != null)
|
||||
.ToArray();
|
||||
|
||||
if( orders.Length == 0 ) return "default";
|
||||
if( orders.Length == 0 ) return (useSelect) ? "select" : "default";
|
||||
|
||||
return orders[ 0 ].cursor ?? "default";
|
||||
return orders[0].cursor ?? ((useSelect) ? "select" : "default");
|
||||
}
|
||||
|
||||
static UnitOrderResult OrderForUnit( Actor self, int2 xy, MouseInput mi, Actor underCursor )
|
||||
@@ -86,19 +124,10 @@ namespace OpenRA.Orders
|
||||
if (self.Owner != self.World.LocalPlayer)
|
||||
return null;
|
||||
|
||||
if (!self.World.Map.IsInMap(xy.X, xy.Y))
|
||||
return null;
|
||||
|
||||
if (self.Destroyed)
|
||||
return null;
|
||||
|
||||
//var old = self.TraitsImplementing<IIssueOrder>()
|
||||
// .OrderByDescending( x => x.OrderPriority( self, xy, mi, underCursor ) )
|
||||
// .Select( x => x.IssueOrder( self, xy, mi, underCursor ) )
|
||||
// .FirstOrDefault( x => x != null );
|
||||
//if( old != null )
|
||||
// return old;
|
||||
|
||||
if( mi.Button == MouseButton.Right )
|
||||
{
|
||||
var uim = self.World.WorldActor.Trait<UnitInfluence>();
|
||||
@@ -111,9 +140,9 @@ namespace OpenRA.Orders
|
||||
|
||||
string cursor = null;
|
||||
if( underCursor != null )
|
||||
if( o.Order.CanTargetUnit( self, underCursor, mi.Modifiers.HasModifier( Modifiers.Ctrl ), mi.Modifiers.HasModifier( Modifiers.Alt ), ref cursor ) )
|
||||
if (o.Order.CanTargetActor(self, underCursor, mi.Modifiers.HasModifier(Modifiers.Ctrl), mi.Modifiers.HasModifier(Modifiers.Alt), mi.Modifiers.HasModifier(Modifiers.Shift), ref cursor))
|
||||
return new UnitOrderResult( self, o.Order, o.Trait, cursor, Target.FromActor( underCursor ) );
|
||||
if( o.Order.CanTargetLocation( self, xy, actorsAt, mi.Modifiers.HasModifier( Modifiers.Ctrl ), mi.Modifiers.HasModifier( Modifiers.Alt ), ref cursor ) )
|
||||
if (o.Order.CanTargetLocation(self, xy, actorsAt, mi.Modifiers.HasModifier(Modifiers.Ctrl), mi.Modifiers.HasModifier(Modifiers.Alt), mi.Modifiers.HasModifier(Modifiers.Shift), ref cursor))
|
||||
return new UnitOrderResult( self, o.Order, o.Trait, cursor, Target.FromCell( xy ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,85 +13,83 @@ using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public enum PowerState { Normal, Low, Critical };
|
||||
public enum WinState { Won, Lost, Undefined };
|
||||
|
||||
public class Player
|
||||
{
|
||||
public Actor PlayerActor;
|
||||
public int Kills;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public enum PowerState { Normal, Low, Critical };
|
||||
public enum WinState { Won, Lost, Undefined };
|
||||
|
||||
public class Player
|
||||
{
|
||||
public Actor PlayerActor;
|
||||
public int Kills;
|
||||
public int Deaths;
|
||||
public WinState WinState = WinState.Undefined;
|
||||
|
||||
public readonly string Palette;
|
||||
public readonly Color Color;
|
||||
public readonly Color Color2;
|
||||
|
||||
public readonly string PlayerName;
|
||||
public readonly string InternalName;
|
||||
public readonly CountryInfo Country;
|
||||
public readonly int Index;
|
||||
public WinState WinState = WinState.Undefined;
|
||||
|
||||
public readonly string Palette;
|
||||
public readonly Color Color;
|
||||
public readonly Color Color2;
|
||||
|
||||
public readonly string PlayerName;
|
||||
public readonly string InternalName;
|
||||
public readonly CountryInfo Country;
|
||||
public readonly int Index;
|
||||
public readonly bool NonCombatant = false;
|
||||
public readonly int ClientIndex;
|
||||
public readonly PlayerReference PlayerRef;
|
||||
public bool IsBot;
|
||||
|
||||
public ShroudRenderer Shroud;
|
||||
public World World { get; private set; }
|
||||
|
||||
public Player( World world, PlayerReference pr, int index )
|
||||
{
|
||||
World = world;
|
||||
Shroud = new ShroudRenderer(this, world.Map);
|
||||
|
||||
Index = index;
|
||||
Palette = "player"+index;
|
||||
|
||||
Color = pr.Color;
|
||||
public bool IsBot;
|
||||
|
||||
public Shroud Shroud { get { return World.LocalShroud; }}
|
||||
public World World { get; private set; }
|
||||
|
||||
public Player(World world, PlayerReference pr, int index)
|
||||
{
|
||||
World = world;
|
||||
|
||||
Index = index;
|
||||
Palette = "player" + index;
|
||||
|
||||
Color = pr.Color;
|
||||
Color2 = pr.Color2;
|
||||
ClientIndex = 0; /* it's a map player, "owned" by host */
|
||||
|
||||
PlayerName = InternalName = pr.Name;
|
||||
NonCombatant = pr.NonCombatant;
|
||||
Country = world.GetCountries()
|
||||
ClientIndex = 0; /* it's a map player, "owned" by host */
|
||||
|
||||
PlayerName = InternalName = pr.Name;
|
||||
NonCombatant = pr.NonCombatant;
|
||||
Country = world.GetCountries()
|
||||
.FirstOrDefault(c => pr.Race == c.Race)
|
||||
?? world.GetCountries().Random(world.SharedRandom);
|
||||
|
||||
PlayerRef = pr;
|
||||
|
||||
PlayerActor = world.CreateActor("Player", new TypeDictionary{ new OwnerInit( this ) });
|
||||
}
|
||||
|
||||
public Player( World world, Session.Client client, PlayerReference pr, int index )
|
||||
{
|
||||
World = world;
|
||||
Shroud = new ShroudRenderer(this, world.Map);
|
||||
|
||||
PlayerRef = pr;
|
||||
|
||||
PlayerActor = world.CreateActor("Player", new TypeDictionary { new OwnerInit(this) });
|
||||
}
|
||||
|
||||
public Player(World world, Session.Client client, PlayerReference pr, int index)
|
||||
{
|
||||
World = world;
|
||||
Index = index;
|
||||
Palette = "player"+index;
|
||||
Color = client.Color1;
|
||||
Color2 = client.Color2;
|
||||
Palette = "player" + index;
|
||||
Color = client.Color1;
|
||||
Color2 = client.Color2;
|
||||
PlayerName = client.Name;
|
||||
InternalName = pr.Name;
|
||||
Country = world.GetCountries()
|
||||
.FirstOrDefault(c => client != null && client.Country == c.Race)
|
||||
|
||||
InternalName = pr.Name;
|
||||
Country = world.GetCountries()
|
||||
.FirstOrDefault(c => client != null && client.Country == c.Race)
|
||||
?? world.GetCountries().Random(world.SharedRandom);
|
||||
|
||||
ClientIndex = client.Index;
|
||||
PlayerRef = pr;
|
||||
|
||||
PlayerActor = world.CreateActor("Player", new TypeDictionary{ new OwnerInit( this ) });
|
||||
}
|
||||
|
||||
public void GiveAdvice(string advice)
|
||||
{
|
||||
Sound.PlayToPlayer(this, advice);
|
||||
}
|
||||
|
||||
public Dictionary<Player, Stance> Stances = new Dictionary<Player, Stance>();
|
||||
}
|
||||
PlayerRef = pr;
|
||||
|
||||
PlayerActor = world.CreateActor("Player", new TypeDictionary { new OwnerInit(this) });
|
||||
}
|
||||
|
||||
public void GiveAdvice(string advice)
|
||||
{
|
||||
Sound.PlayToPlayer(this, advice);
|
||||
}
|
||||
|
||||
public Dictionary<Player, Stance> Stances = new Dictionary<Player, Stance>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ using System.Net.Sockets;
|
||||
|
||||
namespace OpenRA.Server
|
||||
{
|
||||
class Connection
|
||||
public class Connection
|
||||
{
|
||||
public Socket socket;
|
||||
public List<byte> data = new List<byte>();
|
||||
@@ -35,7 +35,7 @@ namespace OpenRA.Server
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
bool ReadDataInner()
|
||||
bool ReadDataInner( Server server )
|
||||
{
|
||||
var rx = new byte[1024];
|
||||
var len = 0;
|
||||
@@ -49,7 +49,7 @@ namespace OpenRA.Server
|
||||
else
|
||||
{
|
||||
if (len == 0)
|
||||
Server.DropClient(this, null);
|
||||
server.DropClient(this);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace OpenRA.Server
|
||||
catch (SocketException e)
|
||||
{
|
||||
if (e.SocketErrorCode == SocketError.WouldBlock) break;
|
||||
Server.DropClient(this, e);
|
||||
server.DropClient(this);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -65,9 +65,9 @@ namespace OpenRA.Server
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ReadData()
|
||||
public void ReadData( Server server )
|
||||
{
|
||||
if (ReadDataInner())
|
||||
if (ReadDataInner(server))
|
||||
while (data.Count >= ExpectLength)
|
||||
{
|
||||
var bytes = PopBytes(ExpectLength);
|
||||
@@ -82,16 +82,16 @@ namespace OpenRA.Server
|
||||
|
||||
case ReceiveState.Data:
|
||||
{
|
||||
Server.DispatchOrders(this, Frame, bytes);
|
||||
server.DispatchOrders(this, Frame, bytes);
|
||||
MostRecentFrame = Frame;
|
||||
ExpectLength = 8;
|
||||
State = ReceiveState.Header;
|
||||
|
||||
Server.UpdateInFlightFrames(this);
|
||||
server.UpdateInFlightFrames(this);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
enum ReceiveState { Header, Data };
|
||||
public enum ReceiveState { Header, Data };
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Server
|
||||
{
|
||||
static class MasterServerQuery
|
||||
public static class MasterServerQuery
|
||||
{
|
||||
public static event Action<GameServer[]> OnComplete = _ => { };
|
||||
public static event Action<string> OnVersion = _ => { };
|
||||
@@ -51,31 +51,6 @@ namespace OpenRA.Server
|
||||
}).Start();
|
||||
}
|
||||
|
||||
public static void GetMOTD(string masterServerUrl)
|
||||
{
|
||||
var motd = Widget.RootWidget.GetWidget<ScrollingTextWidget>("MOTD_SCROLLER");
|
||||
|
||||
// Runs in a separate thread to prevent dns lookup hitches
|
||||
new Thread(() =>
|
||||
{
|
||||
if (motd != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
motd.SetText(GetData(new Uri(masterServerUrl + "motd.php?v=" + ClientVersion)));
|
||||
motd.ResetScroll();
|
||||
}
|
||||
catch
|
||||
{
|
||||
motd.SetText("Welcome to OpenRA. MOTD unable to be loaded from server.");
|
||||
motd.ResetScroll();
|
||||
}
|
||||
}
|
||||
|
||||
ev.Set();
|
||||
}).Start();
|
||||
}
|
||||
|
||||
public static void Tick()
|
||||
{
|
||||
if (ev.WaitOne(TimeSpan.FromMilliseconds(0)))
|
||||
@@ -90,26 +65,9 @@ namespace OpenRA.Server
|
||||
var data = wc.DownloadData(uri);
|
||||
return Encoding.UTF8.GetString(data);
|
||||
}
|
||||
|
||||
public static void GetCurrentVersion(string masterServerUrl)
|
||||
{
|
||||
new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
ServerVersion = GetData(new Uri(masterServerUrl + "VERSION"));
|
||||
}
|
||||
catch
|
||||
{
|
||||
ServerVersion = "";
|
||||
}
|
||||
|
||||
ev2.Set();
|
||||
}).Start();
|
||||
}
|
||||
}
|
||||
|
||||
class GameServer
|
||||
public class GameServer
|
||||
{
|
||||
public readonly int Id = 0;
|
||||
public readonly string Name = null;
|
||||
|
||||
@@ -13,6 +13,6 @@ namespace OpenRA.Server
|
||||
public static class ProtocolVersion
|
||||
{
|
||||
// you *must* increment this whenever you make an incompatible protocol change
|
||||
public static readonly int Version = 6;
|
||||
public static readonly int Version = 7;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -23,51 +22,54 @@ using OpenRA.Network;
|
||||
|
||||
namespace OpenRA.Server
|
||||
{
|
||||
static class Server
|
||||
public class Server
|
||||
{
|
||||
static List<Connection> conns = new List<Connection>();
|
||||
static TcpListener listener = null;
|
||||
static Dictionary<int, List<Connection>> inFlightFrames
|
||||
// Valid player connections
|
||||
public List<Connection> conns = new List<Connection>();
|
||||
|
||||
// Pre-verified player connections
|
||||
public List<Connection> preConns = new List<Connection>();
|
||||
|
||||
TcpListener listener = null;
|
||||
Dictionary<int, List<Connection>> inFlightFrames
|
||||
= new Dictionary<int, List<Connection>>();
|
||||
static Session lobbyInfo;
|
||||
static bool GameStarted = false;
|
||||
static string Name;
|
||||
static int ExternalPort;
|
||||
static int randomSeed;
|
||||
|
||||
TypeDictionary ServerTraits = new TypeDictionary();
|
||||
public Session lobbyInfo;
|
||||
public bool GameStarted = false;
|
||||
public string Name;
|
||||
int randomSeed;
|
||||
|
||||
const int DownloadChunkInterval = 20000;
|
||||
const int DownloadChunkSize = 16384;
|
||||
public ModData ModData;
|
||||
public Map Map;
|
||||
|
||||
const int MasterPingInterval = 60 * 3; // 3 minutes. server has a 5 minute TTL for games, so give ourselves a bit
|
||||
// of leeway.
|
||||
static int lastPing = 0;
|
||||
static bool isInternetServer;
|
||||
static string masterServerUrl;
|
||||
static bool isInitialPing;
|
||||
static ModData ModData;
|
||||
static Map Map;
|
||||
|
||||
public static void ServerMain(ModData modData, Settings settings, string map)
|
||||
bool shutdown = false;
|
||||
public void Shutdown()
|
||||
{
|
||||
shutdown = true;
|
||||
}
|
||||
|
||||
public Server(ModData modData, Settings settings, string map)
|
||||
{
|
||||
Log.AddChannel("server", "server.log");
|
||||
|
||||
isInitialPing = true;
|
||||
Server.masterServerUrl = settings.Server.MasterServer;
|
||||
isInternetServer = settings.Server.AdvertiseOnline;
|
||||
listener = new TcpListener(IPAddress.Any, settings.Server.ListenPort);
|
||||
Name = settings.Server.Name;
|
||||
ExternalPort = settings.Server.ExternalPort;
|
||||
randomSeed = (int)DateTime.Now.ToBinary();
|
||||
ModData = modData;
|
||||
|
||||
foreach (var trait in modData.Manifest.ServerTraits)
|
||||
ServerTraits.Add( modData.ObjectCreator.CreateObject<ServerTrait>(trait) );
|
||||
|
||||
lobbyInfo = new Session( settings.Game.Mods );
|
||||
lobbyInfo.GlobalSettings.RandomSeed = randomSeed;
|
||||
lobbyInfo.GlobalSettings.Map = map;
|
||||
lobbyInfo.GlobalSettings.AllowCheats = settings.Server.AllowCheats;
|
||||
lobbyInfo.GlobalSettings.ServerName = settings.Server.Name;
|
||||
|
||||
LoadMap(); // populates the Session's slots, too.
|
||||
|
||||
foreach (var t in ServerTraits.WithInterface<INotifyServerStart>())
|
||||
t.ServerStarted(this);
|
||||
|
||||
Log.Write("server", "Initial mods: ");
|
||||
foreach( var m in lobbyInfo.GlobalSettings.Mods )
|
||||
Log.Write("server","- {0}", m);
|
||||
@@ -85,102 +87,76 @@ namespace OpenRA.Server
|
||||
|
||||
new Thread( _ =>
|
||||
{
|
||||
var timeout = ServerTraits.WithInterface<ITick>().Min(t => t.TickTimeout);
|
||||
for( ; ; )
|
||||
{
|
||||
var checkRead = new ArrayList();
|
||||
checkRead.Add( listener.Server );
|
||||
foreach( var c in conns ) checkRead.Add( c.socket );
|
||||
|
||||
Socket.Select( checkRead, null, null, MasterPingInterval * 10000 );
|
||||
foreach( var c in preConns ) checkRead.Add( c.socket );
|
||||
|
||||
Socket.Select( checkRead, null, null, timeout );
|
||||
|
||||
foreach( Socket s in checkRead )
|
||||
if( s == listener.Server ) AcceptConnection();
|
||||
else conns.Single( c => c.socket == s ).ReadData();
|
||||
else if (preConns.Count > 0)
|
||||
{
|
||||
var p = preConns.SingleOrDefault( c => c.socket == s );
|
||||
if (p != null) p.ReadData( this );
|
||||
}
|
||||
else if (conns.Count > 0) conns.Single( c => c.socket == s ).ReadData( this );
|
||||
|
||||
if (Environment.TickCount - lastPing > MasterPingInterval * 1000)
|
||||
PingMasterServer();
|
||||
else
|
||||
lock (masterServerMessages)
|
||||
while (masterServerMessages.Count > 0)
|
||||
SendChat(null, masterServerMessages.Dequeue());
|
||||
foreach (var t in ServerTraits.WithInterface<ITick>())
|
||||
t.Tick(this);
|
||||
|
||||
if (conns.Count() == 0)
|
||||
{
|
||||
listener.Stop();
|
||||
GameStarted = false;
|
||||
if (shutdown)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GameStarted = false;
|
||||
foreach (var t in ServerTraits.WithInterface<INotifyServerShutdown>())
|
||||
t.ServerShutdown(this);
|
||||
|
||||
preConns.Clear();
|
||||
conns.Clear();
|
||||
try { listener.Stop(); }
|
||||
catch { }
|
||||
} ) { IsBackground = true }.Start();
|
||||
|
||||
|
||||
}
|
||||
|
||||
static Session.Slot MakeSlotFromPlayerReference(PlayerReference pr)
|
||||
{
|
||||
if (!pr.Playable) return null;
|
||||
return new Session.Slot
|
||||
{
|
||||
MapPlayer = pr.Name,
|
||||
Bot = null, /* todo: allow the map to specify a bot class? */
|
||||
Closed = false,
|
||||
};
|
||||
}
|
||||
|
||||
static void LoadMap()
|
||||
{
|
||||
Map = new Map(ModData.AvailableMaps[lobbyInfo.GlobalSettings.Map].Package);
|
||||
lobbyInfo.Slots = Map.Players
|
||||
.Select(p => MakeSlotFromPlayerReference(p.Value))
|
||||
.Where(s => s != null)
|
||||
.Select((s, i) => { s.Index = i; return s; })
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/* lobby rework todo:
|
||||
*
|
||||
* - auto-assign players to slots
|
||||
* - show all the slots in the lobby ui.
|
||||
* - rework the game start so we actually use the slots.
|
||||
* - all players should be able to click an empty slot to move to it
|
||||
* - host should be able to choose whether a slot is open/closed/bot, with
|
||||
* potentially more than one choice of bot class.
|
||||
* - host should be able to kick a client from the lobby by closing its slot.
|
||||
* - change lobby commands so the host can configure bots, rather than
|
||||
* just configuring itself.
|
||||
* - "teams together" option for team games -- will eliminate most need
|
||||
* for manual spawnpoint choosing.
|
||||
* - pick sensible non-conflicting colors for bots.
|
||||
* - 256 max players is a dirty hack
|
||||
*/
|
||||
|
||||
static int ChooseFreePlayerIndex()
|
||||
int ChooseFreePlayerIndex()
|
||||
{
|
||||
for (var i = 0; i < 8; i++)
|
||||
if (conns.All(c => c.PlayerIndex != i))
|
||||
for (var i = 0; i < 256; i++)
|
||||
if (conns.All(c => c.PlayerIndex != i) && preConns.All(c => c.PlayerIndex != i))
|
||||
return i;
|
||||
|
||||
throw new InvalidOperationException("Already got 8 players");
|
||||
throw new InvalidOperationException("Already got 256 players");
|
||||
}
|
||||
|
||||
static int ChooseFreeSlot()
|
||||
void AcceptConnection()
|
||||
{
|
||||
return lobbyInfo.Slots.First(s => !s.Closed && s.Bot == null
|
||||
&& !lobbyInfo.Clients.Any( c => c.Slot == s.Index )).Index;
|
||||
}
|
||||
Socket newSocket = null;
|
||||
|
||||
static void AcceptConnection()
|
||||
{
|
||||
var newConn = new Connection { socket = listener.AcceptSocket() };
|
||||
try
|
||||
{
|
||||
if (GameStarted)
|
||||
{
|
||||
Log.Write("server", "Rejected connection from {0}; game is already started.",
|
||||
newConn.socket.RemoteEndPoint);
|
||||
newConn.socket.Close();
|
||||
return;
|
||||
}
|
||||
if (!listener.Server.IsBound) return;
|
||||
newSocket = listener.AcceptSocket();
|
||||
}
|
||||
catch
|
||||
{
|
||||
/* could have an exception here when listener 'goes away' when calling AcceptConnection! */
|
||||
/* alternative would be to use locking but the listener doesnt go away without a reason */
|
||||
return;
|
||||
}
|
||||
|
||||
var newConn = new Connection { socket = newSocket };
|
||||
try
|
||||
{
|
||||
newConn.socket.Blocking = false;
|
||||
newConn.socket.NoDelay = true;
|
||||
|
||||
@@ -188,50 +164,94 @@ namespace OpenRA.Server
|
||||
newConn.PlayerIndex = ChooseFreePlayerIndex();
|
||||
newConn.socket.Send(BitConverter.GetBytes(ProtocolVersion.Version));
|
||||
newConn.socket.Send(BitConverter.GetBytes(newConn.PlayerIndex));
|
||||
preConns.Add(newConn);
|
||||
|
||||
// Dispatch a handshake order
|
||||
var request = new HandshakeRequest()
|
||||
{
|
||||
Map = lobbyInfo.GlobalSettings.Map,
|
||||
Mods = lobbyInfo.GlobalSettings.Mods.Select(m => "{0}@{1}".F(m,Mod.AllMods[m].Version)).ToArray()
|
||||
};
|
||||
DispatchOrdersToClient(newConn, 0, 0, new ServerOrder("HandshakeRequest", request.Serialize()).Serialize());
|
||||
}
|
||||
catch (Exception) { DropClient(newConn); }
|
||||
}
|
||||
|
||||
void ValidateClient(Connection newConn, string data)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (GameStarted)
|
||||
{
|
||||
Log.Write("server", "Rejected connection from {0}; game is already started.",
|
||||
newConn.socket.RemoteEndPoint);
|
||||
DropClient(newConn);
|
||||
return;
|
||||
}
|
||||
|
||||
var client = HandshakeResponse.Deserialize(data).Client;
|
||||
|
||||
// Promote connection to a valid client
|
||||
preConns.Remove(newConn);
|
||||
conns.Add(newConn);
|
||||
|
||||
var defaults = new GameRules.PlayerSettings();
|
||||
lobbyInfo.Clients.Add(
|
||||
new Session.Client()
|
||||
{
|
||||
Index = newConn.PlayerIndex,
|
||||
Color1 = defaults.Color1,
|
||||
Color2 = defaults.Color2,
|
||||
Name = defaults.Name,
|
||||
Country = "random",
|
||||
State = Session.ClientState.NotReady,
|
||||
SpawnPoint = 0,
|
||||
Team = 0,
|
||||
Slot = ChooseFreeSlot(),
|
||||
});
|
||||
|
||||
// Enforce correct PlayerIndex and Slot
|
||||
client.Index = newConn.PlayerIndex;
|
||||
client.Slot = ChooseFreeSlot();
|
||||
|
||||
var slotData = lobbyInfo.Slots.FirstOrDefault( x => x.Index == client.Slot );
|
||||
if (slotData != null && slotData.MapPlayer != null)
|
||||
SyncClientToPlayerReference(client, Map.Players[slotData.MapPlayer]);
|
||||
|
||||
lobbyInfo.Clients.Add(client);
|
||||
|
||||
Log.Write("server", "Client {0}: Accepted connection from {1}",
|
||||
newConn.PlayerIndex, newConn.socket.RemoteEndPoint);
|
||||
|
||||
SendChat(newConn, "has joined the game.");
|
||||
|
||||
|
||||
foreach (var t in ServerTraits.WithInterface<IClientJoined>())
|
||||
t.ClientJoined(this, newConn);
|
||||
|
||||
SyncLobbyInfo();
|
||||
}
|
||||
catch (Exception e) { DropClient(newConn, e); }
|
||||
catch (Exception) { DropClient(newConn); }
|
||||
}
|
||||
|
||||
public static void UpdateInFlightFrames(Connection conn)
|
||||
int ChooseFreeSlot()
|
||||
{
|
||||
if (conn.Frame != 0)
|
||||
return lobbyInfo.Slots.First(s => !s.Closed && s.Bot == null
|
||||
&& !lobbyInfo.Clients.Any( c => c.Slot == s.Index )).Index;
|
||||
}
|
||||
|
||||
|
||||
public static void SyncClientToPlayerReference(Session.Client c, PlayerReference pr)
|
||||
{
|
||||
if (pr == null)
|
||||
return;
|
||||
if (pr.LockColor)
|
||||
{
|
||||
if (!inFlightFrames.ContainsKey(conn.Frame))
|
||||
inFlightFrames[conn.Frame] = new List<Connection> { conn };
|
||||
else
|
||||
inFlightFrames[conn.Frame].Add(conn);
|
||||
|
||||
if (conns.All(c => inFlightFrames[conn.Frame].Contains(c)))
|
||||
{
|
||||
inFlightFrames.Remove(conn.Frame);
|
||||
}
|
||||
c.Color1 = pr.Color;
|
||||
c.Color2 = pr.Color2;
|
||||
}
|
||||
if (pr.LockRace)
|
||||
c.Country = pr.Race;
|
||||
}
|
||||
|
||||
public void UpdateInFlightFrames(Connection conn)
|
||||
{
|
||||
if (conn.Frame == 0)
|
||||
return;
|
||||
|
||||
if (!inFlightFrames.ContainsKey(conn.Frame))
|
||||
inFlightFrames[conn.Frame] = new List<Connection> { conn };
|
||||
else
|
||||
inFlightFrames[conn.Frame].Add(conn);
|
||||
|
||||
if (conns.All(c => inFlightFrames[conn.Frame].Contains(c)))
|
||||
inFlightFrames.Remove(conn.Frame);
|
||||
}
|
||||
|
||||
static void DispatchOrdersToClient(Connection c, int client, int frame, byte[] data)
|
||||
void DispatchOrdersToClient(Connection c, int client, int frame, byte[] data)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -242,10 +262,10 @@ namespace OpenRA.Server
|
||||
ms.Write( data );
|
||||
c.socket.Send( ms.ToArray() );
|
||||
}
|
||||
catch( Exception e ) { DropClient( c, e ); }
|
||||
catch (Exception) { DropClient(c); }
|
||||
}
|
||||
|
||||
public static void DispatchOrders(Connection conn, int frame, byte[] data)
|
||||
public void DispatchOrders(Connection conn, int frame, byte[] data)
|
||||
{
|
||||
if (frame == 0 && conn != null)
|
||||
InterpretServerOrders(conn, data);
|
||||
@@ -257,7 +277,7 @@ namespace OpenRA.Server
|
||||
}
|
||||
}
|
||||
|
||||
static void InterpretServerOrders(Connection conn, byte[] data)
|
||||
void InterpretServerOrders(Connection conn, byte[] data)
|
||||
{
|
||||
var ms = new MemoryStream(data);
|
||||
var br = new BinaryReader(ms);
|
||||
@@ -274,373 +294,112 @@ namespace OpenRA.Server
|
||||
catch (EndOfStreamException) { }
|
||||
catch (NotImplementedException) { }
|
||||
}
|
||||
|
||||
static bool InterpretCommand(Connection conn, string cmd)
|
||||
{
|
||||
var dict = new Dictionary<string, Func<string, bool>>
|
||||
{
|
||||
{ "ready",
|
||||
s =>
|
||||
{
|
||||
// if we're downloading, we can't ready up.
|
||||
|
||||
var client = GetClient(conn);
|
||||
if (client.State == Session.ClientState.NotReady)
|
||||
client.State = Session.ClientState.Ready;
|
||||
else if (client.State == Session.ClientState.Ready)
|
||||
client.State = Session.ClientState.NotReady;
|
||||
|
||||
Log.Write("server", "Player @{0} is {1}",
|
||||
conn.socket.RemoteEndPoint, client.State);
|
||||
|
||||
SyncLobbyInfo();
|
||||
|
||||
if (conns.Count > 0 && conns.All(c => GetClient(c).State == Session.ClientState.Ready))
|
||||
InterpretCommand(conn, "startgame");
|
||||
|
||||
return true;
|
||||
}},
|
||||
{ "startgame",
|
||||
s =>
|
||||
{
|
||||
GameStarted = true;
|
||||
foreach( var c in conns )
|
||||
foreach( var d in conns )
|
||||
DispatchOrdersToClient( c, d.PlayerIndex, 0x7FFFFFFF, new byte[] { 0xBF } );
|
||||
|
||||
DispatchOrders(null, 0,
|
||||
new ServerOrder("StartGame", "").Serialize());
|
||||
|
||||
PingMasterServer();
|
||||
return true;
|
||||
}},
|
||||
{ "name",
|
||||
s =>
|
||||
{
|
||||
Log.Write("server", "Player@{0} is now known as {1}", conn.socket.RemoteEndPoint, s);
|
||||
GetClient(conn).Name = s;
|
||||
SyncLobbyInfo();
|
||||
return true;
|
||||
}},
|
||||
{ "lag",
|
||||
s =>
|
||||
{
|
||||
int lag;
|
||||
if (!int.TryParse(s, out lag)) { Log.Write("server", "Invalid order lag: {0}", s); return false; }
|
||||
|
||||
Log.Write("server", "Order lag is now {0} frames.", lag);
|
||||
|
||||
lobbyInfo.GlobalSettings.OrderLatency = lag;
|
||||
SyncLobbyInfo();
|
||||
return true;
|
||||
}},
|
||||
{ "race",
|
||||
s =>
|
||||
{
|
||||
GetClient(conn).Country = s;
|
||||
SyncLobbyInfo();
|
||||
return true;
|
||||
}},
|
||||
{ "team",
|
||||
s =>
|
||||
{
|
||||
int team;
|
||||
if (!int.TryParse(s, out team)) { Log.Write("server", "Invalid team: {0}", s ); return false; }
|
||||
|
||||
GetClient(conn).Team = team;
|
||||
SyncLobbyInfo();
|
||||
return true;
|
||||
}},
|
||||
{ "spawn",
|
||||
s =>
|
||||
{
|
||||
int spawnPoint;
|
||||
if (!int.TryParse(s, out spawnPoint) || spawnPoint < 0 || spawnPoint > 8) //TODO: SET properly!
|
||||
{
|
||||
Log.Write("server", "Invalid spawn point: {0}", s);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lobbyInfo.Clients.Where( c => c != GetClient(conn) ).Any( c => (c.SpawnPoint == spawnPoint) && (c.SpawnPoint != 0) ))
|
||||
{
|
||||
SendChatTo( conn, "You can't be at the same spawn point as another player" );
|
||||
return true;
|
||||
}
|
||||
|
||||
GetClient(conn).SpawnPoint = spawnPoint;
|
||||
SyncLobbyInfo();
|
||||
return true;
|
||||
}},
|
||||
{ "color",
|
||||
s =>
|
||||
{
|
||||
var c = s.Split(',').Select(cc => int.Parse(cc)).ToArray();
|
||||
GetClient(conn).Color1 = Color.FromArgb(c[0],c[1],c[2]);
|
||||
GetClient(conn).Color2 = Color.FromArgb(c[3],c[4],c[5]);
|
||||
SyncLobbyInfo();
|
||||
return true;
|
||||
}},
|
||||
{ "slot",
|
||||
s =>
|
||||
{
|
||||
int slot;
|
||||
if (!int.TryParse(s, out slot)) { Log.Write("server", "Invalid slot: {0}", s ); return false; }
|
||||
|
||||
var slotData = lobbyInfo.Slots.FirstOrDefault( x => x.Index == slot );
|
||||
if (slotData == null || slotData.Closed || slotData.Bot != null
|
||||
|| lobbyInfo.Clients.Any( c => c.Slot == slot ))
|
||||
return false;
|
||||
|
||||
GetClient(conn).Slot = slot;
|
||||
SyncLobbyInfo();
|
||||
return true;
|
||||
}},
|
||||
{ "slot_close",
|
||||
s =>
|
||||
{
|
||||
int slot;
|
||||
if (!int.TryParse(s, out slot)) { Log.Write("server", "Invalid slot: {0}", s ); return false; }
|
||||
|
||||
var slotData = lobbyInfo.Slots.FirstOrDefault( x => x.Index == slot );
|
||||
if (slotData == null)
|
||||
return false;
|
||||
|
||||
if (conn.PlayerIndex != 0)
|
||||
{
|
||||
SendChatTo( conn, "Only the host can alter slots" );
|
||||
return true;
|
||||
}
|
||||
|
||||
slotData.Closed = true;
|
||||
slotData.Bot = null;
|
||||
|
||||
/* kick any player that's in the slot */
|
||||
var occupant = lobbyInfo.Clients.FirstOrDefault( c => c.Slot == slotData.Index );
|
||||
if (occupant != null)
|
||||
{
|
||||
var occupantConn = conns.FirstOrDefault( c => c.PlayerIndex == occupant.Index );
|
||||
if (occupantConn != null)
|
||||
DropClient( occupantConn, new Exception() );
|
||||
}
|
||||
|
||||
SyncLobbyInfo();
|
||||
return true;
|
||||
}},
|
||||
{ "slot_open",
|
||||
s =>
|
||||
{
|
||||
int slot;
|
||||
if (!int.TryParse(s, out slot)) { Log.Write("server", "Invalid slot: {0}", s ); return false; }
|
||||
|
||||
var slotData = lobbyInfo.Slots.FirstOrDefault( x => x.Index == slot );
|
||||
if (slotData == null)
|
||||
return false;
|
||||
|
||||
if (conn.PlayerIndex != 0)
|
||||
{
|
||||
SendChatTo( conn, "Only the host can alter slots" );
|
||||
return true;
|
||||
}
|
||||
|
||||
slotData.Closed = false;
|
||||
slotData.Bot = null;
|
||||
|
||||
SyncLobbyInfo();
|
||||
return true;
|
||||
}},
|
||||
{ "slot_bot",
|
||||
s =>
|
||||
{
|
||||
var parts = s.Split(' ');
|
||||
|
||||
if (parts.Length != 2)
|
||||
{
|
||||
SendChatTo( conn, "Malformed slot_bot command" );
|
||||
return true;
|
||||
}
|
||||
|
||||
int slot;
|
||||
if (!int.TryParse(parts[0], out slot)) { Log.Write("server", "Invalid slot: {0}", s ); return false; }
|
||||
|
||||
var slotData = lobbyInfo.Slots.FirstOrDefault( x => x.Index == slot );
|
||||
if (slotData == null)
|
||||
return false;
|
||||
|
||||
if (conn.PlayerIndex != 0)
|
||||
{
|
||||
SendChatTo( conn, "Only the host can alter slots" );
|
||||
return true;
|
||||
}
|
||||
|
||||
slotData.Bot = parts[1];
|
||||
|
||||
SyncLobbyInfo();
|
||||
return true;
|
||||
}},
|
||||
{ "map",
|
||||
s =>
|
||||
{
|
||||
if (conn.PlayerIndex != 0)
|
||||
{
|
||||
SendChatTo( conn, "Only the host can change the map" );
|
||||
return true;
|
||||
}
|
||||
lobbyInfo.GlobalSettings.Map = s;
|
||||
LoadMap();
|
||||
|
||||
foreach(var client in lobbyInfo.Clients)
|
||||
{
|
||||
client.SpawnPoint = 0;
|
||||
client.State = Session.ClientState.NotReady;
|
||||
}
|
||||
|
||||
SyncLobbyInfo();
|
||||
return true;
|
||||
}},
|
||||
{ "mods",
|
||||
s =>
|
||||
{
|
||||
var args = s.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
lobbyInfo.GlobalSettings.Mods = args.GetRange(0,args.Count - 1).ToArray();
|
||||
lobbyInfo.GlobalSettings.Map = args.Last();
|
||||
SyncLobbyInfo();
|
||||
return true;
|
||||
}},
|
||||
{ "lockteams",
|
||||
s =>
|
||||
{
|
||||
if (conn.PlayerIndex != 0)
|
||||
{
|
||||
SendChatTo( conn, "Only the host can set that option" );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool.TryParse(s, out lobbyInfo.GlobalSettings.LockTeams);
|
||||
SyncLobbyInfo();
|
||||
return true;
|
||||
}},
|
||||
};
|
||||
|
||||
var cmdName = cmd.Split(' ').First();
|
||||
var cmdValue = string.Join(" ", cmd.Split(' ').Skip(1).ToArray());
|
||||
|
||||
Func<string,bool> a;
|
||||
if (!dict.TryGetValue(cmdName, out a))
|
||||
return false;
|
||||
|
||||
Log.Write("server", "Client {0} sent server command: {1}", conn.PlayerIndex, cmd );
|
||||
return a(cmdValue);
|
||||
}
|
||||
|
||||
static void SendChatTo(Connection conn, string text)
|
||||
|
||||
public void SendChatTo(Connection conn, string text)
|
||||
{
|
||||
DispatchOrdersToClient(conn, 0, 0,
|
||||
new ServerOrder("Chat", text).Serialize());
|
||||
}
|
||||
|
||||
static void SendChat(Connection asConn, string text)
|
||||
{
|
||||
DispatchOrders(asConn, 0, new ServerOrder("Chat", text).Serialize());
|
||||
}
|
||||
public void SendChat(Connection asConn, string text)
|
||||
{
|
||||
DispatchOrders(asConn, 0, new ServerOrder("Chat", text).Serialize());
|
||||
}
|
||||
|
||||
static void InterpretServerOrder(Connection conn, ServerOrder so)
|
||||
public void SendDisconnected(Connection asConn)
|
||||
{
|
||||
DispatchOrders(asConn, 0, new ServerOrder("Disconnected", "").Serialize());
|
||||
}
|
||||
|
||||
void InterpretServerOrder(Connection conn, ServerOrder so)
|
||||
{
|
||||
switch (so.Name)
|
||||
{
|
||||
case "Command":
|
||||
{
|
||||
if(GameStarted)
|
||||
SendChatTo(conn, "Cannot change state when game started.");
|
||||
else if (GetClient(conn).State == Session.ClientState.Ready && !(so.Data == "ready" || so.Data == "startgame") )
|
||||
SendChatTo(conn, "Cannot change state when marked as ready.");
|
||||
else if (!InterpretCommand(conn, so.Data))
|
||||
bool handled = false;
|
||||
foreach (var t in ServerTraits.WithInterface<IInterpretCommand>())
|
||||
if ((handled = t.InterpretCommand(this, conn, GetClient(conn), so.Data)))
|
||||
break;
|
||||
|
||||
if (!handled)
|
||||
{
|
||||
Log.Write("server", "Bad server command: {0}", so.Data);
|
||||
SendChatTo(conn, "Bad server command.");
|
||||
};
|
||||
}
|
||||
break;
|
||||
|
||||
case "Chat":
|
||||
Log.Write("server", "Unknown server command: {0}", so.Data);
|
||||
SendChatTo(conn, "Unknown server command: {0}".F(so.Data));
|
||||
}
|
||||
|
||||
break;
|
||||
case "HandshakeResponse":
|
||||
ValidateClient(conn, so.Data);
|
||||
break;
|
||||
case "Chat":
|
||||
case "TeamChat":
|
||||
var fromClient = GetClient(conn);
|
||||
var fromIndex = fromClient != null ? fromClient.Index : 0;
|
||||
|
||||
foreach (var c in conns.Except(conn).ToArray())
|
||||
DispatchOrdersToClient(c, GetClient(conn).Index, 0, so.Serialize());
|
||||
DispatchOrdersToClient(c, fromIndex, 0, so.Serialize());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static Session.Client GetClient(Connection conn)
|
||||
public Session.Client GetClient(Connection conn)
|
||||
{
|
||||
return lobbyInfo.Clients.First(c => c.Index == conn.PlayerIndex);
|
||||
return lobbyInfo.ClientWithIndex(conn.PlayerIndex);
|
||||
}
|
||||
|
||||
public static void DropClient(Connection toDrop, Exception e)
|
||||
public void DropClient(Connection toDrop)
|
||||
{
|
||||
conns.Remove(toDrop);
|
||||
SendChat(toDrop, "Connection Dropped");
|
||||
if (preConns.Contains(toDrop))
|
||||
preConns.Remove(toDrop);
|
||||
else
|
||||
{
|
||||
conns.Remove(toDrop);
|
||||
SendChat(toDrop, "Connection Dropped");
|
||||
|
||||
lobbyInfo.Clients.RemoveAll(c => c.Index == toDrop.PlayerIndex);
|
||||
if (GameStarted)
|
||||
SendDisconnected(toDrop); /* Report disconnection */
|
||||
|
||||
DispatchOrders( toDrop, toDrop.MostRecentFrame, new byte[] { 0xbf } );
|
||||
|
||||
if (conns.Count != 0)
|
||||
SyncLobbyInfo();
|
||||
lobbyInfo.Clients.RemoveAll(c => c.Index == toDrop.PlayerIndex);
|
||||
|
||||
DispatchOrders( toDrop, toDrop.MostRecentFrame, new byte[] { 0xbf } );
|
||||
|
||||
if (conns.Count != 0)
|
||||
SyncLobbyInfo();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
toDrop.socket.Disconnect(false);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
static void SyncLobbyInfo()
|
||||
public void SyncLobbyInfo()
|
||||
{
|
||||
if (!GameStarted) /* don't do this while the game is running, it breaks things. */
|
||||
DispatchOrders(null, 0,
|
||||
new ServerOrder("SyncInfo", lobbyInfo.Serialize()).Serialize());
|
||||
|
||||
PingMasterServer();
|
||||
foreach (var t in ServerTraits.WithInterface<INotifySyncLobbyInfo>())
|
||||
t.LobbyInfoSynced(this);
|
||||
}
|
||||
|
||||
static volatile bool isBusy;
|
||||
static Queue<string> masterServerMessages = new Queue<string>();
|
||||
static void PingMasterServer()
|
||||
|
||||
public void StartGame()
|
||||
{
|
||||
if (isBusy || !isInternetServer) return;
|
||||
GameStarted = true;
|
||||
foreach( var c in conns )
|
||||
foreach( var d in conns )
|
||||
DispatchOrdersToClient( c, d.PlayerIndex, 0x7FFFFFFF, new byte[] { 0xBF } );
|
||||
|
||||
lastPing = Environment.TickCount;
|
||||
isBusy = true;
|
||||
// Drop any unvalidated clients
|
||||
foreach (var c in preConns)
|
||||
DropClient(c);
|
||||
|
||||
DispatchOrders(null, 0,
|
||||
new ServerOrder("StartGame", "").Serialize());
|
||||
|
||||
Action a = () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = "ping.php?port={0}&name={1}&state={2}&players={3}&mods={4}&map={5}";
|
||||
if (isInitialPing) url += "&new=1";
|
||||
|
||||
using (var wc = new WebClient())
|
||||
{
|
||||
wc.DownloadData(
|
||||
masterServerUrl + url.F(
|
||||
ExternalPort, Uri.EscapeUriString(Name),
|
||||
GameStarted ? 2 : 1, // todo: post-game states, etc.
|
||||
lobbyInfo.Clients.Count,
|
||||
string.Join(",", lobbyInfo.GlobalSettings.Mods),
|
||||
lobbyInfo.GlobalSettings.Map));
|
||||
|
||||
if (isInitialPing)
|
||||
{
|
||||
isInitialPing = false;
|
||||
lock (masterServerMessages)
|
||||
masterServerMessages.Enqueue("Master server communication established.");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Log.Write("server", ex.ToString());
|
||||
lock( masterServerMessages )
|
||||
masterServerMessages.Enqueue( "Master server communication failed." );
|
||||
}
|
||||
|
||||
isBusy = false;
|
||||
};
|
||||
|
||||
a.BeginInvoke(null, null);
|
||||
foreach (var t in ServerTraits.WithInterface<IStartGame>())
|
||||
t.GameStarted(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
58
OpenRA.Game/Server/TraitInterfaces.cs
Normal file
58
OpenRA.Game/Server/TraitInterfaces.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 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. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
using System;
|
||||
using OpenRA.Network;
|
||||
|
||||
namespace OpenRA.Server
|
||||
{
|
||||
// Returns true if order is handled
|
||||
public interface IInterpretCommand { bool InterpretCommand(Server server, Connection conn, Session.Client client, string cmd); }
|
||||
public interface INotifySyncLobbyInfo { void LobbyInfoSynced(Server server); }
|
||||
public interface INotifyServerStart { void ServerStarted(Server server); }
|
||||
public interface INotifyServerShutdown { void ServerShutdown(Server server); }
|
||||
public interface IStartGame { void GameStarted(Server server); }
|
||||
public interface IClientJoined { void ClientJoined(Server server, Connection conn); }
|
||||
public interface ITick
|
||||
{
|
||||
void Tick(Server server);
|
||||
int TickTimeout { get; }
|
||||
}
|
||||
|
||||
public abstract class ServerTrait {}
|
||||
|
||||
public class DebugServerTrait : ServerTrait, IInterpretCommand, IStartGame, INotifySyncLobbyInfo, INotifyServerStart, INotifyServerShutdown
|
||||
{
|
||||
public bool InterpretCommand(Server server, Connection conn, Session.Client client, string cmd)
|
||||
{
|
||||
Console.WriteLine("Server received command from player {1}: {0}",cmd, conn.PlayerIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void GameStarted(Server server)
|
||||
{
|
||||
Console.WriteLine("GameStarted()");
|
||||
}
|
||||
|
||||
public void LobbyInfoSynced(Server server)
|
||||
{
|
||||
Console.WriteLine("LobbyInfoSynced()");
|
||||
}
|
||||
|
||||
public void ServerStarted(Server server)
|
||||
{
|
||||
Console.WriteLine("ServerStarted()");
|
||||
}
|
||||
|
||||
public void ServerShutdown(Server server)
|
||||
{
|
||||
Console.WriteLine("ServerShutdown()");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 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. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Drawing;
|
||||
using OpenRA.Graphics;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public class ShroudRenderer
|
||||
{
|
||||
Traits.Shroud shroud;
|
||||
Sprite[] shadowBits = SpriteSheetBuilder.LoadAllSprites("shadow");
|
||||
Sprite[,] sprites, fogSprites;
|
||||
|
||||
bool dirty = true;
|
||||
bool disabled = false;
|
||||
Map map;
|
||||
|
||||
public Rectangle? Bounds { get { return shroud.exploredBounds; } }
|
||||
|
||||
public ShroudRenderer(Player owner, Map map)
|
||||
{
|
||||
this.shroud = owner.World.WorldActor.Trait<Traits.Shroud>();
|
||||
this.map = map;
|
||||
|
||||
sprites = new Sprite[map.MapSize.X, map.MapSize.Y];
|
||||
fogSprites = new Sprite[map.MapSize.X, map.MapSize.Y];
|
||||
|
||||
shroud.Dirty += () => dirty = true;
|
||||
}
|
||||
|
||||
public bool Disabled
|
||||
{
|
||||
get { return disabled; }
|
||||
set { disabled = value; dirty = true;}
|
||||
}
|
||||
|
||||
public bool IsExplored(int2 xy) { return IsExplored(xy.X, xy.Y); }
|
||||
public bool IsExplored(int x, int y)
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
return shroud.exploredCells[x,y];
|
||||
}
|
||||
|
||||
public bool IsVisible(int2 xy) { return IsVisible(xy.X, xy.Y); }
|
||||
public bool IsVisible(int x, int y)
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
return shroud.visibleCells[x,y] != 0;
|
||||
}
|
||||
|
||||
static readonly byte[][] SpecialShroudTiles =
|
||||
{
|
||||
new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
|
||||
new byte[] { 32, 32, 25, 25, 19, 19, 20, 20 },
|
||||
new byte[] { 33, 33, 33, 33, 26, 26, 26, 26, 21, 21, 21, 21, 23, 23, 23, 23 },
|
||||
new byte[] { 36, 36, 36, 36, 30, 30, 30, 30 },
|
||||
new byte[] { 34, 16, 34, 16, 34, 16, 34, 16, 27, 22, 27, 22, 27, 22, 27, 22 },
|
||||
new byte[] { 44 },
|
||||
new byte[] { 37, 37, 37, 37, 37, 37, 37, 37, 31, 31, 31, 31, 31, 31, 31, 31 },
|
||||
new byte[] { 40 },
|
||||
new byte[] { 35, 24, 17, 18 },
|
||||
new byte[] { 39, 39, 29, 29 },
|
||||
new byte[] { 45 },
|
||||
new byte[] { 43 },
|
||||
new byte[] { 38, 28 },
|
||||
new byte[] { 42 },
|
||||
new byte[] { 41 },
|
||||
new byte[] { 46 },
|
||||
};
|
||||
|
||||
Sprite ChooseShroud(int i, int j)
|
||||
{
|
||||
if( !shroud.exploredCells[ i, j ] ) return shadowBits[ 0xf ];
|
||||
|
||||
// bits are for unexploredness: up, right, down, left
|
||||
var v = 0;
|
||||
// bits are for unexploredness: TL, TR, BR, BL
|
||||
var u = 0;
|
||||
|
||||
if( !shroud.exploredCells[ i, j - 1 ] ) { v |= 1; u |= 3; }
|
||||
if( !shroud.exploredCells[ i + 1, j ] ) { v |= 2; u |= 6; }
|
||||
if( !shroud.exploredCells[ i, j + 1 ] ) { v |= 4; u |= 12; }
|
||||
if( !shroud.exploredCells[ i - 1, j ] ) { v |= 8; u |= 9; }
|
||||
|
||||
var uSides = u;
|
||||
|
||||
if( !shroud.exploredCells[ i - 1, j - 1 ] ) u |= 1;
|
||||
if( !shroud.exploredCells[ i + 1, j - 1 ] ) u |= 2;
|
||||
if( !shroud.exploredCells[ i + 1, j + 1 ] ) u |= 4;
|
||||
if( !shroud.exploredCells[ i - 1, j + 1 ] ) u |= 8;
|
||||
|
||||
return shadowBits[ SpecialShroudTiles[ u ^ uSides ][ v ] ];
|
||||
}
|
||||
|
||||
Sprite ChooseFog(int i, int j)
|
||||
{
|
||||
if (shroud.visibleCells[i, j] == 0) return shadowBits[0xf];
|
||||
if (!shroud.exploredCells[i, j]) return shadowBits[0xf];
|
||||
|
||||
// bits are for unexploredness: up, right, down, left
|
||||
var v = 0;
|
||||
// bits are for unexploredness: TL, TR, BR, BL
|
||||
var u = 0;
|
||||
|
||||
if (shroud.visibleCells[i, j - 1] == 0) { v |= 1; u |= 3; }
|
||||
if (shroud.visibleCells[i + 1, j] == 0) { v |= 2; u |= 6; }
|
||||
if (shroud.visibleCells[i, j + 1] == 0) { v |= 4; u |= 12; }
|
||||
if (shroud.visibleCells[i - 1, j] == 0) { v |= 8; u |= 9; }
|
||||
|
||||
var uSides = u;
|
||||
|
||||
if (shroud.visibleCells[i - 1, j - 1] == 0) u |= 1;
|
||||
if (shroud.visibleCells[i + 1, j - 1] == 0) u |= 2;
|
||||
if (shroud.visibleCells[i + 1, j + 1] == 0) u |= 4;
|
||||
if (shroud.visibleCells[i - 1, j + 1] == 0) u |= 8;
|
||||
|
||||
return shadowBits[SpecialShroudTiles[u ^ uSides][v]];
|
||||
}
|
||||
|
||||
internal void Draw( WorldRenderer wr )
|
||||
{
|
||||
if (disabled)
|
||||
return;
|
||||
|
||||
if (dirty)
|
||||
{
|
||||
dirty = false;
|
||||
for (int i = map.TopLeft.X; i < map.BottomRight.X; i++)
|
||||
for (int j = map.TopLeft.Y; j < map.BottomRight.Y; j++)
|
||||
sprites[i, j] = ChooseShroud(i, j);
|
||||
for (int i = map.TopLeft.X; i < map.BottomRight.X; i++)
|
||||
for (int j = map.TopLeft.Y; j < map.BottomRight.Y; j++)
|
||||
fogSprites[i, j] = ChooseFog(i, j);
|
||||
}
|
||||
|
||||
var clipRect = Bounds.HasValue ? Rectangle.Intersect(Bounds.Value, map.Bounds) : map.Bounds;
|
||||
clipRect = Rectangle.Intersect(Game.viewport.ViewBounds(), clipRect);
|
||||
var miny = clipRect.Top;
|
||||
var maxy = clipRect.Bottom;
|
||||
var minx = clipRect.Left;
|
||||
var maxx = clipRect.Right;
|
||||
|
||||
DrawShroud( wr, minx, miny, maxx, maxy, fogSprites, "fog" );
|
||||
DrawShroud( wr, minx, miny, maxx, maxy, sprites, "shroud" );
|
||||
}
|
||||
|
||||
void DrawShroud( WorldRenderer wr, int minx, int miny, int maxx, int maxy, Sprite[,] s, string pal )
|
||||
{
|
||||
var shroudPalette = wr.GetPaletteIndex(pal);
|
||||
|
||||
for (var j = miny; j < maxy; j++)
|
||||
{
|
||||
var starti = minx;
|
||||
for (var i = minx; i < maxx; i++)
|
||||
{
|
||||
if (s[i, j] == shadowBits[0x0f])
|
||||
continue;
|
||||
|
||||
if (starti != i)
|
||||
{
|
||||
s[starti, j].DrawAt(
|
||||
Game.CellSize * new float2(starti, j),
|
||||
shroudPalette,
|
||||
new float2(Game.CellSize * (i - starti), Game.CellSize));
|
||||
starti = i + 1;
|
||||
}
|
||||
|
||||
s[i, j].DrawAt(
|
||||
Game.CellSize * new float2(i, j),
|
||||
shroudPalette);
|
||||
starti = i + 1;
|
||||
}
|
||||
|
||||
if (starti < maxx)
|
||||
s[starti, j].DrawAt(
|
||||
Game.CellSize * new float2(starti, j),
|
||||
shroudPalette,
|
||||
new float2(Game.CellSize * (maxx - starti), Game.CellSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,8 +28,7 @@ namespace OpenRA
|
||||
|
||||
static ISoundSource LoadSound(string filename)
|
||||
{
|
||||
var data = AudLoader.LoadSound(FileSystem.Open(filename));
|
||||
return soundEngine.AddSoundSourceFromMemory(data, 1, 16, 22050);
|
||||
return LoadSoundRaw(AudLoader.LoadSound(FileSystem.Open(filename)));
|
||||
}
|
||||
|
||||
static ISoundSource LoadSoundRaw(byte[] rawData)
|
||||
@@ -48,60 +47,49 @@ namespace OpenRA
|
||||
|
||||
public static void SetListenerPosition(float2 position) { soundEngine.SetListenerPosition(position); }
|
||||
|
||||
public static void Play(string name)
|
||||
static ISound Play(Player player, string name, bool headRelative, float2 pos, float volumeModifier)
|
||||
{
|
||||
if (player != null && player != player.World.LocalPlayer)
|
||||
return null;
|
||||
if (name == "" || name == null)
|
||||
return;
|
||||
return null;
|
||||
|
||||
var sound = sounds[name];
|
||||
soundEngine.Play2D(sound, false, true, float2.Zero, SoundVolume);
|
||||
}
|
||||
|
||||
public static void Play(string name, float2 pos)
|
||||
{
|
||||
if (name == "" || name == null)
|
||||
return;
|
||||
|
||||
var sound = sounds[name];
|
||||
soundEngine.Play2D(sound, false, false, pos, SoundVolume);
|
||||
}
|
||||
|
||||
public static void PlayToPlayer(Player player, string name)
|
||||
{
|
||||
if( player == player.World.LocalPlayer )
|
||||
Play( name );
|
||||
}
|
||||
|
||||
public static void PlayToPlayer(Player player, string name, float2 pos)
|
||||
{
|
||||
if (player == player.World.LocalPlayer)
|
||||
Play(name, pos);
|
||||
return soundEngine.Play2D(sounds[name],
|
||||
false, headRelative, pos,
|
||||
InternalSoundVolume * volumeModifier);
|
||||
}
|
||||
|
||||
public static ISound Play(string name) { return Play(null, name, true, float2.Zero, 1); }
|
||||
public static ISound Play(string name, float2 pos) { return Play(null, name, false, pos, 1); }
|
||||
public static ISound Play(string name, float volumeModifier) { return Play(null, name, true, float2.Zero, volumeModifier); }
|
||||
public static ISound Play(string name, float2 pos, float volumeModifier) { return Play(null, name, false, pos, volumeModifier); }
|
||||
public static ISound PlayToPlayer(Player player, string name) { return Play( player, name, true, float2.Zero, 1); }
|
||||
public static ISound PlayToPlayer(Player player, string name, float2 pos) { return Play(player, name, false, pos, 1); }
|
||||
|
||||
public static void PlayVideo(byte[] raw)
|
||||
{
|
||||
rawSource = LoadSoundRaw(raw);
|
||||
video = soundEngine.Play2D(rawSource, false, true, float2.Zero, SoundVolume);
|
||||
video = soundEngine.Play2D(rawSource, false, true, float2.Zero, InternalSoundVolume);
|
||||
}
|
||||
|
||||
|
||||
public static void PlayVideo()
|
||||
{
|
||||
if (video != null)
|
||||
soundEngine.PauseSound(video, false);
|
||||
}
|
||||
|
||||
|
||||
public static void PauseVideo()
|
||||
{
|
||||
if (video != null)
|
||||
soundEngine.PauseSound(video, true);
|
||||
}
|
||||
|
||||
|
||||
public static void StopVideo()
|
||||
{
|
||||
if (video != null)
|
||||
soundEngine.StopSound(video);
|
||||
}
|
||||
|
||||
|
||||
public static void Tick()
|
||||
{
|
||||
// Song finished
|
||||
@@ -111,13 +99,13 @@ namespace OpenRA
|
||||
OnMusicComplete();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Action OnMusicComplete;
|
||||
public static bool MusicPlaying { get; private set; }
|
||||
|
||||
|
||||
public static void PlayMusic(string name)
|
||||
{
|
||||
PlayMusicThen(name, () => {});
|
||||
PlayMusicThen(name, () => { });
|
||||
}
|
||||
public static void PlayMusicThen(string name, Action then)
|
||||
{
|
||||
@@ -132,13 +120,13 @@ namespace OpenRA
|
||||
return;
|
||||
}
|
||||
StopMusic();
|
||||
|
||||
|
||||
currentMusic = name;
|
||||
MusicPlaying = true;
|
||||
var sound = sounds[name];
|
||||
music = soundEngine.Play2D(sound, false, true, float2.Zero, MusicVolume);
|
||||
}
|
||||
|
||||
|
||||
public static void PlayMusic()
|
||||
{
|
||||
if (music == null)
|
||||
@@ -147,20 +135,26 @@ namespace OpenRA
|
||||
soundEngine.PauseSound(music, false);
|
||||
}
|
||||
|
||||
public static void StopSound(ISound sound)
|
||||
{
|
||||
if (sound != null)
|
||||
soundEngine.StopSound(sound);
|
||||
}
|
||||
|
||||
public static void StopMusic()
|
||||
{
|
||||
if (music != null)
|
||||
soundEngine.StopSound(music);
|
||||
|
||||
|
||||
MusicPlaying = false;
|
||||
currentMusic = null;
|
||||
}
|
||||
|
||||
|
||||
public static void PauseMusic()
|
||||
{
|
||||
if (music == null)
|
||||
return;
|
||||
|
||||
|
||||
MusicPlaying = false;
|
||||
soundEngine.PauseSound(music, true);
|
||||
}
|
||||
@@ -168,16 +162,28 @@ namespace OpenRA
|
||||
public static float GlobalVolume
|
||||
{
|
||||
get { return soundEngine.Volume; }
|
||||
set { soundEngine.Volume = value;}
|
||||
set { soundEngine.Volume = value; }
|
||||
}
|
||||
|
||||
|
||||
static float soundVolumeModifier = 1.0f;
|
||||
public static float SoundVolumeModifier
|
||||
{
|
||||
get { return soundVolumeModifier; }
|
||||
set
|
||||
{
|
||||
soundVolumeModifier = value;
|
||||
soundEngine.SetSoundVolume(InternalSoundVolume, music, video);
|
||||
}
|
||||
}
|
||||
|
||||
static float InternalSoundVolume { get { return SoundVolume * soundVolumeModifier; } }
|
||||
public static float SoundVolume
|
||||
{
|
||||
get { return Game.Settings.Sound.SoundVolume; }
|
||||
set
|
||||
{
|
||||
Game.Settings.Sound.SoundVolume = value;
|
||||
soundEngine.SetSoundVolume(value, music, video);
|
||||
soundEngine.SetSoundVolume(InternalSoundVolume, music, video);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +197,7 @@ namespace OpenRA
|
||||
music.Volume = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static float VideoVolume
|
||||
{
|
||||
get { return Game.Settings.Sound.VideoVolume; }
|
||||
@@ -202,17 +208,17 @@ namespace OpenRA
|
||||
video.Volume = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static float MusicSeekPosition
|
||||
{
|
||||
get { return (music != null)? music.SeekPosition : 0; }
|
||||
get { return (music != null) ? music.SeekPosition : 0; }
|
||||
}
|
||||
|
||||
|
||||
public static float VideoSeekPosition
|
||||
{
|
||||
get { return (video != null)? video.SeekPosition : 0; }
|
||||
get { return (video != null) ? video.SeekPosition : 0; }
|
||||
}
|
||||
|
||||
|
||||
// Returns true if it played a phrase
|
||||
public static bool PlayVoice(string phrase, Actor voicedUnit, string variant)
|
||||
{
|
||||
@@ -228,9 +234,9 @@ namespace OpenRA
|
||||
var clip = vi.Pools.Value[phrase].GetNext();
|
||||
if (clip == null)
|
||||
return false;
|
||||
|
||||
var variantext = (vi.Variants.ContainsKey(variant) && !vi.DisableVariants.Contains(phrase))?
|
||||
vi.Variants[variant][voicedUnit.ActorID % vi.Variants.Count] : vi.DefaultVariant;
|
||||
|
||||
var variantext = (vi.Variants.ContainsKey(variant) && !vi.DisableVariants.Contains(phrase)) ?
|
||||
vi.Variants[variant][voicedUnit.ActorID % vi.Variants[variant].Length] : vi.DefaultVariant;
|
||||
Play(clip + variantext);
|
||||
return true;
|
||||
}
|
||||
@@ -249,8 +255,9 @@ namespace OpenRA
|
||||
void SetSoundVolume(float volume, ISound music, ISound video);
|
||||
}
|
||||
|
||||
interface ISoundSource {}
|
||||
interface ISound
|
||||
interface ISoundSource { }
|
||||
|
||||
public interface ISound
|
||||
{
|
||||
float Volume { get; set; }
|
||||
float SeekPosition { get; }
|
||||
@@ -283,7 +290,7 @@ namespace OpenRA
|
||||
Log.Write("debug", "Failed generating OpenAL source {0}", i);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
sourcePool.Add(source, false);
|
||||
}
|
||||
}
|
||||
@@ -335,10 +342,10 @@ namespace OpenRA
|
||||
get { return volume; }
|
||||
set { Al.alListenerf(Al.AL_GAIN, volume = value); }
|
||||
}
|
||||
|
||||
|
||||
public void PauseSound(ISound sound, bool paused)
|
||||
{
|
||||
int key = ((OpenAlSound) sound).source;
|
||||
int key = ((OpenAlSound)sound).source;
|
||||
int state;
|
||||
Al.alGetSourcei(key, Al.AL_SOURCE_STATE, out state);
|
||||
if (state == Al.AL_PLAYING && paused)
|
||||
@@ -346,9 +353,9 @@ namespace OpenRA
|
||||
else if (state == Al.AL_PAUSED && !paused)
|
||||
Al.alSourcePlay(key);
|
||||
}
|
||||
|
||||
|
||||
public void SetAllSoundsPaused(bool paused)
|
||||
{
|
||||
{
|
||||
foreach (int key in sourcePool.Keys)
|
||||
{
|
||||
int state;
|
||||
@@ -357,35 +364,35 @@ namespace OpenRA
|
||||
Al.alSourcePause(key);
|
||||
else if (state == Al.AL_PAUSED && !paused)
|
||||
Al.alSourcePlay(key);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void SetSoundVolume(float volume, ISound music, ISound video)
|
||||
{
|
||||
var sounds = sourcePool.Select(s => s.Key).Where( b =>
|
||||
{
|
||||
var sounds = sourcePool.Select(s => s.Key).Where(b =>
|
||||
{
|
||||
int state;
|
||||
Al.alGetSourcei(b, Al.AL_SOURCE_STATE, out state);
|
||||
return ((state == Al.AL_PLAYING || state == Al.AL_PAUSED) &&
|
||||
((music != null)? b != ((OpenAlSound) music).source : true) &&
|
||||
((video != null)? b != ((OpenAlSound) video).source : true));
|
||||
return ((state == Al.AL_PLAYING || state == Al.AL_PAUSED) &&
|
||||
((music != null) ? b != ((OpenAlSound)music).source : true) &&
|
||||
((video != null) ? b != ((OpenAlSound)video).source : true));
|
||||
}).ToList();
|
||||
foreach (var s in sounds)
|
||||
{
|
||||
Al.alSourcef(s, Al.AL_GAIN, volume);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void StopSound(ISound sound)
|
||||
{
|
||||
int key = ((OpenAlSound) sound).source;
|
||||
int key = ((OpenAlSound)sound).source;
|
||||
int state;
|
||||
Al.alGetSourcei(key, Al.AL_SOURCE_STATE, out state);
|
||||
if (state == Al.AL_PLAYING || state == Al.AL_PAUSED)
|
||||
Al.alSourceStop(key);
|
||||
}
|
||||
|
||||
|
||||
public void StopAllSounds()
|
||||
{
|
||||
foreach (int key in sourcePool.Keys)
|
||||
@@ -399,7 +406,7 @@ namespace OpenRA
|
||||
|
||||
public void SetListenerPosition(float2 position)
|
||||
{
|
||||
var orientation = new [] { 0f, 0f, 1f, 0f, -1f, 0f };
|
||||
var orientation = new[] { 0f, 0f, 1f, 0f, -1f, 0f };
|
||||
|
||||
Al.alListener3f(Al.AL_POSITION, position.X, position.Y, 50);
|
||||
Al.alListenerfv(Al.AL_ORIENTATION, ref orientation[0]);
|
||||
@@ -451,26 +458,26 @@ namespace OpenRA
|
||||
public float Volume
|
||||
{
|
||||
get { return volume; }
|
||||
set
|
||||
set
|
||||
{
|
||||
if (source != -1)
|
||||
Al.alSourcef(source, Al.AL_GAIN, volume = value);
|
||||
Al.alSourcef(source, Al.AL_GAIN, volume = value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public float SeekPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
float pos;
|
||||
Al.alGetSourcef(source, Al.AL_SAMPLE_OFFSET, out pos);
|
||||
return pos/22050f;
|
||||
return pos / 22050f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool Playing
|
||||
{
|
||||
get
|
||||
get
|
||||
{
|
||||
int state;
|
||||
Al.alGetSourcei(source, Al.AL_SOURCE_STATE, out state);
|
||||
|
||||
@@ -15,12 +15,12 @@ using OpenRA.FileFormats;
|
||||
|
||||
namespace OpenRA.Support
|
||||
{
|
||||
static class PerfHistory
|
||||
public static class PerfHistory
|
||||
{
|
||||
static readonly Color[] colors = { Color.Red, Color.Green,
|
||||
Color.Blue, Color.Yellow,
|
||||
Color.Orange, Color.Fuchsia,
|
||||
Color.Lime, Color.LightBlue,
|
||||
Color.Orange, Color.Yellow,
|
||||
Color.Fuchsia, Color.Lime,
|
||||
Color.LightBlue, Color.Blue,
|
||||
Color.White, Color.Teal };
|
||||
static int nextColor;
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace OpenRA.Support
|
||||
}
|
||||
}
|
||||
|
||||
class PerfItem
|
||||
public class PerfItem
|
||||
{
|
||||
public readonly Color c;
|
||||
public readonly string Name;
|
||||
@@ -90,7 +90,7 @@ namespace OpenRA.Support
|
||||
}
|
||||
}
|
||||
|
||||
class PerfSample : IDisposable
|
||||
public class PerfSample : IDisposable
|
||||
{
|
||||
readonly Stopwatch sw = new Stopwatch();
|
||||
readonly string Item;
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace OpenRA
|
||||
|
||||
public IEnumerable<TraitPair<T>> ActorsWithTraitMultiple<T>( World world )
|
||||
{
|
||||
return ( (TraitContainer<T>)InnerGet( typeof( T ) ) ).All();//.Where( x => x.Actor.IsInWorld );
|
||||
return ( (TraitContainer<T>)InnerGet( typeof( T ) ) ).All();
|
||||
}
|
||||
|
||||
public void RemoveActor( Actor a )
|
||||
|
||||
38
OpenRA.Game/Traits/Activities/CancelableActivity.cs
Executable file
38
OpenRA.Game/Traits/Activities/CancelableActivity.cs
Executable file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenRA.Traits.Activities
|
||||
{
|
||||
public abstract class CancelableActivity : IActivity
|
||||
{
|
||||
protected IActivity NextActivity { get; private set; }
|
||||
protected bool IsCanceled { get; private set; }
|
||||
|
||||
public abstract IActivity Tick( Actor self );
|
||||
protected virtual bool OnCancel( Actor self ) { return true; }
|
||||
|
||||
public void Cancel( Actor self )
|
||||
{
|
||||
IsCanceled = OnCancel( self );
|
||||
if( IsCanceled )
|
||||
NextActivity = null;
|
||||
else if (NextActivity != null)
|
||||
NextActivity.Cancel( self );
|
||||
}
|
||||
|
||||
public void Queue( IActivity activity )
|
||||
{
|
||||
if( NextActivity != null )
|
||||
NextActivity.Queue( activity );
|
||||
else
|
||||
NextActivity = activity;
|
||||
}
|
||||
|
||||
public virtual IEnumerable<float2> GetCurrentPath()
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,8 @@
|
||||
#endregion
|
||||
|
||||
using System.Drawing;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Effects;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
@@ -57,9 +58,11 @@ namespace OpenRA.Traits
|
||||
return;
|
||||
|
||||
var p = target.CenterLocation;
|
||||
var move = self.TraitOrDefault<IMove>();
|
||||
var origin = move != null ? self.CenterLocation - new float2(0, move.Altitude) : self.CenterLocation;
|
||||
|
||||
Game.Renderer.LineRenderer.DrawLine(self.CenterLocation, p, c, c);
|
||||
for (bool b = false; !b; p = self.CenterLocation, b = true)
|
||||
Game.Renderer.LineRenderer.DrawLine(origin, p, c, c);
|
||||
for (bool b = false; !b; p = origin, b = true)
|
||||
{
|
||||
Game.Renderer.LineRenderer.DrawLine(p + new float2(-1, -1), p + new float2(-1, 1), c, c);
|
||||
Game.Renderer.LineRenderer.DrawLine(p + new float2(-1, 1), p + new float2(1, 1), c, c);
|
||||
@@ -67,6 +70,34 @@ namespace OpenRA.Traits
|
||||
Game.Renderer.LineRenderer.DrawLine(p + new float2(1, -1), p + new float2(-1, -1), c, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class LineTargetExts
|
||||
{
|
||||
public static void SetTargetLine(this Actor self, Target target, Color color)
|
||||
{
|
||||
self.SetTargetLine(target, color, true);
|
||||
}
|
||||
|
||||
public static void SetTargetLine(this Actor self, Target target, Color color, bool display)
|
||||
{
|
||||
if (self.Owner != self.World.LocalPlayer)
|
||||
return;
|
||||
|
||||
self.World.AddFrameEndTask(w =>
|
||||
{
|
||||
if (self.Destroyed) return;
|
||||
if (target.IsActor && display)
|
||||
w.Add(new FlashTarget(target.Actor));
|
||||
|
||||
var line = self.TraitOrDefault<DrawLineToTarget>();
|
||||
if (line != null)
|
||||
if (display)
|
||||
line.SetTarget(self, target, color);
|
||||
else
|
||||
line.SetTargetSilently(self, target, color);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
9
OpenRA.Game/Traits/BaseBuilding.cs → OpenRA.Game/Traits/EditorAppearance.cs
Executable file → Normal file
9
OpenRA.Game/Traits/BaseBuilding.cs → OpenRA.Game/Traits/EditorAppearance.cs
Executable file → Normal file
@@ -10,7 +10,10 @@
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
/* tag trait for "bases": mcv/fact */
|
||||
public class BaseBuildingInfo : TraitInfo<BaseBuilding> { }
|
||||
public class BaseBuilding { }
|
||||
public class EditorAppearanceInfo : TraitInfo<EditorAppearance>
|
||||
{
|
||||
public readonly bool RelativeToTopLeft = false;
|
||||
}
|
||||
|
||||
public class EditorAppearance { }
|
||||
}
|
||||
@@ -6,22 +6,18 @@
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Effects;
|
||||
using OpenRA.Traits.Activities;
|
||||
#endregion
|
||||
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.FileFormats;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
public class HealthInfo : ITraitInfo
|
||||
{
|
||||
public readonly int HP = 0;
|
||||
public readonly float Radius = 10;
|
||||
public virtual object Create(ActorInitializer init) { return new Health(init, this); }
|
||||
}
|
||||
|
||||
@@ -80,15 +76,15 @@ namespace OpenRA.Traits
|
||||
|
||||
var oldState = this.DamageState;
|
||||
|
||||
/* apply the damage modifiers, if we have any. */
|
||||
var modifier = (float)self.TraitsImplementing<IDamageModifier>()
|
||||
.Select(t => t.GetDamageModifier(warhead)).Product();
|
||||
/* apply the damage modifiers, if we have any. */
|
||||
var modifier = (float)self.TraitsImplementing<IDamageModifier>().Concat(self.Owner.PlayerActor.TraitsImplementing<IDamageModifier>())
|
||||
.Select(t => t.GetDamageModifier(attacker, warhead)).Product();
|
||||
|
||||
damage = (int)(damage * modifier);
|
||||
|
||||
hp -= damage;
|
||||
|
||||
foreach (var nd in self.TraitsImplementing<INotifyDamage>())
|
||||
hp -= damage;
|
||||
|
||||
foreach (var nd in self.TraitsImplementing<INotifyDamage>().Concat(self.Owner.PlayerActor.TraitsImplementing<INotifyDamage>()))
|
||||
nd.Damaged(self, new AttackInfo
|
||||
{
|
||||
Attacker = attacker,
|
||||
@@ -96,7 +92,9 @@ namespace OpenRA.Traits
|
||||
DamageState = this.DamageState,
|
||||
PreviousDamageState = oldState,
|
||||
DamageStateChanged = this.DamageState != oldState,
|
||||
Warhead = warhead
|
||||
Warhead = warhead,
|
||||
PreviousHealth = hp + damage < 0 ? 0 : hp + damage,
|
||||
Health = hp
|
||||
});
|
||||
|
||||
if (hp <= 0)
|
||||
|
||||
@@ -1,393 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 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. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Effects;
|
||||
using OpenRA.Traits.Activities;
|
||||
using OpenRA.FileFormats;
|
||||
using System.Diagnostics;
|
||||
using OpenRA.Orders;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
public class MobileInfo : ITraitInfo
|
||||
{
|
||||
[FieldLoader.LoadUsing( "LoadSpeeds" )]
|
||||
public readonly Dictionary<string, TerrainInfo> TerrainSpeeds;
|
||||
[FieldLoader.Load] public readonly string[] Crushes;
|
||||
[FieldLoader.Load] public readonly int WaitAverage = 60;
|
||||
[FieldLoader.Load] public readonly int WaitSpread = 20;
|
||||
[FieldLoader.Load] public readonly int InitialFacing = 128;
|
||||
[FieldLoader.Load] public readonly int ROT = 255;
|
||||
[FieldLoader.Load] public readonly int Speed = 1;
|
||||
[FieldLoader.Load] public readonly bool OnRails = false;
|
||||
|
||||
public virtual object Create(ActorInitializer init) { return new Mobile(init, this); }
|
||||
|
||||
static object LoadSpeeds( MiniYaml y )
|
||||
{
|
||||
Dictionary<string,TerrainInfo> ret = new Dictionary<string, TerrainInfo>();
|
||||
foreach (var t in y.NodesDict["TerrainSpeeds"].Nodes)
|
||||
{
|
||||
var speed = (float)FieldLoader.GetValue("speed", typeof(float),t.Value.Value);
|
||||
var cost = t.Value.NodesDict.ContainsKey("PathingCost") ? (int)FieldLoader.GetValue("cost", typeof(int), t.Value.NodesDict["PathingCost"].Value) : (int)(10000/speed);
|
||||
ret.Add(t.Key, new TerrainInfo{Speed = speed, Cost = cost});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public class TerrainInfo
|
||||
{
|
||||
public int Cost = int.MaxValue;
|
||||
public float Speed = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public class Mobile : IIssueOrder, IResolveOrder, IOrderVoice, IOccupySpace, IMove, IFacing, INudge
|
||||
{
|
||||
public readonly Actor self;
|
||||
public readonly MobileInfo Info;
|
||||
public bool IsMoving { get; internal set; }
|
||||
|
||||
int __facing;
|
||||
int2 __fromCell, __toCell;
|
||||
int __altitude;
|
||||
|
||||
[Sync]
|
||||
public int Facing
|
||||
{
|
||||
get { return __facing; }
|
||||
set { __facing = value; }
|
||||
}
|
||||
|
||||
[Sync]
|
||||
public int Altitude
|
||||
{
|
||||
get { return __altitude; }
|
||||
set { __altitude = value; }
|
||||
}
|
||||
|
||||
public int ROT { get { return Info.ROT; } }
|
||||
public int InitialFacing { get { return Info.InitialFacing; } }
|
||||
|
||||
[Sync]
|
||||
public int2 PxPosition { get; set; }
|
||||
[Sync]
|
||||
public int2 fromCell { get { return __fromCell; } }
|
||||
[Sync]
|
||||
public int2 toCell { get { return __toCell; } }
|
||||
|
||||
[Sync]
|
||||
public int PathHash; // written by Move.EvalPath, to temporarily debug this crap.
|
||||
|
||||
public void SetLocation(int2 from, int2 to)
|
||||
{
|
||||
if (fromCell == from && toCell == to) return;
|
||||
RemoveInfluence();
|
||||
__fromCell = from;
|
||||
__toCell = to;
|
||||
AddInfluence();
|
||||
}
|
||||
|
||||
UnitInfluence uim;
|
||||
|
||||
const int avgTicksBeforePathing = 5;
|
||||
const int spreadTicksBeforePathing = 5;
|
||||
internal int ticksBeforePathing = 0;
|
||||
|
||||
public Mobile(ActorInitializer init, MobileInfo info)
|
||||
{
|
||||
this.self = init.self;
|
||||
this.Info = info;
|
||||
|
||||
uim = self.World.WorldActor.Trait<UnitInfluence>();
|
||||
|
||||
if (init.Contains<LocationInit>())
|
||||
{
|
||||
this.__fromCell = this.__toCell = init.Get<LocationInit,int2>();
|
||||
this.PxPosition = Util.CenterOfCell( fromCell );
|
||||
AddInfluence();
|
||||
}
|
||||
|
||||
this.Facing = init.Contains<FacingInit>() ? init.Get<FacingInit,int>() : info.InitialFacing;
|
||||
this.Altitude = init.Contains<AltitudeInit>() ? init.Get<AltitudeInit,int>() : 0;
|
||||
}
|
||||
|
||||
public void SetPosition(Actor self, int2 cell)
|
||||
{
|
||||
SetLocation( cell, cell );
|
||||
PxPosition = Util.CenterOfCell(fromCell);
|
||||
}
|
||||
|
||||
public void SetPxPosition( Actor self, int2 px )
|
||||
{
|
||||
var cell = Util.CellContaining( px );
|
||||
SetLocation( cell, cell );
|
||||
PxPosition = px;
|
||||
}
|
||||
|
||||
public IEnumerable<IOrderTargeter> Orders { get { yield return new MoveOrderTargeter( Info ); } }
|
||||
|
||||
// Note: Returns a valid order even if the unit can't move to the target
|
||||
public Order IssueOrder( Actor self, IOrderTargeter order, Target target )
|
||||
{
|
||||
if( order is MoveOrderTargeter )
|
||||
{
|
||||
if( Info.OnRails ) return null;
|
||||
return new Order( "Move", self, Util.CellContaining( target.CenterLocation ), false );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int2 NearestMoveableCell(int2 target)
|
||||
{
|
||||
if (CanEnterCell(target))
|
||||
return target;
|
||||
|
||||
var searched = new List<int2>(){};
|
||||
// Limit search to a radius of 10 tiles
|
||||
for (int r = 1; r < 10; r++)
|
||||
foreach (var tile in self.World.FindTilesInCircle(target,r).Except(searched))
|
||||
{
|
||||
if (CanEnterCell(tile))
|
||||
return tile;
|
||||
|
||||
searched.Add(tile);
|
||||
}
|
||||
|
||||
// Couldn't find a cell
|
||||
return target;
|
||||
}
|
||||
|
||||
public void ResolveOrder(Actor self, Order order)
|
||||
{
|
||||
if (order.OrderString == "Move")
|
||||
{
|
||||
int2 currentLocation = NearestMoveableCell(order.TargetLocation);
|
||||
if (!CanEnterCell(currentLocation))
|
||||
return;
|
||||
|
||||
if( !order.Queued ) self.CancelActivity();
|
||||
|
||||
self.QueueActivity(new Activities.Move(currentLocation, 8));
|
||||
|
||||
if (self.Owner == self.World.LocalPlayer)
|
||||
self.World.AddFrameEndTask(w =>
|
||||
{
|
||||
w.Add(new MoveFlash(self.World, order.TargetLocation));
|
||||
var line = self.TraitOrDefault<DrawLineToTarget>();
|
||||
if (line != null)
|
||||
line.SetTarget(self, Target.FromCell(currentLocation), Color.Green);
|
||||
});
|
||||
ticksBeforePathing = avgTicksBeforePathing +
|
||||
self.World.SharedRandom.Next( -spreadTicksBeforePathing, spreadTicksBeforePathing );
|
||||
}
|
||||
}
|
||||
|
||||
public string VoicePhraseForOrder(Actor self, Order order)
|
||||
{
|
||||
if (order.OrderString == "Move")
|
||||
return "Move";
|
||||
return null;
|
||||
}
|
||||
|
||||
public int2 TopLeft { get { return toCell; } }
|
||||
|
||||
public IEnumerable<int2> OccupiedCells()
|
||||
{
|
||||
return (fromCell == toCell)
|
||||
? new[] { fromCell }
|
||||
: CanEnterCell(toCell)
|
||||
? new[] { toCell }
|
||||
: new[] { fromCell, toCell };
|
||||
}
|
||||
|
||||
public bool CanEnterCell(int2 p)
|
||||
{
|
||||
return CanEnterCell( p, null, true);
|
||||
}
|
||||
|
||||
public static bool CanEnterCell( World world, MobileInfo mi, int2 cell, Actor ignoreActor, bool checkTransientActors )
|
||||
{
|
||||
var bim = world.WorldActor.Trait<BuildingInfluence>();
|
||||
var uim = world.WorldActor.Trait<UnitInfluence>();
|
||||
return Mobile.CanEnterCell( mi, world, uim, bim, cell, ignoreActor, checkTransientActors );
|
||||
}
|
||||
|
||||
public bool CanEnterCell( int2 cell, Actor ignoreActor, bool checkTransientActors )
|
||||
{
|
||||
var bim = self.World.WorldActor.Trait<BuildingInfluence>();
|
||||
var uim = self.World.WorldActor.Trait<UnitInfluence>();
|
||||
return CanEnterCell( Info, self.World, uim, bim, cell, ignoreActor, checkTransientActors );
|
||||
}
|
||||
|
||||
public static bool CanEnterCell( MobileInfo mobileInfo, World world, UnitInfluence uim, BuildingInfluence bim, int2 cell, Actor ignoreActor, bool checkTransientActors )
|
||||
{
|
||||
if (MovementCostForCell(mobileInfo, world, cell) == int.MaxValue)
|
||||
return false;
|
||||
|
||||
// Check for buildings
|
||||
var building = bim.GetBuildingBlocking(cell);
|
||||
if (building != null && building != ignoreActor)
|
||||
{
|
||||
if (mobileInfo.Crushes == null)
|
||||
return false;
|
||||
|
||||
var crushable = building.TraitsImplementing<ICrushable>();
|
||||
if (crushable.Count() == 0)
|
||||
return false;
|
||||
|
||||
if (!crushable.Any(b => b.CrushClasses.Intersect(mobileInfo.Crushes).Any()))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check mobile actors
|
||||
var blockingActors = uim.GetUnitsAt( cell ).Where( x => x != ignoreActor ).ToList();
|
||||
if (checkTransientActors && blockingActors.Count > 0)
|
||||
{
|
||||
// We can enter a cell with nonshareable units only if we can crush all of them
|
||||
if (mobileInfo.Crushes == null)
|
||||
return false;
|
||||
|
||||
if (blockingActors.Any(a => !(a.HasTrait<ICrushable>() &&
|
||||
a.TraitsImplementing<ICrushable>().Any(b => b.CrushClasses.Intersect(mobileInfo.Crushes).Any()))))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void FinishedMoving(Actor self)
|
||||
{
|
||||
var crushable = uim.GetUnitsAt(toCell).Where(a => a != self && a.HasTrait<ICrushable>());
|
||||
foreach (var a in crushable)
|
||||
{
|
||||
var crushActions = a.TraitsImplementing<ICrushable>().Where(b => b.CrushClasses.Intersect(Info.Crushes).Any());
|
||||
foreach (var b in crushActions)
|
||||
b.OnCrush(self);
|
||||
}
|
||||
}
|
||||
|
||||
public static int MovementCostForCell(MobileInfo info, World world, int2 cell)
|
||||
{
|
||||
if (!world.Map.IsInMap(cell.X,cell.Y))
|
||||
return int.MaxValue;
|
||||
|
||||
var type = world.GetTerrainType(cell);
|
||||
if (!info.TerrainSpeeds.ContainsKey(type))
|
||||
return int.MaxValue;
|
||||
|
||||
return info.TerrainSpeeds[type].Cost;
|
||||
}
|
||||
|
||||
public float MovementSpeedForCell(Actor self, int2 cell)
|
||||
{
|
||||
var type = self.World.GetTerrainType(cell);
|
||||
|
||||
if (!Info.TerrainSpeeds.ContainsKey(type))
|
||||
return 0;
|
||||
|
||||
var modifier = self
|
||||
.TraitsImplementing<ISpeedModifier>()
|
||||
.Select(t => t.GetSpeedModifier())
|
||||
.Product();
|
||||
return Info.Speed * Info.TerrainSpeeds[type].Speed * modifier / 100f;
|
||||
}
|
||||
|
||||
public void AddInfluence()
|
||||
{
|
||||
uim.Add( self, this );
|
||||
}
|
||||
|
||||
public void RemoveInfluence()
|
||||
{
|
||||
uim.Remove( self, this );
|
||||
}
|
||||
|
||||
public void OnNudge(Actor self, Actor nudger)
|
||||
{
|
||||
/* initial fairly braindead implementation. */
|
||||
|
||||
if (self.Owner.Stances[nudger.Owner] != Stance.Ally)
|
||||
return; /* don't allow ourselves to be pushed around
|
||||
* by the enemy! */
|
||||
|
||||
if (!self.IsIdle)
|
||||
return; /* don't nudge if we're busy doing something! */
|
||||
|
||||
// pick an adjacent available cell.
|
||||
var availCells = new List<int2>();
|
||||
var notStupidCells = new List<int2>();
|
||||
|
||||
for( var i = -1; i < 2; i++ )
|
||||
for (var j = -1; j < 2; j++)
|
||||
{
|
||||
var p = toCell + new int2(i, j);
|
||||
if (CanEnterCell(p))
|
||||
availCells.Add(p);
|
||||
else
|
||||
if (p != nudger.Location && p != toCell)
|
||||
notStupidCells.Add(p);
|
||||
}
|
||||
|
||||
var moveTo = availCells.Any() ? availCells.Random(self.World.SharedRandom) :
|
||||
notStupidCells.Any() ? notStupidCells.Random(self.World.SharedRandom) : (int2?)null;
|
||||
|
||||
if (moveTo.HasValue)
|
||||
{
|
||||
self.CancelActivity();
|
||||
if (self.Owner == self.World.LocalPlayer)
|
||||
self.World.AddFrameEndTask(w =>
|
||||
{
|
||||
var line = self.TraitOrDefault<DrawLineToTarget>();
|
||||
if (line != null)
|
||||
line.SetTargetSilently(self, Target.FromCell(moveTo.Value), Color.Green);
|
||||
});
|
||||
self.QueueActivity(new Move(moveTo.Value, 0));
|
||||
|
||||
Log.Write("debug", "OnNudge #{0} from {1} to {2}",
|
||||
self.ActorID, self.Location, moveTo.Value);
|
||||
}
|
||||
else
|
||||
Log.Write("debug", "OnNudge #{0} refuses at {1}",
|
||||
self.ActorID, self.Location);
|
||||
}
|
||||
|
||||
class MoveOrderTargeter : IOrderTargeter
|
||||
{
|
||||
readonly MobileInfo unitType;
|
||||
|
||||
public MoveOrderTargeter( MobileInfo unitType )
|
||||
{
|
||||
this.unitType = unitType;
|
||||
}
|
||||
|
||||
public string OrderID { get { return "Move"; } }
|
||||
public int OrderPriority { get { return 4; } }
|
||||
|
||||
public bool CanTargetUnit( Actor self, Actor target, bool forceAttack, bool forceMove, ref string cursor )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool CanTargetLocation( Actor self, int2 location, List<Actor> actorsAtLocation, bool forceAttack, bool forceMove, ref string cursor )
|
||||
{
|
||||
cursor = "move";
|
||||
if( self.World.LocalPlayer.Shroud.IsVisible( location ) && !self.Trait<Mobile>().CanEnterCell( location ) )
|
||||
cursor = "move-blocked";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
public class DeveloperModeInfo : ITraitInfo
|
||||
@@ -17,7 +18,9 @@ namespace OpenRA.Traits
|
||||
public bool FastBuild = false;
|
||||
public bool FastCharge = false;
|
||||
public bool DisableShroud = false;
|
||||
public bool PathDebug = false;
|
||||
public bool PathDebug = false;
|
||||
public bool UnitInfluenceDebug = false;
|
||||
public bool UnlimitedPower;
|
||||
|
||||
public object Create (ActorInitializer init) { return new DeveloperMode(this); }
|
||||
}
|
||||
@@ -30,14 +33,18 @@ namespace OpenRA.Traits
|
||||
[Sync] public bool FastBuild;
|
||||
[Sync] public bool DisableShroud;
|
||||
[Sync] public bool PathDebug;
|
||||
|
||||
[Sync] public bool UnitInfluenceDebug;
|
||||
[Sync] public bool UnlimitedPower;
|
||||
|
||||
public DeveloperMode(DeveloperModeInfo info)
|
||||
{
|
||||
Info = info;
|
||||
FastBuild = Info.FastBuild;
|
||||
FastCharge = Info.FastCharge;
|
||||
DisableShroud = Info.DisableShroud;
|
||||
PathDebug = Info.PathDebug;
|
||||
PathDebug = Info.PathDebug;
|
||||
UnitInfluenceDebug = info.UnitInfluenceDebug;
|
||||
UnlimitedPower = info.UnlimitedPower;
|
||||
}
|
||||
|
||||
public void ResolveOrder (Actor self, Order order)
|
||||
@@ -70,7 +77,7 @@ namespace OpenRA.Traits
|
||||
{
|
||||
DisableShroud ^= true;
|
||||
if (self.World.LocalPlayer == self.Owner)
|
||||
self.World.LocalPlayer.Shroud.Disabled = DisableShroud;
|
||||
self.World.LocalShroud.Disabled = DisableShroud;
|
||||
break;
|
||||
}
|
||||
case "DevPathDebug":
|
||||
@@ -79,9 +86,8 @@ namespace OpenRA.Traits
|
||||
break;
|
||||
}
|
||||
case "DevUnitDebug":
|
||||
{
|
||||
if (self.World.LocalPlayer == self.Owner)
|
||||
Game.Settings.Debug.ShowCollisions ^= true;
|
||||
{
|
||||
UnitInfluenceDebug ^= true;
|
||||
break;
|
||||
}
|
||||
case "DevGiveExploration":
|
||||
@@ -89,6 +95,11 @@ namespace OpenRA.Traits
|
||||
if (self.World.LocalPlayer == self.Owner)
|
||||
self.World.WorldActor.Trait<Shroud>().ExploreAll(self.World);
|
||||
break;
|
||||
}
|
||||
case "DevUnlimitedPower":
|
||||
{
|
||||
UnlimitedPower ^= true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
|
||||
@@ -36,17 +36,19 @@ namespace OpenRA.Traits
|
||||
|
||||
[Sync]
|
||||
public int Cash;
|
||||
[Sync]
|
||||
public int DisplayCash;
|
||||
|
||||
[Sync]
|
||||
public int Ore;
|
||||
[Sync]
|
||||
public int OreCapacity;
|
||||
[Sync]
|
||||
|
||||
public int DisplayCash;
|
||||
public int DisplayOre;
|
||||
|
||||
public float GetSiloFullness() { return (float)Ore / OreCapacity; }
|
||||
public bool CanGiveOre(int amount)
|
||||
{
|
||||
return Ore + amount <= OreCapacity;
|
||||
}
|
||||
|
||||
public void GiveOre(int num)
|
||||
{
|
||||
|
||||
@@ -17,9 +17,10 @@ namespace OpenRA.Traits
|
||||
public abstract class RenderSimpleInfo : ITraitInfo
|
||||
{
|
||||
public readonly string Image = null;
|
||||
public readonly string[] OverrideTheater = null;
|
||||
public readonly string[] OverrideTileset = null;
|
||||
public readonly string[] OverrideImage = null;
|
||||
public readonly string Palette = null;
|
||||
public readonly float Scale = 1f;
|
||||
public abstract object Create(ActorInitializer init);
|
||||
}
|
||||
|
||||
@@ -28,32 +29,44 @@ namespace OpenRA.Traits
|
||||
public Dictionary<string, AnimationWithOffset> anims = new Dictionary<string, AnimationWithOffset>();
|
||||
public Animation anim { get { return anims[""].Animation; } protected set { anims[""].Animation = value; } }
|
||||
|
||||
public static string GetImage(ActorInfo actor, string Tileset)
|
||||
{
|
||||
var Info = actor.Traits.Get<RenderSimpleInfo>();
|
||||
if (Info.OverrideTileset != null && Tileset != null)
|
||||
for (int i = 0; i < Info.OverrideTileset.Length; i++)
|
||||
if (Info.OverrideTileset[i] == Tileset)
|
||||
return Info.OverrideImage[i];
|
||||
|
||||
return Info.Image ?? actor.Name;
|
||||
}
|
||||
|
||||
string cachedImage = null;
|
||||
public string GetImage(Actor self)
|
||||
{
|
||||
if (cachedImage != null)
|
||||
return cachedImage;
|
||||
|
||||
var Info = self.Info.Traits.Get<RenderSimpleInfo>();
|
||||
if (Info.OverrideTheater != null)
|
||||
for (int i = 0; i < Info.OverrideTheater.Length; i++)
|
||||
if (Info.OverrideTheater[i] == self.World.Map.Theater)
|
||||
return cachedImage = Info.OverrideImage[i];
|
||||
|
||||
return cachedImage = Info.Image ?? self.Info.Name;
|
||||
return cachedImage = GetImage(self.Info, self.World.Map.Tileset);
|
||||
}
|
||||
|
||||
RenderSimpleInfo Info;
|
||||
public RenderSimple(Actor self, Func<int> baseFacing)
|
||||
{
|
||||
anims.Add( "", new Animation( GetImage(self), baseFacing ) );
|
||||
Info = self.Info.Traits.Get<RenderSimpleInfo>();
|
||||
}
|
||||
|
||||
public virtual IEnumerable<Renderable> Render( Actor self )
|
||||
{
|
||||
var palette = self.Info.Traits.Get<RenderSimpleInfo>().Palette;
|
||||
|
||||
foreach( var a in anims.Values )
|
||||
if( a.DisableFunc == null || !a.DisableFunc() )
|
||||
yield return ( palette == null ) ? a.Image( self ) : a.Image( self ).WithPalette(palette);
|
||||
{
|
||||
Renderable ret = a.Image( self );
|
||||
if (Info.Scale != 1f)
|
||||
ret = ret.WithScale(Info.Scale).WithPos(ret.Pos + 0.5f*ret.Sprite.size*(1 - Info.Scale));
|
||||
yield return ( Info.Palette == null ) ? ret : ret.WithPalette(Info.Palette);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Tick(Actor self)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#endregion
|
||||
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
@@ -19,7 +20,6 @@ namespace OpenRA.Traits
|
||||
public readonly int[] Bounds = null;
|
||||
[VoiceReference]
|
||||
public readonly string Voice = null;
|
||||
public readonly float Radius = 10;
|
||||
}
|
||||
|
||||
public class Selectable : IPostRenderSelection
|
||||
@@ -31,13 +31,20 @@ namespace OpenRA.Traits
|
||||
public void RenderAfterWorld (WorldRenderer wr, Actor self)
|
||||
{
|
||||
var bounds = self.GetBounds(true);
|
||||
Color selectionColor = Color.White;
|
||||
|
||||
var xy = new float2(bounds.Left, bounds.Top);
|
||||
var Xy = new float2(bounds.Right, bounds.Top);
|
||||
var xY = new float2(bounds.Left, bounds.Bottom);
|
||||
var XY = new float2(bounds.Right, bounds.Bottom);
|
||||
|
||||
DrawSelectionBox(self, xy, Xy, xY, XY, Color.White);
|
||||
var colorResults = self.TraitsImplementing<ISelectionColorModifier>().Select(t => t.GetSelectionColorModifier(self, selectionColor)).Where(
|
||||
c => c.ToArgb() != selectionColor.ToArgb());
|
||||
|
||||
if (colorResults.Any())
|
||||
selectionColor = colorResults.First();
|
||||
|
||||
DrawSelectionBox(self, xy, Xy, xY, XY, selectionColor);
|
||||
DrawHealthBar(self, xy, Xy);
|
||||
DrawControlGroup(wr, self, xy);
|
||||
DrawPips(wr, self, xY);
|
||||
@@ -158,7 +165,7 @@ namespace OpenRA.Traits
|
||||
|
||||
void DrawUnitPath(Actor self)
|
||||
{
|
||||
if (!self.World.LocalPlayer.PlayerActor.Trait<DeveloperMode>().PathDebug) return;
|
||||
if (self.World.LocalPlayer == null ||!self.World.LocalPlayer.PlayerActor.Trait<DeveloperMode>().PathDebug) return;
|
||||
|
||||
var activity = self.GetCurrentActivity();
|
||||
var mobile = self.TraitOrDefault<IMove>();
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 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. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Drawing;
|
||||
using OpenRA.Graphics;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
public class TargetableInfo : ITraitInfo
|
||||
{
|
||||
public readonly string[] TargetTypes = {};
|
||||
public virtual object Create( ActorInitializer init ) { return new Targetable(this); }
|
||||
}
|
||||
|
||||
public class Targetable : ITargetable
|
||||
{
|
||||
TargetableInfo Info;
|
||||
public Targetable(TargetableInfo info)
|
||||
{
|
||||
Info = info;
|
||||
}
|
||||
|
||||
public string[] TargetTypes
|
||||
{
|
||||
get { return Info.TargetTypes;}
|
||||
}
|
||||
}
|
||||
}
|
||||
116
OpenRA.Game/Traits/TraitsInterfaces.cs
Normal file → Executable file
116
OpenRA.Game/Traits/TraitsInterfaces.cs
Normal file → Executable file
@@ -13,6 +13,7 @@ using System.Drawing;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Network;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
@@ -28,28 +29,33 @@ namespace OpenRA.Traits
|
||||
public int Damage;
|
||||
public DamageState DamageState;
|
||||
public DamageState PreviousDamageState;
|
||||
public bool DamageStateChanged;
|
||||
public bool DamageStateChanged;
|
||||
public int PreviousHealth;
|
||||
public int Health;
|
||||
}
|
||||
|
||||
public interface ITick { void Tick(Actor self); }
|
||||
public interface IRender { IEnumerable<Renderable> Render(Actor self); }
|
||||
|
||||
public interface IIssueOrder
|
||||
{
|
||||
IEnumerable<IOrderTargeter> Orders { get; }
|
||||
Order IssueOrder( Actor self, IOrderTargeter order, Target target );
|
||||
Order IssueOrder( Actor self, IOrderTargeter order, Target target, bool queued );
|
||||
}
|
||||
public interface IOrderTargeter
|
||||
{
|
||||
string OrderID { get; }
|
||||
int OrderPriority { get; }
|
||||
bool CanTargetUnit( Actor self, Actor target, bool forceAttack, bool forceMove, ref string cursor );
|
||||
bool CanTargetLocation( Actor self, int2 location, List<Actor> actorsAtLocation, bool forceAttack, bool forceMove, ref string cursor );
|
||||
bool CanTargetActor( Actor self, Actor target, bool forceAttack, bool forceMove, bool forceQueue, ref string cursor );
|
||||
bool CanTargetLocation(Actor self, int2 location, List<Actor> actorsAtLocation, bool forceAttack, bool forceQueue, bool forceMove, ref string cursor);
|
||||
bool IsQueued { get; }
|
||||
}
|
||||
public interface IResolveOrder { void ResolveOrder(Actor self, Order order); }
|
||||
public interface IResolveOrder { void ResolveOrder(Actor self, Order order); }
|
||||
public interface IValidateOrder { bool OrderValidation(OrderManager orderManager, World world, int clientId, Order order);
|
||||
}
|
||||
public interface IOrderCursor { string CursorForOrder(Actor self, Order order); }
|
||||
public interface IOrderVoice { string VoicePhraseForOrder(Actor self, Order order); }
|
||||
public interface ITargetable { string[] TargetTypes { get; } }
|
||||
|
||||
public interface ICustomUnitOrderGenerator : IOrderGenerator {};
|
||||
public interface INotifySold { void Selling( Actor self ); void Sold( Actor self ); }
|
||||
public interface INotifyDamage { void Damaged(Actor self, AttackInfo e); }
|
||||
public interface INotifyBuildComplete { void BuildingComplete(Actor self); }
|
||||
@@ -68,7 +74,7 @@ namespace OpenRA.Traits
|
||||
Color RadarSignatureColor(Actor self);
|
||||
}
|
||||
|
||||
public interface IVisibilityModifier { bool IsVisible(Actor self, Player byPlayer); }
|
||||
public interface IVisibilityModifier { bool IsVisible(Actor self); }
|
||||
public interface IRadarColorModifier { Color RadarColorOverride(Actor self); }
|
||||
public interface IHasLocation
|
||||
{
|
||||
@@ -100,11 +106,12 @@ namespace OpenRA.Traits
|
||||
}
|
||||
}
|
||||
|
||||
public interface INotifyAttack { void Attacking(Actor self); }
|
||||
public interface INotifyAttack { void Attacking(Actor self, Target target); }
|
||||
public interface IRenderModifier { IEnumerable<Renderable> ModifyRender(Actor self, IEnumerable<Renderable> r); }
|
||||
public interface IDamageModifier { float GetDamageModifier( WarheadInfo warhead ); }
|
||||
public interface ISpeedModifier { float GetSpeedModifier(); }
|
||||
public interface IDamageModifier { float GetDamageModifier(Actor attacker, WarheadInfo warhead); }
|
||||
public interface ISpeedModifier { decimal GetSpeedModifier(); }
|
||||
public interface IFirepowerModifier { float GetFirepowerModifier(); }
|
||||
public interface ISelectionColorModifier { Color GetSelectionColorModifier(Actor self, Color defaultColor); }
|
||||
public interface IPalette { void InitPalette( WorldRenderer wr ); }
|
||||
public interface IPaletteModifier { void AdjustPalette(Dictionary<string,Palette> b); }
|
||||
public interface IPips { IEnumerable<PipType> GetPips(Actor self); }
|
||||
@@ -115,6 +122,7 @@ namespace OpenRA.Traits
|
||||
bool CanEnterCell(int2 location);
|
||||
void SetPosition(Actor self, int2 cell);
|
||||
void SetPxPosition(Actor self, int2 px);
|
||||
void AdjustPxPosition(Actor self, int2 px); /* works like SetPxPosition, but visual only */
|
||||
}
|
||||
|
||||
public interface IMove : ITeleportable
|
||||
@@ -143,22 +151,28 @@ namespace OpenRA.Traits
|
||||
public readonly string Palette;
|
||||
public readonly int Z;
|
||||
public readonly int ZOffset;
|
||||
public float Scale;
|
||||
|
||||
public Renderable(Sprite sprite, float2 pos, string palette, int z, int zOffset)
|
||||
{
|
||||
Sprite = sprite;
|
||||
Pos = pos;
|
||||
Palette = palette;
|
||||
Z = z;
|
||||
ZOffset = zOffset;
|
||||
}
|
||||
public Renderable(Sprite sprite, float2 pos, string palette, int z, int zOffset, float scale)
|
||||
{
|
||||
Sprite = sprite;
|
||||
Pos = pos;
|
||||
Palette = palette;
|
||||
Z = z;
|
||||
ZOffset = zOffset;
|
||||
Scale = scale; /* default */
|
||||
}
|
||||
|
||||
public Renderable(Sprite sprite, float2 pos, string palette, int z)
|
||||
: this(sprite, pos, palette, z, 0) { }
|
||||
public Renderable(Sprite sprite, float2 pos, string palette, int z)
|
||||
: this(sprite, pos, palette, z, 0, 1f) { }
|
||||
|
||||
public Renderable WithPalette(string newPalette) { return new Renderable(Sprite, Pos, newPalette, Z, ZOffset); }
|
||||
public Renderable WithZOffset(int newOffset) { return new Renderable(Sprite, Pos, Palette, Z, newOffset); }
|
||||
public Renderable WithPos(float2 newPos) { return new Renderable(Sprite, newPos, Palette, Z, ZOffset); }
|
||||
public Renderable(Sprite sprite, float2 pos, string palette, int z, float scale)
|
||||
: this(sprite, pos, palette, z, 0, scale) { }
|
||||
|
||||
public Renderable WithScale(float newScale) { return new Renderable(Sprite, Pos, Palette, Z, ZOffset, newScale); }
|
||||
public Renderable WithPalette(string newPalette) { return new Renderable(Sprite, Pos, newPalette, Z, ZOffset, Scale); }
|
||||
public Renderable WithZOffset(int newOffset) { return new Renderable(Sprite, Pos, Palette, Z, newOffset, Scale); }
|
||||
public Renderable WithPos(float2 newPos) { return new Renderable(Sprite, newPos, Palette, Z, ZOffset, Scale); }
|
||||
}
|
||||
|
||||
public interface ITraitInfo { object Create(ActorInitializer init); }
|
||||
@@ -171,7 +185,12 @@ namespace OpenRA.Traits
|
||||
public interface IWorldLoaded { void WorldLoaded(World w); }
|
||||
public interface ICreatePlayers { void CreatePlayers(World w); }
|
||||
|
||||
public interface IBot { void Activate(Player p); }
|
||||
public interface IBotInfo { string Name { get; } }
|
||||
public interface IBot
|
||||
{
|
||||
void Activate(Player p);
|
||||
IBotInfo Info { get; }
|
||||
}
|
||||
|
||||
public interface IActivity
|
||||
{
|
||||
@@ -181,44 +200,16 @@ namespace OpenRA.Traits
|
||||
IEnumerable<float2> GetCurrentPath();
|
||||
}
|
||||
|
||||
public abstract class CancelableActivity : IActivity
|
||||
{
|
||||
protected IActivity NextActivity { get; private set; }
|
||||
protected bool IsCanceled { get; private set; }
|
||||
|
||||
public abstract IActivity Tick( Actor self );
|
||||
protected virtual bool OnCancel() { return true; }
|
||||
|
||||
public void Cancel( Actor self )
|
||||
{
|
||||
IsCanceled = OnCancel();
|
||||
if( IsCanceled )
|
||||
NextActivity = null;
|
||||
else if (NextActivity != null)
|
||||
NextActivity.Cancel( self );
|
||||
}
|
||||
|
||||
public void Queue( IActivity activity )
|
||||
{
|
||||
if( NextActivity != null )
|
||||
NextActivity.Queue( activity );
|
||||
else
|
||||
NextActivity = activity;
|
||||
}
|
||||
|
||||
public virtual IEnumerable<float2> GetCurrentPath()
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IRenderOverlay { void Render( WorldRenderer wr ); }
|
||||
public interface INotifyIdle { void Idle(Actor self); }
|
||||
public interface INotifyIdle { void TickIdle(Actor self); }
|
||||
|
||||
public interface IBlocksBullets { }
|
||||
|
||||
public interface IPostRender { void RenderAfterWorld(WorldRenderer wr, Actor self); }
|
||||
|
||||
public interface IPostRenderSelection { void RenderAfterWorld(WorldRenderer wr, Actor self); }
|
||||
public interface IPreRenderSelection { void RenderBeforeWorld(WorldRenderer wr, Actor self); }
|
||||
public interface IRenderAsTerrain { IEnumerable<Renderable> RenderAsTerrain(Actor self); }
|
||||
|
||||
public struct Target // a target: either an actor, or a fixed location.
|
||||
{
|
||||
@@ -226,7 +217,7 @@ namespace OpenRA.Traits
|
||||
float2 pos;
|
||||
bool valid;
|
||||
|
||||
public static Target FromActor(Actor a) { return new Target { actor = a, valid = true }; }
|
||||
public static Target FromActor(Actor a) { return new Target { actor = a, valid = (a != null) }; }
|
||||
public static Target FromPos(float2 p) { return new Target { pos = p, valid = true }; }
|
||||
public static Target FromCell(int2 c) { return new Target { pos = Util.CenterOfCell(c), valid = true }; }
|
||||
public static Target FromOrder(Order o)
|
||||
@@ -245,4 +236,13 @@ namespace OpenRA.Traits
|
||||
public Actor Actor { get { return IsActor ? actor : null; } }
|
||||
public bool IsActor { get { return actor != null && !actor.Destroyed; } }
|
||||
}
|
||||
|
||||
public interface ITargetable
|
||||
{
|
||||
string[] TargetTypes { get; }
|
||||
IEnumerable<int2> TargetableCells( Actor self );
|
||||
bool TargetableBy(Actor self, Actor byActor);
|
||||
}
|
||||
|
||||
public interface INotifyKeyPress { bool KeyPressed(Actor self, KeyInput e); }
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Support;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
@@ -99,7 +101,8 @@ namespace OpenRA.Traits
|
||||
public static Renderable Centered(Actor self, Sprite s, float2 location)
|
||||
{
|
||||
var pal = self.Owner == null ? "player0" : self.Owner.Palette;
|
||||
var loc = location - 0.5f * s.size;
|
||||
var loc = location - 0.5f * s.size;
|
||||
|
||||
return new Renderable(s, loc.Round(), pal, (int)self.CenterLocation.Y);
|
||||
}
|
||||
|
||||
@@ -109,8 +112,39 @@ namespace OpenRA.Traits
|
||||
(next, a) => { a.Queue( next ); return a; });
|
||||
}
|
||||
|
||||
public static IActivity RunActivity( Actor self, IActivity act )
|
||||
{
|
||||
while( act != null )
|
||||
{
|
||||
var prev = act;
|
||||
|
||||
var sw = new Stopwatch();
|
||||
act = act.Tick( self );
|
||||
var dt = sw.ElapsedTime();
|
||||
if(dt > Game.Settings.Debug.LongTickThreshold)
|
||||
Log.Write("perf", "[{2}] Activity: {0} ({1:0.000} ms)", prev, dt * 1000, Game.LocalTick);
|
||||
|
||||
if( prev == act )
|
||||
break;
|
||||
}
|
||||
return act;
|
||||
}
|
||||
|
||||
public static Color ArrayToColor(int[] x) { return Color.FromArgb(x[0], x[1], x[2]); }
|
||||
|
||||
public static int2 CellContaining(float2 pos) { return (1f / Game.CellSize * pos).ToInt2(); }
|
||||
|
||||
/* pretty crap */
|
||||
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> ts, Thirdparty.Random random)
|
||||
{
|
||||
var items = ts.ToList();
|
||||
while (items.Count > 0)
|
||||
{
|
||||
var t = items.Random(random);
|
||||
yield return t;
|
||||
items.Remove(t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
31
OpenRA.Game/Traits/ValidateOrder.cs
Normal file
31
OpenRA.Game/Traits/ValidateOrder.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 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. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using OpenRA.Network;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
public class ValidateOrderInfo : TraitInfo<ValidateOrder> { }
|
||||
|
||||
public class ValidateOrder : IValidateOrder
|
||||
{
|
||||
public bool OrderValidation(OrderManager orderManager, World world, int clientId, Order order)
|
||||
{
|
||||
// Drop exploiting orders
|
||||
if (order.Subject != null && order.Subject.Owner.ClientIndex != clientId)
|
||||
{
|
||||
Game.Debug("Detected exploit order from {0}: {1}".F(clientId, order.OrderString));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
public class ValuedInfo : TraitInfo<Valued>
|
||||
{
|
||||
public readonly int Cost = 0;
|
||||
}
|
||||
|
||||
public class TooltipInfo : TraitInfo<Tooltip>
|
||||
{
|
||||
public readonly string Description = "";
|
||||
public readonly string Name = "";
|
||||
public readonly string Icon = null;
|
||||
public readonly string[] AlternateName = { };
|
||||
}
|
||||
|
||||
public class Valued { }
|
||||
public class Tooltip { }
|
||||
}
|
||||
@@ -26,24 +26,15 @@ namespace OpenRA.Traits
|
||||
|
||||
public void Render( WorldRenderer wr )
|
||||
{
|
||||
var cliprect = Game.viewport.ShroudBounds( world );
|
||||
cliprect = Rectangle.Intersect(Game.viewport.ViewBounds(), cliprect);
|
||||
|
||||
var minx = cliprect.Left;
|
||||
var maxx = cliprect.Right;
|
||||
|
||||
var miny = cliprect.Top;
|
||||
var maxy = cliprect.Bottom;
|
||||
|
||||
foreach( var rt in world.WorldActor.TraitsImplementing<ResourceType>() )
|
||||
rt.info.PaletteIndex = wr.GetPaletteIndex(rt.info.Palette);
|
||||
|
||||
for (int x = minx; x < maxx; x++)
|
||||
for (int y = miny; y < maxy; y++)
|
||||
var clip = Game.viewport.WorldBounds(world);
|
||||
for (int x = clip.Left; x < clip.Right; x++)
|
||||
for (int y = clip.Top; y < clip.Bottom; y++)
|
||||
{
|
||||
if (world.LocalPlayer != null &&
|
||||
!world.LocalPlayer.Shroud.IsExplored(new int2(x, y)))
|
||||
continue;
|
||||
if (!world.LocalShroud.IsExplored(new int2(x, y)))
|
||||
continue;
|
||||
|
||||
var c = content[x, y];
|
||||
if (c.image != null)
|
||||
@@ -64,11 +55,11 @@ namespace OpenRA.Traits
|
||||
|
||||
var map = w.Map;
|
||||
|
||||
for (int x = map.XOffset; x < map.XOffset + map.Width; x++)
|
||||
for (int y = map.YOffset; y < map.YOffset + map.Height; y++)
|
||||
for (int x = map.Bounds.Left; x < map.Bounds.Right; x++)
|
||||
for (int y = map.Bounds.Top; y < map.Bounds.Bottom; y++)
|
||||
{
|
||||
// Todo: Valid terrain should be specified in the resource
|
||||
if (!w.IsCellBuildable(new int2(x,y), false))
|
||||
if (!AllowResourceAt(new int2(x,y)))
|
||||
continue;
|
||||
|
||||
content[x, y].type = resourceTypes.FirstOrDefault(
|
||||
@@ -77,8 +68,8 @@ namespace OpenRA.Traits
|
||||
content[x, y].image = ChooseContent(content[x, y].type);
|
||||
}
|
||||
|
||||
for (int x = map.XOffset; x < map.XOffset + map.Width; x++)
|
||||
for (int y = map.YOffset; y < map.YOffset + map.Height; y++)
|
||||
for (int x = map.Bounds.Left; x < map.Bounds.Right; x++)
|
||||
for (int y = map.Bounds.Top; y < map.Bounds.Bottom; y++)
|
||||
if (content[x, y].type != null)
|
||||
{
|
||||
content[x, y].density = GetIdealDensity(x, y);
|
||||
@@ -86,6 +77,14 @@ namespace OpenRA.Traits
|
||||
}
|
||||
}
|
||||
|
||||
public bool AllowResourceAt( int2 a )
|
||||
{
|
||||
if( !world.Map.IsInMap( a.X, a.Y ) ) return false;
|
||||
if( !world.GetTerrainInfo( a ).Buildable ) return false;
|
||||
if( world.WorldActor.Trait<UnitInfluence>().AnyUnitsAt( a ) ) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Sprite[] ChooseContent(ResourceType t)
|
||||
{
|
||||
return t.info.Sprites[world.SharedRandom.Next(t.info.Sprites.Length)];
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace OpenRA.Traits
|
||||
|
||||
public Sprite[][] Sprites;
|
||||
public int PaletteIndex;
|
||||
|
||||
public PipType PipColor = PipType.Yellow;
|
||||
|
||||
public object Create(ActorInitializer init) { return new ResourceType(this); }
|
||||
}
|
||||
|
||||
@@ -12,9 +12,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
@@ -26,18 +23,31 @@ namespace OpenRA.Traits
|
||||
public class Shroud
|
||||
{
|
||||
Map map;
|
||||
|
||||
World world;
|
||||
|
||||
public int[,] visibleCells;
|
||||
public bool[,] exploredCells;
|
||||
public Rectangle? exploredBounds;
|
||||
Rectangle? exploredBounds;
|
||||
bool disabled = false;
|
||||
public bool Disabled
|
||||
{
|
||||
get { return disabled || world.LocalPlayer == null; }
|
||||
set { disabled = value; Dirty(); }
|
||||
}
|
||||
|
||||
public Rectangle? Bounds
|
||||
{
|
||||
get { return Disabled ? null : exploredBounds; }
|
||||
}
|
||||
|
||||
public event Action Dirty = () => { };
|
||||
|
||||
public Shroud(World world)
|
||||
{
|
||||
this.world = world;
|
||||
map = world.Map;
|
||||
visibleCells = new int[map.MapSize.X, map.MapSize.Y];
|
||||
exploredCells = new bool[map.MapSize.X, map.MapSize.Y];
|
||||
|
||||
world.ActorAdded += AddActor;
|
||||
world.ActorRemoved += RemoveActor;
|
||||
}
|
||||
@@ -51,10 +61,10 @@ namespace OpenRA.Traits
|
||||
{
|
||||
var min = a - new int2(r, r);
|
||||
var max = a + new int2(r, r);
|
||||
if (min.X < world.Map.XOffset - 1) min.X = world.Map.XOffset - 1;
|
||||
if (min.Y < world.Map.YOffset - 1) min.Y = world.Map.YOffset - 1;
|
||||
if (max.X > world.Map.XOffset + world.Map.Width) max.X = world.Map.XOffset + world.Map.Width;
|
||||
if (max.Y > world.Map.YOffset + world.Map.Height) max.Y = world.Map.YOffset + world.Map.Height;
|
||||
if (min.X < world.Map.Bounds.Left - 1) min.X = world.Map.Bounds.Left - 1;
|
||||
if (min.Y < world.Map.Bounds.Top - 1) min.Y = world.Map.Bounds.Top - 1;
|
||||
if (max.X > world.Map.Bounds.Right) max.X = world.Map.Bounds.Right;
|
||||
if (max.Y > world.Map.Bounds.Bottom) max.Y = world.Map.Bounds.Bottom;
|
||||
|
||||
for (var j = min.Y; j <= max.Y; j++)
|
||||
for (var i = min.X; i <= max.X; i++)
|
||||
@@ -93,13 +103,13 @@ namespace OpenRA.Traits
|
||||
}
|
||||
|
||||
var box = new Rectangle(p.X - v.range, p.Y - v.range, 2 * v.range + 1, 2 * v.range + 1);
|
||||
exploredBounds = exploredBounds.HasValue ?
|
||||
Rectangle.Union(exploredBounds.Value, box) : box;
|
||||
exploredBounds = (exploredBounds.HasValue) ? Rectangle.Union(exploredBounds.Value, box) : box;
|
||||
}
|
||||
|
||||
vis[a] = v;
|
||||
|
||||
Dirty();
|
||||
if (!Disabled)
|
||||
Dirty();
|
||||
}
|
||||
|
||||
public void UpdatePlayerStance(World w, Player player, Stance oldStance, Stance newStance)
|
||||
@@ -122,19 +132,14 @@ namespace OpenRA.Traits
|
||||
|
||||
public static IEnumerable<int2> GetVisOrigins(Actor a)
|
||||
{
|
||||
if (a.Info.Traits.Contains<BuildingInfo>())
|
||||
var ios = a.TraitOrDefault<IOccupySpace>();
|
||||
if (ios != null)
|
||||
{
|
||||
var bi = a.Info.Traits.Get<BuildingInfo>();
|
||||
return Footprint.Tiles(a.Info.Name, bi, a.Location);
|
||||
}
|
||||
else
|
||||
{
|
||||
var mobile = a.TraitOrDefault<Mobile>();
|
||||
if (mobile != null)
|
||||
return new[] { mobile.fromCell, mobile.toCell };
|
||||
else
|
||||
return new[] { (1f / Game.CellSize * a.CenterLocation).ToInt2() };
|
||||
var cells = ios.OccupiedCells();
|
||||
if (cells.Any()) return cells;
|
||||
}
|
||||
|
||||
return new[] { (1f / Game.CellSize * a.CenterLocation).ToInt2() };
|
||||
}
|
||||
|
||||
void RemoveActor(Actor a)
|
||||
@@ -147,8 +152,9 @@ namespace OpenRA.Traits
|
||||
--visibleCells[q.X, q.Y];
|
||||
|
||||
vis.Remove(a);
|
||||
|
||||
Dirty();
|
||||
|
||||
if (!Disabled)
|
||||
Dirty();
|
||||
}
|
||||
|
||||
public void UpdateActor(Actor a)
|
||||
@@ -165,20 +171,21 @@ namespace OpenRA.Traits
|
||||
exploredCells[q.X, q.Y] = true;
|
||||
|
||||
var box = new Rectangle(center.X - range, center.Y - range, 2 * range + 1, 2 * range + 1);
|
||||
exploredBounds = exploredBounds.HasValue ?
|
||||
Rectangle.Union(exploredBounds.Value, box) : box;
|
||||
exploredBounds = (exploredBounds.HasValue) ? Rectangle.Union(exploredBounds.Value, box) : box;
|
||||
|
||||
Dirty();
|
||||
if (!Disabled)
|
||||
Dirty();
|
||||
}
|
||||
|
||||
public void ExploreAll(World world)
|
||||
{
|
||||
for (int i = map.TopLeft.X; i < map.BottomRight.X; i++)
|
||||
for (int j = map.TopLeft.Y; j < map.BottomRight.Y; j++)
|
||||
for (int i = map.Bounds.Left; i < map.Bounds.Right; i++)
|
||||
for (int j = map.Bounds.Top; j < map.Bounds.Bottom; j++)
|
||||
exploredCells[i, j] = true;
|
||||
exploredBounds = new Rectangle(world.Map.TopLeft.X,world.Map.TopLeft.Y,world.Map.Width,world.Map.Height);
|
||||
exploredBounds = world.Map.Bounds;
|
||||
|
||||
Dirty();
|
||||
if (!Disabled)
|
||||
Dirty();
|
||||
}
|
||||
|
||||
public void ResetExploration() // for `hide map` crate
|
||||
@@ -187,7 +194,43 @@ namespace OpenRA.Traits
|
||||
for (var i = 0; i <= exploredCells.GetUpperBound(0); i++)
|
||||
exploredCells[i, j] = visibleCells[i, j] > 0;
|
||||
|
||||
Dirty();
|
||||
if (!Disabled)
|
||||
Dirty();
|
||||
}
|
||||
|
||||
public bool IsExplored(int2 xy) { return IsExplored(xy.X, xy.Y); }
|
||||
public bool IsExplored(int x, int y)
|
||||
{
|
||||
if (!map.IsInMap(x, y))
|
||||
return false;
|
||||
|
||||
if (Disabled)
|
||||
return true;
|
||||
|
||||
return exploredCells[x,y];
|
||||
}
|
||||
|
||||
public bool IsVisible(int2 xy) { return IsVisible(xy.X, xy.Y); }
|
||||
public bool IsVisible(int x, int y)
|
||||
{
|
||||
if (Disabled)
|
||||
return true;
|
||||
|
||||
// Visibility is allowed to extend beyond the map cordon so that
|
||||
// the fog tiles are not visible at the edge of the world
|
||||
if (x < 0 || x >= map.MapSize.X || y < 0 || y >= map.MapSize.Y)
|
||||
return false;
|
||||
|
||||
return visibleCells[x,y] != 0;
|
||||
}
|
||||
|
||||
// Actors are hidden under shroud, but not under fog by default
|
||||
public bool IsVisible(Actor a)
|
||||
{
|
||||
if (a.TraitsImplementing<IVisibilityModifier>().Any(t => !t.IsVisible(a)))
|
||||
return false;
|
||||
|
||||
return Disabled || a.Owner == a.World.LocalPlayer || GetVisOrigins(a).Any(o => IsExplored(o));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,10 +49,10 @@ namespace OpenRA.Traits
|
||||
{
|
||||
var bounds = a.Actor.GetBounds(true);
|
||||
|
||||
if (bounds.Right <= Game.CellSize * self.World.Map.XOffset) continue;
|
||||
if (bounds.Bottom <= Game.CellSize * self.World.Map.YOffset) continue;
|
||||
if (bounds.Left >= Game.CellSize * (self.World.Map.XOffset + self.World.Map.Width)) continue;
|
||||
if (bounds.Top >= Game.CellSize * (self.World.Map.YOffset + self.World.Map.Height)) continue;
|
||||
if (bounds.Right <= Game.CellSize * self.World.Map.Bounds.Left) continue;
|
||||
if (bounds.Bottom <= Game.CellSize * self.World.Map.Bounds.Top) continue;
|
||||
if (bounds.Left >= Game.CellSize * self.World.Map.Bounds.Right) continue;
|
||||
if (bounds.Top >= Game.CellSize * self.World.Map.Bounds.Bottom) continue;
|
||||
|
||||
var i1 = Math.Max(0, (int)bounds.Left / scale);
|
||||
var i2 = Math.Min(bins.GetUpperBound(0), (int)bounds.Right / scale);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user