Compare commits
950 Commits
playtest-2
...
playtest-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
2a3c149769 | ||
|
|
7cfcd94e23 | ||
|
|
247a8c9717 | ||
|
|
4afd8ad783 | ||
|
|
dca77956cb | ||
|
|
22c6c3b3f6 | ||
|
|
1e8e5c8f5c | ||
|
|
c0fcc862d6 | ||
|
|
5a3ccdf0c5 | ||
|
|
1bd802c386 | ||
|
|
3a4e2abedd | ||
|
|
f13343ef2e | ||
|
|
2b1ac6a60f | ||
|
|
8aa180c1f4 | ||
|
|
260e420f85 | ||
|
|
1a203afd89 | ||
|
|
c150fd9475 | ||
|
|
566303a8f1 | ||
|
|
10f8364b99 | ||
|
|
7c31f72db6 | ||
|
|
6b6c9639f9 | ||
|
|
7ca9fcdac9 | ||
|
|
20276291ce | ||
|
|
34fc207a6c | ||
|
|
c974e61680 | ||
|
|
09db76f89f | ||
|
|
6bbf878314 | ||
|
|
4cd3195f9f | ||
|
|
6ea2a06e4b | ||
|
|
597dba8584 | ||
|
|
1c1483377c | ||
|
|
0873741983 | ||
|
|
e0afc08e5f | ||
|
|
cb1deacbb2 | ||
|
|
ab1e930ba3 | ||
|
|
10bf85f57e | ||
|
|
17990ab8b7 | ||
|
|
beecb8aeb1 | ||
|
|
6a25d989a7 | ||
|
|
f8776d773d | ||
|
|
3724f46a3e | ||
|
|
915ad7fb7b | ||
|
|
07023b6c35 | ||
|
|
b78b5b22a3 | ||
|
|
eca098b0b4 | ||
|
|
011a20e8b4 | ||
|
|
9c362f7d41 | ||
|
|
99d92ee095 | ||
|
|
b905d343af | ||
|
|
024b564b8c | ||
|
|
9c0e3ac4c9 | ||
|
|
a2205e9031 | ||
|
|
0df9815d2c | ||
|
|
79d6eb4a2b | ||
|
|
0d8557eadb | ||
|
|
70f6fc7a7a | ||
|
|
11f6a8b945 | ||
|
|
e3d71acb05 | ||
|
|
580f1cfe97 | ||
|
|
3d7434f42e | ||
|
|
ef96604f9e | ||
|
|
465f5d295b | ||
|
|
1feb377d43 | ||
|
|
a065fb464e | ||
|
|
e39917ca19 | ||
|
|
318f496bf9 | ||
|
|
ed8a155249 | ||
|
|
40ac5f7b6e | ||
|
|
517754027e | ||
|
|
ef4f478e10 | ||
|
|
92c30b89f8 | ||
|
|
7331a9c4fb | ||
|
|
d2b9e150f1 | ||
|
|
8ba329f1be | ||
|
|
8868eab247 | ||
|
|
f12889e684 | ||
|
|
5c095bc174 | ||
|
|
0c24a08436 | ||
|
|
9762b540b0 | ||
|
|
fd34f2ba99 | ||
|
|
ad6481c8e8 | ||
|
|
7426d47cd5 | ||
|
|
761f62292f | ||
|
|
63b8555bc9 | ||
|
|
3209da4a4a | ||
|
|
aebef4f1c8 | ||
|
|
d3244184c1 | ||
|
|
39e62354a8 | ||
|
|
f525c3808e | ||
|
|
87a0b52ce5 | ||
|
|
4bc9e01516 | ||
|
|
711d05da98 | ||
|
|
3d805ff40d | ||
|
|
0cd140849b | ||
|
|
d6110b9ef0 | ||
|
|
26d1db778e | ||
|
|
f41aa474aa | ||
|
|
0002e80a19 | ||
|
|
759a52d86e | ||
|
|
44fe0396bb | ||
|
|
dd6d8d916e | ||
|
|
5af8f5e2d9 | ||
|
|
c85503811c | ||
|
|
ab431fe9ee | ||
|
|
7ec9958d47 | ||
|
|
cfc74a6dee | ||
|
|
cecdd73e08 | ||
|
|
381e080b11 | ||
|
|
c6a047cb1a | ||
|
|
4c51733e04 | ||
|
|
8f613b80e8 | ||
|
|
10de282daa | ||
|
|
915e123956 | ||
|
|
8cb7a7b8ce | ||
|
|
ce5cf93077 | ||
|
|
1390bb7428 | ||
|
|
3c5b136216 | ||
|
|
66785e606a | ||
|
|
e2239fa50c | ||
|
|
7f7712aa64 | ||
|
|
b1605e115a | ||
|
|
69b2da86be | ||
|
|
18e965e5aa | ||
|
|
24f0c28f56 | ||
|
|
25af51b4ac | ||
|
|
ecd7064cc3 | ||
|
|
477a21e782 | ||
|
|
899d9af62b | ||
|
|
17eca983ef | ||
|
|
2fc219ecd5 | ||
|
|
49a645cd2d | ||
|
|
7cb8da411d | ||
|
|
4bcdf3cf11 | ||
|
|
1b525ff809 | ||
|
|
cdec3fce26 | ||
|
|
7bdf6a953f | ||
|
|
06b20c8ba5 | ||
|
|
9b484b53ec | ||
|
|
9620b4ed46 | ||
|
|
d8908c44d0 | ||
|
|
cfe705531a | ||
|
|
9a2fd38ab6 | ||
|
|
911e7f62de | ||
|
|
403b81bdc9 | ||
|
|
a0714b00b3 | ||
|
|
d5239ee77a | ||
|
|
03ec97f302 | ||
|
|
3255f95ee9 | ||
|
|
4c8fd5e73d | ||
|
|
7179ef0f45 | ||
|
|
6d5918b11d | ||
|
|
81c484d1c9 | ||
|
|
de1044c24c | ||
|
|
e4c31939d9 | ||
|
|
7f48d6796e | ||
|
|
4fd77aec8e | ||
|
|
92fece01de | ||
|
|
d8d987f844 | ||
|
|
de429a4c62 | ||
|
|
c0ca35a4ff | ||
|
|
1bff8559fb | ||
|
|
de98274165 | ||
|
|
59e2228b2a | ||
|
|
96d1408d45 | ||
|
|
ff45ae2d16 | ||
|
|
47950c9113 | ||
|
|
f402ec7898 | ||
|
|
a3c0448e15 | ||
|
|
699b4b1154 | ||
|
|
911db3feb1 | ||
|
|
c790db8e84 | ||
|
|
d6dd392028 | ||
|
|
636b2a8ea7 | ||
|
|
959d3f8bd7 | ||
|
|
193cb929f1 | ||
|
|
f93e270fe4 | ||
|
|
f19953c39a | ||
|
|
59d5ce1bc8 | ||
|
|
43cf7cd074 | ||
|
|
aba76f4b50 | ||
|
|
8021fc3b20 | ||
|
|
7bf4cb85fa | ||
|
|
6dd03bb339 | ||
|
|
14e517cab5 | ||
|
|
cdcfeb6276 | ||
|
|
c77c63a380 | ||
|
|
9921fe8fad | ||
|
|
f848c5d7d4 | ||
|
|
62c47e3b12 | ||
|
|
094986d066 | ||
|
|
d87e02ab41 | ||
|
|
3f415a8fdf | ||
|
|
846371cf3e | ||
|
|
45c77e64ee | ||
|
|
66493031c8 | ||
|
|
384b26db60 | ||
|
|
d66dbeb312 | ||
|
|
ba9611f544 | ||
|
|
b6e56560d4 | ||
|
|
1f047d439f | ||
|
|
5233ae4770 | ||
|
|
562e07264a | ||
|
|
4dab4ed73f | ||
|
|
a97ddffa53 | ||
|
|
8e589e3c16 | ||
|
|
96f72ce842 | ||
|
|
d8de477edb | ||
|
|
694fb6831a | ||
|
|
c16a515224 | ||
|
|
e2eae7973b | ||
|
|
9e3c938706 | ||
|
|
ef665df2e9 | ||
|
|
271a3eea8d | ||
|
|
2f6315b816 | ||
|
|
ac8d408ba7 | ||
|
|
0fdd49c96a | ||
|
|
e390cf8ab0 | ||
|
|
64d700cd70 | ||
|
|
d3db9d3710 | ||
|
|
9eb05a43f9 | ||
|
|
3165ec5359 | ||
|
|
f4699132d6 | ||
|
|
086bdfb4bd | ||
|
|
61fd12c7da | ||
|
|
9979209c34 | ||
|
|
b0c06d4cd9 | ||
|
|
3a617f8934 | ||
|
|
b35a7d9f8d | ||
|
|
67fa079658 | ||
|
|
c9edbd8a80 | ||
|
|
4b49bf03dc | ||
|
|
6cfad6f2ab | ||
|
|
c0c4e7299b | ||
|
|
afda1647fd | ||
|
|
aff6889995 | ||
|
|
65515d54af | ||
|
|
c5b7c43d23 | ||
|
|
dba5adc91c | ||
|
|
0073a03ca4 | ||
|
|
5509d0d2e9 | ||
|
|
fe52e3722e | ||
|
|
3ddd1581f1 | ||
|
|
56efc3930b | ||
|
|
2f8d81a0f7 | ||
|
|
46486073ab | ||
|
|
3dc11eaa05 | ||
|
|
b62ee4d37c | ||
|
|
532afc3ff8 | ||
|
|
9ddb2beced | ||
|
|
1e758ec3e0 | ||
|
|
aa1d44428d | ||
|
|
23da8a24bd | ||
|
|
fef291a27e | ||
|
|
f49e56d660 | ||
|
|
927ab00f4d | ||
|
|
3f870e7e48 | ||
|
|
f2a321186f | ||
|
|
410daecab6 | ||
|
|
966e3bb71a | ||
|
|
652f06f604 | ||
|
|
2a10af2007 | ||
|
|
4cb26c0e3c | ||
|
|
8455dadb3c | ||
|
|
3c19b3df73 | ||
|
|
c796e155e7 | ||
|
|
ce9caec291 | ||
|
|
0330ef2b9e | ||
|
|
872a304714 | ||
|
|
59cc50df4c | ||
|
|
a1fd84fb15 | ||
|
|
317007f7ce | ||
|
|
387212d9ea | ||
|
|
c9ca5e207b | ||
|
|
ce78944a9e | ||
|
|
1fb336b7da | ||
|
|
e4d05407d9 | ||
|
|
f7eca1157e | ||
|
|
a64245fc10 | ||
|
|
ad8038867d | ||
|
|
fb90400524 | ||
|
|
08b25d10c1 | ||
|
|
ba09bbba8e | ||
|
|
0d2e1d7de5 | ||
|
|
22861fca5a | ||
|
|
692ef539f9 | ||
|
|
d07b0e4400 | ||
|
|
9848d1f403 | ||
|
|
83eae029b7 | ||
|
|
99e225c279 | ||
|
|
1785c4e4a8 | ||
|
|
69753f8a08 | ||
|
|
5593b9ffdd | ||
|
|
1b66635670 | ||
|
|
f1b1f3bd52 | ||
|
|
ff611dac32 | ||
|
|
e0a4cb4763 | ||
|
|
a3246866fb | ||
|
|
b94c2fc7c4 | ||
|
|
d530cf22da | ||
|
|
94e5b02a39 | ||
|
|
40533a50c1 | ||
|
|
d4182b1929 | ||
|
|
482129499d | ||
|
|
366cd02761 | ||
|
|
f549e7e5a8 | ||
|
|
4cab7c7fc4 | ||
|
|
e3939d4805 | ||
|
|
2ae6983bc2 | ||
|
|
ad00193a17 | ||
|
|
810667abbc | ||
|
|
a66efd56f2 | ||
|
|
36ba15e7f9 | ||
|
|
d809fb1e7b | ||
|
|
d0bc834a57 | ||
|
|
483ca8cc4c | ||
|
|
06aba5da46 | ||
|
|
106fbcd5af | ||
|
|
f456e41a45 | ||
|
|
7dc3aee4a2 | ||
|
|
c91bee8091 | ||
|
|
82875e15f9 | ||
|
|
d6f8b7d850 | ||
|
|
72fe873b26 | ||
|
|
e9f34b1e21 | ||
|
|
d15ab79370 | ||
|
|
303613b052 | ||
|
|
051c451867 | ||
|
|
ac8a3526a4 | ||
|
|
be47fb55e8 | ||
|
|
5b8fc75d4a | ||
|
|
629e73ac29 | ||
|
|
7e5b861caf | ||
|
|
f3dcc14fca | ||
|
|
fee68d0231 | ||
|
|
83f56d152f | ||
|
|
a04ea789f5 | ||
|
|
94becc3ddc | ||
|
|
8452e1bdf0 | ||
|
|
5a513bc69d | ||
|
|
5a173a2bd3 | ||
|
|
d29b068d59 | ||
|
|
8dbbaca8da | ||
|
|
10a1f7ec1e | ||
|
|
8286dba919 | ||
|
|
bd735059d0 | ||
|
|
147acbf096 | ||
|
|
91aa5302a8 | ||
|
|
2b6328f0ee | ||
|
|
1bc2136771 | ||
|
|
4ad0a52104 | ||
|
|
b9450b0f63 | ||
|
|
03bb74ea84 | ||
|
|
8b4551c605 | ||
|
|
f645049054 | ||
|
|
aa832244fb | ||
|
|
190311c68c | ||
|
|
b8ed9d7da5 | ||
|
|
ca32d51375 | ||
|
|
b0e5bb3718 | ||
|
|
96ebbfe75c | ||
|
|
5c6a6ee350 | ||
|
|
bbf2d68e9d | ||
|
|
59cf76b4ae | ||
|
|
e97440819d | ||
|
|
87cd37a30b | ||
|
|
f406dfa277 | ||
|
|
24039f593c | ||
|
|
cceb4401aa | ||
|
|
f9445cb282 | ||
|
|
706adb6d0b | ||
|
|
dd7e578fe6 | ||
|
|
83d1df19ba | ||
|
|
975fa28f62 | ||
|
|
452f7f7bff | ||
|
|
626db661ab | ||
|
|
9ea3a404a3 | ||
|
|
55c3e9d5bb | ||
|
|
f616a527ee | ||
|
|
a594e9f830 | ||
|
|
45d712d390 | ||
|
|
dbbdc171d2 | ||
|
|
befe22e170 | ||
|
|
b284e82aa7 | ||
|
|
ed5a3338fe | ||
|
|
68dcec87c7 | ||
|
|
39d7e54e7f | ||
|
|
404a4ad578 | ||
|
|
c9d0a7a301 | ||
|
|
54ffdc51b4 | ||
|
|
9e53774299 | ||
|
|
f70a6aafb1 | ||
|
|
c3fa9f7aa8 | ||
|
|
a73ef3affd | ||
|
|
4e5d26a2d1 | ||
|
|
4b1bc7acd4 | ||
|
|
95d29c6910 | ||
|
|
c88ea2bd7c | ||
|
|
af157867d8 | ||
|
|
9476e16df6 | ||
|
|
9c148bf3f0 | ||
|
|
c8c96b9f6b | ||
|
|
108d7764ec | ||
|
|
5396cad2b9 | ||
|
|
2bae3d9d2f | ||
|
|
9cbaa9679d | ||
|
|
6228a962f6 | ||
|
|
1baff27553 | ||
|
|
4855b6bca6 | ||
|
|
ff0c66e38c | ||
|
|
83d2ca07f1 | ||
|
|
26152489f3 | ||
|
|
4ca4d7b5dd | ||
|
|
0dddf12831 | ||
|
|
25c098bad6 | ||
|
|
be9528dcb2 | ||
|
|
965385980f | ||
|
|
ca92cd03db | ||
|
|
cdec95b73a | ||
|
|
76bf3077e9 | ||
|
|
53858a7789 | ||
|
|
b98e25c9a7 | ||
|
|
be46f44bc9 | ||
|
|
fee6d450e4 | ||
|
|
173dc59039 | ||
|
|
950dd4e85c | ||
|
|
6632feb696 | ||
|
|
7b2622e67b | ||
|
|
9e5528ef83 | ||
|
|
3d920670e5 | ||
|
|
4d2afd827f | ||
|
|
9a4d0dfe5e | ||
|
|
2a240e62aa | ||
|
|
7a88adcf96 |
18
.gitignore
vendored
18
.gitignore
vendored
@@ -10,22 +10,13 @@ obj
|
||||
mods/*/*.dll
|
||||
|
||||
# Red Alert binary files
|
||||
*.[mM][iI][xX]
|
||||
|
||||
# Crap generated by OpenRa
|
||||
sheet-*.png
|
||||
log.txt
|
||||
|
||||
*.rep
|
||||
mods/*/packages/*.[mM][iI][xX]
|
||||
|
||||
#binary stuff
|
||||
/*.dll
|
||||
*.pdb
|
||||
*.mdb
|
||||
*.exe
|
||||
OpenRA
|
||||
OpenRA.app
|
||||
*.vqa
|
||||
|
||||
# backup files by various editors
|
||||
*~
|
||||
@@ -33,10 +24,8 @@ OpenRA.app
|
||||
# dependency DLLs (different for every platform!)
|
||||
cg.dll
|
||||
cgGL.dll
|
||||
glfw.dll
|
||||
|
||||
/OpenRa.Gl.dll
|
||||
settings.ini
|
||||
|
||||
#monodevelop
|
||||
*.pidb
|
||||
@@ -55,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
|
||||
|
||||
4
AUTHORS
4
AUTHORS
@@ -6,4 +6,6 @@ The OpenRA developers are:
|
||||
* Matthew Bowra-Dean
|
||||
* Paul Chote
|
||||
* Alli Witheford
|
||||
* Joakim Lindberg
|
||||
* Joakim Lindberg
|
||||
* Andrew Riedi
|
||||
* Tim Mylemans
|
||||
|
||||
75
HACKING
75
HACKING
@@ -5,43 +5,82 @@ There are n main sections to OpenRA: UI, Rendering, unit behaviour, ...
|
||||
All units/structures/most things in the map are Actors. Actors contain a collection of traits.
|
||||
Traits consist of an info class and a class that does stuff
|
||||
|
||||
Actor assembly is done via the mod's yaml files. A section exists for each actor type, and within that section we list the traits the actor should have. These get looked up in the loaded mod DLLs. Each trait can contain properties, which are automatically loaded into the corresponding fields on the trait's ITraitInfo.
|
||||
Actor assembly is done via the mod's yaml files. A section exists for each actor type,
|
||||
and within that section we list the traits the actor should have.
|
||||
These get looked up in the loaded mod DLLs. Each trait can contain properties,
|
||||
which are automatically loaded into the corresponding fields on the trait's ITraitInfo.
|
||||
|
||||
- Traits: look at TraitInterfaces.cs
|
||||
We've tried to make individual traits implement as self-contained a unit of functionality as possible - all cross-trait references should be in terms of an interface from TraitInterfaces.cs.
|
||||
We've tried to make individual traits implement as self-contained a unit of functionality
|
||||
as possible - all cross-trait references should be in terms of an interface from
|
||||
TraitInterfaces.cs.
|
||||
|
||||
- Things an actor can be *doing* are represented as IActivity implementations. Actor has a queue of these. There's a standard set of activities in OpenRa.Game/Traits/Activities, and mods tend to define more as they need them. (RA defines various special-infantry actions as activities).
|
||||
- Things an actor can be *doing* are represented as IActivity implementations.
|
||||
Actor has a queue of these. There's a standard set of activities in
|
||||
OpenRa.Game/Traits/Activities, and mods tend to define more as they need them. (RA
|
||||
defines various special-infantry actions as activities).
|
||||
|
||||
- Units offer orders they can perform (given context) through traits that implement IIssueOrder. Every trait with this interface is given a chance to generate orders for the current context.
|
||||
- Units offer orders they can perform (given context) through traits that implement IIssueOrder.
|
||||
Every trait with this interface is given a chance to generate orders for the current context.
|
||||
|
||||
- For more complex things that require modal UI (like special abilities, RA-style sell/repair buttons, etc) we have IOrderGenerator implementations. This can completely replace the normal actors-provide-orders model temporarily. IOGs wiring is provided through OpenRa.Game/Controller.cs (ToggleInputMode<T>, CancelInputMode)
|
||||
- For more complex things that require modal UI (like special abilities,
|
||||
RA-style sell/repair buttons, etc) we have IOrderGenerator implementations. This can
|
||||
completely replace the normal actors-provide-orders model temporarily. IOGs wiring is
|
||||
provided through OpenRa.Game/Controller.cs (ToggleInputMode<T>, CancelInputMode)
|
||||
|
||||
- Things that don't affect gameplay, or (increasingly) are just transient are implemented as IEffect, rather than real Actors. This is similar to the temp ents mechanism in many other game engines.
|
||||
- Things that don't affect gameplay, or (increasingly) are just transient are implemented as
|
||||
IEffect, rather than real Actors. This is similar to the temp ents mechanism in many other
|
||||
game engines.
|
||||
|
||||
- Most player-level or global-level game behavior is implemented as traits on special Player and World actors. These are accessible via Player.PlayerActor and World.WorldActor. This includes production queue support, ore/tiberium growth, various palette manipulation magic.
|
||||
- Most player-level or global-level game behavior is implemented as traits on special Player
|
||||
and World actors. These are accessible via Player.PlayerActor and World.WorldActor. This
|
||||
includes production queue support, ore/tiberium growth, various palette manipulation magic.
|
||||
|
||||
- Many traits can be modified by adding an appropriate IFooModifier implementation to the unit. This includes rendering, where IRenderModifier allows you to define an arbitrary transform on the Renderables emitted by the actor's IRender implementation(s). Examples are things like cloaking, invisibility to certain players, flying units with shadows, etc. Other modifiers can affect movement speed, damage taken, weapon firepower, etc.
|
||||
- Many traits can be modified by adding an appropriate IFooModifier implementation to the unit.
|
||||
This includes rendering, where IRenderModifier allows you to define an arbitrary transform on
|
||||
the Renderables emitted by the actor's IRender implementation(s). Examples are things like
|
||||
cloaking, invisibility to certain players, flying units with shadows, etc. Other modifiers
|
||||
can affect movement speed, damage taken, weapon firepower, etc.
|
||||
|
||||
Game code is collected into "Mod" units. Mods can be added prior to starting the game. Currently there is no dependancy mechanism, but provided you are doing additions or overrides you can add multiple mods without problem.
|
||||
Game code is collected into "Mod" units. Mods can be added prior to starting the game.
|
||||
Currently there is no dependancy mechanism, but provided you are doing additions or overrides
|
||||
you can add multiple mods without problem.
|
||||
Everything is a mod (including RA - which is loaded by default).
|
||||
|
||||
The contents of the mod is defined in a manifest file mod.yaml. This lists the packages containing art assets (typically .mix files), yaml files defining actor defintions, and ini files containing legacy information that have yet to be ported over to the (relatively new) yaml system.
|
||||
The contents of the mod is defined in a manifest file mod.yaml. This lists the packages
|
||||
containing art assets (typically .mix files), yaml files defining actor defintions,
|
||||
and ini files containing legacy information that have yet to be ported over to
|
||||
the (relatively new) yaml system.
|
||||
|
||||
The unit artwork itself must be defined in a Sequences file (typically Sequences.xml; check mod.yaml for a list of what the mod uses); the format is self explanatory. There is also the SequenceEditor tool to make this easy.
|
||||
The unit artwork itself must be defined in a Sequences file (typically Sequences.xml;
|
||||
check mod.yaml for a list of what the mod uses); the format is self explanatory. There is
|
||||
also the SequenceEditor tool to make this easy.
|
||||
|
||||
Chrome artwork is similarly defined in Chrome.xml. Chrome is already mod dependent. Sortof ;) mod-dependent *behavior* would be nice too, not just skinning. This is a property of the traits however; once we port UI into traits this will become a non-issue.
|
||||
Chrome artwork is similarly defined in Chrome.xml. Chrome is already mod dependent. Sortof ;)
|
||||
mod-dependent *behavior* would be nice too, not just skinning. This is a property of the traits
|
||||
however; once we port UI into traits this will become a non-issue.
|
||||
|
||||
Rendering
|
||||
OpenRa.Game/Chrome.cs is the user interface.
|
||||
Three renderers (SpriteRenderer, LineRenderer, Rgba?Renderer) render most stuff. Don't forget to flush the renderer (if you want to see anything).
|
||||
Three renderers (SpriteRenderer, LineRenderer, Rgba?Renderer) render most stuff. Don't forget
|
||||
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)
|
||||
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 . There's also a few at http://github.com/chrisforbes/OpenRA/issues (which won't be ported over, but no more bugs should be added here).
|
||||
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/ .
|
||||
|
||||
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 . Alli: setup howto for this?
|
||||
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 http://github.com/OpenRA/OpenRA .
|
||||
See http://help.github.com/ for working with GitHub and see http://progit.org/ for working
|
||||
with Git.
|
||||
|
||||
|
||||
|
||||
Other things we probably want to put in here:
|
||||
- A guide on how to add a generic unit via yaml using existing traits
|
||||
@@ -50,6 +89,6 @@ Other things we probably want to put in here:
|
||||
- VFS (OpenRa.FileFormats.FileSystem, Package, Folder classes)
|
||||
- Trait inheritance (and the magicness of ^ActorType)
|
||||
- Removing inherited traits (prepend `-` to the trait name)
|
||||
- Multiple instances of a trait (`@` and all subsequent characters are ignored for the purposes
|
||||
of looking up the trait.
|
||||
- Multiple instances of a trait (`@` and all subsequent characters are ignored for
|
||||
the purposes of looking up the trait.
|
||||
|
||||
|
||||
119
INSTALL
119
INSTALL
@@ -1,44 +1,51 @@
|
||||
Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean,
|
||||
Paul Chote, Alli Witheford.
|
||||
|
||||
This file is part of OpenRA.
|
||||
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.
|
||||
|
||||
OpenRA is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
To run OpenRA, several files are needed from the original game disks.
|
||||
|
||||
OpenRA is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
The required files for the Red Alert mod are:
|
||||
EITHER:
|
||||
* conquer.mix
|
||||
* temperat.mix
|
||||
* interior.mix
|
||||
* snow.mix
|
||||
* sounds.mix
|
||||
* allies.mix
|
||||
* russian.mix
|
||||
OR:
|
||||
* main.mix
|
||||
AND:
|
||||
* redalert.mix
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
|
||||
These need to be copied into the mods/ra/packages/ directory.
|
||||
|
||||
To compile OpenRA, open the OpenRA.sln solution in the main folder,
|
||||
or build it from the command-line with MSBuild.
|
||||
|
||||
To run OpenRA, several files from the original game’s install need
|
||||
to be copied to the root of the project directory. The required files are:
|
||||
The required files for the Command and Conquer mod are:
|
||||
* cclocal.mix
|
||||
* speech.mix
|
||||
* conquer.mix
|
||||
* sounds.mix
|
||||
* tempicnh.mix
|
||||
* temperat.mix
|
||||
* winter.mix
|
||||
* desert.mix
|
||||
|
||||
redalert.mix
|
||||
conquer.mix
|
||||
temperat.mix
|
||||
interior.mix
|
||||
snow.mix
|
||||
sounds.mix
|
||||
allies.mix
|
||||
russian.mix
|
||||
general.mix
|
||||
|
||||
hires1.mix
|
||||
expand2.mix
|
||||
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.
|
||||
|
||||
Red Alert has been released by EA Games as freeware so it shouldn’t
|
||||
be too hard to find a legal copy of the CD images. Unfortunately the
|
||||
installer is 16-bit and so won’t run on 64-bit operating systems. This
|
||||
can be worked around by using the Red Alert Setup Manager
|
||||
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
|
||||
systems. This can be worked around by using the Red Alert Setup Manager
|
||||
(http://ra.afraid.org/html/downloads/utilities-3.html).
|
||||
Make sure you apply the no-CD protection fix so all the files needed
|
||||
are installed to the hard drive.
|
||||
@@ -46,12 +53,50 @@ are installed to the hard drive.
|
||||
Dependencies - Make sure you have these installed, or you'll
|
||||
have very strange errors.
|
||||
|
||||
|
||||
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
|
||||
(http://developer.download.nvidia.com/cg/Cg_2.2/Cg-2.2_October2009_Setup.exe)
|
||||
(http://developer.download.nvidia.com/cg/Cg_2.2/Cg-2.2_October2009_Setup.exe)
|
||||
|
||||
To compile OpenRA, open the OpenRA.sln solution in the main folder,
|
||||
or build it from the command-line with MSBuild.
|
||||
|
||||
Run the game with `OpenRA.Game.exe Game.Mods=ra` for Red Alert
|
||||
or `OpenRA.Game.exe Game.Mods=cnc` for Command & Conquer
|
||||
|
||||
|
||||
UBUNTU (substitute comparable packages for other linux distros):
|
||||
|
||||
* mono-gmcs
|
||||
* freetype
|
||||
* libmono-corlib1.0-cil
|
||||
* libmono-winforms2.0-cil
|
||||
* libopenal1
|
||||
* libsdl1.2-dev
|
||||
* nvidia-cg-toolkit (download the latest version from
|
||||
http://developer.nvidia.com/object/cg_download.html)
|
||||
|
||||
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
|
||||
your GAC with the following script
|
||||
|
||||
#!/bin/sh
|
||||
gacutil -i thirdparty/Tao/Tao.Cg.dll
|
||||
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
|
||||
|
||||
To compile OpenRA, run `make' from the command line.
|
||||
Run the game with `mono OpenRA.Game.exe Game.Mods=ra` for Red Alert
|
||||
or `mono OpenRA.Game.exe Game.Mods=cnc` for Command & Conquer
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean,
|
||||
Paul Chote, Alli Witheford.
|
||||
|
||||
This file is part of OpenRA.
|
||||
|
||||
OpenRA is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OpenRA is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
To compile OpenRA, open the OpenRA.sln solution in the main folder,
|
||||
or build it from the command-line with MSBuild.
|
||||
|
||||
To run OpenRA, several files from the original game’s install need
|
||||
to be copied to the root of the project directory. The required files are:
|
||||
|
||||
redalert.mix
|
||||
conquer.mix
|
||||
temperat.mix
|
||||
interior.mix
|
||||
snow.mix
|
||||
sounds.mix
|
||||
allies.mix
|
||||
russian.mix
|
||||
general.mix
|
||||
|
||||
hires1.mix
|
||||
expand2.mix
|
||||
|
||||
Red Alert has been released by EA Games as freeware so it shouldn’t
|
||||
be too hard to find a legal copy of the CD images. Unfortunately the
|
||||
installer is 16-bit and so won’t run on 64-bit operating systems. This
|
||||
can be worked around by using the Red Alert Setup Manager
|
||||
(http://ra.afraid.org/html/downloads/utilities-3.html).
|
||||
Make sure you apply the no-CD protection fix so all the files needed
|
||||
are installed to the hard drive.
|
||||
|
||||
Dependencies - Make sure you have these installed, or you'll
|
||||
have very strange errors.
|
||||
|
||||
* mono-gmcs
|
||||
* libglfw2
|
||||
* nvidia-cg-toolkit
|
||||
* libmono-*
|
||||
|
||||
OpenRA is incompatible with Compiz, please disable desktop effects when trying
|
||||
to run OpenRA or the game will crash.
|
||||
353
Makefile
353
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
|
||||
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 thirdparty/ICSharpCode.SharpZipLib.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,121 +205,49 @@ 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
|
||||
|
||||
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 seqed
|
||||
|
||||
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
|
||||
@cp -r mods/cnc/bits $(INSTALL_DIR)/mods/cnc
|
||||
@cp -r mods/cnc/rules $(INSTALL_DIR)/mods/cnc
|
||||
@cp -r mods/cnc/sequences $(INSTALL_DIR)/mods/cnc
|
||||
@cp -r mods/cnc/tilesets $(INSTALL_DIR)/mods/cnc
|
||||
@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/extras $(INSTALL_DIR)/mods/ra
|
||||
@cp -r mods/ra/bits $(INSTALL_DIR)/mods/ra
|
||||
@cp -r mods/ra/chrome $(INSTALL_DIR)/mods/ra
|
||||
@cp -r mods/ra/rules $(INSTALL_DIR)/mods/ra
|
||||
@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
|
||||
@echo "cd "$(datadir)"/openra" >> openra
|
||||
@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/packages/ra-packages.php and"
|
||||
@echo "http://open-ra.org/packages/cnc-packages.php"
|
||||
@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."
|
||||
@@ -133,42 +256,4 @@ 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.Form1.resources:
|
||||
resgen2 OpenRA.Editor/Form1.resx OpenRA.Editor.Form1.resources 1> /dev/null
|
||||
editor: 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))))
|
||||
@-rm $(DESTDIR)$(bindir)/openra
|
||||
@@ -18,7 +18,7 @@ namespace OpenRA.Editor
|
||||
{
|
||||
public Bitmap Bitmap;
|
||||
public ActorInfo Info;
|
||||
public bool Centered;
|
||||
public EditorAppearanceInfo Appearance;
|
||||
}
|
||||
|
||||
class BrushTemplate
|
||||
|
||||
244
OpenRA.Editor/Form1.Designer.cs
generated
Normal file → Executable file
244
OpenRA.Editor/Form1.Designer.cs
generated
Normal file → Executable file
@@ -30,8 +30,9 @@
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
|
||||
this.toolStripContainer1 = new System.Windows.Forms.ToolStripContainer();
|
||||
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
|
||||
this.splitContainer2 = new System.Windows.Forms.SplitContainer();
|
||||
this.pmMiniMap = new System.Windows.Forms.PictureBox();
|
||||
this.tabControl1 = new System.Windows.Forms.TabControl();
|
||||
this.tabPage1 = new System.Windows.Forms.TabPage();
|
||||
this.tilePalette = new System.Windows.Forms.FlowLayoutPanel();
|
||||
@@ -50,6 +51,8 @@
|
||||
this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.cCRedAlertMapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.bitmapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuExport = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuMinimapToPNG = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.exotToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
@@ -57,40 +60,33 @@
|
||||
this.resizeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.spawnpointsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.layersFloaterToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.tt = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.folderBrowser = new System.Windows.Forms.FolderBrowserDialog();
|
||||
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.toolStripContainer1.ContentPanel.SuspendLayout();
|
||||
this.toolStripContainer1.TopToolStripPanel.SuspendLayout();
|
||||
this.toolStripContainer1.SuspendLayout();
|
||||
this.splitContainer1.Panel1.SuspendLayout();
|
||||
this.splitContainer1.Panel2.SuspendLayout();
|
||||
this.splitContainer1.SuspendLayout();
|
||||
this.splitContainer2.Panel1.SuspendLayout();
|
||||
this.splitContainer2.Panel2.SuspendLayout();
|
||||
this.splitContainer2.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pmMiniMap)).BeginInit();
|
||||
this.tabControl1.SuspendLayout();
|
||||
this.tabPage1.SuspendLayout();
|
||||
this.tabPage2.SuspendLayout();
|
||||
this.tabPage3.SuspendLayout();
|
||||
this.menuStrip1.SuspendLayout();
|
||||
this.splitContainer3.Panel1.SuspendLayout();
|
||||
this.splitContainer3.Panel2.SuspendLayout();
|
||||
this.splitContainer3.SuspendLayout();
|
||||
this.statusStrip1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// toolStripContainer1
|
||||
//
|
||||
//
|
||||
// toolStripContainer1.ContentPanel
|
||||
//
|
||||
this.toolStripContainer1.ContentPanel.Controls.Add(this.splitContainer1);
|
||||
this.toolStripContainer1.ContentPanel.Size = new System.Drawing.Size(985, 681);
|
||||
this.toolStripContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.toolStripContainer1.Location = new System.Drawing.Point(0, 0);
|
||||
this.toolStripContainer1.Name = "toolStripContainer1";
|
||||
this.toolStripContainer1.Size = new System.Drawing.Size(985, 705);
|
||||
this.toolStripContainer1.TabIndex = 1;
|
||||
this.toolStripContainer1.Text = "toolStripContainer1";
|
||||
//
|
||||
// toolStripContainer1.TopToolStripPanel
|
||||
//
|
||||
this.toolStripContainer1.TopToolStripPanel.Controls.Add(this.menuStrip1);
|
||||
//
|
||||
// splitContainer1
|
||||
//
|
||||
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
@@ -99,15 +95,45 @@
|
||||
//
|
||||
// splitContainer1.Panel1
|
||||
//
|
||||
this.splitContainer1.Panel1.Controls.Add(this.tabControl1);
|
||||
this.splitContainer1.Panel1.Controls.Add(this.splitContainer2);
|
||||
//
|
||||
// splitContainer1.Panel2
|
||||
//
|
||||
this.splitContainer1.Panel2.Controls.Add(this.surface1);
|
||||
this.splitContainer1.Size = new System.Drawing.Size(985, 681);
|
||||
this.splitContainer1.Size = new System.Drawing.Size(985, 744);
|
||||
this.splitContainer1.SplitterDistance = 198;
|
||||
this.splitContainer1.TabIndex = 0;
|
||||
//
|
||||
// splitContainer2
|
||||
//
|
||||
this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.splitContainer2.Location = new System.Drawing.Point(0, 0);
|
||||
this.splitContainer2.Name = "splitContainer2";
|
||||
this.splitContainer2.Orientation = System.Windows.Forms.Orientation.Horizontal;
|
||||
//
|
||||
// splitContainer2.Panel1
|
||||
//
|
||||
this.splitContainer2.Panel1.Controls.Add(this.pmMiniMap);
|
||||
//
|
||||
// splitContainer2.Panel2
|
||||
//
|
||||
this.splitContainer2.Panel2.Controls.Add(this.tabControl1);
|
||||
this.splitContainer2.Size = new System.Drawing.Size(198, 744);
|
||||
this.splitContainer2.SplitterDistance = 164;
|
||||
this.splitContainer2.TabIndex = 1;
|
||||
//
|
||||
// pmMiniMap
|
||||
//
|
||||
this.pmMiniMap.BackColor = System.Drawing.Color.Black;
|
||||
this.pmMiniMap.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
|
||||
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, 164);
|
||||
this.pmMiniMap.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
this.pmMiniMap.TabIndex = 1;
|
||||
this.pmMiniMap.TabStop = false;
|
||||
//
|
||||
// tabControl1
|
||||
//
|
||||
this.tabControl1.Controls.Add(this.tabPage1);
|
||||
@@ -115,19 +141,21 @@
|
||||
this.tabControl1.Controls.Add(this.tabPage3);
|
||||
this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tabControl1.Location = new System.Drawing.Point(0, 0);
|
||||
this.tabControl1.Margin = new System.Windows.Forms.Padding(3, 3, 3, 0);
|
||||
this.tabControl1.Multiline = true;
|
||||
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, 681);
|
||||
this.tabControl1.Size = new System.Drawing.Size(198, 576);
|
||||
this.tabControl1.TabIndex = 0;
|
||||
//
|
||||
// tabPage1
|
||||
//
|
||||
this.tabPage1.Controls.Add(this.tilePalette);
|
||||
this.tabPage1.Location = new System.Drawing.Point(4, 22);
|
||||
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, 655);
|
||||
this.tabPage1.Size = new System.Drawing.Size(190, 552);
|
||||
this.tabPage1.TabIndex = 0;
|
||||
this.tabPage1.Text = "Templates";
|
||||
this.tabPage1.UseVisualStyleBackColor = true;
|
||||
@@ -139,16 +167,16 @@
|
||||
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, 649);
|
||||
this.tilePalette.Size = new System.Drawing.Size(184, 546);
|
||||
this.tilePalette.TabIndex = 1;
|
||||
//
|
||||
// tabPage2
|
||||
//
|
||||
this.tabPage2.Controls.Add(this.actorPalette);
|
||||
this.tabPage2.Location = new System.Drawing.Point(4, 22);
|
||||
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, 655);
|
||||
this.tabPage2.Size = new System.Drawing.Size(190, 552);
|
||||
this.tabPage2.TabIndex = 1;
|
||||
this.tabPage2.Text = "Actors";
|
||||
this.tabPage2.UseVisualStyleBackColor = true;
|
||||
@@ -160,15 +188,15 @@
|
||||
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, 649);
|
||||
this.actorPalette.Size = new System.Drawing.Size(184, 546);
|
||||
this.actorPalette.TabIndex = 2;
|
||||
//
|
||||
// tabPage3
|
||||
//
|
||||
this.tabPage3.Controls.Add(this.resourcePalette);
|
||||
this.tabPage3.Location = new System.Drawing.Point(4, 22);
|
||||
this.tabPage3.Location = new System.Drawing.Point(4, 20);
|
||||
this.tabPage3.Name = "tabPage3";
|
||||
this.tabPage3.Size = new System.Drawing.Size(190, 655);
|
||||
this.tabPage3.Size = new System.Drawing.Size(190, 552);
|
||||
this.tabPage3.TabIndex = 2;
|
||||
this.tabPage3.Text = "Resources";
|
||||
this.tabPage3.UseVisualStyleBackColor = true;
|
||||
@@ -180,15 +208,15 @@
|
||||
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, 655);
|
||||
this.resourcePalette.Size = new System.Drawing.Size(190, 552);
|
||||
this.resourcePalette.TabIndex = 3;
|
||||
//
|
||||
// menuStrip1
|
||||
//
|
||||
this.menuStrip1.Dock = System.Windows.Forms.DockStyle.None;
|
||||
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.fileToolStripMenuItem,
|
||||
this.mapToolStripMenuItem});
|
||||
this.mapToolStripMenuItem,
|
||||
this.toolsToolStripMenuItem});
|
||||
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
|
||||
this.menuStrip1.Name = "menuStrip1";
|
||||
this.menuStrip1.Size = new System.Drawing.Size(985, 24);
|
||||
@@ -205,6 +233,7 @@
|
||||
this.saveAsToolStripMenuItem,
|
||||
this.toolStripSeparator2,
|
||||
this.toolStripMenuItem1,
|
||||
this.mnuExport,
|
||||
this.toolStripSeparator3,
|
||||
this.exotToolStripMenuItem});
|
||||
this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
|
||||
@@ -216,20 +245,20 @@
|
||||
this.newToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("newToolStripMenuItem.Image")));
|
||||
this.newToolStripMenuItem.ImageTransparentColor = System.Drawing.Color.Fuchsia;
|
||||
this.newToolStripMenuItem.Name = "newToolStripMenuItem";
|
||||
this.newToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||
this.newToolStripMenuItem.Size = new System.Drawing.Size(123, 22);
|
||||
this.newToolStripMenuItem.Text = "&New...";
|
||||
this.newToolStripMenuItem.Click += new System.EventHandler(this.NewClicked);
|
||||
//
|
||||
// toolStripSeparator1
|
||||
//
|
||||
this.toolStripSeparator1.Name = "toolStripSeparator1";
|
||||
this.toolStripSeparator1.Size = new System.Drawing.Size(149, 6);
|
||||
this.toolStripSeparator1.Size = new System.Drawing.Size(120, 6);
|
||||
//
|
||||
// openToolStripMenuItem
|
||||
//
|
||||
this.openToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("openToolStripMenuItem.Image")));
|
||||
this.openToolStripMenuItem.Name = "openToolStripMenuItem";
|
||||
this.openToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||
this.openToolStripMenuItem.Size = new System.Drawing.Size(123, 22);
|
||||
this.openToolStripMenuItem.Text = "&Open...";
|
||||
this.openToolStripMenuItem.Click += new System.EventHandler(this.OpenClicked);
|
||||
//
|
||||
@@ -237,21 +266,21 @@
|
||||
//
|
||||
this.saveToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("saveToolStripMenuItem.Image")));
|
||||
this.saveToolStripMenuItem.Name = "saveToolStripMenuItem";
|
||||
this.saveToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||
this.saveToolStripMenuItem.Size = new System.Drawing.Size(123, 22);
|
||||
this.saveToolStripMenuItem.Text = "&Save";
|
||||
this.saveToolStripMenuItem.Click += new System.EventHandler(this.SaveClicked);
|
||||
//
|
||||
// saveAsToolStripMenuItem
|
||||
//
|
||||
this.saveAsToolStripMenuItem.Name = "saveAsToolStripMenuItem";
|
||||
this.saveAsToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||
this.saveAsToolStripMenuItem.Size = new System.Drawing.Size(123, 22);
|
||||
this.saveAsToolStripMenuItem.Text = "Save &As...";
|
||||
this.saveAsToolStripMenuItem.Click += new System.EventHandler(this.SaveAsClicked);
|
||||
//
|
||||
// toolStripSeparator2
|
||||
//
|
||||
this.toolStripSeparator2.Name = "toolStripSeparator2";
|
||||
this.toolStripSeparator2.Size = new System.Drawing.Size(149, 6);
|
||||
this.toolStripSeparator2.Size = new System.Drawing.Size(120, 6);
|
||||
//
|
||||
// toolStripMenuItem1
|
||||
//
|
||||
@@ -259,7 +288,7 @@
|
||||
this.cCRedAlertMapToolStripMenuItem,
|
||||
this.bitmapToolStripMenuItem});
|
||||
this.toolStripMenuItem1.Name = "toolStripMenuItem1";
|
||||
this.toolStripMenuItem1.Size = new System.Drawing.Size(152, 22);
|
||||
this.toolStripMenuItem1.Size = new System.Drawing.Size(123, 22);
|
||||
this.toolStripMenuItem1.Text = "&Import";
|
||||
//
|
||||
// cCRedAlertMapToolStripMenuItem
|
||||
@@ -276,16 +305,33 @@
|
||||
this.bitmapToolStripMenuItem.Name = "bitmapToolStripMenuItem";
|
||||
this.bitmapToolStripMenuItem.Size = new System.Drawing.Size(195, 22);
|
||||
this.bitmapToolStripMenuItem.Text = "&Bitmap...";
|
||||
this.bitmapToolStripMenuItem.Visible = false;
|
||||
//
|
||||
// mnuExport
|
||||
//
|
||||
this.mnuExport.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.mnuMinimapToPNG});
|
||||
this.mnuExport.Name = "mnuExport";
|
||||
this.mnuExport.Size = new System.Drawing.Size(123, 22);
|
||||
this.mnuExport.Text = "&Export";
|
||||
//
|
||||
// mnuMinimapToPNG
|
||||
//
|
||||
this.mnuMinimapToPNG.Image = ((System.Drawing.Image)(resources.GetObject("mnuMinimapToPNG.Image")));
|
||||
this.mnuMinimapToPNG.Name = "mnuMinimapToPNG";
|
||||
this.mnuMinimapToPNG.Size = new System.Drawing.Size(163, 22);
|
||||
this.mnuMinimapToPNG.Text = "Minimap to PNG";
|
||||
this.mnuMinimapToPNG.Click += new System.EventHandler(this.mnuMinimapToPNG_Click);
|
||||
//
|
||||
// toolStripSeparator3
|
||||
//
|
||||
this.toolStripSeparator3.Name = "toolStripSeparator3";
|
||||
this.toolStripSeparator3.Size = new System.Drawing.Size(149, 6);
|
||||
this.toolStripSeparator3.Size = new System.Drawing.Size(120, 6);
|
||||
//
|
||||
// exotToolStripMenuItem
|
||||
//
|
||||
this.exotToolStripMenuItem.Name = "exotToolStripMenuItem";
|
||||
this.exotToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||
this.exotToolStripMenuItem.Size = new System.Drawing.Size(123, 22);
|
||||
this.exotToolStripMenuItem.Text = "E&xit";
|
||||
this.exotToolStripMenuItem.Click += new System.EventHandler(this.CloseClicked);
|
||||
//
|
||||
@@ -329,17 +375,83 @@
|
||||
this.spawnpointsToolStripMenuItem.Text = "&Spawnpoints";
|
||||
this.spawnpointsToolStripMenuItem.Click += new System.EventHandler(this.SpawnPointsClicked);
|
||||
//
|
||||
// toolsToolStripMenuItem
|
||||
//
|
||||
this.toolsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.layersFloaterToolStripMenuItem});
|
||||
this.toolsToolStripMenuItem.Name = "toolsToolStripMenuItem";
|
||||
this.toolsToolStripMenuItem.Size = new System.Drawing.Size(48, 20);
|
||||
this.toolsToolStripMenuItem.Text = "Tools";
|
||||
this.toolsToolStripMenuItem.Visible = false;
|
||||
//
|
||||
// layersFloaterToolStripMenuItem
|
||||
//
|
||||
this.layersFloaterToolStripMenuItem.Name = "layersFloaterToolStripMenuItem";
|
||||
this.layersFloaterToolStripMenuItem.Size = new System.Drawing.Size(144, 22);
|
||||
this.layersFloaterToolStripMenuItem.Text = "Layers floater";
|
||||
this.layersFloaterToolStripMenuItem.Click += new System.EventHandler(this.layersFloaterToolStripMenuItem_Click);
|
||||
//
|
||||
// tt
|
||||
//
|
||||
this.tt.ShowAlways = true;
|
||||
//
|
||||
// saveFileDialog
|
||||
//
|
||||
this.saveFileDialog.DefaultExt = "*.png";
|
||||
this.saveFileDialog.Filter = "PNG Image (*.png)|";
|
||||
this.saveFileDialog.Title = "Export minimap to PNG";
|
||||
//
|
||||
// splitContainer3
|
||||
//
|
||||
this.splitContainer3.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.splitContainer3.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
|
||||
this.splitContainer3.IsSplitterFixed = true;
|
||||
this.splitContainer3.Location = new System.Drawing.Point(0, 0);
|
||||
this.splitContainer3.Name = "splitContainer3";
|
||||
this.splitContainer3.Orientation = System.Windows.Forms.Orientation.Horizontal;
|
||||
//
|
||||
// splitContainer3.Panel1
|
||||
//
|
||||
this.splitContainer3.Panel1.Controls.Add(this.menuStrip1);
|
||||
//
|
||||
// splitContainer3.Panel2
|
||||
//
|
||||
this.splitContainer3.Panel2.Controls.Add(this.splitContainer1);
|
||||
this.splitContainer3.Size = new System.Drawing.Size(985, 773);
|
||||
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;
|
||||
this.surface1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.surface1.Location = new System.Drawing.Point(0, 0);
|
||||
this.surface1.Name = "surface1";
|
||||
this.surface1.Size = new System.Drawing.Size(783, 681);
|
||||
this.surface1.Size = new System.Drawing.Size(783, 744);
|
||||
this.surface1.TabIndex = 5;
|
||||
this.surface1.Text = "surface1";
|
||||
//
|
||||
@@ -347,36 +459,44 @@
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(985, 705);
|
||||
this.Controls.Add(this.toolStripContainer1);
|
||||
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;
|
||||
this.MainMenuStrip = this.menuStrip1;
|
||||
this.Name = "Form1";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "OpenRA Editor";
|
||||
this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyUp);
|
||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.OnFormClosing);
|
||||
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
|
||||
this.toolStripContainer1.ContentPanel.ResumeLayout(false);
|
||||
this.toolStripContainer1.TopToolStripPanel.ResumeLayout(false);
|
||||
this.toolStripContainer1.TopToolStripPanel.PerformLayout();
|
||||
this.toolStripContainer1.ResumeLayout(false);
|
||||
this.toolStripContainer1.PerformLayout();
|
||||
this.splitContainer1.Panel1.ResumeLayout(false);
|
||||
this.splitContainer1.Panel2.ResumeLayout(false);
|
||||
this.splitContainer1.ResumeLayout(false);
|
||||
this.splitContainer2.Panel1.ResumeLayout(false);
|
||||
this.splitContainer2.Panel2.ResumeLayout(false);
|
||||
this.splitContainer2.ResumeLayout(false);
|
||||
((System.ComponentModel.ISupportInitialize)(this.pmMiniMap)).EndInit();
|
||||
this.tabControl1.ResumeLayout(false);
|
||||
this.tabPage1.ResumeLayout(false);
|
||||
this.tabPage2.ResumeLayout(false);
|
||||
this.tabPage3.ResumeLayout(false);
|
||||
this.menuStrip1.ResumeLayout(false);
|
||||
this.menuStrip1.PerformLayout();
|
||||
this.splitContainer3.Panel1.ResumeLayout(false);
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.ToolStripContainer toolStripContainer1;
|
||||
private System.Windows.Forms.SplitContainer splitContainer1;
|
||||
private System.Windows.Forms.ToolTip tt;
|
||||
private System.Windows.Forms.TabControl tabControl1;
|
||||
@@ -385,8 +505,7 @@
|
||||
private System.Windows.Forms.TabPage tabPage2;
|
||||
private System.Windows.Forms.FlowLayoutPanel actorPalette;
|
||||
private System.Windows.Forms.TabPage tabPage3;
|
||||
private System.Windows.Forms.FlowLayoutPanel resourcePalette;
|
||||
private System.Windows.Forms.FolderBrowserDialog folderBrowser;
|
||||
private System.Windows.Forms.FlowLayoutPanel resourcePalette;
|
||||
private Surface surface1;
|
||||
private System.Windows.Forms.MenuStrip menuStrip1;
|
||||
private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
|
||||
@@ -406,6 +525,17 @@
|
||||
private System.Windows.Forms.ToolStripMenuItem resizeToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator4;
|
||||
private System.Windows.Forms.ToolStripMenuItem spawnpointsToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem toolsToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem layersFloaterToolStripMenuItem;
|
||||
private System.Windows.Forms.PictureBox pmMiniMap;
|
||||
private System.Windows.Forms.SplitContainer splitContainer2;
|
||||
private System.Windows.Forms.ToolStripMenuItem mnuExport;
|
||||
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;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
271
OpenRA.Editor/Form1.cs
Normal file → Executable file
271
OpenRA.Editor/Form1.cs
Normal file → Executable file
@@ -11,12 +11,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Editor
|
||||
@@ -32,18 +31,21 @@ namespace OpenRA.Editor
|
||||
|
||||
Text = "OpenRA Editor (mod:{0})".F(currentMod);
|
||||
|
||||
Game.modData = new ModData( currentMod );
|
||||
Game.modData = new ModData(currentMod);
|
||||
|
||||
Rules.LoadRules(Game.modData.Manifest, new Map());
|
||||
|
||||
folderBrowser.SelectedPath = new string[] { Environment.CurrentDirectory, "mods", currentMod, "maps" }
|
||||
.Aggregate(Path.Combine);
|
||||
surface1.AfterChange += OnMapChanged;
|
||||
surface1.MousePositionChanged += s => toolStripStatusLabelMousePosition.Text = s;
|
||||
}
|
||||
|
||||
surface1.AfterChange += MakeDirty;
|
||||
void OnMapChanged()
|
||||
{
|
||||
MakeDirty();
|
||||
pmMiniMap.Image = Minimap.AddStaticResources(surface1.Map, Minimap.TerrainBitmap(surface1.Map, true));
|
||||
}
|
||||
|
||||
void MakeDirty() { dirty = true; }
|
||||
|
||||
string loadedMapName;
|
||||
string currentMod = "ra";
|
||||
TileSet tileset;
|
||||
@@ -57,10 +59,10 @@ namespace OpenRA.Editor
|
||||
|
||||
loadedMapName = mapname;
|
||||
|
||||
Game.modData = new ModData( currentMod );
|
||||
Game.modData = new ModData(currentMod);
|
||||
|
||||
// load the map
|
||||
var map = new Map(new Folder(mapname));
|
||||
var map = new Map(new Folder(mapname, 0));
|
||||
|
||||
// upgrade maps that have no player definitions. editor doesnt care,
|
||||
// but this breaks the game pretty badly.
|
||||
@@ -81,31 +83,31 @@ namespace OpenRA.Editor
|
||||
|
||||
loadedMapName = null;
|
||||
|
||||
Game.modData = new ModData( currentMod );
|
||||
Game.modData = new ModData(currentMod);
|
||||
|
||||
PrepareMapResources(Game.modData.Manifest, map);
|
||||
|
||||
MakeDirty();
|
||||
}
|
||||
|
||||
// this code is insanely stupid, and mostly my fault -- chrisf
|
||||
void PrepareMapResources(Manifest manifest, Map map)
|
||||
{
|
||||
Rules.LoadRules(manifest, map);
|
||||
tileset = Rules.TileSets[map.Theater];
|
||||
Rules.LoadRules(manifest, map);
|
||||
tileset = Rules.TileSets[map.Tileset];
|
||||
tileset.LoadTiles();
|
||||
var palette = new Palette(FileSystem.Open(tileset.Palette), true);
|
||||
|
||||
surface1.Bind(map, tileset, palette);
|
||||
|
||||
surface1.Bind(map, tileset, palette);
|
||||
// construct the palette of tiles
|
||||
var palettes = new[] { tilePalette, actorPalette, resourcePalette };
|
||||
foreach (var p in palettes) { p.Visible = false; p.SuspendLayout(); }
|
||||
|
||||
foreach (var t in tileset.Templates)
|
||||
{
|
||||
try
|
||||
{
|
||||
var bitmap = RenderTemplate(tileset, (ushort)t.Key, palette);
|
||||
var bitmap = RenderUtils.RenderTemplate(tileset, (ushort)t.Key, palette);
|
||||
var ibox = new PictureBox
|
||||
{
|
||||
Image = bitmap,
|
||||
@@ -119,7 +121,6 @@ namespace OpenRA.Editor
|
||||
|
||||
var template = t.Value;
|
||||
tilePalette.Controls.Add(ibox);
|
||||
|
||||
tt.SetToolTip(ibox,
|
||||
"{1}:{0} ({2}x{3})".F(
|
||||
template.Image,
|
||||
@@ -137,16 +138,18 @@ namespace OpenRA.Editor
|
||||
try
|
||||
{
|
||||
var info = Rules.Info[a];
|
||||
if( !info.Traits.Contains<RenderSimpleInfo>() ) continue;
|
||||
var template = RenderActor(info, tileset, palette);
|
||||
if (!info.Traits.Contains<RenderSimpleInfo>()) continue;
|
||||
var template = RenderUtils.RenderActor(info, tileset, palette);
|
||||
var ibox = new PictureBox
|
||||
{
|
||||
Image = template.Bitmap,
|
||||
Width = template.Bitmap.Width / 2,
|
||||
Height = template.Bitmap.Height / 2,
|
||||
SizeMode = PictureBoxSizeMode.StretchImage
|
||||
Width = 32,
|
||||
Height = 32,
|
||||
SizeMode = PictureBoxSizeMode.Zoom,
|
||||
BorderStyle = BorderStyle.FixedSingle
|
||||
};
|
||||
|
||||
|
||||
ibox.Click += (_, e) => surface1.SetActor(template);
|
||||
|
||||
actorPalette.Controls.Add(ibox);
|
||||
@@ -168,15 +171,18 @@ namespace OpenRA.Editor
|
||||
{
|
||||
try
|
||||
{
|
||||
var template = RenderResourceType(a, tileset.Extensions, palette);
|
||||
var template = RenderUtils.RenderResourceType(a, tileset.Extensions, palette);
|
||||
var ibox = new PictureBox
|
||||
{
|
||||
Image = template.Bitmap,
|
||||
Width = template.Bitmap.Width,
|
||||
Height = template.Bitmap.Height,
|
||||
SizeMode = PictureBoxSizeMode.StretchImage
|
||||
Width = 32,
|
||||
Height = 32,
|
||||
SizeMode = PictureBoxSizeMode.Zoom,
|
||||
BorderStyle = BorderStyle.FixedSingle
|
||||
};
|
||||
|
||||
|
||||
|
||||
ibox.Click += (_, e) => surface1.SetResource(template);
|
||||
|
||||
resourcePalette.Controls.Add(ibox);
|
||||
@@ -193,103 +199,12 @@ namespace OpenRA.Editor
|
||||
|
||||
surface1.BindResourceTemplates(resourceTemplates);
|
||||
|
||||
foreach (var p in palettes) { p.Visible = true; p.ResumeLayout(); }
|
||||
}
|
||||
|
||||
static Bitmap RenderTemplate(TileSet ts, ushort n, Palette p)
|
||||
{
|
||||
var template = ts.Templates[n];
|
||||
var tile = ts.Tiles[n];
|
||||
|
||||
var bitmap = new Bitmap(24 * template.Size.X, 24 * template.Size.Y);
|
||||
var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
|
||||
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||
|
||||
unsafe
|
||||
foreach (var p in palettes)
|
||||
{
|
||||
int* q = (int*)data.Scan0.ToPointer();
|
||||
var stride = data.Stride >> 2;
|
||||
|
||||
for (var u = 0; u < template.Size.X; u++)
|
||||
for (var v = 0; v < template.Size.Y; v++)
|
||||
if (tile.TileBitmapBytes[u + v * template.Size.X] != null)
|
||||
{
|
||||
var rawImage = tile.TileBitmapBytes[u + v * template.Size.X];
|
||||
for (var i = 0; i < 24; i++)
|
||||
for (var j = 0; j < 24; j++)
|
||||
q[(v * 24 + j) * stride + u * 24 + i] = p.GetColor(rawImage[i + 24 * j]).ToArgb();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < 24; i++)
|
||||
for (var j = 0; j < 24; j++)
|
||||
q[(v * 24 + j) * stride + u * 24 + i] = Color.Transparent.ToArgb();
|
||||
}
|
||||
}
|
||||
|
||||
bitmap.UnlockBits(data);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
static ActorTemplate RenderActor(ActorInfo info, TileSet tileset, Palette p)
|
||||
{
|
||||
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)
|
||||
image = ri.OverrideImage[i];
|
||||
|
||||
image = image ?? ri.Image ?? info.Name;
|
||||
using (var s = FileSystem.OpenWithExts(image, tileset.Extensions))
|
||||
{
|
||||
var shp = new ShpReader(s);
|
||||
var frame = shp[0];
|
||||
|
||||
var bitmap = new Bitmap(shp.Width, shp.Height);
|
||||
var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
|
||||
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||
|
||||
unsafe
|
||||
{
|
||||
int* q = (int*)data.Scan0.ToPointer();
|
||||
var stride = data.Stride >> 2;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
bitmap.UnlockBits(data);
|
||||
return new ActorTemplate { Bitmap = bitmap, Info = info, Centered = !info.Traits.Contains<BuildingInfo>() };
|
||||
}
|
||||
}
|
||||
|
||||
static ResourceTemplate RenderResourceType(ResourceTypeInfo info, string[] exts, Palette p)
|
||||
{
|
||||
var image = info.SpriteNames[0];
|
||||
using (var s = FileSystem.OpenWithExts(image, exts))
|
||||
{
|
||||
var shp = new ShpReader(s);
|
||||
var frame = shp[shp.ImageCount - 1];
|
||||
|
||||
var bitmap = new Bitmap(shp.Width, shp.Height);
|
||||
var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
|
||||
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||
|
||||
unsafe
|
||||
{
|
||||
int* q = (int*)data.Scan0.ToPointer();
|
||||
var stride = data.Stride >> 2;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
bitmap.UnlockBits(data);
|
||||
return new ResourceTemplate { Bitmap = bitmap, Info = info, Value = shp.ImageCount - 1 };
|
||||
p.Visible = true;
|
||||
p.ResumeLayout();
|
||||
}
|
||||
pmMiniMap.Image = Minimap.AddStaticResources(surface1.Map, Minimap.TerrainBitmap(surface1.Map, true));
|
||||
}
|
||||
|
||||
void ResizeClicked(object sender, EventArgs e)
|
||||
@@ -298,16 +213,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)
|
||||
{
|
||||
@@ -318,7 +235,7 @@ namespace OpenRA.Editor
|
||||
surface1.Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SaveClicked(object sender, EventArgs e)
|
||||
{
|
||||
if (loadedMapName == null)
|
||||
@@ -326,30 +243,63 @@ namespace OpenRA.Editor
|
||||
else
|
||||
{
|
||||
surface1.Map.PlayerCount = surface1.Map.Waypoints.Count;
|
||||
surface1.Map.Package = new Folder(loadedMapName);
|
||||
surface1.Map.Save(loadedMapName);
|
||||
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SaveAsClicked(object sender, EventArgs e)
|
||||
{
|
||||
folderBrowser.ShowNewFolderButton = true;
|
||||
|
||||
if (DialogResult.OK == folderBrowser.ShowDialog())
|
||||
using (var nms = new MapSelect())
|
||||
{
|
||||
loadedMapName = folderBrowser.SelectedPath;
|
||||
SaveClicked(sender, e);
|
||||
nms.MapFolderPath = new string[] { Environment.CurrentDirectory, "mods", currentMod, "maps" }
|
||||
.Aggregate(Path.Combine);
|
||||
|
||||
nms.txtNew.ReadOnly = false;
|
||||
nms.btnOk.Text = "Save";
|
||||
nms.txtNew.Text = "unnamed";
|
||||
nms.txtPathOut.ReadOnly = false;
|
||||
|
||||
if (DialogResult.OK == nms.ShowDialog())
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
SaveClicked(sender, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenClicked(object sender, EventArgs e)
|
||||
{
|
||||
folderBrowser.ShowNewFolderButton = true;
|
||||
using (var nms = new MapSelect())
|
||||
{
|
||||
nms.MapFolderPath = new string[] { Environment.CurrentDirectory, "mods", currentMod, "maps" }
|
||||
.Aggregate(Path.Combine);
|
||||
|
||||
if (DialogResult.OK == folderBrowser.ShowDialog())
|
||||
LoadMap(folderBrowser.SelectedPath);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NewClicked(object sender, EventArgs e)
|
||||
@@ -362,13 +312,12 @@ namespace OpenRA.Editor
|
||||
|
||||
if (DialogResult.OK == nmd.ShowDialog())
|
||||
{
|
||||
var map = new Map();
|
||||
var map = new Map(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.Tileset = nmd.theater.SelectedItem as string;
|
||||
map.Players.Add("Neutral", new PlayerReference("Neutral", Rules.Info["world"].Traits.WithInterface<CountryInfo>().First().Race, true, true));
|
||||
NewMap(map);
|
||||
}
|
||||
@@ -405,9 +354,11 @@ namespace OpenRA.Editor
|
||||
|
||||
void ImportLegacyMapClicked(object sender, EventArgs e)
|
||||
{
|
||||
var currentDirectory = Directory.GetCurrentDirectory();
|
||||
using (var ofd = new OpenFileDialog { Filter = "Legacy maps (*.ini;*.mpr)|*.ini;*.mpr" })
|
||||
if (DialogResult.OK == ofd.ShowDialog())
|
||||
{
|
||||
Directory.SetCurrentDirectory( currentDirectory );
|
||||
/* massive hack: we should be able to call NewMap() with the imported Map object,
|
||||
* but something's not right internally in it, unless loaded via the real maploader */
|
||||
|
||||
@@ -415,10 +366,9 @@ namespace OpenRA.Editor
|
||||
Directory.CreateDirectory(savePath);
|
||||
|
||||
var map = LegacyMapImporter.Import(ofd.FileName);
|
||||
map.Package = new Folder(savePath);
|
||||
map.Players.Add("Neutral", new PlayerReference("Neutral",
|
||||
map.Players.Add("Neutral", new PlayerReference("Neutral",
|
||||
Rules.Info["world"].Traits.WithInterface<CountryInfo>().First().Race, true, true));
|
||||
|
||||
|
||||
map.Save(savePath);
|
||||
LoadMap(savePath);
|
||||
loadedMapName = null; /* editor needs to think this hasnt been saved */
|
||||
@@ -432,7 +382,7 @@ namespace OpenRA.Editor
|
||||
{
|
||||
if (!dirty) return;
|
||||
|
||||
switch (MessageBox.Show("The map has been modified since it was last saved. Save changes now?",
|
||||
switch (MessageBox.Show("The map has been modified since it was last saved. " + "\r\n" + "Save changes now?",
|
||||
"Unsaved Changes", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation))
|
||||
{
|
||||
case DialogResult.Yes: SaveClicked(null, EventArgs.Empty); break;
|
||||
@@ -440,5 +390,38 @@ namespace OpenRA.Editor
|
||||
case DialogResult.Cancel: e.Cancel = true; break;
|
||||
}
|
||||
}
|
||||
|
||||
private void layersFloaterToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
var pb = new PaletteBox();
|
||||
pb.Show();
|
||||
}
|
||||
|
||||
private void mnuMinimapToPNG_Click(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
saveFileDialog.InitialDirectory = new string[] { Environment.CurrentDirectory, "maps" }
|
||||
.Aggregate(Path.Combine);
|
||||
|
||||
FileInfo file = new FileInfo(loadedMapName + ".png");
|
||||
string name = file.Name;
|
||||
saveFileDialog.FileName = name;
|
||||
|
||||
saveFileDialog.ShowDialog();
|
||||
if (saveFileDialog.FileName == "")
|
||||
{
|
||||
saveFileDialog.FileName = name;
|
||||
}
|
||||
else
|
||||
{
|
||||
Bitmap png = new Bitmap(pmMiniMap.Image);
|
||||
|
||||
png.Save(saveFileDialog.FileName, System.Drawing.Imaging.ImageFormat.Png);
|
||||
}
|
||||
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
266
OpenRA.Editor/Form1.resx
Normal file → Executable file
266
OpenRA.Editor/Form1.resx
Normal file → Executable file
@@ -118,7 +118,7 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>309, 17</value>
|
||||
<value>198, 17</value>
|
||||
</metadata>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="newToolStripMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
@@ -237,6 +237,23 @@
|
||||
z2ki+Wo1CWklROkMCiT8wEm0kXEsCTmrAiTbDtcEpTVdZOS1oDfWk5xZ6RPeQZeUR8zxK0Qe1BO65xjr
|
||||
t0YotXhjAEAeQ7It8ZSESUjkznIq2bYsTGYW29JZxIs2nFEdLOSdfwFwpvLxRKIY2AAAAABJRU5ErkJg
|
||||
gg==
|
||||
</value>
|
||||
</data>
|
||||
<data name="mnuMinimapToPNG.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
|
||||
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsMAAALDAE/QCLIAAACR0lE
|
||||
QVQ4T6WTXUiTARSGdxkRRBdB0IUREV1EKRFEYYuQAklL8sKQfphUoJSUWJbLxAVZWqyBio5WajorM1PL
|
||||
xExUlLQfbU5n0lygRerMP/qxOZ92vpjfCKLAi3P5Puc957xHA2gWUoo49147gWUqbSPrdjOXChrQm56S
|
||||
lFVFvKEcXaqV2OQiohMt7Esw+6Ro5gGeWfhbff0BY9NzDI3N0f/JS6fLy7rd6f8HcNsdOI6EYD8cjK3V
|
||||
roiLawdYu+vivwHSWcQM1THz/DQv96+ixeHlVvU71oQFAGTmP+37bdsPBf8WZ4fRFhVEXdcsBQ9srN6Z
|
||||
pjqQhQUCAmcW29JZxA8fvaWyY5YcawdBOy6oANm2H9A3MMypDLNS7b1flJnFtnQWcVmrB2NhMytD9SpA
|
||||
TiUA5+CYInQOjWPrH2V7RBzmijdo9x5VKr+yj6ImD9nmelZsS1UBcme/+OPIJIOfv/Gi201ZXQ/hMfFU
|
||||
Peuktqnbd3QN+U/cXM6tYfmW8ypAQiKdh91TTE7PYHNOYKl6z916F3m+eSNjT1B4vxHjzWoFYrhRwbLN
|
||||
51SAJOynx8vo5Ay9rimKHzu5XtJDpuU1WZY2Eg1WgrXRJOpNxKdcUyBLN6WoAInnyPh3XvW6KW/4MC9O
|
||||
z2ki+Wo1CWklROkMCiT8wEm0kXEsCTmrAiTbDtcEpTVdZOS1oDfWk5xZ6RPeQZeUR8zxK0Qe1BO65xjr
|
||||
t0YotXhjAEAeQ7It8ZSESUjkznIq2bYsTGYW29JZxIs2nFEdLOSdfwFwpvLxRKIY2AAAAABJRU5ErkJg
|
||||
gg==
|
||||
</value>
|
||||
</data>
|
||||
<data name="propertiesToolStripMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
@@ -291,7 +308,250 @@
|
||||
<metadata name="tt.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="folderBrowser.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>182, 17</value>
|
||||
<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>
|
||||
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
AAABAAIAMDAAAAEAIACoJQAAJgAAACAgAAABACAAqBAAAM4lAAAoAAAAMAAAAGAAAAABACAAAAAAAAAk
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAQAAAAQAAAAJQAA
|
||||
ADcAAAA+AAAAOAAAACoAAAAYAAAACwAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAUAAAApAAAAVAAAAGIBAQFNAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAASAAAAOgAA
|
||||
AFkAAAA6AAAAMQEBAUYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA
|
||||
AAoAAAAkAAAAUQAAAHkAAACJAAAAhQAAAHMAAABTAAAAMAAAABUAAAAGAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAIAAAAkAAAAaQEBAZYCAgLaAAAAWgAAAAsAAAABAAAAAAAAAAAAAAAAAAAABwAA
|
||||
AB8AAABMAAAAfwAAAHkAAAA8AgIAwgEBAWkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAQAAAA8AAAA9AAAAgwAAALMAAADFHRcU3QAAAMMAAACgAAAAdwAAAEUAAAAeAAAABwAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASAAAAUgAAAJgDBATpAgIG/wEAAI8AAAAYAAAABAAA
|
||||
AAIAAAAOAAAALgAAAGIAAACVAAAApgEAAJUEBADsAwQK/wAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAABYAAABPAAAAngAAAMx3YFX3jXZv/5RuY/9mT0X2GxENygcG
|
||||
BYsAAABKAAAAGgAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAKAAAAQAAAAJMBAQHRBApN/wQK
|
||||
U/8CAADQAQEARwAAACkAAABIAAAAeQAAAKMAAAC5AgAA1QQGJf8FC1v/AQEHrAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAABwAAACEAAABcAAAApQICAtOFc2z8hnJt/5h6
|
||||
cf+hfXL/mHty/3VkXewZExGLAAAAOQAAAAwAAAABAAAAAQAAAAIAAAAFAAAACAAAABAAAAAqAAAAYwAA
|
||||
AKABAAC8AwAV+QUWxv8ED4P/BgcX/AEAALAAAACWAAAAsQAAAMkEBArzBAli/wMOtf8CCVD/AwIAbAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAEAAAADQAAAB1AAAAsgcE
|
||||
BNmHcGj8mYiB/6GMhf+lkIj/moiB/5qIgP9dTUfqAgICYAAAACAAAAAGAAAABAAAAAoAAAAZAAAAKgAA
|
||||
AEEAAABqAQEBmgEBAbkHCw7dBgcH+QEAi/8FHf//AgG0/wIABP8GFRntBiIw6wcYHf8BAET/AgR6/wMF
|
||||
X/8DACD5AAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAFAAAAGwAA
|
||||
AE8AAACXAAAAxQwLCuGSfHX9momC/5yLg/+cjIX/mYaA/5aGf/+MdWv/GhUSoQAAAEAAAAAXAAAAEAAA
|
||||
AB8AAAA+AAAAZgAAAIwAAACtCQkJx0A5N+UYdqT/JsXu/wgdZ/8AAIr/Cyx0/x6Rwf8luf3/Jbv//yCd
|
||||
3P8TYIj/FWaB/xdtjf8EDhfkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAEAAAAIAAAAKQAAAG4AAACxBwYD1HdiWPmcioL/mYiA/56Nhf+ejoX/lYV//5WGf/+Wgnv/U0M96AAA
|
||||
AHUAAAA4AAAALwAAAEYAAABzAAAApAAAAMIkGRHhIBkT6F1WVfYqPUb/KM3//yOt5f8NN0b/J8r+/ybE
|
||||
+P8ZfqT/G4ay/x6Tz/8hpej/IKTl/xuLwf8FExmmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAIAAAANAAAANwAAAIQAAAC+IhoW5Jt2av+Qfnf/no6G/5WHgP+vnpb/koJ7/5iG
|
||||
f/+Zh4D/k4B6/kU4M8UAAABvAAAAagAAAIcAAACsAAAAzAMDAt+FW0z8fV5R/VtQS/2PgHn/UUhH/yKV
|
||||
tf8z////I62//wcXX/8CBMz/AABw/w9HXv8muv3/Iq30/xh0p/8BAABfAAAABAAAAAIAAAABAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAYAAAAWAAAASAAAAJQAAADESzkz86CCd/+mlI3/rJyT/4Z5
|
||||
cf+djIX/m4qD/5CAef9xZF7/jHx1/3BiWe0GBQSzAAAAsAAAAMQBAQDWCgUE5U83KfaCa2D/lH11/3hp
|
||||
Y/+QgHn/noyF/xQODf8SU4X/CBuY/wUK//8BAOn/Cypv/yfC5v8luvf/IJvN/yKx+f8MKzrlAAAAIgAA
|
||||
ABAAAAAHAAAAAwAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAwAAAAqAAAAZgAAAKgAAADMVEdA+LKg
|
||||
mP+pmZH/rp+U/4Z3b/9+cGr/joB5/4R3cf9zZ1//dmhi/4p5cv8fHRjlAAAA3AYEBOQgGhfzV0U4/pqA
|
||||
df+wnpX/no6F/4V1bv9yZF7/ERIX/wUNzf8AAJ//AgK+/wAA8v8LJlr/K9bh/y/w//8NO0r/EEh+/ynZ
|
||||
//8SVHj/AAAAywAAAEgAAAAnAAAAEgAAAAQAAAABAAAAAAAAAAAAAAABAAAABAAAABcAAABIAAAAjwAA
|
||||
AMAAAADZZlxV+rSkmv+4qZ//oZGJ/419dv+QgHn/eGlh/35waP+Nf3b/emxl/5KCe/9kVEr+GxQO+Ug9
|
||||
OP2Mf3j+loZ+/52LhP+qmpH/oZGI/15STf8ICTL/CB30/wUTzv8il4j/HYac/wgcRf8qz93/NP///xZg
|
||||
av8AAJb/D0W8/y3h/f8TWZ7/AQBY/wYEAPABAQCJAAAAQwAAAB0AAAAIAAAAAAAAAAAAAAABAAAACQAA
|
||||
ACgAAABoAAAArQAAANCCaV74pJSN/8i4rf+omY//koF6/4t7dP9+b2j/koB5/5uJgv+UhHz/iXlx/3Bh
|
||||
XP9lW1f/UEM8/4F0bf+jk4r/m4qC/52Nhf+gj4f/OjIs/wgUf/8KKv//AgDw/xlhU/87////L+b//y3g
|
||||
8P8w7v//F2N//wAAqP8BAP//E1SY/y7n//8PPn//AADr/wYWv/8GCCv/AQMEuQAAAEkAAAAWAAAAAAAA
|
||||
AAAAAAAEAAAAEQAAADwAAACCAAAAvCUbGOKhjoT+sKCW/7ionf+Yh4D/gHBp/3hoYP+KeHH/kYF4/5+P
|
||||
h/+Xhn//h3hx/3hoYf+XhoD/cF5V/5WEff+ejYX/nYyF/21hWv8LCQz/ChmV/wop//8IJPr/Ch/z/wcS
|
||||
of8eiZD/Nv///zP///8jrKD/AABe/wYZ//8CAJ3/H4+m/yrT/v8IGoz/BAzS/wUWy/8GFKX/BQYm/wUG
|
||||
BOUBAQFBAAAAAAAAAAEAAAAHAAAAHgAAAFUAAACbAAAAyUc/Ou6ejYX/pJOL/6WUjP+ejoX/iXpz/4t7
|
||||
cv+Xh37/mId//5+Ohv+pmI//koJ5/5GBeP+cjIP/dGNb/4t7dP+bioP/iHx2/zEtKf8hHyT/KipE/yYk
|
||||
Jv8lIiX/ISEp/xkSPP8REA7/Gnh5/zb//f86//D/IqGV/wAAlf8NMYr/Nv///xhkgf8eKzT/KDg//xca
|
||||
Hf8lLDP/ERUd/wYHCu0BAQFxAAAAAAAAAAEAAAAMAAAAMAAAAHQAAAC0AwMD1WleWfidjYT/pZSL/5yK
|
||||
g/+OfHX/hnly/5CBef+Pfnb/m4uA/6aTiP+di4P/moqC/5mJgf+VhX3/lIN7/5eFfv+gjof/nIqE/5iH
|
||||
gP+djYT/kIF4/5CAeP+Le3P/hnZv/4x9df+UhX3/SD04/xA6bf8XW6z/EESi/wYMPv8y+f//JKu7/1uA
|
||||
kv+Nx+P/kcvn/1d4jv9KVmr/LD9N/xIfKacAAAAUAAAAAAAAAAIAAAAQAAAARAAAAJMAAADGKSYj5KWV
|
||||
jP+woJb/pZSM/4V0b/+MfXb/nYyE/5iJgf+UhHv/pI+G/6GRh/+Qf3j/lYR9/6CRiv+ZiID/mYeA/5mI
|
||||
gf+ci4P/n46G/5iIf/+KenP/gXBo/31tZf+GdG3/iHdv/4d1b/+LhYP/WGRm/wYASP8AAP//BAmf/zHr
|
||||
4f8jrK3/WH6O/3y71P+JxeH/jcjj/4292f9FWWj+JC86+QkPE3EAAAANAAAAAAAAAAIAAAATAAAAUAAA
|
||||
AKIAAADNh3p0/K2dk/+snZL/no2F/4N0bf+RgXr/jXx1/5yLg/+YiID/mYiA/5ODfP+NfHX/mYiB/56M
|
||||
hf+Yh4D/nY2E/5CBeP+Sgnr/loZ+/5qJgf+nl47/nY2E/5SEe/+PfXb/e3Rx/1Zocv9fg5L/Pml+/x4s
|
||||
Pf8OKmv/Ip6E/xhrdf9kjqD/i8zm/4HC3P+Fwdz/iL7X/32xyv8xPkn/Hiw28wUIClcAAAAMAAAAAAAA
|
||||
AAIAAAARAAAATQAAAKEAAADWqZqR/6STi/+ikon/b2Nd/3xqZP9wYlr/fm5n/4p5cv+OfXb/i3t0/4V1
|
||||
bv+MfHX/loR9/5qJgf+WhX3/jn53/4h3b/+Id2//mYmA/6OSiv+hkYn/qJiP/5uNhP9zdnj/T2h6/0uO
|
||||
rf9UlbP/Z7LP/052hv8RMD3/CSPV/woMIf+GxOD/gsbf/3+/3P+Dvdf/erDJ/3epwv9jla//HTxM6wAD
|
||||
BEQAAAAFAAAAAAAAAAMAAAAQAAAASgAAAJ4AAADQopOK/56Mhf+gj4f/kX95/4NzbP+DdG3/fGxl/35u
|
||||
Z/96a2P/dGZf/4R0bf+Nfnb/i3pz/5B/d/+TgXj/nY2E/5+Ohv+di4T/o5KK/6CPiP+fjof/oJGJ/2Vw
|
||||
dv8/X3T/Y6XF/1WiyP9cosb/drzY/3e51P8JD3b/Ch/e/z1WXv9/wNr/gL3W/4C92v+GxN3/baK9/4G8
|
||||
1P9/wuH/ETJIlwAAABQAAAACAAAAAQAAAAYAAAAXAAAAUAAAAKIAAADMjoJ5/6iYj/+qmpH/oI+H/5GB
|
||||
ev+SgXr/lYN8/5GBev+RgXn/koF6/41+dv+Le3L/jXdt/4x6c/+XhX3/mYeA/5iHgP+ejYX/opKJ/5qK
|
||||
gf+NfnX/a3By/zhhdv9orsv/YKvP/0uZv/9krc3/c7zZ/3u72P8sP1P/Cg8t/2qbrv9zrsf/e7TN/4W/
|
||||
2P+FwNv/erbR/3Oiwv9pqsf/AwkOUgAAAAgAAAABAAAAAwAAAA0AAAAoAAAAZQAAAKwAAADQm4yE/7Sl
|
||||
mv+klIv/oI+H/5iGgP+Le3T/lIN8/5ODfP+Ug33/lIJ7/5iHf/+ch3//kX54/3JjXf+Ugnz/loV+/5WE
|
||||
ff+ai4L/jHx0/4NzbP+HhYP/R2p//1iny/+Iyeb/O3Wo/0+fwv9vvNv/eL/c/3m72P9LcH7/DAoA/4jG
|
||||
3v93uNH/fL3X/4zD2/9/udX/f8De/4y+1/87cJDxAQIDLgAAAAcAAAAAAAAABwAAABwAAABKAAAAiQAA
|
||||
AL4AAADap5iP/66elf+hkIj/oI+I/5SDfP+Qf3j/koB5/5KAef+aioL/lYR8/56Ohf+ci4T/mIZ//4Ry
|
||||
bP+RgXr/momC/458df97bmX/gGRU/4l2bP9Qh6b/XqrO/2Oozf9hp8v/Ya3O/2q72v94vdz/gcbi/3y9
|
||||
2/9nnbD/U3OB/4zN5/+Bw9z/gL7a/32zyf92sMn/iMfg/3251f8pUWjIAQEBIQAAAAIAAAAAAAAADQAA
|
||||
ADQAAAB1AAAAsAAAAM8pIyDsrJuS/56Nhf+ejIX/k4J7/4Z1bv+Pfnf/kYF6/4d1b/+cjIP/mouD/5qI
|
||||
gf+UhH3/i3pz/4t5cv+XhX7/koB5/2teVf94fH//dK/I/2e42P9RkL7/YrDR/1qoyf9eqsv/WqbJ/3bB
|
||||
3v97w9//fcTh/32+3P+DxN//g8Db/4TJ4/9/v93/gbzX/3Kmv/9qqMD/i7/b/1N/p/8ZLjp9AgQEFgAA
|
||||
AAAAAAAAAAAAGAAAAEwAAACWAAAAxickIemAcWj/loV+/4t5cv9/bmf/fW1m/3BjW/96aGL/bmBZ/31t
|
||||
Zv+Ne3X/koF6/4x8df+FdW7/i3t0/5SCe/+Qf3j/gnFq/2trav9rtdT/bLTU/2KjyP9tt9f/X6XI/2Ow
|
||||
zf9Zp8r/Z7DQ/3m/3v+Axd//fL3b/3/B3v+IzOb/h8nk/4rL5P+ExeP/hMXe/2qeuf92scb/oc7q/0yC
|
||||
pv8OHihoAQEBDQAAAAAAAAAAAAAAJwAAAGMAAACqBwYG03FjXvqTgnv/bl9Y/3dnYP91Z2D/d2dg/3Bg
|
||||
W/98bGX/gnNs/3trZP+BcGj/fGxk/3NlXv+EdG3/lIV9/6COh/+NfHX/ZF5a/0p5nP9ajar/WJO9/3K+
|
||||
2v9/vdz/YKPG/2muzv9cqcn/ZbHR/3K72P9+wN3/hcXi/4fI5P+Jy+T/f8Dc/4fF3/+NzOX/iMbf/3m3
|
||||
0f9nlrT/b5uz/yhVb+YFCw5KAAEBCQAAAAAAAAAAAAAAQAAAAIMAAAC7CAUF21BEPv5uYFj/hndw/4l6
|
||||
cv+KenT/momB/5KBev+RgXr/k4F6/5mJgv+Tgnr/iHdw/4p5c/+ThH3/m4yF/6CPiP+CdXH/TnKL/0tv
|
||||
k/8hITb/M0Zh/2OjyP90u9j/abHO/2etzf9cqMn/Z7HR/3e92f95u9j/eb7b/5PV8P+Mzeb/fsPd/3/D
|
||||
3f+Szeb/h8Lf/3/A3v+Mu9T/RnGF/hEiMcwAAABAAAAABwAAAAAAAAAAAAAAXgAAAKQAAADMKCUj6ZmH
|
||||
gP+ejIX/lIN8/5iHf/+YiH//mYeA/5OCe/+UhH3/hXRu/4x6dP+SgHn/mYeA/5qJgf+bioP/loV+/4h3
|
||||
cf9bXl//bLrc/3S/4P9LdJj/KiNI/z1ehf9mr9L/d77b/1uoy/9Vo8b/bLDN/3q/3P97vdr/hMrj/5LQ
|
||||
7P+Oz+v/iMzm/4TF3/+Gwdz/hMbh/4rO6P+Oxd7/PGyF/hocHNUCAwNeAAABEQAAAAEAAAAAAAAAdQAA
|
||||
ALUAAADekIB7/pODev+ejob/mIeA/6iXj/+jkor/mIZ//5KAef+RgHn/jn12/5qIgf+cioP/no2F/6OR
|
||||
iv+hkYj/iHdw/2JVTf9NfJX/YqrR/3O31/93weH/VoKj/2+y0f9ppM3/ebvb/1Kfxf9nsdD/YKPG/3G3
|
||||
1v96vNn/gsTg/4rL5/+FyuT/gsTh/4jK5v+Fw93/d7/b/5zT7/97r8//Y6vF/zVZaNwRGx5dAAABEgAA
|
||||
AAIAAAAAAAAAiQAAAL8jHxzoq5yR/56Nhf+hkIj/rJyT/6mZkP+qmpH/m4uD/5KBev+WhX3/kH55/5WD
|
||||
fP+VhH3/oo+J/6iXj/+cjIP/c2Ja/3xwaP9WncX/abLS/3O62f9qtNT/d8Hd/3vD4P+MxuP/Y6bM/1up
|
||||
yv9qtdT/ZK3M/3jA3f95wN//hMrl/4rM5/+Ky+X/hcbk/4rO6P98u9v/hMni/6zZ9f9km77/K1V37gkQ
|
||||
FGoBAQEHAAAAAAAAAAAAAAAAAAAAngAAAMlZUEzxoI+H/6uckv+omI//oZCJ/6STi/+ikIr/nImD/5aE
|
||||
ff+aiIH/j313/4x7dP+UhH3/m4mC/6SUi/+Ugnv/cVxO/3ymuv9KibL/dLnZ/3rB3v9fpsj/b73b/3G5
|
||||
1v99x+P/ZLLQ/02Xwf9rtNj/fMXh/3O52P91vt3/eb7b/37A3f+HyOL/js/p/4/Q6v+Dx+T/erPX/5XL
|
||||
5/9Jl7v+GjJBrgEBAQoAAAAAAAAAAAAAAAAAAAAAAAAApw0NDdaIeHD9kX95/6qbkf+unpX/j395/459
|
||||
dv+pmJD/n46H/56Ohf+gkIj/kH95/4x8dv+JeXL/kIB4/5WFfP9yZmH/b36H/2aqzv9nr8//esLe/3m/
|
||||
3v9rtdT/arbW/2iz0P9rvdr/UqbG/2q41P9gqs3/crjX/2Spzf+AyeT/f8Hd/3m+2/+Cx+H/k9Ht/4rH
|
||||
5P+Fx+T/mc7p/2amxP8nSF36AAAAawAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqBUSEN2Ug3r9lYV8/6OT
|
||||
iv+klIv/l4V+/4p6dP+omI//p5aO/56Ohf+WhX3/j394/3hoZP9uXFb/emtk/3NnXv9ydXn/bLXV/2Sp
|
||||
zv92vdz/fcbh/3zE4f9xu9v/Z67Q/16kx/9QnMD/Xq/O/2Wy0f94wN3/c7jV/3q83P9encj/gsrj/33C
|
||||
3v+CxN//jMjk/4bI4/+Lz+n/lM/q/0d+nf8sLiv3BQYHTwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArg8P
|
||||
DdqSgX38qZiQ/6WVjP+ci4P/lYR9/419d/+KeHH/opGJ/5WFfP91YlX/hGpX/2ZkY/9HUlj/OFVn/1eQ
|
||||
tf9fpcj/Y6jP/3W+3f96wt7/drrX/36/3f9+x+L/bbjV/2241f9hsc7/ZrTS/2u41P92udX/h83o/4fI
|
||||
5f97wd3/dr3a/3K72/+DxuL/hsfj/3jA3P+c0+//fLPT/2avyf5HfZLyHzI3SwAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAux0aGuGWhH39rp6V/6qbkf+Yh3//koB6/5B/ef+Rf3j/fWtk/4huXf+jrK//d7jX/2Ow
|
||||
1v9kstb/Up3I/2Wlyv9krdD/Y6/S/2y52f9ut9b/er3Z/4PG4v99wt7/f8Xh/3jE4P91wd7/bbrW/3W9
|
||||
2v99w97/hMnl/4jL5/+BxuD/i8/p/3m+3v+Izef/fbzc/4XK4/+s2fX/ZZ7C/y1dhOwTJzK9FxobTQAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAxzo0Muqbi4T/saKY/7Cgl/+fjYb/kIB6/49+d/+Bcmn/fGxg/3u7
|
||||
1P9sqc3/Z6vP/2Kny/9vuNf/abfW/3O72P95w9//cLjV/3C+2v9wuNX/gsTh/33A3/9wudb/db7b/2i1
|
||||
0v9uutf/cLzY/4HG4v+P0er/icvk/4XH4v+Ozej/itDr/43S7P+O0Ov/hcnm/3q02P+TyeX+SZu//yNH
|
||||
XKEHBwcnCgkITAAAAAEAAAAAAAAAAAAAAAAAAAABCwsNtZKEfPicjIX/sqKY/66elP+hj4n/opGJ/5GC
|
||||
ef+OfnP/icbj/1qdw/9ZmL//f8Xh/3fA3f91wd3/eb7d/4PL5f+EyuP/fsbg/3e+3f93vNv/gMHe/3u9
|
||||
3P9wt9T/dbnW/2m21f9rtNP/cbnW/3693f9dh7T/Ypa8/4bH4f9zp8f/gsLf/47M6P+R0On/jtDs/5jP
|
||||
6v9mp8X/GTJFrgAAACYCAgISAgMDEwYGBxAAAAAAAAAAAAAAAAAAAAABExMTgJCGffWdj4f/t6ee/6iX
|
||||
jv+tnJT/pZWM/5KBc/+WtML/ZqbL/2Gkyv9xvt3/fMXh/3rB3v93vdz/g8nl/37E4P90vdn/ecDd/3nA
|
||||
3f97vdz/d7jV/3a71/96wd7/fr/d/3G41f9zuNX/hcfk/1yMtP9Fa5n/LUd8/2yhxP9RcJj/cqPG/4nC
|
||||
3/+MyuP/hc3n/5PO6v9Hf5//AwUFYgAAAB8AAAAHBAMDCAsLDBoEBAQHAAAAAAAAAAAAAAAAAAAADwcH
|
||||
BzMPDg1MPTk2ro6EevKbjYT/o5SK/6GKev95vNn/aqrO/3O61/99xOP/esLe/37E3/96xd//e8Pf/3i9
|
||||
2v9qstL/a7bT/3W+2v9wtNH/dLnV/2+20/9yvdf/eLnW/36+2/+Mzur/i9Pt/16cxP85XpP/Q1mD/2CV
|
||||
vf9roMP/jMfi/4nF4f+DyOL/hMnm/2yfwv5lvNn/TImj2wAAABMAAAADAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAABAAAABgAAABYAAAA4FBIQlIKCgux4qsn3dKvM/H/A3f+Ky+j/i8nl/4HG
|
||||
4v94vdz/gsLf/4TI4v90u9j/dbva/3W51v9xuNf/bbXS/3O82f99vtz/ervZ/4jK5f+T1fL/iMvn/3m8
|
||||
3v98vt3/TnWg/1qQvP96ttf/h8jk/1aErf9qs9P/n9Tv/2ufv/8lUoL+ECQ0jwAAAAwAAAABAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAEAAAACgAAAB4OEBFRHiktejZN
|
||||
WKdagZTVaY2i4GiRpPF+ssv/kdPv/5DV8f+Fy+f/hMPf/4bI5P9/xuP/ebzb/3m92v94vNn/fMHd/4/Q
|
||||
6v+W1PD/l9jy/4fF4f9HcqH/aqHI/3Ot0f9Mf6v/h8Xi/3y93f8lTIL/iL/f/3Cx1P8mTG3QAAAAHwAA
|
||||
AAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAABAAAAAwAAAAcDBAQYBQYHJQYICTsNEhVlJTM8w1Buf/9Zeo7/i87q/4PE4P+SzOn/icbj/3e4
|
||||
1f97wt//isbi/5PR7f+b2PX/ldPp/4S72v9hmcL/ZqHI/1mMuv+Gw+D/aaPK/1CLuf9hibj/ic3n/kh4
|
||||
qf8OGCV2AAAADQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAFAAAADgAAADIAAAByJzlD0Xmz
|
||||
zvx/vNn/gb/e/3W20/+Exd//k8vn/5nU7/+V0Oz/mNTw/5/a8v9xrtL/UIW0/1WBsP80YJn/h8Pi/4fN
|
||||
6P9nmsb/SYqz/1+WsvIHCw4+AAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAQAAAAPAAAAHxAVGmwxSVajUHqN00Jkdsg8WWW2NlFewXOftfSc1PD/kc7r/4O72/9zrtH/bpzE/2OL
|
||||
u/9Zi73/ib3d/2WSvf9oiLf/TYCr/z9phdIDBQcsAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAMAAAAHAwQFFAEBARUAAAAUAAAAJQYHCFUyQkquUnmO7m+i
|
||||
yf9oosb/cKPC/1N4rP9ZgKz/isPg/2SXwv9Yibf/OnGf/xkoSawAAAAbAAAAAQAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAA
|
||||
AAcAAAAPAAAAGwUGB2UGBgiAIC40vHGbv/pnm8H+jcfg/1yJvf+Bt9f/T4Op+Q8bInUAAAANAAAAAQAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAALAAAAGgkLD1UfLDWMITVCrT1ced93pMf/c6bE/BMd
|
||||
IVUAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAIAAAAFgID
|
||||
AysUHSFgHC02gQAAAAoAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AB/gfgP
|
||||
AAD8AD+AcA8AAPwAH8AADwAA/AAPgAAfAAD4AAAAAB8AAPgAAAAAHwAA8AAAAAA/AADwAAAAAD8AAPAA
|
||||
AAAABwAA4AAAAAABAADgAAAAAAAAAMAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAIAAAAAAAAAAgAAAAAAA
|
||||
AACAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAA
|
||||
AAAAAwAAAAAAAAADAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAABAAAAAAAAAAEAAAAAAAAABwAAAAAAAAAP
|
||||
AAAAAAAAAB8AAAAAAAAAHwAAAAAAAAAfAAAAAAAAAB8AAAAAAAAADgAAAAAAAAAOAAAAAAAAAAcAAAAA
|
||||
AAAAPwAAwAAAAAA/AADwAAAAAH8AAP8AAAAA/wAA//gAAAD/AAD//wAAAP8AAP//wAAA/wAA///+AAD/
|
||||
AAD////wAf8AAP////wB/wAAKAAAACAAAABAAAAAAQAgAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAABAAAAEQAAADsAAABeAAAAXwAAAEIAAAAeAAAACAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAFQAAAF8CAgKIAAAAMAAAAAMAAAAAAAAAAAAAAAQAAAAhAAAAXQAAAFMBAQB6AQEBLAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAmAAAAgwAAALwdFxTdAAAAsgAAAHcAAAAyAAAABwAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAJAAAAUgICAsECAgb/AQAAVAAAAAQAAAAIAAAALgAAAHwAAACmAgIAwQME
|
||||
Cv8AAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAABgAAADkAAACiQTYw5Yp0bv+ObWP9WkVA5SYg
|
||||
HJQAAAAqAAAABQAAAAEAAAACAAAABAAAABIAAABSAQEBsAQFMPwEC2f0BAQLogEAAG4AAACVAQECxgMF
|
||||
MeoEC2L/AwIEjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAQAAAAVQAAALJIOzbrmYiB/6OO
|
||||
h/+aiIH/fGtk9QICAmAAAAATAAAABAAAABIAAAAqAAAAVgEBAZoFBgjLBgcH+QMPxf8CAbT/BAsO9gYi
|
||||
MOsEDDH/AgR6/wMDP/wAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAACIAAACCAwICzWxd
|
||||
V/aaiYH/nY2F/5eGgP+UgXn/Ny0nxQAAAEIAAAAgAAAARgAAAIUJBgS4FBEO2DdQXvcnyff/DkGI/xl7
|
||||
uf8ho9f/IKHZ/x2Ox/8bhbP/DkZf4wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAANwAA
|
||||
AKEiGhbklnpx/56Ohv+ik4v/koJ7/5mHgP+TgHr+Ih0amgAAAGoAAACaAAAAzEQvJ+59XlH9dmlj/lFI
|
||||
R/8rytr/I62//wUOlv8AAHD/G4Gu/yKt9P8MOlSvAAAABAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAgAA
|
||||
ABUAAABXAAAAs09BOvaplIz/rZ6U/4p8dP+VhX7/f3Fq/4FybP9IQDrhAAAAxgoJB90wJh3yh29k/ZmG
|
||||
ff+AcWr/WE9O/wscgP8FD6v/BQzN/xuAqP8iqsr/GHKm/xqDq/kAAAB3AAAAIgAAAAsAAAACAAAAAAAA
|
||||
AAEAAAAEAAAAMAAAAI8AAADNZlxV+rannf+hkYn/j394/3hpYf+GeG//emxl/3trY/8bFA75a15Y/paG
|
||||
fv+kk4v/oZGI/zMuQP8IHfT/FFWr/x2GnP8ZdpH/NP///wswgP8PRbz/IJ3O/wEAWP8EAwC9AAAAQwAA
|
||||
ABMAAAAAAAAAAgAAAA0AAABUAAAAtVNGP+qqmpL/sKGX/4l5cv+Dc2z/koF5/5qJgf+IeXH/emtm/2BR
|
||||
Sf+Wh3//nIuE/25iXf8iJmH/CSPe/wYQ8v8ff6H/M/P//y3f5P8MMm//AwfR/xlyn/8chcL/Agbf/wYS
|
||||
l/8DBBXcAgICYgAAAAAAAAAEAAAAHgAAAHgAAADJcmZg96STi/+ikYn/iXpz/5GBeP+Yh3//pJOL/5KC
|
||||
ef+Xh37/dGNb/5ODfP+IfHb/KSYn/yoqRP8mIyb/ISEp/xURJf8aeHn/OP/3/yKhlf8HGZD/Nv///xtI
|
||||
W/8oOD//HiMo/xEVHf8DBQWvAAAAAAAAAAgAAAA6AAAAoRcVEt2XiH/+pZSM/49+eP+Sg3v/k4R7/6CN
|
||||
g/+ejIP/mIeA/5qKgv+XhX7/nIqD/56Mhf+Whn7/iXlw/4h4cP+Hd2//jX95/1BRT/8MJpn/Cieh/yOo
|
||||
s/8+laX/fLLL/4/K5f9deY//KTZD/AcMEE8AAAAAAAAACwAAAFAAAAC4h3p0/K2dk/+ejYX/int0/418
|
||||
df+aioL/mYiA/5CAef+ZiIH/m4qD/52NhP+Rgnn/loZ+/6GQiP+djYT/koF5/3t0cf9bdoL/Pml+/xYr
|
||||
VP8inoT/Pn2L/4vM5v+Dwtz/iL7X/1d4iv8eLDbzAwQFMgAAAAAAAAAKAAAATAAAALqml47/oZGI/4Bx
|
||||
a/99bWb/fW1m/4R0bf+AcWr/iXly/5F/eP+VhHz/loZ+/5OCev+ejoX/oZGJ/6SVjP9tdXr/WYeg/1Wa
|
||||
vP9vt9T/OFyE/woh2v9Teo//gcLb/4LA2/90qcP/d6/K/xc2S8EAAQEYAAAAAQAAAA8AAABQAAAAt46C
|
||||
ef+pmZD/oI+H/5KBev+Vg3z/kYF6/5KBev+MfXT/jXdt/5KAeP+Zh4D/m4qD/6KSif+UhHv/a3By/1CI
|
||||
of9gq8//WKPG/3O82f9UfZb/Cg8t/2+lu/97tM3/hcDa/3q20f9upsX/AwkOUgAAAAUAAAAFAAAAJwAA
|
||||
AHcAAADFoZKK/6qakf+gj4j/koF6/5OCe/+VhH7/lYN8/5yKgv+Vgnz/h3dw/5iHgP+Pf3f/hnBk/3l+
|
||||
gf9Tiqf/abDT/06Ru/9otdX/fcPf/2qiuf8wP0H/hMTd/36+2f+AuNH/hMTf/1uPqu4BAgIoAAAAAwAA
|
||||
AA0AAABVAAAAsBUREd6sm5L/no2F/5OCe/+LenP/kYF6/5KBef+ai4P/l4Z//4t6c/+Rf3j/koB5/3Jt
|
||||
av90r8j/XKTL/2Kw0f9cqcr/WqbJ/3nC3/99xOH/gMHe/4PA2/+CxOD/gbzX/26nwP+Lv9v/NlZxvgIE
|
||||
BBYAAAAAAAAAIAAAAHwDAgLNal9Z+YJya/9+bmf/empj/3ZmYP94amP/gnFq/4d3b/+Dc2z/kIB5/5WD
|
||||
fP9zaGL/X4qh/2Kkyf9xttb/YKTH/2GszP9msdH/e8Dd/4HB3/+GyeP/g8Xg/4nJ4/+Gxt//cKjC/4i1
|
||||
z/8iQFOmAQEBCwAAAAAAAABAAAAAnwgFBdtfUkv/hndw/4p6c/+aiYH/koF6/5OBev+Whn7/iHdw/49/
|
||||
eP+bjIX/kYJ9/05yi/82SGX/M0Zh/2yv0P9psc7/YqvL/2ex0f94vNn/eb7b/5DR6/9+w93/icji/4fC
|
||||
3/+Gvtn/RnGF/gkRGYYAAAAHAAAAAAAAAGoAAADBXVJO9JuKgv+WhX7/n4+G/5mHgP+Tgnv/inly/5WD
|
||||
ff+cioP/n46G/49+d/9lam3/Z7LX/2urzP9AU3b/X5m+/3i92/9bp8r/ZqrK/3m82v+Dx+L/jM7p/4XI
|
||||
5P+GxeD/fsPe/4zG4f9QjKb/GCUqnAAAARIAAAABAAAAiREQD9SrnJH/oI+H/6yck/+qmpH/m4uD/5SD
|
||||
fP+Qfnn/lYR9/6KPif+ikon/c2Ja/2mHl/9pstL/b7fX/3fB3f+ExeL/Y6bM/2Ovz/9krcz/ecDe/4TK
|
||||
5f+KzOb/hcbk/4PF4v+EyeL/iLra/ytVd+4FCQs5AAAAAAAAAAAAAACjPDYz5JmHgP+rnJL/mIiB/6CP
|
||||
h/+ejIX/nIuD/5B+eP+Ofnf/loV9/5CBef9wbWv/ZaPD/3e+3P9wt9b/bbrZ/3G82f9brMv/YazP/3e/
|
||||
3P9zu9r/fMDc/4HE3/+R0Ov/icrm/4rB4P9blLH+DRohjQEBAQMAAAAAAAAAAAAAAKhVSkbtlYV8/6SU
|
||||
i/+XhX7/mYmC/6eWjv+aioH/j394/3NiXf96a2T/c25s/2y11f9ts9X/fcbh/3fA3v9nrtD/V6DE/16v
|
||||
zv9vudf/c7jV/2yt0v+CyuP/gMPf/4zI5P+JzOb/lM/q/zlWY/sFBgdPAAAAAAAAAAAAAAAAAAAAtVZM
|
||||
SO2sm5P/oZGI/5SCfP+PfXf/kH53/46BeP9+kZf/Xoaa/0V5mP9gosb/Y6zR/3O82/94vNj/f8Tg/3a/
|
||||
2/9vvNn/arfU/3W92f+Gy+f/g8fj/4HG4v9+xOH/gsLg/5LO6f9xqcv/PG2F5xsmKUwAAAAAAAAAAAAA
|
||||
AAAAAADHa19a9bGimP+ol4//kIB6/4h4cP98bGD/dLLR/2erz/9psNH/abfW/3a/3P9wuNX/cLvY/4LE
|
||||
4f93vdv/db7b/2u41f9wvNj/iMzm/4nL5P+KyuX/itDr/47R7P+Fyeb/h7/f/0mbv/8VJzJkCgkITAAA
|
||||
AAEAAAAAAAAAARAQEJuXioL7taWb/6qZkP+kk4v/ko6J/3i21/9ipsv/fsXh/3jA3f9+xOH/f8bh/3zD
|
||||
3/95vt3/fL3a/3e82v96vNr/brfV/3vA3f9gj7j/SG+c/26gwf96s9P/jsrl/4rP6v92sc//DhwliAEB
|
||||
ARgDAwMOBgYGDQAAAAAAAAABAAAADwsLCkA9OTaulImA+aOUiv+No6r/aqrO/3i/3f96wt7/fMXf/3vD
|
||||
3/9xuNb/a7bT/3O51v90udX/cbrV/3i51v+FxuP/i9Pt/0x9rP9DWYP/ZpvA/4zH4v+Gx+L/hMnm/2mu
|
||||
zv9MiaPbAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAMAAAAVCgkIT0NPWJVJan27Zpas33mq
|
||||
xPB4ssz8isvn/4TJ5f99v93/e8De/3O51/95vdv/e77b/5HR7f+Q0u3/ca3Q/1yLtP9lncT/h8fj/1mR
|
||||
uP+Uyuf/Snuh9AgSGlcAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA
|
||||
AAMCAgIQBQYHJQoND1AlMzzDVXSH/4vO6v+LyOX/icbj/3m92v+KxuL/l9Xx/5XT6f9zqs7/ZqHI/3Co
|
||||
zf9po8r/WYq5/4nN5/4rSGe7AAAADQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAcAAAAuFB0ieE9zhsNonLbpXo+k32SNo+CQxeD9ldHu/4K9
|
||||
3P9fkbz/Un6x/4jA4P9vocn/S4Wv/ypEU4wAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAABQMEBRQBAQEVAAAAJRwl
|
||||
KoJSeY7ubKLI/3Cjwv9WfKz/isPg/16Qvf86cZ//DRQlZAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAEAAAABAAAABgAAAA4DAwQ+ERcaa0BccbdWfpDWZY+092GVtvsJDxE3AAAAAQAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAABQAAABYLEBJGHC02gQAAAAYAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAADwD4MD8AeAA+AAAAfgAAAH4AAAD+AAAAPAAAAAgAAAAIAAAACAAAAAgAAAAIAA
|
||||
AACAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAAAAAAAAwAAAAMAAAAHAAAABwAAAAIAAAACAAAAD4AA
|
||||
AA/4AAAf/4AAH//wAB///gAf///wPw==
|
||||
</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -17,6 +17,7 @@ using OpenRA;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
|
||||
namespace OpenRA.Editor
|
||||
{
|
||||
@@ -401,7 +402,7 @@ namespace OpenRA.Editor
|
||||
{
|
||||
new LocationInit(new int2(loc % MapSize, loc / MapSize)),
|
||||
new OwnerInit(parts[0]),
|
||||
new HealthInit(float.Parse(parts[2])/256),
|
||||
new HealthInit(float.Parse(parts[2], NumberFormatInfo.InvariantInfo)/256),
|
||||
new FacingInit((section == "INFANTRY") ? int.Parse(parts[6]) : int.Parse(parts[4])),
|
||||
new ActorStanceInit(stance),
|
||||
};
|
||||
|
||||
375
OpenRA.Editor/MapSelect.Designer.cs
generated
Normal file
375
OpenRA.Editor/MapSelect.Designer.cs
generated
Normal file
@@ -0,0 +1,375 @@
|
||||
namespace OpenRA.Editor
|
||||
{
|
||||
partial class MapSelect
|
||||
{
|
||||
/// <summary>
|
||||
/// TODO
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// TODO
|
||||
/// </summary>
|
||||
/// <param name="disposing">TODO</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Код, автоматически созданный конструктором форм Windows
|
||||
|
||||
/// <summary>
|
||||
/// TODO
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MapSelect));
|
||||
this.MapList = new System.Windows.Forms.ListView();
|
||||
this.colMapName = new System.Windows.Forms.ColumnHeader("(отсутствует)");
|
||||
this.MapIconsList = new System.Windows.Forms.ImageList(this.components);
|
||||
this.btnCancel = new System.Windows.Forms.Button();
|
||||
this.btnOk = new System.Windows.Forms.Button();
|
||||
this.lblNew = new System.Windows.Forms.Label();
|
||||
this.txtNew = new System.Windows.Forms.TextBox();
|
||||
this.pbMinimap = new System.Windows.Forms.PictureBox();
|
||||
this.pnlBottom = new System.Windows.Forms.Panel();
|
||||
this.txtPathOut = new System.Windows.Forms.TextBox();
|
||||
this.lblPathOut = new System.Windows.Forms.Label();
|
||||
this.lblPath = new System.Windows.Forms.Label();
|
||||
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
|
||||
this.lblMapList = new System.Windows.Forms.Label();
|
||||
this.txtDesc = new System.Windows.Forms.TextBox();
|
||||
this.lblDesc = new System.Windows.Forms.Label();
|
||||
this.txtTheater = new System.Windows.Forms.TextBox();
|
||||
this.lblTheater = new System.Windows.Forms.Label();
|
||||
this.txtAuthor = new System.Windows.Forms.TextBox();
|
||||
this.lblAuthor = new System.Windows.Forms.Label();
|
||||
this.txtTitle = new System.Windows.Forms.TextBox();
|
||||
this.lblMapName = new System.Windows.Forms.Label();
|
||||
this.lblMinimap = new System.Windows.Forms.Label();
|
||||
this.pictureBox1 = new System.Windows.Forms.PictureBox();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pbMinimap)).BeginInit();
|
||||
this.pnlBottom.SuspendLayout();
|
||||
this.splitContainer1.Panel1.SuspendLayout();
|
||||
this.splitContainer1.Panel2.SuspendLayout();
|
||||
this.splitContainer1.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// MapList
|
||||
//
|
||||
this.MapList.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.MapList.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
|
||||
this.colMapName});
|
||||
this.MapList.FullRowSelect = true;
|
||||
this.MapList.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None;
|
||||
this.MapList.LargeImageList = this.MapIconsList;
|
||||
this.MapList.Location = new System.Drawing.Point(15, 25);
|
||||
this.MapList.MultiSelect = false;
|
||||
this.MapList.Name = "MapList";
|
||||
this.MapList.Size = new System.Drawing.Size(273, 294);
|
||||
this.MapList.SmallImageList = this.MapIconsList;
|
||||
this.MapList.StateImageList = this.MapIconsList;
|
||||
this.MapList.TabIndex = 0;
|
||||
this.MapList.UseCompatibleStateImageBehavior = false;
|
||||
this.MapList.View = System.Windows.Forms.View.Details;
|
||||
this.MapList.SelectedIndexChanged += new System.EventHandler(this.MapList_SelectedIndexChanged);
|
||||
//
|
||||
// colMapName
|
||||
//
|
||||
this.colMapName.Text = "Map name";
|
||||
this.colMapName.Width = 240;
|
||||
//
|
||||
// MapIconsList
|
||||
//
|
||||
this.MapIconsList.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
|
||||
this.MapIconsList.ImageSize = new System.Drawing.Size(24, 24);
|
||||
this.MapIconsList.TransparentColor = System.Drawing.Color.Transparent;
|
||||
//
|
||||
// btnCancel
|
||||
//
|
||||
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.btnCancel.Location = new System.Drawing.Point(407, 35);
|
||||
this.btnCancel.Name = "btnCancel";
|
||||
this.btnCancel.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnCancel.TabIndex = 3;
|
||||
this.btnCancel.Text = "Cancel";
|
||||
this.btnCancel.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// btnOk
|
||||
//
|
||||
this.btnOk.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.btnOk.Location = new System.Drawing.Point(326, 35);
|
||||
this.btnOk.Name = "btnOk";
|
||||
this.btnOk.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnOk.TabIndex = 2;
|
||||
this.btnOk.Text = "Open";
|
||||
this.btnOk.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// lblNew
|
||||
//
|
||||
this.lblNew.AutoSize = true;
|
||||
this.lblNew.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
|
||||
this.lblNew.Location = new System.Drawing.Point(12, 40);
|
||||
this.lblNew.Name = "lblNew";
|
||||
this.lblNew.Size = new System.Drawing.Size(69, 13);
|
||||
this.lblNew.TabIndex = 3;
|
||||
this.lblNew.Text = "Map name:";
|
||||
//
|
||||
// txtNew
|
||||
//
|
||||
this.txtNew.BackColor = System.Drawing.SystemColors.Window;
|
||||
this.txtNew.Location = new System.Drawing.Point(88, 37);
|
||||
this.txtNew.Name = "txtNew";
|
||||
this.txtNew.ReadOnly = true;
|
||||
this.txtNew.Size = new System.Drawing.Size(232, 20);
|
||||
this.txtNew.TabIndex = 1;
|
||||
//
|
||||
// pbMinimap
|
||||
//
|
||||
this.pbMinimap.BackColor = System.Drawing.Color.Black;
|
||||
this.pbMinimap.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
|
||||
this.pbMinimap.Location = new System.Drawing.Point(32, 25);
|
||||
this.pbMinimap.Name = "pbMinimap";
|
||||
this.pbMinimap.Size = new System.Drawing.Size(124, 124);
|
||||
this.pbMinimap.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
this.pbMinimap.TabIndex = 5;
|
||||
this.pbMinimap.TabStop = false;
|
||||
//
|
||||
// pnlBottom
|
||||
//
|
||||
this.pnlBottom.Controls.Add(this.pictureBox1);
|
||||
this.pnlBottom.Controls.Add(this.txtPathOut);
|
||||
this.pnlBottom.Controls.Add(this.lblPathOut);
|
||||
this.pnlBottom.Controls.Add(this.lblPath);
|
||||
this.pnlBottom.Controls.Add(this.btnCancel);
|
||||
this.pnlBottom.Controls.Add(this.btnOk);
|
||||
this.pnlBottom.Controls.Add(this.txtNew);
|
||||
this.pnlBottom.Controls.Add(this.lblNew);
|
||||
this.pnlBottom.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||
this.pnlBottom.Location = new System.Drawing.Point(0, 332);
|
||||
this.pnlBottom.MaximumSize = new System.Drawing.Size(0, 70);
|
||||
this.pnlBottom.Name = "pnlBottom";
|
||||
this.pnlBottom.Size = new System.Drawing.Size(494, 70);
|
||||
this.pnlBottom.TabIndex = 6;
|
||||
//
|
||||
// txtPathOut
|
||||
//
|
||||
this.txtPathOut.BackColor = System.Drawing.SystemColors.Window;
|
||||
this.txtPathOut.Location = new System.Drawing.Point(55, 10);
|
||||
this.txtPathOut.Name = "txtPathOut";
|
||||
this.txtPathOut.ReadOnly = true;
|
||||
this.txtPathOut.Size = new System.Drawing.Size(265, 20);
|
||||
this.txtPathOut.TabIndex = 0;
|
||||
this.txtPathOut.TextChanged += new System.EventHandler(this.txtPathOut_TextChanged);
|
||||
//
|
||||
// lblPathOut
|
||||
//
|
||||
this.lblPathOut.AutoSize = true;
|
||||
this.lblPathOut.Location = new System.Drawing.Point(55, 13);
|
||||
this.lblPathOut.Name = "lblPathOut";
|
||||
this.lblPathOut.Size = new System.Drawing.Size(0, 13);
|
||||
this.lblPathOut.TabIndex = 6;
|
||||
//
|
||||
// lblPath
|
||||
//
|
||||
this.lblPath.AutoSize = true;
|
||||
this.lblPath.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
|
||||
this.lblPath.Location = new System.Drawing.Point(12, 13);
|
||||
this.lblPath.Name = "lblPath";
|
||||
this.lblPath.Size = new System.Drawing.Size(37, 13);
|
||||
this.lblPath.TabIndex = 5;
|
||||
this.lblPath.Text = "Path:";
|
||||
//
|
||||
// splitContainer1
|
||||
//
|
||||
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.splitContainer1.Location = new System.Drawing.Point(0, 0);
|
||||
this.splitContainer1.Name = "splitContainer1";
|
||||
//
|
||||
// splitContainer1.Panel1
|
||||
//
|
||||
this.splitContainer1.Panel1.Controls.Add(this.lblMapList);
|
||||
this.splitContainer1.Panel1.Controls.Add(this.MapList);
|
||||
//
|
||||
// splitContainer1.Panel2
|
||||
//
|
||||
this.splitContainer1.Panel2.Controls.Add(this.txtDesc);
|
||||
this.splitContainer1.Panel2.Controls.Add(this.lblDesc);
|
||||
this.splitContainer1.Panel2.Controls.Add(this.txtTheater);
|
||||
this.splitContainer1.Panel2.Controls.Add(this.lblTheater);
|
||||
this.splitContainer1.Panel2.Controls.Add(this.txtAuthor);
|
||||
this.splitContainer1.Panel2.Controls.Add(this.lblAuthor);
|
||||
this.splitContainer1.Panel2.Controls.Add(this.txtTitle);
|
||||
this.splitContainer1.Panel2.Controls.Add(this.lblMapName);
|
||||
this.splitContainer1.Panel2.Controls.Add(this.lblMinimap);
|
||||
this.splitContainer1.Panel2.Controls.Add(this.pbMinimap);
|
||||
this.splitContainer1.Size = new System.Drawing.Size(494, 332);
|
||||
this.splitContainer1.SplitterDistance = 300;
|
||||
this.splitContainer1.TabIndex = 7;
|
||||
//
|
||||
// lblMapList
|
||||
//
|
||||
this.lblMapList.AutoSize = true;
|
||||
this.lblMapList.Location = new System.Drawing.Point(12, 9);
|
||||
this.lblMapList.Name = "lblMapList";
|
||||
this.lblMapList.Size = new System.Drawing.Size(81, 13);
|
||||
this.lblMapList.TabIndex = 1;
|
||||
this.lblMapList.Text = "Available maps:";
|
||||
//
|
||||
// txtDesc
|
||||
//
|
||||
this.txtDesc.BackColor = System.Drawing.SystemColors.ButtonFace;
|
||||
this.txtDesc.Location = new System.Drawing.Point(16, 289);
|
||||
this.txtDesc.Name = "txtDesc";
|
||||
this.txtDesc.ReadOnly = true;
|
||||
this.txtDesc.Size = new System.Drawing.Size(162, 20);
|
||||
this.txtDesc.TabIndex = 14;
|
||||
//
|
||||
// lblDesc
|
||||
//
|
||||
this.lblDesc.AutoSize = true;
|
||||
this.lblDesc.Location = new System.Drawing.Point(13, 273);
|
||||
this.lblDesc.Name = "lblDesc";
|
||||
this.lblDesc.Size = new System.Drawing.Size(63, 13);
|
||||
this.lblDesc.TabIndex = 13;
|
||||
this.lblDesc.Text = "Description:";
|
||||
//
|
||||
// txtTheater
|
||||
//
|
||||
this.txtTheater.BackColor = System.Drawing.SystemColors.ButtonFace;
|
||||
this.txtTheater.Location = new System.Drawing.Point(16, 252);
|
||||
this.txtTheater.Name = "txtTheater";
|
||||
this.txtTheater.ReadOnly = true;
|
||||
this.txtTheater.Size = new System.Drawing.Size(162, 20);
|
||||
this.txtTheater.TabIndex = 12;
|
||||
//
|
||||
// lblTheater
|
||||
//
|
||||
this.lblTheater.AutoSize = true;
|
||||
this.lblTheater.Location = new System.Drawing.Point(13, 236);
|
||||
this.lblTheater.Name = "lblTheater";
|
||||
this.lblTheater.Size = new System.Drawing.Size(47, 13);
|
||||
this.lblTheater.TabIndex = 11;
|
||||
this.lblTheater.Text = "Tileset:";
|
||||
//
|
||||
// txtAuthor
|
||||
//
|
||||
this.txtAuthor.BackColor = System.Drawing.SystemColors.ButtonFace;
|
||||
this.txtAuthor.Location = new System.Drawing.Point(16, 214);
|
||||
this.txtAuthor.Name = "txtAuthor";
|
||||
this.txtAuthor.ReadOnly = true;
|
||||
this.txtAuthor.Size = new System.Drawing.Size(162, 20);
|
||||
this.txtAuthor.TabIndex = 10;
|
||||
//
|
||||
// lblAuthor
|
||||
//
|
||||
this.lblAuthor.AutoSize = true;
|
||||
this.lblAuthor.Location = new System.Drawing.Point(13, 198);
|
||||
this.lblAuthor.Name = "lblAuthor";
|
||||
this.lblAuthor.Size = new System.Drawing.Size(41, 13);
|
||||
this.lblAuthor.TabIndex = 9;
|
||||
this.lblAuthor.Text = "Author:";
|
||||
//
|
||||
// txtTitle
|
||||
//
|
||||
this.txtTitle.BackColor = System.Drawing.SystemColors.ButtonFace;
|
||||
this.txtTitle.Location = new System.Drawing.Point(16, 177);
|
||||
this.txtTitle.Name = "txtTitle";
|
||||
this.txtTitle.ReadOnly = true;
|
||||
this.txtTitle.Size = new System.Drawing.Size(162, 20);
|
||||
this.txtTitle.TabIndex = 8;
|
||||
//
|
||||
// lblMapName
|
||||
//
|
||||
this.lblMapName.AutoSize = true;
|
||||
this.lblMapName.Location = new System.Drawing.Point(13, 161);
|
||||
this.lblMapName.Name = "lblMapName";
|
||||
this.lblMapName.Size = new System.Drawing.Size(30, 13);
|
||||
this.lblMapName.TabIndex = 7;
|
||||
this.lblMapName.Text = "Title:";
|
||||
//
|
||||
// lblMinimap
|
||||
//
|
||||
this.lblMinimap.AutoSize = true;
|
||||
this.lblMinimap.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
|
||||
this.lblMinimap.Location = new System.Drawing.Point(29, 9);
|
||||
this.lblMinimap.Name = "lblMinimap";
|
||||
this.lblMinimap.Size = new System.Drawing.Size(71, 13);
|
||||
this.lblMinimap.TabIndex = 6;
|
||||
this.lblMinimap.Text = "Map preview:";
|
||||
//
|
||||
// pictureBox1
|
||||
//
|
||||
this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image")));
|
||||
this.pictureBox1.Location = new System.Drawing.Point(336, -9);
|
||||
this.pictureBox1.Name = "pictureBox1";
|
||||
this.pictureBox1.Size = new System.Drawing.Size(54, 35);
|
||||
this.pictureBox1.TabIndex = 7;
|
||||
this.pictureBox1.TabStop = false;
|
||||
this.pictureBox1.Visible = false;
|
||||
//
|
||||
// MapSelect
|
||||
//
|
||||
this.AcceptButton = this.btnOk;
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.CancelButton = this.btnCancel;
|
||||
this.ClientSize = new System.Drawing.Size(494, 402);
|
||||
this.Controls.Add(this.splitContainer1);
|
||||
this.Controls.Add(this.pnlBottom);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "MapSelect";
|
||||
this.ShowIcon = false;
|
||||
this.ShowInTaskbar = false;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "Select map";
|
||||
this.Load += new System.EventHandler(this.MapSelect_Load);
|
||||
((System.ComponentModel.ISupportInitialize)(this.pbMinimap)).EndInit();
|
||||
this.pnlBottom.ResumeLayout(false);
|
||||
this.pnlBottom.PerformLayout();
|
||||
this.splitContainer1.Panel1.ResumeLayout(false);
|
||||
this.splitContainer1.Panel1.PerformLayout();
|
||||
this.splitContainer1.Panel2.ResumeLayout(false);
|
||||
this.splitContainer1.Panel2.PerformLayout();
|
||||
this.splitContainer1.ResumeLayout(false);
|
||||
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public System.Windows.Forms.ListView MapList;
|
||||
public System.Windows.Forms.Button btnCancel;
|
||||
public System.Windows.Forms.Button btnOk;
|
||||
public System.Windows.Forms.Label lblNew;
|
||||
public System.Windows.Forms.TextBox txtNew;
|
||||
public System.Windows.Forms.ColumnHeader colMapName;
|
||||
public System.Windows.Forms.ImageList MapIconsList;
|
||||
public System.Windows.Forms.PictureBox pbMinimap;
|
||||
public System.Windows.Forms.Panel pnlBottom;
|
||||
public System.Windows.Forms.SplitContainer splitContainer1;
|
||||
public System.Windows.Forms.Label lblMinimap;
|
||||
public System.Windows.Forms.TextBox txtTheater;
|
||||
public System.Windows.Forms.Label lblTheater;
|
||||
public System.Windows.Forms.TextBox txtAuthor;
|
||||
public System.Windows.Forms.Label lblAuthor;
|
||||
public System.Windows.Forms.TextBox txtTitle;
|
||||
public System.Windows.Forms.Label lblMapName;
|
||||
public System.Windows.Forms.TextBox txtDesc;
|
||||
public System.Windows.Forms.Label lblDesc;
|
||||
public System.Windows.Forms.Label lblMapList;
|
||||
public System.Windows.Forms.Label lblPathOut;
|
||||
public System.Windows.Forms.Label lblPath;
|
||||
public System.Windows.Forms.TextBox txtPathOut;
|
||||
private System.Windows.Forms.PictureBox pictureBox1;
|
||||
}
|
||||
}
|
||||
66
OpenRA.Editor/MapSelect.cs
Normal file
66
OpenRA.Editor/MapSelect.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Graphics;
|
||||
|
||||
namespace OpenRA.Editor
|
||||
{
|
||||
public partial class MapSelect : Form
|
||||
{
|
||||
public string MapFolderPath;
|
||||
|
||||
public MapSelect()
|
||||
{
|
||||
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)
|
||||
{
|
||||
ListViewItem map1 = new ListViewItem(subDirectory.Name);
|
||||
map1.ImageIndex = 0;
|
||||
MapList.Items.Add(map1);
|
||||
}
|
||||
|
||||
// hack
|
||||
if (txtNew.Text != "unnamed")
|
||||
MapList.Items[0].Selected = true;
|
||||
}
|
||||
|
||||
void MapList_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (MapList.SelectedItems.Count == 1)
|
||||
{
|
||||
txtNew.Text = MapList.SelectedItems[0].Text;
|
||||
var map = new Map(new Folder(Path.Combine(MapFolderPath, MapList.SelectedItems[0].Text), 0));
|
||||
txtTitle.Text = map.Title;
|
||||
txtAuthor.Text = map.Author;
|
||||
txtTheater.Text = map.Tileset;
|
||||
txtDesc.Text = map.Description;
|
||||
pbMinimap.Image = null;
|
||||
try
|
||||
{
|
||||
pbMinimap.Image = Minimap.AddStaticResources(map, Minimap.TerrainBitmap(map, true));
|
||||
}
|
||||
catch (Exception ed)
|
||||
{
|
||||
Console.WriteLine("No map preview image found: {0}", ed.ToString());
|
||||
}
|
||||
finally { }
|
||||
}
|
||||
}
|
||||
|
||||
void txtPathOut_TextChanged(object sender, EventArgs e)
|
||||
{
|
||||
MapFolderPath = txtPathOut.Text;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
177
OpenRA.Editor/MapSelect.resx
Normal file
177
OpenRA.Editor/MapSelect.resx
Normal file
@@ -0,0 +1,177 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="MapIconsList.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="pictureBox1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAArjSURBVFhHrVcJUFRXFmVpkE0Wg0ZEMoIBl8RodGSMmVES
|
||||
R0NiChVxCyIIAmFfhe6maeh9AZodZHFpUFQWcQmKisiuLKLEKFFEcIuljtZMYmI5Uc/c14EZYyapmpq8
|
||||
qlP/9/+/373v3HPu+19P7/cf+jSlEWHCOEuzZfNn2Yvo3Pj3D/PTjCwYAwtgyeFwFk163Tos2tvtWJZw
|
||||
zeUjmYFP6vL9fhw31vRPI8/9LnmMrtCWZhtPcJ7hbC/W8DwfnS0LR0t5NKoFazHcqsaprE3YGbMQLrYW
|
||||
W/7fBAxogjGEd5wcXuPK4jxa60qD75VrfIbKMrYMD7VlPX/Ul49rp8RoyPbFIaEnhpuU6NRGoVa2HKuX
|
||||
zEj/X8vAVmlIsDY2Nna2sjBd+ee5b7Yq4tfgfm8a7pxNw60OFa6e4uFmhxr/uFSIexfz0VsZh7PaIJzb
|
||||
F42u8hAcV2/ANz3KF8Hub52kucb+Fv+jAccbm5quWOfxYX5y6Mcnq/IDh1oreX/vOcx9drdbjTudUgye
|
||||
Tsb1piT0H4/DACUw0JiEh33ZePhlFi7UxOCLtI1o2x6IW2cUqM/egLtdediV9MnjSTYm60bE+Ys8rMzN
|
||||
zd1c3/vLgVRV3lN1XjkqyzNoNaEUjIvrLXwMtQsw1JqEy/UxuHg0DJcp+KXj8bh0IobOucSGDA8vpqE6
|
||||
+VNINixBU0kQbp5JJ8jxDSXdeyAE5arlAxTZ/BfRra2tS8Tqwh9L9tQhq6gSabm7odLkoP8oF90V/rjR
|
||||
LsRgExc32oS40ZFCiQgw2Mgj+gW42pBI4OPSsUh6Jgn1mX4ojXJH865IXDzCx50uDe6fV+P8oQQ4Tx6r
|
||||
pOATmVteKgdjXu/Mhk1h328rO4TMwn3gp2YhmquEX7gAfYfjcb4qGH0HQ3D5WAiunU7ApfpofH0ymhIS
|
||||
UwkSqQRb6VoYvm6IZvXG7W4VBVbjwQU1/tanIQZkGDqtwOmSjzHXxXaLsZnZpy85QpdAnfunax/mllRD
|
||||
ma2FQJyHxORMJAqzwBVpUJgtRLF8E7bxlqKjzA991QEYaIijgFwMt6VggJK60hCFr46F4sqprbjbLaf6
|
||||
S0icIh1rwy1i3OvNQff+GEgj3r87/c1JLIGfjX1LP/F6KJDlIy1vN4SyQohVRdAU7IVIXQxuSiZ8/OOw
|
||||
JYyP8EiqfV04Lh2JoHIwMSbiwoFgtO1ah97aTSTMKGIjDl/WhONWl5jKlYrbZ9OJGRWVIg/+axf8SK5a
|
||||
8WoC5fPm//l2qqIIOaVVECm3QZG5C7L0HZCklYJLTCQKNUiWFYAnykJYRDRaywNxrTGRyhKNKyciiIEY
|
||||
dFd743z1ZioT6eJkHK41kXDbRFSKbHzTpcJwsxoHc71fTLGzyBix+L/zUFlaWX+VTcFZ4NzSamQXV1Lw
|
||||
EggkeeBRAsnSfKgyy6DKKcNnxEahIgRfHg5B/7EoDDYn4ma7FD0VAThfG4Cemi3EDI90IKWVq3GbHHKz
|
||||
nUpCbrh6QokEr5mPTEw4773MQij9aPZY5fNYrC6hEhRAnrkTjJEkcS5ieWokS/IhUZdCrtlJbGQiIjQQ
|
||||
/YeD0VMVhoTVbmgs8iVBctG11xtdlUHkDGpQpIN7velUAhmG2+W43spclI6K9JWY6TyxlGKOGxWjG53s
|
||||
5HBMBpZ7+iI6UY44vhoiVTH4ohxCLoTyArqu1OlBll6CZHE20mI9EOjuis3L5uFCbTh6D4Wgu8offYeC
|
||||
qSRccgcPD/qyMEiBb55VYahFgSuNckhjlr2YaGMqH2ntOiKmEAQGBgbnNviGPg2LFSM4UoiAUD78ghLo
|
||||
yEVojBhRiQqEx0nAI5sKiBH/z+OgVfuic7c/Lh+NwZndfughDfQdCtL1hKFmEW51ymn1EjpSAh1ytFCL
|
||||
Pl+nhKebY8XLTYn16DWEgzY2ttdzyI6yjB06+uP4acRANokvhzRRjBR5oe4aN4WupeYgNCIWZ2vj0b17
|
||||
I/UKf3Tt34jOis9otTyyILmkORUDTRKyqxj9DSI07Y0k26Yhes38Roo3YVQH7OVhGiGR0K7O0epEqCRB
|
||||
shKwRIQkQrZqMelAIM6ncihII9uQSCXZEi2HSMjDXvVqXKSGdTLbg6z6OVkwGcfLQvHB4jkYbJMS/UL0
|
||||
1aeSM6So1Hg/oljTR93AupEVYSXhGE+soZa8X+cIlgALzFhg5/HJ6YhIzACXVs9EKiHRMmckSfMQGx0G
|
||||
rXIldU4/dJSvJx3EoSDZC7OszHByTxTOfZGEq00y9B3lo70iElamhp4Ujy1eN8wIrEOd0BRW6DpiWEwq
|
||||
giKSdYiIl5IOUskRKh39AmKECZNP7Mg0O6Ci59Pzd4O/NQK7FBvQULyGOmMUpBsX4KqjGTL5qzHQIqJy
|
||||
iNG6Px5dtYlY7jqhmeKxvUE32C7FGKiP58uf5W6v0ekgPkmNrYIMor4YqcoinQBFdGQrl2ZsB4/cwBJR
|
||||
Zml/+k0sxcQnQZzgDWHIIni9NQHSHU5IS15EPSEN11ok6G9UQs1dh/lvv/GdkZHR3NEETOhkIUE7zvb1
|
||||
wRRFwYv0vD3UBTORJCEdSHMhydgFCXVHVnuWEGvZrDewjqmzKbHDNjHmFl5KBlasWokiFwtop5nhyL6p
|
||||
2JO/CN2HBdSgMpHJ88GH82c8s7b4T1vmUPA3CFxCp19QzBPdtpxdRqon2kkDKRSYXeOJcyBUFMI7IJ70
|
||||
kYsEgRLTXGbgrzNnwmWqC2Y4OsLZZjx43El4ggXI1zohy24MAFd4u9tj8YLpmD71DTiMtwGHY1A7ygB7
|
||||
52N29CLULV7qeT+HnFCoPairu87/RC9rTjE8JYJoY/p4lR8lVQAp7Rc+/pHY7OKE61NN8a21MbQpfwC+
|
||||
m4fbQ7MQNdcCfWMMsTPWDi2yyRgyNkDhOGPYGhpeo97jP5oAcwJjYQ4hk3DOyzvwn9v3HtUJjHXAzSFc
|
||||
RHHlCAznIyAsCZa2k56u3xT2gtHPmGHCdXtnHh47mKBqjS1K5Q7ITrCjlf8RqXPM0bDUCr3bp2C9nj70
|
||||
DDinKMYSwmujCbAjY4G9ZrN3tyMLP/D4IT1vP/J21CBeoKaa55MTUhAeK8J7i91v0DMdxqZmgw7Oc74N
|
||||
iFQ8T5YX6ZzisWI9Uiebo8rKCOcsjLAv1Z72BydUxE5ElcTuKf2P9RunEeGzmD8bTIzsI0I7wc7xdgyf
|
||||
NiayWVoueZ1qz7zPT8l5McnBqZieYZsYO/a4zF7yaKWP4HlASMKLj9xX4WrXWzhdMxUFlkaI1tPDAZE9
|
||||
Htc4wWuhOas5Y/oXgUezYDdZdmK2N6zzCfuB1V1GFkulNswT5cFllms73f+QMIWwjCDjGJnUW1rbfu2z
|
||||
wvXJk85pUH5gieNJdugvd8QV7RSc0TjgiMwevkvH3np1xa/+ZplZE9jecNh14UcPmO8VmnLyfzGr/feU
|
||||
mB/dY7VjybItdSZhrb6+fvEYY6PuMWOMn7ZbGeILfQNYmnN+mO1o/OBdCw74HAMsMjR8NjL/r+Yx+p3H
|
||||
XhjKZs93uxMZr4Z/uOb54qWf3bGf4pw3EpC1UPYsS3g0EU9K4uBsI86zYHPOY3MOp4zuRRLyDfT1O941
|
||||
Mrzvb86Bob4h+0r6zcEmdiFICA2vvT55kLFBYOJhnYt9or06TOmCJ7HTbGxgADpndmaJMU29QxAQKvTo
|
||||
vhnHkN1nX12/OlgCNoT3CZsIvgRWc9ao2IT/bTD3+BID7N1/zisBWCKOBB/CagKf8PboJP8CoUmu3yhA
|
||||
ga8AAAAASUVORK5CYII=
|
||||
</value>
|
||||
</data>
|
||||
</root>
|
||||
24
OpenRA.Editor/NewMapDialog.Designer.cs
generated
Normal file → Executable file
24
OpenRA.Editor/NewMapDialog.Designer.cs
generated
Normal file → Executable file
@@ -55,7 +55,7 @@
|
||||
this.button2.Location = new System.Drawing.Point(229, 160);
|
||||
this.button2.Name = "button2";
|
||||
this.button2.Size = new System.Drawing.Size(75, 23);
|
||||
this.button2.TabIndex = 12;
|
||||
this.button2.TabIndex = 7;
|
||||
this.button2.Text = "OK";
|
||||
this.button2.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -65,7 +65,7 @@
|
||||
this.button1.Location = new System.Drawing.Point(310, 160);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(75, 23);
|
||||
this.button1.TabIndex = 13;
|
||||
this.button1.TabIndex = 8;
|
||||
this.button1.Text = "Cancel";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -106,12 +106,13 @@
|
||||
0});
|
||||
this.cordonBottom.Name = "cordonBottom";
|
||||
this.cordonBottom.Size = new System.Drawing.Size(105, 20);
|
||||
this.cordonBottom.TabIndex = 8;
|
||||
this.cordonBottom.TabIndex = 5;
|
||||
this.cordonBottom.Value = new decimal(new int[] {
|
||||
112,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.cordonBottom.Enter += new System.EventHandler(this.SelectText);
|
||||
//
|
||||
// cordonTop
|
||||
//
|
||||
@@ -129,6 +130,7 @@
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.cordonTop.Enter += new System.EventHandler(this.SelectText);
|
||||
//
|
||||
// cordonRight
|
||||
//
|
||||
@@ -140,12 +142,13 @@
|
||||
0});
|
||||
this.cordonRight.Name = "cordonRight";
|
||||
this.cordonRight.Size = new System.Drawing.Size(105, 20);
|
||||
this.cordonRight.TabIndex = 5;
|
||||
this.cordonRight.TabIndex = 4;
|
||||
this.cordonRight.Value = new decimal(new int[] {
|
||||
112,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.cordonRight.Enter += new System.EventHandler(this.SelectText);
|
||||
//
|
||||
// cordonLeft
|
||||
//
|
||||
@@ -157,12 +160,13 @@
|
||||
0});
|
||||
this.cordonLeft.Name = "cordonLeft";
|
||||
this.cordonLeft.Size = new System.Drawing.Size(105, 20);
|
||||
this.cordonLeft.TabIndex = 7;
|
||||
this.cordonLeft.TabIndex = 2;
|
||||
this.cordonLeft.Value = new decimal(new int[] {
|
||||
16,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.cordonLeft.Enter += new System.EventHandler(this.SelectText);
|
||||
//
|
||||
// height
|
||||
//
|
||||
@@ -179,12 +183,13 @@
|
||||
0});
|
||||
this.height.Name = "height";
|
||||
this.height.Size = new System.Drawing.Size(105, 20);
|
||||
this.height.TabIndex = 6;
|
||||
this.height.TabIndex = 1;
|
||||
this.height.Value = new decimal(new int[] {
|
||||
128,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.height.Enter += new System.EventHandler(this.SelectText);
|
||||
//
|
||||
// width
|
||||
//
|
||||
@@ -201,12 +206,13 @@
|
||||
0});
|
||||
this.width.Name = "width";
|
||||
this.width.Size = new System.Drawing.Size(105, 20);
|
||||
this.width.TabIndex = 4;
|
||||
this.width.TabIndex = 0;
|
||||
this.width.Value = new decimal(new int[] {
|
||||
128,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.width.Enter += new System.EventHandler(this.SelectText);
|
||||
//
|
||||
// label4
|
||||
//
|
||||
@@ -215,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
|
||||
//
|
||||
@@ -224,7 +230,7 @@
|
||||
this.theater.Location = new System.Drawing.Point(169, 121);
|
||||
this.theater.Name = "theater";
|
||||
this.theater.Size = new System.Drawing.Size(216, 21);
|
||||
this.theater.TabIndex = 15;
|
||||
this.theater.TabIndex = 6;
|
||||
//
|
||||
// NewMapDialog
|
||||
//
|
||||
|
||||
5
OpenRA.Editor/NewMapDialog.cs
Normal file → Executable file
5
OpenRA.Editor/NewMapDialog.cs
Normal file → Executable file
@@ -18,5 +18,10 @@ namespace OpenRA.Editor
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void SelectText(object sender, System.EventArgs e)
|
||||
{
|
||||
(sender as NumericUpDown).Select(0, (sender as NumericUpDown).ToString().Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
OpenRA.Editor/OpenRA.Editor.Icon.ico
Normal file
BIN
OpenRA.Editor/OpenRA.Editor.Icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -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>
|
||||
@@ -12,6 +12,7 @@
|
||||
<AssemblyName>OpenRA.Editor</AssemblyName>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ApplicationIcon>OpenRA.Editor.Icon.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
@@ -33,6 +34,7 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
@@ -59,12 +61,24 @@
|
||||
<DependentUpon>Form1.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="LegacyMapImporter.cs" />
|
||||
<Compile Include="MapSelect.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MapSelect.Designer.cs">
|
||||
<DependentUpon>MapSelect.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="NewMapDialog.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="NewMapDialog.Designer.cs">
|
||||
<DependentUpon>NewMapDialog.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="PaletteBox.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="PaletteBox.Designer.cs">
|
||||
<DependentUpon>PaletteBox.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="PropertiesDialog.cs">
|
||||
<SubType>Form</SubType>
|
||||
@@ -76,9 +90,15 @@
|
||||
<EmbeddedResource Include="Form1.resx">
|
||||
<DependentUpon>Form1.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="MapSelect.resx">
|
||||
<DependentUpon>MapSelect.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="NewMapDialog.resx">
|
||||
<DependentUpon>NewMapDialog.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="PaletteBox.resx">
|
||||
<DependentUpon>PaletteBox.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="PropertiesDialog.resx">
|
||||
<DependentUpon>PropertiesDialog.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
@@ -103,6 +123,7 @@
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
<Compile Include="RenderUtils.cs" />
|
||||
<Compile Include="ResizeDialog.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
@@ -123,6 +144,9 @@
|
||||
<Name>OpenRA.Game</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="OpenRA.Editor.Icon.ico" />
|
||||
</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.
|
||||
|
||||
165
OpenRA.Editor/PaletteBox.Designer.cs
generated
Normal file
165
OpenRA.Editor/PaletteBox.Designer.cs
generated
Normal file
@@ -0,0 +1,165 @@
|
||||
namespace OpenRA.Editor
|
||||
{
|
||||
partial class PaletteBox
|
||||
{
|
||||
/// <summary>
|
||||
/// Требуется переменная конструктора.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Освободить все используемые ресурсы.
|
||||
/// </summary>
|
||||
/// <param name="disposing">истинно, если управляемый ресурс должен быть удален; иначе ложно.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Код, автоматически созданный конструктором форм Windows
|
||||
|
||||
/// <summary>
|
||||
/// Обязательный метод для поддержки конструктора - не изменяйте
|
||||
/// содержимое данного метода при помощи редактора кода.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.LayerBox = new System.Windows.Forms.ComboBox();
|
||||
this.tabControl1 = new System.Windows.Forms.TabControl();
|
||||
this.tabPage1 = new System.Windows.Forms.TabPage();
|
||||
this.pbtilePalette = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.tabPage2 = new System.Windows.Forms.TabPage();
|
||||
this.pbactorPalette = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.tabPage3 = new System.Windows.Forms.TabPage();
|
||||
this.pbresourcePalette = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.tabControl1.SuspendLayout();
|
||||
this.tabPage1.SuspendLayout();
|
||||
this.tabPage2.SuspendLayout();
|
||||
this.tabPage3.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// LayerBox
|
||||
//
|
||||
this.LayerBox.Dock = System.Windows.Forms.DockStyle.Top;
|
||||
this.LayerBox.FormattingEnabled = true;
|
||||
this.LayerBox.Location = new System.Drawing.Point(0, 0);
|
||||
this.LayerBox.Name = "LayerBox";
|
||||
this.LayerBox.Size = new System.Drawing.Size(194, 21);
|
||||
this.LayerBox.TabIndex = 0;
|
||||
//
|
||||
// tabControl1
|
||||
//
|
||||
this.tabControl1.Controls.Add(this.tabPage1);
|
||||
this.tabControl1.Controls.Add(this.tabPage2);
|
||||
this.tabControl1.Controls.Add(this.tabPage3);
|
||||
this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tabControl1.Location = new System.Drawing.Point(0, 21);
|
||||
this.tabControl1.Margin = new System.Windows.Forms.Padding(3, 3, 3, 0);
|
||||
this.tabControl1.Multiline = true;
|
||||
this.tabControl1.Name = "tabControl1";
|
||||
this.tabControl1.SelectedIndex = 0;
|
||||
this.tabControl1.Size = new System.Drawing.Size(194, 357);
|
||||
this.tabControl1.TabIndex = 1;
|
||||
//
|
||||
// tabPage1
|
||||
//
|
||||
this.tabPage1.Controls.Add(this.pbtilePalette);
|
||||
this.tabPage1.Location = new System.Drawing.Point(4, 22);
|
||||
this.tabPage1.Name = "tabPage1";
|
||||
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
|
||||
this.tabPage1.Size = new System.Drawing.Size(186, 331);
|
||||
this.tabPage1.TabIndex = 0;
|
||||
this.tabPage1.Text = "Templates";
|
||||
this.tabPage1.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// pbtilePalette
|
||||
//
|
||||
this.pbtilePalette.AutoScroll = true;
|
||||
this.pbtilePalette.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.pbtilePalette.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.pbtilePalette.Location = new System.Drawing.Point(3, 3);
|
||||
this.pbtilePalette.Name = "pbtilePalette";
|
||||
this.pbtilePalette.Size = new System.Drawing.Size(180, 325);
|
||||
this.pbtilePalette.TabIndex = 1;
|
||||
//
|
||||
// tabPage2
|
||||
//
|
||||
this.tabPage2.Controls.Add(this.pbactorPalette);
|
||||
this.tabPage2.Location = new System.Drawing.Point(4, 22);
|
||||
this.tabPage2.Name = "tabPage2";
|
||||
this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
|
||||
this.tabPage2.Size = new System.Drawing.Size(186, 331);
|
||||
this.tabPage2.TabIndex = 1;
|
||||
this.tabPage2.Text = "Actors";
|
||||
this.tabPage2.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// pbactorPalette
|
||||
//
|
||||
this.pbactorPalette.AutoScroll = true;
|
||||
this.pbactorPalette.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.pbactorPalette.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.pbactorPalette.Location = new System.Drawing.Point(3, 3);
|
||||
this.pbactorPalette.Name = "pbactorPalette";
|
||||
this.pbactorPalette.Size = new System.Drawing.Size(180, 325);
|
||||
this.pbactorPalette.TabIndex = 2;
|
||||
//
|
||||
// tabPage3
|
||||
//
|
||||
this.tabPage3.Controls.Add(this.pbresourcePalette);
|
||||
this.tabPage3.Location = new System.Drawing.Point(4, 22);
|
||||
this.tabPage3.Name = "tabPage3";
|
||||
this.tabPage3.Size = new System.Drawing.Size(186, 331);
|
||||
this.tabPage3.TabIndex = 2;
|
||||
this.tabPage3.Text = "Resources";
|
||||
this.tabPage3.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// pbresourcePalette
|
||||
//
|
||||
this.pbresourcePalette.AutoScroll = true;
|
||||
this.pbresourcePalette.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.pbresourcePalette.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.pbresourcePalette.Location = new System.Drawing.Point(0, 0);
|
||||
this.pbresourcePalette.Name = "pbresourcePalette";
|
||||
this.pbresourcePalette.Size = new System.Drawing.Size(186, 331);
|
||||
this.pbresourcePalette.TabIndex = 3;
|
||||
//
|
||||
// PaletteBox
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(194, 378);
|
||||
this.Controls.Add(this.tabControl1);
|
||||
this.Controls.Add(this.LayerBox);
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "PaletteBox";
|
||||
this.ShowIcon = false;
|
||||
this.ShowInTaskbar = false;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "Palette Box";
|
||||
this.TopMost = true;
|
||||
this.tabControl1.ResumeLayout(false);
|
||||
this.tabPage1.ResumeLayout(false);
|
||||
this.tabPage2.ResumeLayout(false);
|
||||
this.tabPage3.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public System.Windows.Forms.ComboBox LayerBox;
|
||||
public System.Windows.Forms.TabControl tabControl1;
|
||||
public System.Windows.Forms.TabPage tabPage1;
|
||||
public System.Windows.Forms.FlowLayoutPanel pbtilePalette;
|
||||
public System.Windows.Forms.TabPage tabPage2;
|
||||
public System.Windows.Forms.FlowLayoutPanel pbactorPalette;
|
||||
public System.Windows.Forms.TabPage tabPage3;
|
||||
public System.Windows.Forms.FlowLayoutPanel pbresourcePalette;
|
||||
|
||||
}
|
||||
}
|
||||
19
OpenRA.Editor/PaletteBox.cs
Normal file
19
OpenRA.Editor/PaletteBox.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace OpenRA.Editor
|
||||
{
|
||||
public partial class PaletteBox : Form
|
||||
{
|
||||
public PaletteBox()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
120
OpenRA.Editor/PaletteBox.resx
Normal file
120
OpenRA.Editor/PaletteBox.resx
Normal file
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
161
OpenRA.Editor/RenderUtils.cs
Normal file
161
OpenRA.Editor/RenderUtils.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;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
|
||||
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,
|
||||
PixelFormat.Format8bppIndexed);
|
||||
|
||||
bitmap.Palette = MakeSystemPalette(p);
|
||||
|
||||
var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
|
||||
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
|
||||
|
||||
unsafe
|
||||
{
|
||||
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++)
|
||||
if (tile.TileBitmapBytes[u + v * template.Size.X] != null)
|
||||
{
|
||||
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] = 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] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bitmap.UnlockBits(data);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
static Bitmap RenderShp(ShpReader shp, Palette p)
|
||||
{
|
||||
var frame = shp[0];
|
||||
|
||||
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.Format8bppIndexed);
|
||||
|
||||
unsafe
|
||||
{
|
||||
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] = frame.Image[i + shp.Width * j];
|
||||
}
|
||||
|
||||
bitmap.UnlockBits(data);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public static ActorTemplate RenderActor(ActorInfo info, TileSet tileset, Palette p)
|
||||
{
|
||||
var ri = info.Traits.Get<RenderSimpleInfo>();
|
||||
string image = null;
|
||||
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;
|
||||
using (var s = FileSystem.OpenWithExts(image, tileset.Extensions))
|
||||
{
|
||||
var shp = new ShpReader(s);
|
||||
var bitmap = RenderShp(shp, p);
|
||||
|
||||
try
|
||||
{
|
||||
using (var s2 = FileSystem.OpenWithExts(image + "2", tileset.Extensions))
|
||||
{
|
||||
var shp2 = new ShpReader(s2);
|
||||
var roofBitmap = RenderShp(shp2, p);
|
||||
|
||||
using (var g = System.Drawing.Graphics.FromImage(bitmap))
|
||||
g.DrawImage(roofBitmap, 0, 0);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
return new ActorTemplate
|
||||
{
|
||||
Bitmap = bitmap,
|
||||
Info = info,
|
||||
Appearance = info.Traits.GetOrDefault<EditorAppearanceInfo>()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static ResourceTemplate RenderResourceType(ResourceTypeInfo info, string[] exts, Palette p)
|
||||
{
|
||||
var image = info.SpriteNames[0];
|
||||
using (var s = FileSystem.OpenWithExts(image, exts))
|
||||
{
|
||||
var shp = new ShpReader(s);
|
||||
var frame = shp[shp.ImageCount - 1];
|
||||
|
||||
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.Format8bppIndexed);
|
||||
|
||||
unsafe
|
||||
{
|
||||
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] = frame.Image[i + shp.Width * j];
|
||||
}
|
||||
|
||||
bitmap.UnlockBits(data);
|
||||
return new ResourceTemplate { Bitmap = bitmap, Info = info, Value = shp.ImageCount - 1 };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
210
OpenRA.Editor/Surface.cs
Normal file → Executable file
210
OpenRA.Editor/Surface.cs
Normal file → Executable file
@@ -15,6 +15,7 @@ using System.Drawing.Imaging;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Editor
|
||||
{
|
||||
@@ -25,6 +26,8 @@ namespace OpenRA.Editor
|
||||
public Palette Palette { get; private set; }
|
||||
int2 Offset;
|
||||
|
||||
float Zoom = 1.0f;
|
||||
|
||||
BrushTemplate Brush;
|
||||
ActorTemplate Actor;
|
||||
ResourceTemplate Resource;
|
||||
@@ -32,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>();
|
||||
@@ -42,6 +46,7 @@ namespace OpenRA.Editor
|
||||
TileSet = ts;
|
||||
Palette = p;
|
||||
Brush = null;
|
||||
PlayerPalettes = null;
|
||||
Chunks.Clear();
|
||||
}
|
||||
|
||||
@@ -80,13 +85,45 @@ namespace OpenRA.Editor
|
||||
Offset -= dx;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
|
||||
protected override void OnMouseWheel(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseWheel(e);
|
||||
|
||||
if (Map == null) return;
|
||||
|
||||
Zoom *= e.Delta > 0 ? 4.0f / 3.0f : .75f;
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
protected override void OnMouseLeave(EventArgs e)
|
||||
{
|
||||
base.OnMouseLeave(e);
|
||||
|
||||
this.Parent.Focus();
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
protected override void OnMouseEnter(EventArgs e)
|
||||
{
|
||||
base.OnMouseLeave(e);
|
||||
|
||||
this.Focus();
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
protected override void OnMouseMove(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseMove(e);
|
||||
|
||||
if (Map == null) return;
|
||||
|
||||
var oldMousePos = MousePos;
|
||||
MousePos = new int2(e.Location);
|
||||
MousePositionChanged(GetBrushLocation().ToString());
|
||||
|
||||
if (e.Button == MouseButtons.Middle || (e.Button != MouseButtons.None && IsPanning))
|
||||
Scroll(oldMousePos - MousePos);
|
||||
@@ -96,7 +133,7 @@ namespace OpenRA.Editor
|
||||
Erase();
|
||||
|
||||
if (e.Button == MouseButtons.Left)
|
||||
Draw();
|
||||
Draw();
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
@@ -138,7 +175,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;
|
||||
@@ -170,7 +207,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);
|
||||
@@ -208,18 +245,27 @@ namespace OpenRA.Editor
|
||||
|
||||
void Erase()
|
||||
{
|
||||
// Crash preventing
|
||||
var BrushLocation = GetBrushLocation();
|
||||
|
||||
if (Map == null || BrushLocation.X >= Map.MapSize.X ||
|
||||
BrushLocation.Y >= Map.MapSize.Y ||
|
||||
BrushLocation.X < 0 ||
|
||||
BrushLocation.Y < 0)
|
||||
return;
|
||||
|
||||
Actor = null;
|
||||
Brush = null;
|
||||
Resource = null;
|
||||
Waypoint = null;
|
||||
|
||||
var key = Map.Actors.FirstOrDefault(a => a.Value.Location() == GetBrushLocation());
|
||||
var key = Map.Actors.FirstOrDefault(a => a.Value.Location() == BrushLocation);
|
||||
if (key.Key != null) Map.Actors.Remove(key.Key);
|
||||
|
||||
if (Map.MapResources[GetBrushLocation().X, GetBrushLocation().Y].type != 0)
|
||||
if (Map.MapResources[BrushLocation.X, BrushLocation.Y].type != 0)
|
||||
{
|
||||
Map.MapResources[GetBrushLocation().X, GetBrushLocation().Y] = new TileReference<byte, byte>();
|
||||
var ch = new int2((GetBrushLocation().X) / ChunkSize, (GetBrushLocation().Y) / ChunkSize);
|
||||
Map.MapResources[BrushLocation.X, BrushLocation.Y] = new TileReference<byte, byte>();
|
||||
var ch = new int2((BrushLocation.X) / ChunkSize, (BrushLocation.Y) / ChunkSize);
|
||||
if (Chunks.ContainsKey(ch))
|
||||
{
|
||||
Chunks[ch].Dispose();
|
||||
@@ -227,7 +273,7 @@ namespace OpenRA.Editor
|
||||
}
|
||||
}
|
||||
|
||||
var k = Map.Waypoints.FirstOrDefault(a => a.Value == GetBrushLocation());
|
||||
var k = Map.Waypoints.FirstOrDefault(a => a.Value == BrushLocation);
|
||||
if (k.Key != null) Map.Waypoints.Remove(k.Key);
|
||||
|
||||
AfterChange();
|
||||
@@ -294,6 +340,8 @@ namespace OpenRA.Editor
|
||||
{
|
||||
base.OnMouseDown(e);
|
||||
|
||||
if (Map == null) return;
|
||||
|
||||
if (!IsPanning)
|
||||
{
|
||||
if (e.Button == MouseButtons.Right) Erase();
|
||||
@@ -308,7 +356,7 @@ namespace OpenRA.Editor
|
||||
Bitmap RenderChunk(int u, int v)
|
||||
{
|
||||
|
||||
var bitmap = new Bitmap(ChunkSize * 24, ChunkSize * 24);
|
||||
var bitmap = new Bitmap(ChunkSize * TileSet.TileSize, ChunkSize * TileSet.TileSize);
|
||||
bitmap.SetPixel(0, 0, Color.Green);
|
||||
|
||||
var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
|
||||
@@ -326,9 +374,9 @@ namespace OpenRA.Editor
|
||||
var tile = TileSet.Tiles[tr.type];
|
||||
var index = (tr.image < tile.TileBitmapBytes.Count) ? tr.image : (byte)0;
|
||||
var rawImage = tile.TileBitmapBytes[index];
|
||||
for (var x = 0; x < 24; x++)
|
||||
for (var y = 0; y < 24; y++)
|
||||
p[(j * 24 + y) * stride + i * 24 + x] = Palette.GetColor(rawImage[x + 24 * y]).ToArgb();
|
||||
for (var x = 0; x < TileSet.TileSize; x++)
|
||||
for (var y = 0; y < TileSet.TileSize; y++)
|
||||
p[(j * TileSet.TileSize + y) * stride + i * TileSet.TileSize + x] = Palette.GetColor(rawImage[x + TileSet.TileSize * y]).ToArgb();
|
||||
|
||||
if (Map.MapResources[u * ChunkSize + i, v * ChunkSize + j].type != 0)
|
||||
{
|
||||
@@ -339,12 +387,12 @@ namespace OpenRA.Editor
|
||||
int* q = (int*)srcdata.Scan0.ToPointer();
|
||||
var srcstride = srcdata.Stride >> 2;
|
||||
|
||||
for (var x = 0; x < 24; x++)
|
||||
for (var y = 0; y < 24; y++)
|
||||
for (var x = 0; x < TileSet.TileSize; x++)
|
||||
for (var y = 0; y < TileSet.TileSize; y++)
|
||||
{
|
||||
var c = q[y * srcstride + x];
|
||||
if ((c & 0xff000000) != 0) /* quick & dirty, i cbf doing real alpha */
|
||||
p[(j * 24 + y) * stride + i * 24 + x] = c;
|
||||
p[(j * TileSet.TileSize + y) * stride + i * TileSet.TileSize + x] = c;
|
||||
}
|
||||
|
||||
resourceImage.UnlockBits(srcdata);
|
||||
@@ -358,28 +406,82 @@ namespace OpenRA.Editor
|
||||
|
||||
int2 GetBrushLocation()
|
||||
{
|
||||
var v = MousePos - Offset;
|
||||
return new int2(v.X / 24, v.Y / 24);
|
||||
var vX = (int)Math.Floor((MousePos.X - Offset.X) / Zoom);
|
||||
var vY = (int)Math.Floor((MousePos.Y - Offset.Y) / Zoom);
|
||||
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)
|
||||
{
|
||||
g.DrawImage(t.Bitmap,
|
||||
((24 * p + Offset
|
||||
- (t.Centered
|
||||
? new int2(t.Bitmap.Width / 2 - 12, t.Bitmap.Height / 2 - 12)
|
||||
: int2.Zero)).ToPoint()));
|
||||
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 = 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)
|
||||
{
|
||||
float OffsetX = bmp.Width / 2 - TileSet.TileSize / 2;
|
||||
float DrawX = TileSet.TileSize * location.X * Zoom + Offset.X - OffsetX;
|
||||
|
||||
float OffsetY = bmp.Height / 2 - TileSet.TileSize / 2;
|
||||
float DrawY = TileSet.TileSize * location.Y * Zoom + Offset.Y - OffsetY;
|
||||
|
||||
float width = bmp.Width * Zoom;
|
||||
float height = bmp.Height * Zoom;
|
||||
RectangleF sourceRect = new RectangleF(0, 0, bmp.Width, bmp.Height);
|
||||
RectangleF destRect = new RectangleF(DrawX, DrawY, width, height);
|
||||
g.DrawImage(bmp, destRect, sourceRect, GraphicsUnit.Pixel);
|
||||
}
|
||||
|
||||
void DrawActorBorder(System.Drawing.Graphics g, int2 p, ActorTemplate t)
|
||||
{
|
||||
var origin = (24 * p + Offset
|
||||
- (t.Centered
|
||||
? new int2(t.Bitmap.Width / 2 - 12, t.Bitmap.Height / 2 - 12)
|
||||
: int2.Zero)).ToPoint();
|
||||
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 = centered ? t.Bitmap.Height / 2 - TileSet.TileSize / 2 : 0;
|
||||
float DrawY = TileSet.TileSize * p.Y * Zoom + Offset.Y - OffsetY;
|
||||
|
||||
g.DrawRectangle(CordonPen,
|
||||
origin.X, origin.Y,
|
||||
t.Bitmap.Width, t.Bitmap.Height );
|
||||
DrawX, DrawY,
|
||||
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)
|
||||
@@ -387,42 +489,56 @@ namespace OpenRA.Editor
|
||||
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);
|
||||
e.Graphics.DrawImage(Chunks[x], (24 * ChunkSize * x + Offset).ToPoint());
|
||||
|
||||
Bitmap bmp = Chunks[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);
|
||||
e.Graphics.DrawImage(bmp, destRect, sourceRect, GraphicsUnit.Pixel);
|
||||
}
|
||||
|
||||
e.Graphics.DrawRectangle(CordonPen,
|
||||
new Rectangle(Map.XOffset * 24 + Offset.X, Map.YOffset * 24 + Offset.Y, Map.Width * 24, Map.Height * 24));
|
||||
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, new Rectangle(
|
||||
24 * wp.Value.X + Offset.X + 4,
|
||||
24 * wp.Value.Y + Offset.Y + 4,
|
||||
16, 16));
|
||||
e.Graphics.DrawRectangle(Pens.LimeGreen,
|
||||
TileSet.TileSize * wp.Value.X * Zoom + Offset.X + 4,
|
||||
TileSet.TileSize * wp.Value.Y * Zoom + Offset.Y + 4,
|
||||
(TileSet.TileSize - 8) * Zoom, (TileSet.TileSize - 8) * Zoom);
|
||||
|
||||
if (Brush != null)
|
||||
e.Graphics.DrawImage(Brush.Bitmap,
|
||||
(24 * GetBrushLocation() + Offset).ToPoint());
|
||||
TileSet.TileSize * GetBrushLocation().X * Zoom + Offset.X,
|
||||
TileSet.TileSize * GetBrushLocation().Y * Zoom + Offset.Y,
|
||||
Brush.Bitmap.Width * Zoom,
|
||||
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)
|
||||
e.Graphics.DrawImage(Resource.Bitmap,
|
||||
(24 * GetBrushLocation() + Offset).ToPoint());
|
||||
DrawImage(e.Graphics, Resource.Bitmap, GetBrushLocation());
|
||||
|
||||
if (Waypoint != null)
|
||||
e.Graphics.DrawRectangle(Pens.LimeGreen, new Rectangle(
|
||||
24 * GetBrushLocation().X + Offset.X + 4,
|
||||
24 * GetBrushLocation().Y + Offset.Y + 4,
|
||||
16, 16));
|
||||
e.Graphics.DrawRectangle(Pens.LimeGreen,
|
||||
TileSet.TileSize * GetBrushLocation().X * Zoom + Offset.X + 4,
|
||||
TileSet.TileSize * GetBrushLocation().Y * Zoom + Offset.Y + 4,
|
||||
(TileSet.TileSize - 8) * Zoom, (TileSet.TileSize - 8) * Zoom);
|
||||
|
||||
if (Brush == null && Actor == null && Resource == null)
|
||||
{
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
22
OpenRA.FileFormats/Exts.cs
Normal file → Executable file
22
OpenRA.FileFormats/Exts.cs
Normal file → Executable file
@@ -73,5 +73,27 @@ namespace OpenRA
|
||||
{
|
||||
return mi.GetCustomAttributes(typeof(T), true).Length != 0;
|
||||
}
|
||||
|
||||
public static T[] GetCustomAttributes<T>( this MemberInfo mi, bool inherit )
|
||||
where T : class
|
||||
{
|
||||
return (T[])mi.GetCustomAttributes( typeof( T ), inherit );
|
||||
}
|
||||
|
||||
public static T[] GetCustomAttributes<T>( this ParameterInfo mi )
|
||||
where T : class
|
||||
{
|
||||
return (T[])mi.GetCustomAttributes( typeof( T ), true );
|
||||
}
|
||||
|
||||
public static T Clamp<T>(this T val, T min, T max) where T : IComparable<T>
|
||||
{
|
||||
if (val.CompareTo(min) < 0)
|
||||
return min;
|
||||
else if (val.CompareTo(max) > 0)
|
||||
return max;
|
||||
else
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
29
OpenRA.FileFormats/FieldLoader.cs
Normal file → Executable file
29
OpenRA.FileFormats/FieldLoader.cs
Normal file → Executable file
@@ -13,6 +13,7 @@ using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Globalization;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
@@ -99,11 +100,19 @@ namespace OpenRA.FileFormats
|
||||
else if (fieldType == typeof(float))
|
||||
{
|
||||
float res;
|
||||
if (float.TryParse(x.Replace("%",""), out res))
|
||||
if (float.TryParse(x.Replace("%",""), System.Globalization.NumberStyles.Any, NumberFormatInfo.InvariantInfo, out res))
|
||||
return res * (x.Contains( '%' ) ? 0.01f : 1f);
|
||||
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;
|
||||
|
||||
@@ -111,9 +120,9 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
var parts = x.Split(',');
|
||||
if (parts.Length == 3)
|
||||
return Color.FromArgb(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]));
|
||||
return Color.FromArgb(int.Parse(parts[0]).Clamp(0, 255), int.Parse(parts[1]).Clamp(0, 255), int.Parse(parts[2]).Clamp(0, 255));
|
||||
if (parts.Length == 4)
|
||||
return Color.FromArgb(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), int.Parse(parts[3]));
|
||||
return Color.FromArgb(int.Parse(parts[0]).Clamp(0, 255), int.Parse(parts[1]).Clamp(0, 255), int.Parse(parts[2]).Clamp(0, 255), int.Parse(parts[3]).Clamp(0, 255));
|
||||
return InvalidValueAction(x,fieldType, field);
|
||||
}
|
||||
|
||||
@@ -177,11 +186,12 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
var ret = new Dictionary<FieldInfo, Func<string, Type, MiniYaml, object>>();
|
||||
|
||||
foreach( var field in type.GetFields() )
|
||||
foreach( var ff in type.GetFields() )
|
||||
{
|
||||
var load = (LoadAttribute[])field.GetCustomAttributes( typeof( LoadAttribute ), false );
|
||||
var loadUsing = (LoadUsingAttribute[])field.GetCustomAttributes( typeof( LoadUsingAttribute ), false );
|
||||
var fromYamlKey = (FieldFromYamlKeyAttribute[])field.GetCustomAttributes( typeof( FieldFromYamlKeyAttribute ), false );
|
||||
var field = ff;
|
||||
var load = field.GetCustomAttributes<LoadAttribute>( false );
|
||||
var loadUsing = field.GetCustomAttributes<LoadUsingAttribute>( false );
|
||||
var fromYamlKey = field.GetCustomAttributes<FieldFromYamlKeyAttribute>( false );
|
||||
if( loadUsing.Length != 0 )
|
||||
ret[ field ] = ( _1, fieldType, yaml ) => loadUsing[ 0 ].LoaderFunc( field )( yaml );
|
||||
else if( fromYamlKey.Length != 0 )
|
||||
@@ -262,7 +272,10 @@ namespace OpenRA.FileFormats
|
||||
if (f.FieldType == typeof(Color))
|
||||
{
|
||||
var c = (Color)v;
|
||||
return "{0},{1},{2},{3}".F(c.A,c.R,c.G,c.B);
|
||||
return "{0},{1},{2},{3}".F(((int)c.A).Clamp(0, 255),
|
||||
((int)c.R).Clamp(0, 255),
|
||||
((int)c.G).Clamp(0, 255),
|
||||
((int)c.B).Clamp(0, 255));
|
||||
}
|
||||
|
||||
return f.FieldType.IsArray
|
||||
|
||||
@@ -102,6 +102,20 @@ namespace OpenRA.FileFormats
|
||||
return output;
|
||||
}
|
||||
|
||||
public static float SoundLength(Stream s)
|
||||
{
|
||||
var br = new BinaryReader(s);
|
||||
var sampleRate = br.ReadUInt16();
|
||||
/*var dataSize = */ br.ReadInt32();
|
||||
var outputSize = br.ReadInt32();
|
||||
var flags = (SoundFlags) br.ReadByte();
|
||||
|
||||
var samples = outputSize;
|
||||
if (0 != (flags & SoundFlags.Stereo)) samples /= 2;
|
||||
if (0 != (flags & SoundFlags._16Bit)) samples /= 2;
|
||||
return samples / sampleRate;
|
||||
}
|
||||
|
||||
public static byte[] LoadSound(Stream s)
|
||||
{
|
||||
var br = new BinaryReader(s);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,22 @@ 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);
|
||||
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(".Z", StringComparison.InvariantCultureIgnoreCase))
|
||||
return new InstallShieldPackage(filename, order++);
|
||||
else
|
||||
return new Folder(filename);
|
||||
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 +68,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 +87,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 +107,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 );
|
||||
}
|
||||
@@ -17,7 +17,9 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
readonly string path;
|
||||
|
||||
public Folder(string path) { this.path = path; }
|
||||
int priority;
|
||||
|
||||
public Folder(string path, int priority) { this.path = path; this.priority = priority; }
|
||||
|
||||
public Stream GetContent(string filename)
|
||||
{
|
||||
@@ -28,12 +30,18 @@ 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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
121
OpenRA.FileFormats/Filesystem/InstallShieldPackage.cs
Normal file
121
OpenRA.FileFormats/Filesystem/InstallShieldPackage.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
#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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,17 +20,20 @@ namespace OpenRA.FileFormats
|
||||
Stream GetContent(string filename);
|
||||
bool Exists(string filename);
|
||||
IEnumerable<uint> AllFileHashes();
|
||||
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;
|
||||
int priority;
|
||||
|
||||
public Package(string filename)
|
||||
public MixFile(string filename, int priority)
|
||||
{
|
||||
this.priority = priority;
|
||||
s = FileSystem.Open(filename);
|
||||
|
||||
BinaryReader reader = new BinaryReader(s);
|
||||
@@ -149,6 +152,12 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
return index.ContainsKey(PackageEntry.HashFilename(filename));
|
||||
}
|
||||
|
||||
|
||||
public int Priority
|
||||
{
|
||||
get { return 1000 + priority; }
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
52
OpenRA.FileFormats/Filesystem/ZipFile.cs
Normal file
52
OpenRA.FileFormats/Filesystem/ZipFile.cs
Normal 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using SZipFile = ICSharpCode.SharpZipLib.Zip.ZipFile;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
public class ZipFile : IFolder
|
||||
{
|
||||
readonly SZipFile pkg;
|
||||
int priority;
|
||||
|
||||
public ZipFile(string filename, int priority)
|
||||
{
|
||||
this.priority = priority;
|
||||
pkg = new SZipFile(File.OpenRead(filename));
|
||||
}
|
||||
|
||||
public Stream GetContent(string filename)
|
||||
{
|
||||
return pkg.GetInputStream(pkg.GetEntry(filename));
|
||||
}
|
||||
|
||||
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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
@@ -52,13 +50,13 @@ namespace OpenRA.FileFormats.Graphics
|
||||
public interface IVertexBuffer<T>
|
||||
{
|
||||
void Bind();
|
||||
void SetData( T[] vertices );
|
||||
void SetData( T[] vertices, int length );
|
||||
}
|
||||
|
||||
public interface IIndexBuffer
|
||||
{
|
||||
void Bind();
|
||||
void SetData( ushort[] indices );
|
||||
void SetData( ushort[] indices, int length );
|
||||
}
|
||||
|
||||
public interface IShader
|
||||
|
||||
@@ -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,22 +18,26 @@ namespace OpenRA.FileFormats
|
||||
public class Manifest
|
||||
{
|
||||
public readonly string[]
|
||||
Folders, Packages, Rules,
|
||||
Sequences, Chrome, Assemblies, ChromeLayout,
|
||||
Mods, Folders, Packages, Rules, ServerTraits,
|
||||
Sequences, Cursors, Chrome, Assemblies, ChromeLayout,
|
||||
Weapons, Voices, Music, Movies, TileSets;
|
||||
|
||||
public readonly string ShellmapUid, LoadScreen;
|
||||
public readonly int TileSize = 24;
|
||||
|
||||
public Manifest(string[] mods)
|
||||
{
|
||||
Mods = mods;
|
||||
var yaml = mods
|
||||
.Select(m => MiniYaml.FromFile("mods/" + m + "/mod.yaml"))
|
||||
.Aggregate(MiniYaml.Merge);
|
||||
|
||||
|
||||
// Todo: Use fieldloader
|
||||
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");
|
||||
Assemblies = YamlList(yaml, "Assemblies");
|
||||
ChromeLayout = YamlList(yaml, "ChromeLayout");
|
||||
@@ -45,6 +49,9 @@ namespace OpenRA.FileFormats
|
||||
|
||||
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)
|
||||
TileSize = int.Parse(yaml.First( x => x.Key == "TileSize" ).Value.Value);
|
||||
}
|
||||
|
||||
static string[] YamlList(List<MiniYamlNode> ys, string key)
|
||||
|
||||
@@ -17,13 +17,14 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
public class MapStub
|
||||
{
|
||||
public readonly IFolder Package;
|
||||
public IFolder Container { get; protected set; }
|
||||
|
||||
// Yaml map data
|
||||
public readonly string Uid;
|
||||
public string Uid { get; protected set; }
|
||||
[FieldLoader.Load] public bool Selectable;
|
||||
|
||||
[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,16 +36,17 @@ 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(IFolder container)
|
||||
{
|
||||
Package = package;
|
||||
var yaml = MiniYaml.FromStream(Package.GetContent("map.yaml"));
|
||||
Container = container;
|
||||
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 = Container.GetContent("map.uid").ReadAllText();
|
||||
}
|
||||
|
||||
static object LoadWaypoints( MiniYaml y )
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -17,15 +17,15 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
public readonly List<byte[]> TileBitmapBytes = new List<byte[]>();
|
||||
|
||||
public Terrain( Stream stream )
|
||||
public Terrain( Stream stream, int size )
|
||||
{
|
||||
// Try loading as a cnc .tem
|
||||
BinaryReader reader = new BinaryReader( stream );
|
||||
int Width = reader.ReadUInt16();
|
||||
int Height = reader.ReadUInt16();
|
||||
|
||||
if( Width != 24 || Height != 24 )
|
||||
throw new InvalidDataException( string.Format( "{0}x{1}", Width, Height ) );
|
||||
|
||||
if( Width != size || Height != size )
|
||||
throw new InvalidDataException( "{0}x{1} != {2}x{2}".F(Width, Height, size ) );
|
||||
|
||||
/*NumTiles = */reader.ReadUInt16();
|
||||
/*Zero1 = */reader.ReadUInt16();
|
||||
@@ -46,9 +46,7 @@ namespace OpenRA.FileFormats
|
||||
reader = new BinaryReader( stream );
|
||||
Width = reader.ReadUInt16();
|
||||
Height = reader.ReadUInt16();
|
||||
if( Width != 24 || Height != 24 )
|
||||
throw new InvalidDataException( string.Format( "{0}x{1}", Width, Height ) );
|
||||
|
||||
|
||||
/*NumTiles = */reader.ReadUInt16();
|
||||
reader.ReadUInt16();
|
||||
/*XDim = */reader.ReadUInt16();
|
||||
@@ -67,8 +65,8 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
if (b != 255)
|
||||
{
|
||||
stream.Position = ImgStart + b * 24 * 24;
|
||||
TileBitmapBytes.Add(new BinaryReader(stream).ReadBytes(24 * 24));
|
||||
stream.Position = ImgStart + b * size * size;
|
||||
TileBitmapBytes.Add(new BinaryReader(stream).ReadBytes(size * size));
|
||||
}
|
||||
else
|
||||
TileBitmapBytes.Add(null);
|
||||
|
||||
@@ -74,11 +74,12 @@ namespace OpenRA.FileFormats
|
||||
public string Name;
|
||||
public string Id;
|
||||
public string Palette;
|
||||
public int TileSize = 24;
|
||||
public string[] Extensions;
|
||||
public Dictionary<string, TerrainTypeInfo> Terrain = new Dictionary<string, TerrainTypeInfo>();
|
||||
public Dictionary<ushort, Terrain> Tiles = new Dictionary<ushort, Terrain>();
|
||||
public Dictionary<ushort, TileTemplate> Templates = new Dictionary<ushort, TileTemplate>();
|
||||
static List<string> fields = new List<string>() {"Name", "Id", "Palette", "Extensions"};
|
||||
static List<string> fields = new List<string>() {"Name", "TileSize", "Id", "Palette", "Extensions"};
|
||||
|
||||
public TileSet() {}
|
||||
public TileSet( string filepath )
|
||||
@@ -103,20 +104,13 @@ namespace OpenRA.FileFormats
|
||||
using( Stream s = FileSystem.OpenWithExts(t.Value.Image, Extensions) )
|
||||
{
|
||||
if( !Tiles.ContainsKey( t.Key ) )
|
||||
Tiles.Add( t.Key, new Terrain( s ) );
|
||||
Tiles.Add( t.Key, new Terrain( s, TileSize ) );
|
||||
}
|
||||
}
|
||||
|
||||
public void Save(string filepath)
|
||||
{
|
||||
var root = new List<MiniYamlNode>();
|
||||
foreach (var field in fields)
|
||||
{
|
||||
FieldInfo f = this.GetType().GetField(field);
|
||||
if (f.GetValue(this) == null) continue;
|
||||
root.Add( new MiniYamlNode( field, FieldSaver.FormatValue( this, f ) ) );
|
||||
}
|
||||
|
||||
var gen = new List<MiniYamlNode>();
|
||||
foreach (var field in fields)
|
||||
{
|
||||
@@ -144,7 +138,7 @@ namespace OpenRA.FileFormats
|
||||
if( Tiles.TryGetValue( r.type, out tile ) )
|
||||
return tile.TileBitmapBytes[ r.image ];
|
||||
|
||||
byte[] missingTile = new byte[ 24 * 24 ];
|
||||
byte[] missingTile = new byte[ TileSize * TileSize ];
|
||||
for( int i = 0 ; i < missingTile.Length ; i++ )
|
||||
missingTile[ i ] = 0x36;
|
||||
|
||||
|
||||
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,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>
|
||||
@@ -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,14 @@
|
||||
<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\PackageWriter.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 +118,7 @@
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<ItemGroup>
|
||||
<Folder Include="Filesystem\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -8,11 +8,8 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
@@ -57,6 +54,7 @@ namespace OpenRA.FileFormats
|
||||
colors[0] = 0;
|
||||
if (remapTransparent)
|
||||
{
|
||||
colors[1] = 178u << 24; // Hack for d2k; may have side effects
|
||||
colors[3] = 178u << 24;
|
||||
colors[4] = 140u << 24;
|
||||
}
|
||||
|
||||
38
OpenRA.FileFormats/PlayerColorRemap.cs
Normal file → Executable file
38
OpenRA.FileFormats/PlayerColorRemap.cs
Normal file → Executable file
@@ -15,15 +15,16 @@ using System.Linq;
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
// TODO: ship this out of here.
|
||||
public enum PaletteFormat { ra, cnc, d2k }
|
||||
|
||||
public class PlayerColorRemap : IPaletteRemap
|
||||
{
|
||||
Dictionary<int, Color> remapColors;
|
||||
|
||||
public PlayerColorRemap(Color c1, Color c2, bool useSplitRamp)
|
||||
public PlayerColorRemap(Color c1, Color c2, PaletteFormat fmt)
|
||||
{
|
||||
var baseIndex = useSplitRamp ? 0xb0 : 80;
|
||||
var ramp = useSplitRamp
|
||||
var baseIndex = (fmt == PaletteFormat.cnc) ? 0xb0 : (fmt == PaletteFormat.d2k) ? 240 : 80;
|
||||
var ramp = (fmt == PaletteFormat.cnc)
|
||||
? new[] { 0, 2, 4, 6, 8, 10, 13, 15, 1, 3, 5, 7, 9, 11, 12, 14 }
|
||||
: new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
||||
|
||||
@@ -31,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),
|
||||
@@ -45,5 +46,34 @@ namespace OpenRA.FileFormats
|
||||
return remapColors.TryGetValue(index, out c)
|
||||
? c : original;
|
||||
}
|
||||
|
||||
// hk is hue in the range [0,1] instead of [0,360]
|
||||
public static Color ColorFromHSL(float hk, float s, float l)
|
||||
{
|
||||
// Convert from HSL to RGB
|
||||
var q = (l < 0.5f) ? l * (1 + s) : l + s - (l * s);
|
||||
var p = 2 * l - q;
|
||||
|
||||
float[] trgb = { hk + 1 / 3.0f,
|
||||
hk,
|
||||
hk - 1/3.0f };
|
||||
float[] rgb = { 0, 0, 0 };
|
||||
|
||||
for (int k = 0; k < 3; k++)
|
||||
{
|
||||
while (trgb[k] < 0) trgb[k] += 1.0f;
|
||||
while (trgb[k] > 1) trgb[k] -= 1.0f;
|
||||
}
|
||||
|
||||
for (int k = 0; k < 3; k++)
|
||||
{
|
||||
if (trgb[k] < 1 / 6.0f) { rgb[k] = (p + ((q - p) * 6 * trgb[k])); }
|
||||
else if (trgb[k] >= 1 / 6.0f && trgb[k] < 0.5) { rgb[k] = q; }
|
||||
else if (trgb[k] >= 0.5f && trgb[k] < 2.0f / 3) { rgb[k] = (p + ((q - p) * 6 * (2.0f / 3 - trgb[k]))); }
|
||||
else { rgb[k] = p; }
|
||||
}
|
||||
|
||||
return Color.FromArgb((int)(rgb[0] * 255), (int)(rgb[1] * 255), (int)(rgb[2] * 255));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ); }
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@ namespace OpenRA
|
||||
public static int2 operator -(int2 a, int2 b) { return new int2(a.X - b.X, a.Y - b.Y); }
|
||||
public static int2 operator *(int a, int2 b) { return new int2(a * b.X, a * b.Y); }
|
||||
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); }
|
||||
@@ -59,5 +62,21 @@ namespace OpenRA
|
||||
{
|
||||
return (uint)((orig & 0xff000000) >> 24) | ((orig & 0x00ff0000) >> 8) | ((orig & 0x0000ff00) << 8) | ((orig & 0x000000ff) << 24);
|
||||
}
|
||||
|
||||
public static int Lerp( int a, int b, int mul, int div )
|
||||
{
|
||||
return a + ( b - a ) * mul / div;
|
||||
}
|
||||
|
||||
public static int2 Lerp( int2 a, int2 b, int mul, int div )
|
||||
{
|
||||
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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
5
OpenRA.FileFormats/Thirdparty/Random.cs
vendored
5
OpenRA.FileFormats/Thirdparty/Random.cs
vendored
@@ -18,6 +18,8 @@ namespace OpenRA.Thirdparty
|
||||
{
|
||||
uint[] mt = new uint[624];
|
||||
int index = 0;
|
||||
|
||||
public int Last;
|
||||
|
||||
public Random() : this(Environment.TickCount) { }
|
||||
|
||||
@@ -39,7 +41,8 @@ namespace OpenRA.Thirdparty
|
||||
y ^= y >> 18;
|
||||
|
||||
index = (index + 1) % 624;
|
||||
return (int)(y % int.MaxValue);
|
||||
Last = (int)(y % int.MaxValue);
|
||||
return Last;
|
||||
}
|
||||
|
||||
public int Next(int low, int high) { return low + Next() % (high - low); }
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
* 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.FileFormats;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.Traits;
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Traits;
|
||||
using OpenRA.Traits.Activities;
|
||||
|
||||
namespace OpenRA
|
||||
@@ -26,11 +26,12 @@ namespace OpenRA
|
||||
public readonly World World;
|
||||
public readonly uint ActorID;
|
||||
|
||||
public int2 Location { get { return Trait<IOccupySpace>().TopLeft; } }
|
||||
public int2 Location { get { return Trait<IOccupySpace>().TopLeft; } }
|
||||
public float2 CenterLocation { get { return Trait<IHasLocation>().PxPosition; } }
|
||||
[Sync]
|
||||
public Player Owner;
|
||||
|
||||
IActivity currentActivity;
|
||||
private IActivity currentActivity;
|
||||
public Group Group;
|
||||
|
||||
internal Actor(World world, string name, TypeDictionary initDict )
|
||||
@@ -52,9 +53,6 @@ namespace OpenRA
|
||||
AddTrait(trait.Create(init));
|
||||
}
|
||||
|
||||
if( CenterLocation == float2.Zero && HasTrait<IOccupySpace>() )
|
||||
CenterLocation = Traits.Util.CenterOfCell(Location);
|
||||
|
||||
Size = Lazy.New(() =>
|
||||
{
|
||||
var si = Info.Traits.GetOrDefault<SelectableInfo>();
|
||||
@@ -63,39 +61,25 @@ 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;
|
||||
currentActivity = a.Tick(this) ?? new Idle();
|
||||
if (currentActivity == null)
|
||||
foreach (var ni in TraitsImplementing<INotifyIdle>())
|
||||
ni.TickIdle(this);
|
||||
|
||||
if (a == currentActivity) break;
|
||||
|
||||
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; }
|
||||
}
|
||||
|
||||
public float2 CenterLocation;
|
||||
|
||||
OpenRA.FileFormats.Lazy<float2> Size;
|
||||
|
||||
public IEnumerable<Renderable> Render()
|
||||
@@ -105,34 +89,12 @@ namespace OpenRA
|
||||
return mods.Aggregate(sprites, (m, p) => p.ModifyRender(this, m));
|
||||
}
|
||||
|
||||
public Order Order( int2 xy, MouseInput mi )
|
||||
{
|
||||
if (Owner != World.LocalPlayer)
|
||||
return null;
|
||||
|
||||
if (!World.Map.IsInMap(xy.X, xy.Y))
|
||||
return null;
|
||||
|
||||
if (Destroyed)
|
||||
return null;
|
||||
|
||||
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();
|
||||
|
||||
return TraitsImplementing<IIssueOrder>()
|
||||
.Select( x => x.IssueOrder( this, xy, mi, underCursor ) )
|
||||
.FirstOrDefault( x => x != null );
|
||||
}
|
||||
|
||||
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]);
|
||||
|
||||
@@ -145,21 +107,21 @@ 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 )
|
||||
{
|
||||
if( currentActivity == null )
|
||||
{
|
||||
currentActivity = nextActivity;
|
||||
return;
|
||||
}
|
||||
var act = currentActivity;
|
||||
while( act.NextActivity != null )
|
||||
{
|
||||
act = act.NextActivity;
|
||||
}
|
||||
act.NextActivity = nextActivity;
|
||||
else
|
||||
currentActivity.Queue( nextActivity );
|
||||
}
|
||||
|
||||
public void CancelActivity()
|
||||
@@ -168,7 +130,6 @@ namespace OpenRA
|
||||
currentActivity.Cancel( this );
|
||||
}
|
||||
|
||||
// For pathdebug, et al
|
||||
public IActivity GetCurrentActivity()
|
||||
{
|
||||
return currentActivity;
|
||||
@@ -221,6 +182,8 @@ namespace OpenRA
|
||||
{
|
||||
World.AddFrameEndTask( w =>
|
||||
{
|
||||
if (Destroyed) return;
|
||||
|
||||
World.Remove( this );
|
||||
World.traitDict.RemoveActor( this );
|
||||
Destroyed = true;
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
@@ -45,7 +45,7 @@ namespace OpenRA.FileFormats
|
||||
foreach( var init in InitDict )
|
||||
{
|
||||
var initName = init.GetType().Name;
|
||||
ret.NodesDict.Add( initName.Substring( 0, initName.Length - 4 ), FieldSaver.Save( init ) );
|
||||
ret.Nodes.Add( new MiniYamlNode( initName.Substring( 0, initName.Length - 4 ), FieldSaver.Save( init ) ) );
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -17,12 +17,12 @@ namespace OpenRA
|
||||
CursorSequence sequence;
|
||||
public Cursor(string cursor)
|
||||
{
|
||||
sequence = SequenceProvider.GetCursorSequence(cursor);
|
||||
sequence = CursorProvider.GetCursorSequence(cursor);
|
||||
}
|
||||
|
||||
public void Draw(int frame, float2 pos)
|
||||
public void Draw(WorldRenderer wr, int frame, float2 pos)
|
||||
{
|
||||
Game.Renderer.SpriteRenderer.DrawSprite(sequence.GetSprite(frame), pos - sequence.Hotspot, sequence.Palette);
|
||||
sequence.GetSprite(frame).DrawAt(wr, pos - sequence.Hotspot, sequence.Palette);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
23
OpenRA.Game/Exts.cs
Normal file → Executable file
23
OpenRA.Game/Exts.cs
Normal file → Executable file
@@ -12,7 +12,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
@@ -34,17 +33,17 @@ namespace OpenRA
|
||||
return xs.Aggregate(1f, (a, x) => a * x);
|
||||
}
|
||||
|
||||
public static V GetOrAdd<K, V>( this Dictionary<K, V> d, K k )
|
||||
public static V GetOrAdd<K, V>(this Dictionary<K, V> d, K k)
|
||||
where V : new()
|
||||
{
|
||||
return d.GetOrAdd( k, _ => new V() );
|
||||
return d.GetOrAdd(k, _ => new V());
|
||||
}
|
||||
|
||||
public static V GetOrAdd<K, V>( this Dictionary<K, V> d, K k, Func<K, V> createFn )
|
||||
public static V GetOrAdd<K, V>(this Dictionary<K, V> d, K k, Func<K, V> createFn)
|
||||
{
|
||||
V ret;
|
||||
if( !d.TryGetValue( k, out ret ) )
|
||||
d.Add( k, ret = createFn( k ) );
|
||||
if (!d.TryGetValue(k, out ret))
|
||||
d.Add(k, ret = createFn(k));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -54,18 +53,18 @@ namespace OpenRA
|
||||
return xs[r.Next(xs.Length)];
|
||||
}
|
||||
|
||||
public static void DoTimed<T>( this IEnumerable<T> e, Action<T> a, string text, double time )
|
||||
public static void DoTimed<T>(this IEnumerable<T> e, Action<T> a, string text, double time)
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
|
||||
e.Do( x =>
|
||||
e.Do(x =>
|
||||
{
|
||||
var t = sw.ElapsedTime();
|
||||
a( x );
|
||||
a(x);
|
||||
var dt = sw.ElapsedTime() - t;
|
||||
if( dt > time )
|
||||
Log.Write("perf", text, x, dt * 1000);
|
||||
} );
|
||||
if (dt > time)
|
||||
Log.Write("perf", text, x, dt * 1000, Game.LocalTick);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
393
OpenRA.Game/Game.cs
Normal file → Executable file
393
OpenRA.Game/Game.cs
Normal file → Executable file
@@ -6,71 +6,62 @@
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using System.Net;
|
||||
using System.Windows.Forms;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Server;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Traits;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
using Timer = OpenRA.Support.Timer;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Server;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
using XRandom = OpenRA.Thirdparty.Random;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public static class Game
|
||||
{
|
||||
public static readonly int CellSize = 24;
|
||||
public static int CellSize { get { return modData.Manifest.TileSize; } }
|
||||
|
||||
public static ModData modData;
|
||||
public static World world;
|
||||
private static WorldRenderer worldRenderer;
|
||||
|
||||
public static Viewport viewport;
|
||||
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 Session LobbyInfo = new Session();
|
||||
public static bool HasInputFocus = false;
|
||||
|
||||
static void LoadMap(string uid)
|
||||
{
|
||||
var map = modData.PrepareMap(uid);
|
||||
|
||||
viewport = new Viewport(new float2(Renderer.Resolution), map.TopLeft, map.BottomRight, Renderer);
|
||||
world = null; // trying to access the old world will NRE, rather than silently doing it wrong.
|
||||
world = new World(modData.Manifest, map);
|
||||
}
|
||||
|
||||
public static void MoveViewport(int2 loc)
|
||||
public static void MoveViewport(float2 loc)
|
||||
{
|
||||
viewport.Center(loc);
|
||||
}
|
||||
|
||||
internal static string CurrentHost = "";
|
||||
internal static int CurrentPort = 0;
|
||||
|
||||
internal static void JoinServer(string host, int port)
|
||||
public static void JoinServer(string host, int port)
|
||||
{
|
||||
if (orderManager != null) orderManager.Dispose();
|
||||
|
||||
CurrentHost = host;
|
||||
CurrentPort = port;
|
||||
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 ) );
|
||||
lastConnectionState = ConnectionState.PreConnecting;
|
||||
ConnectionStateChanged();
|
||||
|
||||
orderManager = new OrderManager(new NetworkConnection(host, port), ChooseReplayFilename());
|
||||
ConnectionStateChanged(orderManager);
|
||||
}
|
||||
|
||||
static string ChooseReplayFilename()
|
||||
@@ -80,71 +71,37 @@ namespace OpenRA
|
||||
|
||||
static void JoinLocal()
|
||||
{
|
||||
lastConnectionState = ConnectionState.PreConnecting;
|
||||
ConnectionStateChanged();
|
||||
|
||||
if (orderManager != null) orderManager.Dispose();
|
||||
orderManager = new OrderManager(new EchoConnection());
|
||||
orderManager = new OrderManager("<no server>", -1, new EchoConnection());
|
||||
lastConnectionState = ConnectionState.PreConnecting;
|
||||
ConnectionStateChanged( orderManager );
|
||||
}
|
||||
|
||||
static int lastTime = Environment.TickCount;
|
||||
|
||||
static void ResetTimer()
|
||||
{
|
||||
lastTime = Environment.TickCount;
|
||||
}
|
||||
|
||||
internal static int RenderFrame = 0;
|
||||
internal static int LocalTick = 0;
|
||||
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
|
||||
|
||||
public static event Action ConnectionStateChanged = () => { };
|
||||
static ConnectionState lastConnectionState = ConnectionState.PreConnecting;
|
||||
|
||||
public static event Action<OrderManager> ConnectionStateChanged = _ => { };
|
||||
static ConnectionState lastConnectionState = ConnectionState.PreConnecting;
|
||||
public static int LocalClientId { get { return orderManager.Connection.LocalClientId; } }
|
||||
|
||||
static void Tick()
|
||||
static void Tick( OrderManager orderManager, Viewport viewPort )
|
||||
{
|
||||
if (orderManager.Connection.ConnectionState != lastConnectionState)
|
||||
{
|
||||
lastConnectionState = orderManager.Connection.ConnectionState;
|
||||
ConnectionStateChanged();
|
||||
}
|
||||
|
||||
int t = Environment.TickCount;
|
||||
int dt = t - lastTime;
|
||||
if (dt >= Settings.Game.Timestep)
|
||||
if (orderManager.Connection.ConnectionState != lastConnectionState)
|
||||
{
|
||||
using (new PerfSample("tick_time"))
|
||||
{
|
||||
lastTime += Settings.Game.Timestep;
|
||||
Widget.DoTick(world);
|
||||
|
||||
orderManager.TickImmediate(world);
|
||||
|
||||
var isNetTick = LocalTick % NetTickScale == 0;
|
||||
|
||||
if (!isNetTick || orderManager.IsReadyForNextFrame)
|
||||
{
|
||||
++LocalTick;
|
||||
|
||||
if (isNetTick) orderManager.Tick(world);
|
||||
|
||||
world.OrderGenerator.Tick(world);
|
||||
world.Selection.Tick(world);
|
||||
world.Tick();
|
||||
|
||||
PerfHistory.Tick();
|
||||
}
|
||||
else
|
||||
if (orderManager.FrameNumber == 0)
|
||||
lastTime = Environment.TickCount;
|
||||
}
|
||||
lastConnectionState = orderManager.Connection.ConnectionState;
|
||||
ConnectionStateChanged( orderManager );
|
||||
}
|
||||
|
||||
Tick( orderManager );
|
||||
if( orderManager.world != worldRenderer.world )
|
||||
Tick( worldRenderer.world.orderManager );
|
||||
|
||||
using (new PerfSample("render"))
|
||||
{
|
||||
++RenderFrame;
|
||||
viewport.DrawRegions(world);
|
||||
viewport.DrawRegions(worldRenderer, new DefaultInputHandler( orderManager.world ));
|
||||
Sound.SetListenerPosition(viewport.Location + .5f * new float2(viewport.Width, viewport.Height));
|
||||
}
|
||||
|
||||
@@ -156,64 +113,73 @@ namespace OpenRA
|
||||
MasterServerQuery.Tick();
|
||||
}
|
||||
|
||||
internal static event Action LobbyInfoChanged = () => { };
|
||||
|
||||
internal static void SyncLobbyInfo(string data)
|
||||
private static void Tick( OrderManager orderManager )
|
||||
{
|
||||
LobbyInfo = Session.Deserialize(data);
|
||||
int t = Environment.TickCount;
|
||||
int dt = t - orderManager.LastTickTime;
|
||||
if (dt >= Settings.Game.Timestep)
|
||||
using( new PerfSample( "tick_time" ) )
|
||||
{
|
||||
orderManager.LastTickTime += Settings.Game.Timestep;
|
||||
Widget.DoTick();
|
||||
var world = orderManager.world;
|
||||
if( orderManager.GameStarted && world.LocalPlayer != null )
|
||||
++Viewport.TicksSinceLastMove;
|
||||
Sound.Tick();
|
||||
Sync.CheckSyncUnchanged( world, () => { orderManager.TickImmediate(); } );
|
||||
|
||||
if( !world.GameHasStarted )
|
||||
world.SharedRandom = new XRandom( LobbyInfo.GlobalSettings.RandomSeed );
|
||||
var isNetTick = LocalTick % NetTickScale == 0;
|
||||
|
||||
if (orderManager.Connection.ConnectionState == ConnectionState.Connected)
|
||||
world.SetLocalPlayer(orderManager.Connection.LocalClientId);
|
||||
if( !isNetTick || orderManager.IsReadyForNextFrame )
|
||||
{
|
||||
++orderManager.LocalFrameNumber;
|
||||
|
||||
if (orderManager.FramesAhead != LobbyInfo.GlobalSettings.OrderLatency
|
||||
&& !orderManager.GameStarted)
|
||||
{
|
||||
orderManager.FramesAhead = LobbyInfo.GlobalSettings.OrderLatency;
|
||||
Debug("Order lag is now {0} frames.".F(LobbyInfo.GlobalSettings.OrderLatency));
|
||||
}
|
||||
Log.Write( "debug", "--Tick: {0} ({1})", LocalTick, isNetTick ? "net" : "local" );
|
||||
|
||||
if( isNetTick ) orderManager.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;
|
||||
}
|
||||
}
|
||||
|
||||
public static event Action LobbyInfoChanged = () => { };
|
||||
public static event Action ConnectedToLobby = () => { };
|
||||
|
||||
internal static void SyncLobbyInfo()
|
||||
{
|
||||
LobbyInfoChanged();
|
||||
}
|
||||
|
||||
public static void IssueOrder(Order o) { orderManager.IssueOrder(o); } /* avoid exposing the OM to mod code */
|
||||
|
||||
|
||||
public static event Action AfterGameStart = () => {};
|
||||
public static event Action<World> AfterGameStart = _ => {};
|
||||
public static event Action BeforeGameStart = () => {};
|
||||
internal static void StartGame(string map)
|
||||
internal static void StartGame(string mapUID)
|
||||
{
|
||||
BeforeGameStart();
|
||||
LoadMap(map);
|
||||
|
||||
var map = modData.PrepareMap(mapUID);
|
||||
viewport = new Viewport(new int2(Renderer.Resolution), map.Bounds, Renderer);
|
||||
orderManager.world = new World(modData.Manifest, map, orderManager);
|
||||
worldRenderer = new WorldRenderer(orderManager.world);
|
||||
|
||||
if (orderManager.GameStarted) return;
|
||||
Widget.SelectedWidget = null;
|
||||
Widget.SelectedWidget = null;
|
||||
|
||||
orderManager.StartGame();
|
||||
viewport.RefreshPalette();
|
||||
AfterGameStart();
|
||||
}
|
||||
|
||||
public static void DispatchMouseInput(MouseInputEvent ev, MouseEventArgs e, Modifiers modifierKeys)
|
||||
{
|
||||
if (world == null)
|
||||
return;
|
||||
|
||||
int sync = world.SyncHash();
|
||||
var initialWorld = world;
|
||||
|
||||
var mi = new MouseInput
|
||||
{
|
||||
Button = (MouseButton)(int)e.Button,
|
||||
Event = ev,
|
||||
Location = new int2(e.Location),
|
||||
Modifiers = modifierKeys,
|
||||
};
|
||||
Widget.HandleInput(world, mi);
|
||||
|
||||
if (sync != world.SyncHash() && world == initialWorld)
|
||||
throw new InvalidOperationException("Desync in DispatchMouseInput");
|
||||
orderManager.LocalFrameNumber = 0;
|
||||
orderManager.StartGame();
|
||||
worldRenderer.RefreshPalette();
|
||||
AfterGameStart( orderManager.world );
|
||||
}
|
||||
|
||||
public static bool IsHost
|
||||
@@ -221,62 +187,56 @@ namespace OpenRA
|
||||
get { return orderManager.Connection.LocalClientId == 0; }
|
||||
}
|
||||
|
||||
internal static Session.Client LocalClient
|
||||
{
|
||||
get { return LobbyInfo.Clients.FirstOrDefault(c => c.Index == orderManager.Connection.LocalClientId); }
|
||||
}
|
||||
|
||||
public static void HandleKeyEvent(KeyInput e)
|
||||
{
|
||||
if (world == null)
|
||||
return;
|
||||
|
||||
int sync = world.SyncHash();
|
||||
|
||||
if (Widget.HandleKeyPress(e))
|
||||
return;
|
||||
|
||||
if (sync != Game.world.SyncHash())
|
||||
throw new InvalidOperationException("Desync in OnKeyPress");
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
AppDomain.CurrentDomain.AssemblyResolve += FileSystem.ResolveAssembly;
|
||||
|
||||
var defaultSupport = Environment.GetFolderPath(Environment.SpecialFolder.Personal)
|
||||
+ Path.DirectorySeparatorChar + "OpenRA";
|
||||
|
||||
SupportDir = args.GetValue("SupportDir", defaultSupport);
|
||||
Settings = new Settings(SupportDir + "settings.yaml", args);
|
||||
|
||||
Log.LogPath = SupportDir + "Logs" + Path.DirectorySeparatorChar;
|
||||
Log.AddChannel("perf", "perf.log");
|
||||
Log.AddChannel("debug", "debug.log");
|
||||
Log.AddChannel("sync", "syncreport.log");
|
||||
AppDomain.CurrentDomain.AssemblyResolve += FileSystem.ResolveAssembly;
|
||||
|
||||
var defaultSupport = Environment.GetFolderPath(Environment.SpecialFolder.Personal)
|
||||
+ Path.DirectorySeparatorChar + "OpenRA";
|
||||
|
||||
SupportDir = args.GetValue("SupportDir", defaultSupport);
|
||||
Settings = new Settings(SupportDir + "settings.yaml", args);
|
||||
|
||||
Settings.Save();
|
||||
|
||||
Log.LogPath = SupportDir + "Logs" + Path.DirectorySeparatorChar;
|
||||
Log.AddChannel("perf", "perf.log");
|
||||
Log.AddChannel("debug", "debug.log");
|
||||
Log.AddChannel("sync", "syncreport.log");
|
||||
|
||||
FileSystem.Mount("."); // Needed to access shaders
|
||||
Renderer.Initialize( Game.Settings.Graphics.Mode );
|
||||
Renderer.SheetSize = Settings.Game.SheetSize;
|
||||
Renderer = new Renderer();
|
||||
|
||||
LobbyInfo.GlobalSettings.Mods = Settings.Game.Mods;
|
||||
modData = new ModData( LobbyInfo.GlobalSettings.Mods );
|
||||
Console.WriteLine("Available mods:");
|
||||
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 => 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;
|
||||
|
||||
PerfHistory.items["cursor"].hasNormalTick = false;
|
||||
|
||||
|
||||
JoinLocal();
|
||||
StartGame(modData.Manifest.ShellmapUid);
|
||||
|
||||
|
||||
// TODO: unhardcode this
|
||||
modData.WidgetLoader.LoadWidget( new Dictionary<string,object>(), Widget.RootWidget, "PERF_BG" );
|
||||
Widget.OpenWindow("MAINMENU_BG");
|
||||
|
||||
Game.orderManager.LastTickTime = Environment.TickCount;
|
||||
}
|
||||
|
||||
static bool quit;
|
||||
@@ -284,55 +244,90 @@ namespace OpenRA
|
||||
{
|
||||
while (!quit)
|
||||
{
|
||||
{
|
||||
Tick( orderManager, viewport );
|
||||
Application.DoEvents();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void Exit() { quit = true; }
|
||||
public static void Exit() { quit = true; }
|
||||
|
||||
public static Action<Color,string,string> AddChatLine = (c,n,s) => {};
|
||||
|
||||
|
||||
public static void Debug(string s)
|
||||
{
|
||||
public static void Debug(string s, params object[] args)
|
||||
{
|
||||
AddChatLine(Color.White, "Debug", String.Format(s,args));
|
||||
}
|
||||
|
||||
public static void Disconnect()
|
||||
{
|
||||
if (IsHost && server != null)
|
||||
server.Shutdown();
|
||||
|
||||
orderManager.Dispose();
|
||||
var shellmap = modData.Manifest.ShellmapUid;
|
||||
var shellmap = modData.Manifest.ShellmapUid;
|
||||
LobbyInfo = new Session();
|
||||
JoinLocal();
|
||||
StartGame(shellmap);
|
||||
|
||||
|
||||
Widget.RootWidget.CloseWindow();
|
||||
Widget.RootWidget.OpenWindow("MAINMENU_BG");
|
||||
}
|
||||
|
||||
static string baseSupportDir = null;
|
||||
public static string SupportDir
|
||||
{
|
||||
set
|
||||
{
|
||||
var dir = value;
|
||||
|
||||
// Expand paths relative to the personal directory
|
||||
if (dir.ElementAt(0) == '~')
|
||||
dir = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + dir.Substring(1);
|
||||
|
||||
if (!Directory.Exists(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
Widget.CloseWindow();
|
||||
Widget.OpenWindow("MAINMENU_BG");
|
||||
}
|
||||
|
||||
static string baseSupportDir = null;
|
||||
public static string SupportDir
|
||||
{
|
||||
set
|
||||
{
|
||||
var dir = value;
|
||||
|
||||
// Expand paths relative to the personal directory
|
||||
if (dir.ElementAt(0) == '~')
|
||||
dir = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + dir.Substring(1);
|
||||
|
||||
if (!Directory.Exists(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
baseSupportDir = dir + Path.DirectorySeparatorChar;
|
||||
}
|
||||
}
|
||||
get { return baseSupportDir; }
|
||||
}
|
||||
|
||||
public static T CreateObject<T>( string name )
|
||||
{
|
||||
return modData.ObjectCreator.CreateObject<T>( name );
|
||||
}
|
||||
|
||||
public static void RejoinLobby(World world)
|
||||
{
|
||||
var map = orderManager.LobbyInfo.GlobalSettings.Map;
|
||||
var host = orderManager.Host;
|
||||
var port = orderManager.Port;
|
||||
var isHost = Game.IsHost;
|
||||
|
||||
Disconnect();
|
||||
ConnectedToLobby += () =>
|
||||
{
|
||||
if (world.LocalPlayer != null)
|
||||
{
|
||||
/* Try to get back the old slot */
|
||||
Game.orderManager.IssueOrder(Order.Command("race " + world.LocalPlayer.Country.Race));
|
||||
Game.orderManager.IssueOrder(Order.Command("slot " + world.LobbyInfo.ClientWithIndex(world.LocalPlayer.ClientIndex).Slot));
|
||||
}else /* a spectator */
|
||||
{
|
||||
Game.orderManager.IssueOrder(Order.Command("spectator"));
|
||||
}
|
||||
|
||||
ConnectedToLobby = null;
|
||||
};
|
||||
if (isHost)
|
||||
CreateAndJoinServer( Settings, map );
|
||||
else
|
||||
JoinServer(host, port);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -14,15 +14,21 @@ namespace OpenRA.GameRules
|
||||
{
|
||||
public class MusicInfo
|
||||
{
|
||||
[FieldLoader.Load] public readonly string Filename = null;
|
||||
[FieldLoader.Load] public readonly string Title = null;
|
||||
[FieldLoader.Load] public readonly int Length = 0; // seconds
|
||||
public readonly string Filename = null;
|
||||
public readonly string Title = null;
|
||||
public readonly int Length = 0; // seconds
|
||||
public readonly bool Exists = false;
|
||||
|
||||
public MusicInfo( string key, MiniYaml value )
|
||||
{
|
||||
FieldLoader.Load(this, value);
|
||||
if (Filename == null)
|
||||
Filename = key+".aud";
|
||||
Filename = key+".aud";
|
||||
Title = value.Value;
|
||||
|
||||
if (!FileSystem.Exists(Filename))
|
||||
return;
|
||||
|
||||
Exists = true;
|
||||
Length = (int)AudLoader.SoundLength(FileSystem.Open(Filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@ namespace OpenRA
|
||||
{
|
||||
public static class Rules
|
||||
{
|
||||
public static TechTree TechTree;
|
||||
|
||||
public static Dictionary<string, ActorInfo> Info;
|
||||
public static Dictionary<string, WeaponInfo> Weapons;
|
||||
public static Dictionary<string, VoiceInfo> Voices;
|
||||
@@ -29,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);
|
||||
|
||||
@@ -41,8 +40,6 @@ namespace OpenRA
|
||||
var t = new TileSet(file);
|
||||
TileSets.Add(t.Id,t);
|
||||
}
|
||||
|
||||
TechTree = new TechTree();
|
||||
}
|
||||
|
||||
static Dictionary<string, T> LoadYamlRules<T>(string[] files, List<MiniYamlNode> dict, Func<MiniYamlNode, Dictionary<string, MiniYaml>, T> f)
|
||||
|
||||
@@ -8,13 +8,14 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.FileFormats.Graphics;
|
||||
using System;
|
||||
using OpenRA.Server;
|
||||
|
||||
namespace OpenRA.GameRules
|
||||
{
|
||||
@@ -24,22 +25,24 @@ namespace OpenRA.GameRules
|
||||
public int ListenPort = 1234;
|
||||
public int ExternalPort = 1234;
|
||||
public bool AdvertiseOnline = true;
|
||||
public string MasterServer = "http://open-ra.org/master/";
|
||||
public string MasterServer = "http://master.open-ra.org/";
|
||||
public bool AllowCheats = false;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -48,6 +51,8 @@ namespace OpenRA.GameRules
|
||||
public float SoundVolume = 0.5f;
|
||||
public float MusicVolume = 0.5f;
|
||||
public float VideoVolume = 0.5f;
|
||||
public bool Shuffle = false;
|
||||
public bool Repeat = false;
|
||||
}
|
||||
|
||||
public class PlayerSettings
|
||||
@@ -63,9 +68,13 @@ 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;
|
||||
public float ViewportEdgeScrollStep = 10f;
|
||||
|
||||
// Internal game settings
|
||||
public int Timestep = 40;
|
||||
@@ -103,12 +112,12 @@ namespace OpenRA.GameRules
|
||||
|
||||
FieldLoader.UnknownFieldAction = (s,f) =>
|
||||
{
|
||||
System.Console.WriteLine( "Ignoring unknown field `{0}` on `{1}`".F( s, f.Name ) );
|
||||
Console.WriteLine( "Ignoring unknown field `{0}` on `{1}`".F( s, f.Name ) );
|
||||
};
|
||||
|
||||
if (File.Exists(SettingsFile))
|
||||
{
|
||||
System.Console.WriteLine("Loading settings file {0}",SettingsFile);
|
||||
Console.WriteLine("Loading settings file {0}",SettingsFile);
|
||||
var yaml = MiniYaml.DictFromFile(SettingsFile);
|
||||
|
||||
foreach (var kv in Sections)
|
||||
@@ -120,7 +129,7 @@ namespace OpenRA.GameRules
|
||||
foreach (var kv in Sections)
|
||||
foreach (var f in kv.Value.GetType().GetFields())
|
||||
if (args.Contains(kv.Key+"."+f.Name))
|
||||
OpenRA.FileFormats.FieldLoader.LoadField( kv.Value, f.Name, args.GetValue(kv.Key+"."+f.Name, "") );
|
||||
FieldLoader.LoadField( kv.Value, f.Name, args.GetValue(kv.Key+"."+f.Name, "") );
|
||||
|
||||
FieldLoader.UnknownFieldAction = err1;
|
||||
FieldLoader.InvalidValueAction = err2;
|
||||
@@ -130,16 +139,11 @@ namespace OpenRA.GameRules
|
||||
{
|
||||
var root = new List<MiniYamlNode>();
|
||||
foreach( var kv in Sections )
|
||||
root.Add( new MiniYamlNode( kv.Key, SectionYaml( kv.Value ) ) );
|
||||
root.Add( new MiniYamlNode( kv.Key, FieldSaver.SaveDifferences(kv.Value, Activator.CreateInstance(kv.Value.GetType())) ) );
|
||||
|
||||
root.WriteToFile(SettingsFile);
|
||||
}
|
||||
|
||||
MiniYaml SectionYaml(object section)
|
||||
{
|
||||
return FieldSaver.SaveDifferences(section, Activator.CreateInstance(section.GetType()));
|
||||
}
|
||||
|
||||
void LoadSectionYaml(MiniYaml yaml, object section)
|
||||
{
|
||||
object defaults = Activator.CreateInstance(section.GetType());
|
||||
|
||||
@@ -1,100 +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.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.GameRules
|
||||
{
|
||||
public class TechTree
|
||||
{
|
||||
readonly Cache<string, List<ActorInfo>> producesIndex = new Cache<string, List<ActorInfo>>(x => new List<ActorInfo>());
|
||||
|
||||
public TechTree()
|
||||
{
|
||||
foreach( var info in Rules.Info.Values )
|
||||
{
|
||||
var pi = info.Traits.GetOrDefault<ProductionInfo>();
|
||||
if (pi != null)
|
||||
foreach( var p in pi.Produces )
|
||||
producesIndex[ p ].Add( info );
|
||||
}
|
||||
}
|
||||
|
||||
public Cache<string, List<Actor>> GatherBuildings( Player player )
|
||||
{
|
||||
var ret = new Cache<string, List<Actor>>( x => new List<Actor>() );
|
||||
if (player == null)
|
||||
return ret;
|
||||
|
||||
foreach( var b in player.World.Queries.OwnedBy[player].Where( x=>x.Info.Traits.Contains<BuildingInfo>() ) )
|
||||
{
|
||||
ret[ b.Info.Name ].Add( b );
|
||||
var tt = b.Info.Traits.GetOrDefault<TooltipInfo>();
|
||||
if( tt != null )
|
||||
foreach( var alt in tt.AlternateName )
|
||||
ret[ alt ].Add( b );
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool CanBuild( ActorInfo info, Player player, Cache<string, List<Actor>> playerBuildings )
|
||||
{
|
||||
if (player == null)
|
||||
return false;
|
||||
|
||||
var bi = info.Traits.GetOrDefault<BuildableInfo>();
|
||||
if( bi == null ) return false;
|
||||
|
||||
if( !bi.Owner.Contains( player.Country.Race ) )
|
||||
return false;
|
||||
|
||||
foreach( var p in bi.Prerequisites )
|
||||
if( playerBuildings[ p ].Count == 0 )
|
||||
return false;
|
||||
|
||||
if( producesIndex[ bi.Queue ].All( x => playerBuildings[ x.Name ].Count == 0 ) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public IEnumerable<string> BuildableItems( Player player, params string[] categories )
|
||||
{
|
||||
if (player == null)
|
||||
yield break;
|
||||
|
||||
var playerBuildings = GatherBuildings( player );
|
||||
foreach (var unit in AllBuildables(categories))
|
||||
if( CanBuild( unit, player, playerBuildings ) )
|
||||
yield return unit.Name;
|
||||
}
|
||||
|
||||
public IEnumerable<ActorInfo> AllBuildables(params string[] categories)
|
||||
{
|
||||
return Rules.Info.Values
|
||||
.Where( x => x.Name[ 0 ] != '^' )
|
||||
.Where( x => x.Traits.Contains<BuildableInfo>() )
|
||||
.Where( x => categories.Contains(x.Traits.Get<BuildableInfo>().Queue) );
|
||||
}
|
||||
|
||||
public IEnumerable<ActorInfo> UnitBuiltAt( ActorInfo info )
|
||||
{
|
||||
var bi = info.Traits.Get<BuildableInfo>();
|
||||
var builtAt = bi.BuiltAt;
|
||||
if( builtAt.Length != 0 )
|
||||
return builtAt.Select( x => Rules.Info[ x.ToLowerInvariant() ] );
|
||||
else
|
||||
return producesIndex[ bi.Queue ];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ namespace OpenRA.GameRules
|
||||
: new Dictionary<string, string[]>();
|
||||
}
|
||||
|
||||
public readonly Lazy<Dictionary<string, VoicePool>> Pools;
|
||||
public readonly OpenRA.FileFormats.Lazy<Dictionary<string, VoicePool>> Pools;
|
||||
|
||||
public VoiceInfo( MiniYaml y )
|
||||
{
|
||||
@@ -41,6 +41,9 @@ namespace OpenRA.GameRules
|
||||
|
||||
if (!Voices.ContainsKey("Attack"))
|
||||
Voices.Add("Attack", Voices["Move"]);
|
||||
|
||||
if (!Voices.ContainsKey("AttackMove"))
|
||||
Voices.Add("AttackMove", Voices["Move"]);
|
||||
|
||||
Pools = Lazy.New(() => Voices.ToDictionary( a => a.Key, a => new VoicePool(a.Value) ));
|
||||
}
|
||||
|
||||
@@ -12,47 +12,55 @@ using System.Collections.Generic;
|
||||
using OpenRA.Effects;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.GameRules
|
||||
{
|
||||
public class WarheadInfo
|
||||
{
|
||||
public readonly int Spread = 1; // distance (in pixels) from the explosion center at which damage is 1/2.
|
||||
public readonly float[] Verses = { 1, 1, 1, 1, 1 }; // damage vs each armortype
|
||||
public readonly bool Ore = false; // can this damage ore?
|
||||
public readonly string Explosion = null; // explosion effect to use
|
||||
public readonly string WaterExplosion = null; // explosion effect on hitting water (usually a splash)
|
||||
public readonly string SmudgeType = null; // type of smudge to apply
|
||||
public readonly int[] Size = { 0, 0 }; // size of the explosion. provide 2 values for a ring effect (outer/inner)
|
||||
public readonly int InfDeath = 0; // infantry death animation to use
|
||||
public readonly string ImpactSound = null; // sound to play on impact
|
||||
public readonly string WaterImpactSound = null; // sound to play on impact with water
|
||||
public readonly int Damage = 0; // how much (raw) damage to deal
|
||||
public readonly int Delay = 0; // delay in ticks before dealing the damage. 0=instant (old model)
|
||||
public readonly DamageModel DamageModel = DamageModel.Normal; // which damage model to use
|
||||
[FieldLoader.Load] public readonly int Spread = 1; // distance (in pixels) from the explosion center at which damage is 1/2.
|
||||
[FieldLoader.LoadUsing( "LoadVersus" )]
|
||||
public readonly Dictionary<string, float> Versus; // damage vs each armortype
|
||||
[FieldLoader.Load] public readonly bool Ore = false; // can this damage ore?
|
||||
[FieldLoader.Load] public readonly string Explosion = null; // explosion effect to use
|
||||
[FieldLoader.Load] public readonly string WaterExplosion = null; // explosion effect on hitting water (usually a splash)
|
||||
[FieldLoader.Load] public readonly string SmudgeType = null; // type of smudge to apply
|
||||
[FieldLoader.Load] public readonly int[] Size = { 0, 0 }; // size of the explosion. provide 2 values for a ring effect (outer/inner)
|
||||
[FieldLoader.Load] public readonly int InfDeath = 0; // infantry death animation to use
|
||||
[FieldLoader.Load] public readonly string ImpactSound = null; // sound to play on impact
|
||||
[FieldLoader.Load] public readonly string WaterImpactSound = null; // sound to play on impact with water
|
||||
[FieldLoader.Load] public readonly int Damage = 0; // how much (raw) damage to deal
|
||||
[FieldLoader.Load] public readonly int Delay = 0; // delay in ticks before dealing the damage. 0=instant (old model)
|
||||
[FieldLoader.Load] public readonly DamageModel DamageModel = DamageModel.Normal; // which damage model to use
|
||||
[FieldLoader.Load] public readonly bool PreventProne = false; // whether we should prevent prone response in infantry.
|
||||
|
||||
public float EffectivenessAgainst(Actor self)
|
||||
{
|
||||
var health = self.Info.Traits.GetOrDefault<HealthInfo>();
|
||||
if (health == null) return 0f;
|
||||
var armor = self.Info.Traits.GetOrDefault<ArmorInfo>();
|
||||
if (armor == null || armor.Type == null) return 1;
|
||||
|
||||
return Verses[(int)(health.Armor)];
|
||||
float versus;
|
||||
return Versus.TryGetValue(armor.Type, out versus) ? versus : 1;
|
||||
}
|
||||
|
||||
public WarheadInfo( MiniYaml yaml )
|
||||
{
|
||||
FieldLoader.Load( this, yaml );
|
||||
}
|
||||
|
||||
static object LoadVersus( MiniYaml y )
|
||||
{
|
||||
return y.NodesDict.ContainsKey( "Versus" )
|
||||
? y.NodesDict[ "Versus" ].NodesDict.ToDictionary(
|
||||
a => a.Key,
|
||||
a => (float)FieldLoader.GetValue( "(value)", typeof( float ), a.Value.Value ) )
|
||||
: new Dictionary<string, float>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum ArmorType
|
||||
{
|
||||
none = 0,
|
||||
wood = 1,
|
||||
light = 2,
|
||||
heavy = 3,
|
||||
concrete = 4,
|
||||
}
|
||||
|
||||
public enum DamageModel
|
||||
{
|
||||
@@ -85,6 +93,7 @@ namespace OpenRA.GameRules
|
||||
[FieldLoader.Load] public readonly bool Underwater = false;
|
||||
[FieldLoader.Load] public readonly string[] ValidTargets = { "Ground" };
|
||||
[FieldLoader.Load] public readonly int BurstDelay = 5;
|
||||
[FieldLoader.Load] public readonly float MinRange = 0;
|
||||
|
||||
[FieldLoader.LoadUsing( "LoadProjectile" )] public IProjectileInfo Projectile;
|
||||
[FieldLoader.LoadUsing( "LoadWarheads" )] public List<WarheadInfo> Warheads;
|
||||
|
||||
67
OpenRA.Game/Graphics/CursorProvider.cs
Normal file
67
OpenRA.Game/Graphics/CursorProvider.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
#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.Xml;
|
||||
using OpenRA.FileFormats;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
public static class CursorProvider
|
||||
{
|
||||
static Dictionary<string, CursorSequence> cursors;
|
||||
|
||||
public static void Initialize(string[] sequenceFiles)
|
||||
{
|
||||
cursors = new Dictionary<string, CursorSequence>();
|
||||
|
||||
foreach (var f in sequenceFiles)
|
||||
LoadSequenceSource(f);
|
||||
}
|
||||
|
||||
static void LoadSequenceSource(string filename)
|
||||
{
|
||||
XmlDocument document = new XmlDocument();
|
||||
document.Load(FileSystem.Open(filename));
|
||||
|
||||
foreach (XmlElement eCursor in document.SelectNodes("/sequences/cursor"))
|
||||
LoadSequencesForCursor(eCursor);
|
||||
}
|
||||
|
||||
static void LoadSequencesForCursor(XmlElement eCursor)
|
||||
{
|
||||
Game.modData.LoadScreen.Display();
|
||||
string cursorSrc = eCursor.GetAttribute("src");
|
||||
string palette = eCursor.GetAttribute("palette");
|
||||
|
||||
foreach (XmlElement eSequence in eCursor.SelectNodes("./sequence"))
|
||||
cursors.Add(eSequence.GetAttribute("name"), new CursorSequence(cursorSrc, palette, eSequence));
|
||||
|
||||
}
|
||||
|
||||
public static bool HasCursorSequence(string cursor)
|
||||
{
|
||||
return cursors.ContainsKey(cursor);
|
||||
}
|
||||
|
||||
public static CursorSequence GetCursorSequence(string cursor)
|
||||
{
|
||||
try { return cursors[cursor]; }
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Cursor does not have a sequence `{0}`".F(cursor));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
|
||||
Sprite[] LoadCursors(string filename)
|
||||
{
|
||||
{
|
||||
try
|
||||
{
|
||||
var shp = new Dune2ShpReader(FileSystem.OpenWithExts(filename, exts));
|
||||
|
||||
@@ -13,45 +13,46 @@ using OpenRA.FileFormats.Graphics;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
public class LineRenderer
|
||||
public class LineRenderer : Renderer.IBatchRenderer
|
||||
{
|
||||
Renderer renderer;
|
||||
IVertexBuffer<Vertex> vertexBuffer;
|
||||
IIndexBuffer indexBuffer; /* kindof a waste of space, but the GPU likes indexing, oh well */
|
||||
|
||||
const int linesPerBatch = 1024;
|
||||
|
||||
Vertex[] vertices = new Vertex[ 2 * linesPerBatch ];
|
||||
ushort[] indices = new ushort[ 2 * linesPerBatch ];
|
||||
int lines = 0;
|
||||
Vertex[] vertices = new Vertex[ Renderer.TempBufferSize ];
|
||||
ushort[] indices = new ushort[ Renderer.TempBufferSize ];
|
||||
int nv = 0, ni = 0;
|
||||
|
||||
public LineRenderer( Renderer renderer )
|
||||
{
|
||||
this.renderer = renderer;
|
||||
vertexBuffer = renderer.Device.CreateVertexBuffer(vertices.Length );
|
||||
indexBuffer = renderer.Device.CreateIndexBuffer( indices.Length );
|
||||
}
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
if( lines > 0 )
|
||||
if( ni > 0 )
|
||||
{
|
||||
renderer.LineShader.Render( () =>
|
||||
{
|
||||
vertexBuffer.SetData( vertices );
|
||||
indexBuffer.SetData( indices );
|
||||
renderer.DrawBatch( vertexBuffer, indexBuffer,
|
||||
var vb = renderer.GetTempVertexBuffer();
|
||||
var ib = renderer.GetTempIndexBuffer();
|
||||
vb.SetData( vertices, nv );
|
||||
ib.SetData( indices, ni );
|
||||
renderer.DrawBatch( vb, ib,
|
||||
nv, ni / 2, PrimitiveType.LineList );
|
||||
} );
|
||||
|
||||
nv = 0; ni = 0;
|
||||
lines = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawLine( float2 start, float2 end, Color startColor, Color endColor )
|
||||
{
|
||||
Renderer.CurrentBatchRenderer = this;
|
||||
|
||||
if( ni + 2 > Renderer.TempBufferSize )
|
||||
Flush();
|
||||
if( nv + 2 > Renderer.TempBufferSize )
|
||||
Flush();
|
||||
|
||||
indices[ ni++ ] = (ushort)nv;
|
||||
|
||||
vertices[ nv++ ] = new Vertex( start,
|
||||
@@ -63,9 +64,6 @@ namespace OpenRA.Graphics
|
||||
vertices[ nv++ ] = new Vertex( end,
|
||||
new float2( endColor.R / 255.0f, endColor.G / 255.0f ),
|
||||
new float2( endColor.B / 255.0f, endColor.A / 255.0f ) );
|
||||
|
||||
if( ++lines >= linesPerBatch )
|
||||
Flush();
|
||||
}
|
||||
|
||||
public void FillRect( RectangleF r, Color color )
|
||||
|
||||
@@ -14,16 +14,29 @@ using System.Drawing.Imaging;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
using System.IO;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
public class Minimap
|
||||
{
|
||||
public static Bitmap TerrainBitmap(Map map)
|
||||
{
|
||||
return TerrainBitmap(map, false);
|
||||
}
|
||||
|
||||
public static Bitmap TerrainBitmap(Map map, bool actualSize)
|
||||
{
|
||||
var tileset = Rules.TileSets[map.Tileset];
|
||||
var size = Util.NextPowerOf2(Math.Max(map.Width, map.Height));
|
||||
Bitmap terrain = new Bitmap(size, size);
|
||||
var width = map.Bounds.Width;
|
||||
var height = map.Bounds.Height;
|
||||
|
||||
if (!actualSize)
|
||||
{
|
||||
width = height = Util.NextPowerOf2(Math.Max(map.Bounds.Width, map.Bounds.Height));
|
||||
}
|
||||
|
||||
Bitmap terrain = new Bitmap(width, height);
|
||||
|
||||
var bitmapData = terrain.LockBits(new Rectangle(0, 0, terrain.Width, terrain.Height),
|
||||
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
|
||||
@@ -32,12 +45,15 @@ 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));
|
||||
|
||||
*(c + (y * bitmapData.Stride >> 2) + x) = tileset.Terrain[type].Color.ToArgb();
|
||||
}
|
||||
}
|
||||
@@ -59,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;
|
||||
|
||||
@@ -84,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);
|
||||
@@ -93,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;
|
||||
@@ -111,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);
|
||||
@@ -119,16 +135,16 @@ namespace OpenRA.Graphics
|
||||
unsafe
|
||||
{
|
||||
int* c = (int*)bitmapData.Scan0;
|
||||
|
||||
|
||||
foreach (var t in world.Queries.WithTraitMultiple<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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,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,13 +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;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
@@ -23,10 +24,10 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
internal static int SheetSize;
|
||||
|
||||
public IShader SpriteShader { get; private set; } /* note: shared shader params */
|
||||
public IShader LineShader { get; private set; }
|
||||
public IShader RgbaSpriteShader { get; private set; }
|
||||
public IShader WorldSpriteShader { get; private set; }
|
||||
internal IShader SpriteShader { get; private set; } /* note: shared shader params */
|
||||
internal IShader LineShader { get; private set; }
|
||||
internal IShader RgbaSpriteShader { get; private set; }
|
||||
internal IShader WorldSpriteShader { get; private set; }
|
||||
|
||||
public SpriteRenderer SpriteRenderer { get; private set; }
|
||||
public SpriteRenderer RgbaSpriteRenderer { get; private set; }
|
||||
@@ -37,12 +38,18 @@ namespace OpenRA.Graphics
|
||||
|
||||
public readonly SpriteFont RegularFont, BoldFont, TitleFont;
|
||||
|
||||
internal const int TempBufferSize = 8192;
|
||||
const int TempBufferCount = 8;
|
||||
|
||||
Queue<IVertexBuffer<Vertex>> tempBuffersV = new Queue<IVertexBuffer<Vertex>>();
|
||||
Queue<IIndexBuffer> tempBuffersI = new Queue<IIndexBuffer>();
|
||||
|
||||
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 );
|
||||
@@ -52,13 +59,18 @@ namespace OpenRA.Graphics
|
||||
RegularFont = new SpriteFont("FreeSans.ttf", 14);
|
||||
BoldFont = new SpriteFont("FreeSansBold.ttf", 14);
|
||||
TitleFont = new SpriteFont("titles.ttf", 48);
|
||||
|
||||
for( int i = 0 ; i < TempBufferCount ; i++ )
|
||||
{
|
||||
tempBuffersV.Enqueue( device.CreateVertexBuffer( TempBufferSize ) );
|
||||
tempBuffersI.Enqueue( device.CreateIndexBuffer( TempBufferSize ) );
|
||||
}
|
||||
}
|
||||
|
||||
public IGraphicsDevice Device { get { return device; } }
|
||||
internal IGraphicsDevice Device { get { return device; } }
|
||||
|
||||
public void BeginFrame(float2 scroll)
|
||||
{
|
||||
device.Begin();
|
||||
device.Clear(Color.Black);
|
||||
|
||||
float2 r1 = new float2(2f/Resolution.Width, -2f/Resolution.Height);
|
||||
@@ -73,16 +85,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 )
|
||||
{
|
||||
device.End();
|
||||
device.Present();
|
||||
Flush();
|
||||
device.Present( inputHandler );
|
||||
}
|
||||
|
||||
public void DrawBatch<T>(IVertexBuffer<T> vertices, IIndexBuffer indices,
|
||||
@@ -111,15 +123,9 @@ namespace OpenRA.Graphics
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
WorldSpriteRenderer.Flush();
|
||||
RgbaSpriteRenderer.Flush();
|
||||
LineRenderer.Flush();
|
||||
CurrentBatchRenderer = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static IGraphicsDevice device;
|
||||
|
||||
public static Size Resolution { get { return device.WindowSize; } }
|
||||
@@ -127,14 +133,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;
|
||||
@@ -154,5 +160,49 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal IVertexBuffer<Vertex> GetTempVertexBuffer()
|
||||
{
|
||||
var ret = tempBuffersV.Dequeue();
|
||||
tempBuffersV.Enqueue( ret );
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal IIndexBuffer GetTempIndexBuffer()
|
||||
{
|
||||
var ret = tempBuffersI.Dequeue();
|
||||
tempBuffersI.Enqueue( ret );
|
||||
return ret;
|
||||
}
|
||||
|
||||
public interface IBatchRenderer
|
||||
{
|
||||
void Flush();
|
||||
}
|
||||
|
||||
static IBatchRenderer currentBatchRenderer;
|
||||
public static IBatchRenderer CurrentBatchRenderer
|
||||
{
|
||||
get { return currentBatchRenderer; }
|
||||
set
|
||||
{
|
||||
if( currentBatchRenderer == value ) return;
|
||||
if( currentBatchRenderer != null )
|
||||
currentBatchRenderer.Flush();
|
||||
currentBatchRenderer = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void EnableScissor(int left, int top, int width, int height)
|
||||
{
|
||||
Flush();
|
||||
Device.EnableScissor( left, top, width, height );
|
||||
}
|
||||
|
||||
public void DisableScissor()
|
||||
{
|
||||
Flush();
|
||||
Device.DisableScissor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#endregion
|
||||
|
||||
using System.Xml;
|
||||
using OpenRA.FileFormats;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
@@ -24,34 +26,55 @@ namespace OpenRA.Graphics
|
||||
public int Facings { get { return facings; } }
|
||||
public int Tick { get { return tick; } }
|
||||
|
||||
public Sequence(string unit, XmlElement e)
|
||||
string srcOverride;
|
||||
public Sequence(string unit, string name, MiniYaml info)
|
||||
{
|
||||
string srcOverride = e.GetAttribute("src");
|
||||
Name = e.GetAttribute("name");
|
||||
|
||||
srcOverride = info.Value;
|
||||
Name = name;
|
||||
var d = info.NodesDict;
|
||||
|
||||
sprites = SpriteSheetBuilder.LoadAllSprites(string.IsNullOrEmpty(srcOverride) ? unit : srcOverride );
|
||||
start = int.Parse(e.GetAttribute("start"));
|
||||
start = int.Parse(d["Start"].Value);
|
||||
|
||||
if (e.GetAttribute("length") == "*" || e.GetAttribute("end") == "*")
|
||||
length = sprites.Length - Start;
|
||||
else if (e.HasAttribute("length"))
|
||||
length = int.Parse(e.GetAttribute("length"));
|
||||
else if (e.HasAttribute("end"))
|
||||
length = int.Parse(e.GetAttribute("end")) - int.Parse(e.GetAttribute("start"));
|
||||
else
|
||||
if (!d.ContainsKey("Length"))
|
||||
length = 1;
|
||||
else if (d["Length"].Value == "*")
|
||||
length = sprites.Length - Start;
|
||||
else
|
||||
length = int.Parse(d["Length"].Value);
|
||||
|
||||
if( e.HasAttribute( "facings" ) )
|
||||
facings = int.Parse( e.GetAttribute( "facings" ) );
|
||||
|
||||
if(d.ContainsKey("Facings"))
|
||||
facings = int.Parse(d["Facings"].Value);
|
||||
else
|
||||
facings = 1;
|
||||
|
||||
if (e.HasAttribute("tick"))
|
||||
tick = int.Parse(e.GetAttribute("tick"));
|
||||
if(d.ContainsKey("Tick"))
|
||||
tick = int.Parse(d["Tick"].Value);
|
||||
else
|
||||
tick = 40;
|
||||
}
|
||||
|
||||
|
||||
public MiniYaml Save()
|
||||
{
|
||||
var root = new List<MiniYamlNode>();
|
||||
|
||||
root.Add(new MiniYamlNode("Start", start.ToString()));
|
||||
|
||||
if (length > 1 && (start != 0 || length != sprites.Length - start))
|
||||
root.Add(new MiniYamlNode("Length", length.ToString()));
|
||||
else if (length > 1 && length == sprites.Length - start)
|
||||
root.Add(new MiniYamlNode("Length", "*"));
|
||||
|
||||
if (facings > 1)
|
||||
root.Add(new MiniYamlNode("Facings", facings.ToString()));
|
||||
|
||||
if (tick != 40)
|
||||
root.Add(new MiniYamlNode("Tick", tick.ToString()));
|
||||
|
||||
return new MiniYaml(srcOverride, root);
|
||||
}
|
||||
|
||||
public Sprite GetSprite( int frame )
|
||||
{
|
||||
return GetSprite( frame, 0 );
|
||||
|
||||
@@ -20,53 +20,37 @@ namespace OpenRA.Graphics
|
||||
public static class SequenceProvider
|
||||
{
|
||||
static Dictionary<string, Dictionary<string, Sequence>> units;
|
||||
static Dictionary<string, CursorSequence> cursors;
|
||||
|
||||
public static void Initialize(string[] sequenceFiles)
|
||||
public static void Initialize(string[] sequenceFiles, List<MiniYamlNode> sequenceNodes)
|
||||
{
|
||||
units = new Dictionary<string, Dictionary<string, Sequence>>();
|
||||
cursors = new Dictionary<string, CursorSequence>();
|
||||
|
||||
foreach (var f in sequenceFiles)
|
||||
LoadSequenceSource(f);
|
||||
if (sequenceFiles.Length == 0)
|
||||
return;
|
||||
|
||||
var sequences = sequenceFiles.Select(s => MiniYaml.FromFile(s)).Aggregate(sequenceNodes, MiniYaml.Merge);
|
||||
|
||||
foreach (var s in sequences)
|
||||
LoadSequencesForUnit(s.Key, s.Value);
|
||||
}
|
||||
|
||||
static void LoadSequenceSource(string filename)
|
||||
{
|
||||
XmlDocument document = new XmlDocument();
|
||||
document.Load(FileSystem.Open(filename));
|
||||
|
||||
foreach (XmlElement eUnit in document.SelectNodes("/sequences/unit"))
|
||||
LoadSequencesForUnit(eUnit);
|
||||
|
||||
foreach (XmlElement eCursor in document.SelectNodes("/sequences/cursor"))
|
||||
LoadSequencesForCursor(eCursor);
|
||||
}
|
||||
|
||||
static void LoadSequencesForCursor(XmlElement eCursor)
|
||||
static void LoadSequencesForUnit(string unit, MiniYaml sequences)
|
||||
{
|
||||
Game.modData.LoadScreen.Display();
|
||||
string cursorSrc = eCursor.GetAttribute("src");
|
||||
string palette = eCursor.GetAttribute("palette");
|
||||
|
||||
foreach (XmlElement eSequence in eCursor.SelectNodes("./sequence"))
|
||||
cursors.Add(eSequence.GetAttribute("name"), new CursorSequence(cursorSrc, palette, eSequence));
|
||||
|
||||
}
|
||||
|
||||
static void LoadSequencesForUnit(XmlElement eUnit)
|
||||
{
|
||||
Game.modData.LoadScreen.Display();
|
||||
string unitName = eUnit.GetAttribute("name");
|
||||
try {
|
||||
var sequences = eUnit.SelectNodes("./sequence").OfType<XmlElement>()
|
||||
.Select(e => new Sequence(unitName, e))
|
||||
.ToDictionary(s => s.Name);
|
||||
|
||||
units.Add(unitName, sequences);
|
||||
var seq = sequences.NodesDict.ToDictionary(x => x.Key, x => new Sequence(unit,x.Key,x.Value));
|
||||
units.Add(unit, seq);
|
||||
} catch (FileNotFoundException) {} // Do nothing; we can crash later if we actually wanted art
|
||||
}
|
||||
|
||||
public static MiniYaml SaveSequencesForUnit(string unitname)
|
||||
{
|
||||
var ret = new List<MiniYamlNode>();
|
||||
foreach (var s in units[unitname])
|
||||
ret.Add(new MiniYamlNode(s.Key, s.Value.Save()));
|
||||
|
||||
return new MiniYaml(null, ret);
|
||||
}
|
||||
|
||||
public static Sequence GetSequence(string unitName, string sequenceName)
|
||||
{
|
||||
try { return units[unitName][sequenceName]; }
|
||||
@@ -81,20 +65,5 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
return units[unit].ContainsKey(seq);
|
||||
}
|
||||
|
||||
public static bool HasCursorSequence(string cursor)
|
||||
{
|
||||
return cursors.ContainsKey(cursor);
|
||||
}
|
||||
|
||||
public static CursorSequence GetCursorSequence(string cursor)
|
||||
{
|
||||
try { return cursors[cursor]; }
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Cursor does not have a sequence `{0}`".F(cursor));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,26 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
return uvhax[ k ];
|
||||
}
|
||||
|
||||
public void DrawAt( WorldRenderer wr, float2 location, string palette )
|
||||
{
|
||||
Game.Renderer.SpriteRenderer.DrawSprite( this, location, wr, palette, this.size );
|
||||
}
|
||||
|
||||
public void DrawAt( float2 location, int paletteIndex )
|
||||
{
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
public enum TextureChannel
|
||||
|
||||
@@ -80,17 +80,20 @@ namespace OpenRA.Graphics
|
||||
GlyphInfo CreateGlyph(Pair<char,Color> c)
|
||||
{
|
||||
var index = FT.FT_Get_Char_Index(face, (uint)c.First);
|
||||
FT.FT_Load_Glyph(face, index, FT.FT_LOAD_RENDER);
|
||||
if (0 != FT.FT_Load_Glyph(face, index, FT.FT_LOAD_RENDER))
|
||||
throw new InvalidOperationException( "FT_Load_Glyph failed." );
|
||||
|
||||
var _face = (FT_FaceRec)Marshal.PtrToStructure(face, typeof(FT_FaceRec));
|
||||
var _glyph = (FT_GlyphSlotRec)Marshal.PtrToStructure(_face.glyph, typeof(FT_GlyphSlotRec));
|
||||
|
||||
var s = builder.Allocate(new Size(_glyph.metrics.width >> 6, _glyph.metrics.height >> 6));
|
||||
var s = builder.Allocate(
|
||||
new Size(_glyph.metrics.width.ToInt32() >> 6,
|
||||
_glyph.metrics.height.ToInt32() >> 6));
|
||||
|
||||
var g = new GlyphInfo
|
||||
{
|
||||
Sprite = s,
|
||||
Advance = _glyph.metrics.horiAdvance / 64f,
|
||||
Advance = _glyph.metrics.horiAdvance.ToInt32() / 64f,
|
||||
Offset = { X = _glyph.bitmap_left, Y = -_glyph.bitmap_top }
|
||||
};
|
||||
|
||||
|
||||
@@ -12,28 +12,20 @@ using OpenRA.FileFormats.Graphics;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
public class SpriteRenderer
|
||||
public class SpriteRenderer : Renderer.IBatchRenderer
|
||||
{
|
||||
IVertexBuffer<Vertex> vertexBuffer;
|
||||
IIndexBuffer indexBuffer;
|
||||
Renderer renderer;
|
||||
IShader shader;
|
||||
|
||||
const int spritesPerBatch = 1024;
|
||||
|
||||
Vertex[] vertices = new Vertex[4 * spritesPerBatch];
|
||||
ushort[] indices = new ushort[6 * spritesPerBatch];
|
||||
Vertex[] vertices = new Vertex[Renderer.TempBufferSize];
|
||||
ushort[] indices = new ushort[Renderer.TempBufferSize];
|
||||
Sheet currentSheet = null;
|
||||
int sprites = 0;
|
||||
int nv = 0, ni = 0;
|
||||
|
||||
public SpriteRenderer(Renderer renderer, IShader shader)
|
||||
{
|
||||
this.renderer = renderer;
|
||||
this.shader = shader;
|
||||
|
||||
vertexBuffer = renderer.Device.CreateVertexBuffer( vertices.Length );
|
||||
indexBuffer = renderer.Device.CreateIndexBuffer( indices.Length );
|
||||
}
|
||||
|
||||
public SpriteRenderer(Renderer renderer)
|
||||
@@ -41,14 +33,16 @@ namespace OpenRA.Graphics
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
if (sprites > 0)
|
||||
if (ni > 0)
|
||||
{
|
||||
shader.SetValue( "DiffuseTexture", currentSheet.Texture );
|
||||
shader.Render(() =>
|
||||
{
|
||||
vertexBuffer.SetData(vertices);
|
||||
indexBuffer.SetData(indices);
|
||||
renderer.DrawBatch(vertexBuffer, indexBuffer,
|
||||
var vb = renderer.GetTempVertexBuffer();
|
||||
var ib = renderer.GetTempIndexBuffer();
|
||||
vb.SetData(vertices, nv);
|
||||
ib.SetData(indices, ni);
|
||||
renderer.DrawBatch(vb, ib,
|
||||
new Range<int>(0, nv),
|
||||
new Range<int>(0, ni),
|
||||
PrimitiveType.TriangleList,
|
||||
@@ -57,30 +51,34 @@ namespace OpenRA.Graphics
|
||||
|
||||
nv = 0; ni = 0;
|
||||
currentSheet = null;
|
||||
sprites = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, float2 location, string palette)
|
||||
public void DrawSprite(Sprite s, float2 location, WorldRenderer wr, string palette)
|
||||
{
|
||||
DrawSprite(s, location, Game.world.WorldRenderer.GetPaletteIndex(palette), s.size);
|
||||
DrawSprite(s, location, wr.GetPaletteIndex(palette), s.size);
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, float2 location, string palette, float2 size)
|
||||
public void DrawSprite(Sprite s, float2 location, WorldRenderer wr, string palette, float2 size)
|
||||
{
|
||||
DrawSprite(s, location, Game.world.WorldRenderer.GetPaletteIndex(palette), size);
|
||||
DrawSprite(s, location, wr.GetPaletteIndex(palette), size);
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, float2 location, int paletteIndex, float2 size)
|
||||
{
|
||||
Renderer.CurrentBatchRenderer = this;
|
||||
|
||||
if (s.sheet != currentSheet)
|
||||
Flush();
|
||||
|
||||
if( nv + 4 > Renderer.TempBufferSize )
|
||||
Flush();
|
||||
if( ni + 6 > Renderer.TempBufferSize )
|
||||
Flush();
|
||||
|
||||
currentSheet = s.sheet;
|
||||
Util.FastCreateQuad(vertices, indices, location.ToInt2(), s, paletteIndex, nv, ni, size);
|
||||
nv += 4; ni += 6;
|
||||
if (++sprites >= spritesPerBatch)
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -31,23 +31,23 @@ namespace OpenRA.Graphics
|
||||
this.map = world.Map;
|
||||
|
||||
Size tileSize = new Size( Game.CellSize, Game.CellSize );
|
||||
|
||||
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
|
||||
// before the palettes are created
|
||||
// before the palettes are created. Therefore assumes that "terrain" is the first palette to be defined
|
||||
Util.FastCreateQuad(vertices, indices, Game.CellSize * new float2(i, j), tile, 0, nv, ni, tile.size);
|
||||
nv += 4;
|
||||
ni += 6;
|
||||
@@ -57,36 +57,36 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
|
||||
vertexBuffer = Game.Renderer.Device.CreateVertexBuffer( vertices.Length );
|
||||
vertexBuffer.SetData( vertices );
|
||||
vertexBuffer.SetData( vertices, nv );
|
||||
|
||||
indexBuffer = Game.Renderer.Device.CreateIndexBuffer( indices.Length );
|
||||
indexBuffer.SetData( indices );
|
||||
indexBuffer.SetData( indices, ni );
|
||||
}
|
||||
|
||||
public void Draw( Viewport viewport )
|
||||
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 / 24.0f + 2);
|
||||
int visibleRows = (int)(viewport.Height * 1f / Game.CellSize + 2);
|
||||
|
||||
int firstRow = (int)((viewport.Location.Y) / 24.0f - 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;
|
||||
@@ -99,7 +99,7 @@ namespace OpenRA.Graphics
|
||||
PrimitiveType.TriangleList, Game.Renderer.SpriteShader));
|
||||
|
||||
foreach (var r in world.WorldActor.TraitsImplementing<IRenderOverlay>())
|
||||
r.Render();
|
||||
r.Render( wr );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
121
OpenRA.Game/Graphics/Viewport.cs
Normal file → Executable file
121
OpenRA.Game/Graphics/Viewport.cs
Normal file → Executable file
@@ -8,10 +8,10 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Traits;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
@@ -19,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;
|
||||
|
||||
@@ -42,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);
|
||||
@@ -50,80 +50,52 @@ 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( World world )
|
||||
public void DrawRegions( WorldRenderer wr, IInputHandler inputHandler )
|
||||
{
|
||||
renderer.BeginFrame(scrollPosition);
|
||||
world.WorldRenderer.Draw();
|
||||
|
||||
Widget.DoDraw(world);
|
||||
|
||||
|
||||
wr.Draw();
|
||||
Widget.DoDraw( wr );
|
||||
var cursorName = Widget.RootWidget.GetCursorOuter(Viewport.LastMousePos) ?? "default";
|
||||
var c = new Cursor(cursorName);
|
||||
c.Draw((int)cursorFrame, Viewport.LastMousePos + Location);
|
||||
new Cursor(cursorName).Draw(wr, (int)cursorFrame, Viewport.LastMousePos + Location);
|
||||
|
||||
renderer.RgbaSpriteRenderer.Flush();
|
||||
renderer.SpriteRenderer.Flush();
|
||||
renderer.WorldSpriteRenderer.Flush();
|
||||
|
||||
renderer.EndFrame();
|
||||
}
|
||||
|
||||
public void RefreshPalette()
|
||||
{
|
||||
Game.world.WorldRenderer.palette.Update(
|
||||
Game.world.WorldActor.TraitsImplementing<IPaletteModifier>());
|
||||
renderer.EndFrame( inputHandler );
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
cursorFrame += 0.5f;
|
||||
RefreshPalette();
|
||||
}
|
||||
|
||||
public float2 ViewToWorld(int2 loc)
|
||||
@@ -135,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)
|
||||
@@ -148,15 +120,40 @@ namespace OpenRA.Graphics
|
||||
.Select(a => a.CenterLocation)
|
||||
.Aggregate((a, b) => a + b);
|
||||
|
||||
scrollPosition = this.NormalizeScrollPosition((avgPos - .5f * new float2(Width, Height)));
|
||||
scrollPosition = this.NormalizeScrollPosition((avgPos.ToInt2() - screenSize / 2));
|
||||
}
|
||||
|
||||
public Rectangle ViewBounds(World world)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
public Rectangle? ShroudBounds()
|
||||
int2 cachedScroll = new int2(int.MaxValue, int.MaxValue);
|
||||
Rectangle cachedRect;
|
||||
|
||||
public Rectangle WorldBounds(World world)
|
||||
{
|
||||
var localPlayer = Game.world.LocalPlayer;
|
||||
if (localPlayer == null) return null;
|
||||
if (localPlayer.Shroud.Disabled) return null;
|
||||
return localPlayer.Shroud.Bounds;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,9 +19,11 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
public class WorldRenderer
|
||||
{
|
||||
readonly World world;
|
||||
public readonly World world;
|
||||
internal readonly TerrainRenderer terrainRenderer;
|
||||
internal readonly UiOverlay uiOverlay;
|
||||
internal readonly ShroudRenderer shroudRenderer;
|
||||
|
||||
public readonly UiOverlay uiOverlay;
|
||||
internal readonly HardwarePalette palette;
|
||||
|
||||
internal WorldRenderer(World world)
|
||||
@@ -29,8 +31,12 @@ namespace OpenRA.Graphics
|
||||
this.world = world;
|
||||
|
||||
terrainRenderer = new TerrainRenderer(world, this);
|
||||
shroudRenderer = new ShroudRenderer(world);
|
||||
uiOverlay = new UiOverlay();
|
||||
palette = new HardwarePalette(world.Map);
|
||||
|
||||
foreach( var pal in world.traitDict.ActorsWithTraitMultiple<IPalette>( world ) )
|
||||
pal.Trait.InitPalette( this );
|
||||
}
|
||||
|
||||
public int GetPaletteIndex(string name) { return palette.GetPaletteIndex(name); }
|
||||
@@ -41,40 +47,13 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
public int Compare(Renderable x, Renderable y)
|
||||
{
|
||||
var result = x.ZOffset.CompareTo(y.ZOffset);
|
||||
if (result == 0)
|
||||
result = x.Pos.Y.CompareTo(y.Pos.Y);
|
||||
|
||||
return result;
|
||||
return (x.Z + x.ZOffset).CompareTo(y.Z + y.ZOffset);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -88,38 +67,42 @@ namespace OpenRA.Graphics
|
||||
|
||||
var effects = world.Effects.SelectMany(e => e.Render());
|
||||
|
||||
worldSprites = renderables.Concat(effects).ToArray();
|
||||
return renderables.Concat(effects);
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
var bounds = GetBoundsRect();
|
||||
Game.Renderer.Device.EnableScissor(bounds.Left, bounds.Top, bounds.Width, bounds.Height);
|
||||
RefreshPalette();
|
||||
var bounds = Game.viewport.ViewBounds(world);
|
||||
Game.Renderer.EnableScissor(bounds.Left, bounds.Top, bounds.Width, bounds.Height);
|
||||
|
||||
terrainRenderer.Draw(Game.viewport);
|
||||
terrainRenderer.Draw(this, Game.viewport);
|
||||
|
||||
if (world.OrderGenerator != null)
|
||||
world.OrderGenerator.RenderBeforeWorld(world);
|
||||
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);
|
||||
|
||||
Game.Renderer.SpriteRenderer.Flush();
|
||||
Game.Renderer.LineRenderer.Flush();
|
||||
|
||||
foreach (var image in worldSprites)
|
||||
Game.Renderer.SpriteRenderer.DrawSprite(image.Sprite, image.Pos, image.Palette);
|
||||
uiOverlay.Draw(world);
|
||||
Game.Renderer.SpriteRenderer.Flush();
|
||||
// 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(world);
|
||||
world.OrderGenerator.RenderAfterWorld(this, world);
|
||||
|
||||
if (world.LocalPlayer != null)
|
||||
world.LocalPlayer.Shroud.Draw();
|
||||
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.SpriteRenderer.Flush();
|
||||
|
||||
Game.Renderer.Device.DisableScissor();
|
||||
|
||||
Game.Renderer.LineRenderer.Flush();
|
||||
Game.Renderer.Flush();
|
||||
}
|
||||
|
||||
void DrawBox(RectangleF r, Color color)
|
||||
@@ -133,20 +116,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 * 24), new float2(world.Map.MapSize.X * 24, j * 24), Color.Black, Color.Black);
|
||||
Game.Renderer.LineRenderer.DrawLine(new float2(j * 24, 0), new float2(j * 24, world.Map.MapSize.Y * 24), Color.Black, Color.Black);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawSelectionBox(Actor selectedUnit, Color c)
|
||||
{
|
||||
var bounds = selectedUnit.GetBounds(true);
|
||||
@@ -187,7 +156,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++)
|
||||
@@ -197,5 +166,10 @@ namespace OpenRA.Graphics
|
||||
prev = pos;
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshPalette()
|
||||
{
|
||||
palette.Update( world.WorldActor.TraitsImplementing<IPaletteModifier>() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
112
OpenRA.Game/Map.cs
Executable file → Normal file
112
OpenRA.Game/Map.cs
Executable file → Normal file
@@ -19,61 +19,55 @@ using OpenRA.FileFormats;
|
||||
|
||||
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)
|
||||
{
|
||||
MapSize = new int2(1, 1);
|
||||
Tileset = tileset;
|
||||
MapResources = new TileReference<byte, byte>[1, 1];
|
||||
MapTiles = new TileReference<ushort, byte>[1, 1]
|
||||
{ { new TileReference<ushort, byte> {
|
||||
type = (ushort)0xffffu,
|
||||
image = (byte)0xffu,
|
||||
index = (byte)0xffu } } };
|
||||
|
||||
var tile = OpenRA.Rules.TileSets[Tileset].Templates.First();
|
||||
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);
|
||||
ResizeCordon(0,0,0,0);
|
||||
|
||||
Title = "Name your map here";
|
||||
Description = "Describe your map here";
|
||||
@@ -87,21 +81,16 @@ namespace OpenRA
|
||||
public int2 Location = int2.Zero;
|
||||
public string Owner = null;
|
||||
}
|
||||
|
||||
|
||||
public Map(MapStub stub) : this(stub.Container) {}
|
||||
public Map(IFolder package)
|
||||
: base(package)
|
||||
{
|
||||
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.
|
||||
@@ -190,13 +179,23 @@ 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)
|
||||
{
|
||||
// Todo: save to a zip file in the support dir by default
|
||||
Container = new Folder(filepath, 0);
|
||||
MapFormat = 3;
|
||||
|
||||
var root = new List<MiniYamlNode>();
|
||||
@@ -217,9 +216,12 @@ 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 ) );
|
||||
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));
|
||||
|
||||
SaveBinaryData(Path.Combine(filepath, "map.bin"));
|
||||
root.WriteToFile(Path.Combine(filepath, "map.yaml"));
|
||||
@@ -244,7 +246,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");
|
||||
@@ -306,17 +308,12 @@ namespace OpenRA
|
||||
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();
|
||||
var data = Container.GetContent("map.yaml").ReadAllBytes()
|
||||
.Concat(Container.GetContent("map.bin").ReadAllBytes()).ToArray();
|
||||
|
||||
// Take the SHA1
|
||||
using (var csp = SHA1.Create())
|
||||
@@ -332,7 +329,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)
|
||||
@@ -351,5 +348,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,7 +13,6 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Support;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
@@ -21,13 +20,14 @@ namespace OpenRA
|
||||
{
|
||||
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 );
|
||||
ObjectCreator = new ObjectCreator( Manifest );
|
||||
LoadScreen = ObjectCreator.CreateObject<ILoadScreen>(Manifest.LoadScreen);
|
||||
@@ -39,6 +39,7 @@ namespace OpenRA
|
||||
SheetBuilder = new SheetBuilder( TextureChannel.Red );
|
||||
CursorSheetBuilder = new CursorSheetBuilder( this );
|
||||
AvailableMaps = FindMaps( mods );
|
||||
WidgetLoader = new WidgetLoader( this );
|
||||
}
|
||||
|
||||
// TODO: Do this nicer
|
||||
@@ -48,10 +49,13 @@ namespace OpenRA
|
||||
.Where(p => Directory.Exists(p))
|
||||
.SelectMany(p => Directory.GetDirectories(p)).ToList();
|
||||
|
||||
return paths.Select(p => new MapStub(new Folder(p))).ToDictionary(m => m.Uid);
|
||||
return paths.Select(p => new MapStub(new Folder(p, int.MaxValue))).ToDictionary(m => m.Uid);
|
||||
}
|
||||
|
||||
string cachedTheatre = null;
|
||||
string cachedTileset = null;
|
||||
bool previousMapHadSequences = true;
|
||||
IFolder previousMapMount = null;
|
||||
|
||||
public Map PrepareMap(string uid)
|
||||
{
|
||||
LoadScreen.Display();
|
||||
@@ -59,15 +63,34 @@ 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]);
|
||||
|
||||
// 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)
|
||||
FileSystem.Mount(map.Container);
|
||||
|
||||
// Store a reference so we can unload it next time
|
||||
previousMapMount = map.Container;
|
||||
|
||||
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);
|
||||
cachedTheatre = map.Theater;
|
||||
CursorSheetBuilder = new CursorSheetBuilder( this );
|
||||
CursorProvider.Initialize(Manifest.Cursors);
|
||||
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
|
||||
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();
|
||||
@@ -73,9 +101,11 @@ namespace OpenRA.Network
|
||||
foreach( var p in packets )
|
||||
packetFn( p.FromClient, p.Data );
|
||||
}
|
||||
|
||||
public virtual void Dispose() { }
|
||||
}
|
||||
|
||||
class NetworkConnection : EchoConnection, IDisposable
|
||||
class NetworkConnection : EchoConnection
|
||||
{
|
||||
TcpClient socket;
|
||||
int clientId;
|
||||
@@ -112,12 +142,13 @@ namespace OpenRA.Network
|
||||
receivedPackets.Add( new ReceivedPacket { FromClient = client, Data = buf } );
|
||||
}
|
||||
}
|
||||
catch( SocketException )
|
||||
catch { }
|
||||
finally
|
||||
{
|
||||
connectionState = ConnectionState.NotConnected;
|
||||
if( socket != null )
|
||||
socket.Close();
|
||||
}
|
||||
catch ( IOException ) { socket.Close(); }
|
||||
catch (ThreadAbortException ) { socket.Close(); }
|
||||
}
|
||||
) { IsBackground = true };
|
||||
t.Start();
|
||||
@@ -126,23 +157,42 @@ 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 );
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
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;
|
||||
public void Dispose ()
|
||||
|
||||
public override void Dispose ()
|
||||
{
|
||||
if (disposed) return;
|
||||
disposed = true;
|
||||
@@ -157,48 +207,4 @@ namespace OpenRA.Network
|
||||
|
||||
~NetworkConnection() { Dispose(); }
|
||||
}
|
||||
|
||||
class ReplayConnection : IConnection
|
||||
{
|
||||
//uint nextFrame = 1;
|
||||
FileStream replayStream;
|
||||
|
||||
public ReplayConnection( string replayFilename )
|
||||
{
|
||||
replayStream = File.OpenRead( replayFilename );
|
||||
}
|
||||
|
||||
public int LocalClientId
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
public ConnectionState ConnectionState
|
||||
{
|
||||
get { return ConnectionState.Connected; }
|
||||
}
|
||||
|
||||
public void Send( byte[] packet )
|
||||
{
|
||||
// do nothing; ignore locally generated orders
|
||||
}
|
||||
|
||||
public void Receive( Action<int, byte[]> packetFn )
|
||||
{
|
||||
if( replayStream == null ) return;
|
||||
|
||||
var reader = new BinaryReader( replayStream );
|
||||
while( replayStream.Position < replayStream.Length )
|
||||
{
|
||||
var client = reader.ReadInt32();
|
||||
var packetLen = reader.ReadInt32();
|
||||
var packet = reader.ReadBytes( packetLen );
|
||||
packetFn( client, packet );
|
||||
|
||||
if( !Game.orderManager.GameStarted )
|
||||
return;
|
||||
}
|
||||
replayStream = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
56
OpenRA.Game/Network/FrameData.cs
Executable file
56
OpenRA.Game/Network/FrameData.cs
Executable file
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
class FrameData
|
||||
{
|
||||
public struct ClientOrder
|
||||
{
|
||||
public int Client;
|
||||
public Order Order;
|
||||
}
|
||||
|
||||
readonly Dictionary<int, int> clientQuitTimes = new Dictionary<int, int>();
|
||||
readonly Dictionary<int, Dictionary<int, byte[]>> framePackets = new Dictionary<int, Dictionary<int, byte[]>>();
|
||||
|
||||
public IEnumerable<int> ClientsPlayingInFrame( int frame )
|
||||
{
|
||||
return clientQuitTimes
|
||||
.Where( x => frame <= x.Value )
|
||||
.Select( x => x.Key )
|
||||
.OrderBy( x => x );
|
||||
}
|
||||
|
||||
public void ClientQuit( int clientId, int lastClientFrame )
|
||||
{
|
||||
clientQuitTimes[clientId] = lastClientFrame;
|
||||
}
|
||||
|
||||
public void AddFrameOrders( int clientId, int frame, byte[] orders )
|
||||
{
|
||||
var frameData = framePackets.GetOrAdd( frame );
|
||||
frameData.Add( clientId, orders );
|
||||
}
|
||||
|
||||
public bool IsReadyForFrame( int frame )
|
||||
{
|
||||
var frameData = framePackets.GetOrAdd( frame );
|
||||
return ClientsPlayingInFrame( frame )
|
||||
.All( client => frameData.ContainsKey( client ) );
|
||||
}
|
||||
|
||||
public IEnumerable<ClientOrder> OrdersForFrame( World world, int frame )
|
||||
{
|
||||
var frameData = framePackets[ frame ];
|
||||
var clientData = ClientsPlayingInFrame( frame )
|
||||
.ToDictionary( k => k, v => frameData[ v ] );
|
||||
|
||||
return clientData
|
||||
.SelectMany( x => x.Value
|
||||
.ToOrderList( world )
|
||||
.Select( o => new ClientOrder { Client = x.Key, Order = o } ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,11 @@ 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) { }
|
||||
public Order(string orderString, Actor subject, bool queued)
|
||||
: this(orderString, subject, null, int2.Zero, null, queued, int2.Zero) { }
|
||||
|
||||
public byte[] Serialize()
|
||||
{
|
||||
@@ -80,13 +87,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 +119,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 +139,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 +149,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 +183,32 @@ 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 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)
|
||||
public static Order CancelProduction(Actor subject, string item, int count)
|
||||
{
|
||||
return new Order("CancelProduction", subject, item);
|
||||
return new Order("CancelProduction", subject, false) { TargetLocation = new int2(count, 0), TargetString = item };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,8 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
@@ -22,22 +20,6 @@ namespace OpenRA.Network
|
||||
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,47 +16,46 @@ using OpenRA.FileFormats;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
class OrderManager : IDisposable
|
||||
public class OrderManager : IDisposable
|
||||
{
|
||||
SyncReport syncReport = new SyncReport();
|
||||
readonly SyncReport syncReport;
|
||||
readonly FrameData frameData = new FrameData();
|
||||
|
||||
public int FrameNumber { get; private set; }
|
||||
public Session LobbyInfo = new Session( Game.Settings.Game.Mods );
|
||||
public Session.Client LocalClient { get { return LobbyInfo.ClientWithIndex( Connection.LocalClientId ); } }
|
||||
public World world;
|
||||
|
||||
public readonly string Host;
|
||||
public readonly int Port;
|
||||
|
||||
public int NetFrameNumber { get; private set; }
|
||||
public int LocalFrameNumber;
|
||||
public int FramesAhead = 0;
|
||||
|
||||
public bool GameStarted { get { return FrameNumber != 0; } }
|
||||
public int LastTickTime = Environment.TickCount;
|
||||
|
||||
public bool GameStarted { get { return NetFrameNumber != 0; } }
|
||||
public IConnection Connection { get; private set; }
|
||||
|
||||
public readonly int SyncHeaderSize = 9;
|
||||
|
||||
Dictionary<int, int> clientQuitTimes = new Dictionary<int, int>();
|
||||
|
||||
Dictionary<int, Dictionary<int, byte[]>> frameClientData =
|
||||
new Dictionary<int, Dictionary<int, byte[]>>();
|
||||
List<Order> localOrders = new List<Order>();
|
||||
|
||||
FileStream replaySaveFile;
|
||||
|
||||
public void StartGame()
|
||||
{
|
||||
if (GameStarted) return;
|
||||
|
||||
FrameNumber = 1;
|
||||
for( int i = FrameNumber ; i <= FramesAhead ; i++ )
|
||||
Connection.Send( new List<Order>().Serialize( i ) );
|
||||
NetFrameNumber = 1;
|
||||
for( int i = NetFrameNumber ; i <= FramesAhead ; i++ )
|
||||
Connection.Send( i, new List<byte[]>() );
|
||||
}
|
||||
|
||||
public OrderManager( IConnection conn )
|
||||
public OrderManager( string host, int port, IConnection conn )
|
||||
{
|
||||
this.Host = host;
|
||||
this.Port = port;
|
||||
Connection = conn;
|
||||
}
|
||||
|
||||
public OrderManager( IConnection conn, string replayFilename )
|
||||
: this( conn )
|
||||
{
|
||||
string path = Game.SupportDir + "Replays" + Path.DirectorySeparatorChar;
|
||||
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
|
||||
replaySaveFile = File.Create( path + replayFilename );
|
||||
syncReport = new SyncReport( this );
|
||||
}
|
||||
|
||||
public void IssueOrders( Order[] orders )
|
||||
@@ -70,11 +69,11 @@ namespace OpenRA.Network
|
||||
localOrders.Add( order );
|
||||
}
|
||||
|
||||
public void TickImmediate( World world )
|
||||
public void TickImmediate()
|
||||
{
|
||||
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[]>>();
|
||||
@@ -84,26 +83,23 @@ namespace OpenRA.Network
|
||||
{
|
||||
var frame = BitConverter.ToInt32( packet, 0 );
|
||||
if( packet.Length == 5 && packet[ 4 ] == 0xBF )
|
||||
clientQuitTimes[ clientId ] = frame;
|
||||
frameData.ClientQuit( clientId, frame );
|
||||
else if( packet.Length >= 5 && packet[ 4 ] == 0x65 )
|
||||
CheckSync( packet );
|
||||
else if( frame == 0 )
|
||||
immediatePackets.Add( Pair.New( clientId, packet ) );
|
||||
else
|
||||
frameClientData.GetOrAdd( frame ).Add( clientId, packet );
|
||||
frameData.AddFrameOrders( clientId, frame, packet );
|
||||
} );
|
||||
|
||||
foreach( var p in immediatePackets )
|
||||
{
|
||||
foreach( var o in p.Second.ToOrderList( world ) )
|
||||
UnitOrders.ProcessOrder( world, p.First, o );
|
||||
WriteImmediateToReplay( immediatePackets );
|
||||
}
|
||||
UnitOrders.ProcessOrder( this, world, p.First, o );
|
||||
}
|
||||
|
||||
Dictionary<int, byte[]> syncForFrame = new Dictionary<int, byte[]>();
|
||||
|
||||
void CheckSync(byte[] packet)
|
||||
void CheckSync( byte[] packet )
|
||||
{
|
||||
var frame = BitConverter.ToInt32(packet, 0);
|
||||
byte[] existingSync;
|
||||
@@ -134,14 +130,9 @@ namespace OpenRA.Network
|
||||
syncForFrame.Add(frame, packet);
|
||||
}
|
||||
|
||||
void OutOfSync( int frame , int index)
|
||||
{
|
||||
var frameData = clientQuitTimes
|
||||
.Where( x => frame <= x.Value )
|
||||
.OrderBy( x => x.Key )
|
||||
.ToDictionary( k => k.Key, v => frameClientData[ FrameNumber ][ v.Key ] );
|
||||
|
||||
var order = frameData.SelectMany( o => o.Value.ToOrderList( Game.world ).Select( a => new { Client = o.Key, Order = a } ) ).ElementAt(index);
|
||||
void OutOfSync(int frame, int index)
|
||||
{
|
||||
var order = frameData.OrdersForFrame( world, frame ).ElementAt(index);
|
||||
throw new InvalidOperationException("Out of sync in frame {0}.\n {1}".F(frame, order.Order.ToString()));
|
||||
}
|
||||
|
||||
@@ -157,72 +148,32 @@ namespace OpenRA.Network
|
||||
|
||||
public bool IsReadyForNextFrame
|
||||
{
|
||||
get
|
||||
{
|
||||
return FrameNumber > 0 &&
|
||||
clientQuitTimes
|
||||
.Where( x => FrameNumber <= x.Value )
|
||||
.All( x => frameClientData.GetOrAdd( FrameNumber ).ContainsKey( x.Key ) );
|
||||
}
|
||||
get { return NetFrameNumber >= 1 && frameData.IsReadyForFrame( NetFrameNumber ); }
|
||||
}
|
||||
|
||||
public void Tick( World world )
|
||||
public void Tick()
|
||||
{
|
||||
if( !IsReadyForNextFrame )
|
||||
throw new InvalidOperationException();
|
||||
|
||||
Connection.Send( localOrders.Serialize( FrameNumber + FramesAhead ) );
|
||||
Connection.Send( NetFrameNumber + FramesAhead, localOrders.Select( o => o.Serialize() ).ToList() );
|
||||
localOrders.Clear();
|
||||
|
||||
var frameData = clientQuitTimes
|
||||
.Where( x => FrameNumber <= x.Value )
|
||||
.OrderBy( x => x.Key )
|
||||
.ToDictionary( k => k.Key, v => frameClientData[ FrameNumber ][ v.Key ] );
|
||||
var sync = new List<int>();
|
||||
sync.Add( world.SyncHash() );
|
||||
|
||||
foreach( var order in frameData.SelectMany( o => o.Value.ToOrderList( world ).Select( a => new { Client = o.Key, Order = a } ) ) )
|
||||
foreach( var order in frameData.OrdersForFrame( world, NetFrameNumber) )
|
||||
{
|
||||
UnitOrders.ProcessOrder( world, order.Client, order.Order );
|
||||
UnitOrders.ProcessOrder( this, world, order.Client, order.Order );
|
||||
sync.Add( world.SyncHash() );
|
||||
}
|
||||
|
||||
var ss = sync.SerializeSync( FrameNumber );
|
||||
Connection.Send( ss );
|
||||
WriteToReplay( frameData, ss );
|
||||
var ss = sync.SerializeSync();
|
||||
Connection.SendSync( NetFrameNumber, ss );
|
||||
|
||||
syncReport.UpdateSyncReport();
|
||||
|
||||
CheckSync( ss );
|
||||
|
||||
++FrameNumber;
|
||||
}
|
||||
|
||||
void WriteToReplay( Dictionary<int, byte[]> frameData, byte[] syncData )
|
||||
{
|
||||
if( replaySaveFile == null ) return;
|
||||
|
||||
foreach( var f in frameData )
|
||||
{
|
||||
replaySaveFile.Write( BitConverter.GetBytes( f.Key ) );
|
||||
replaySaveFile.Write( BitConverter.GetBytes( f.Value.Length ) );
|
||||
replaySaveFile.Write( f.Value );
|
||||
}
|
||||
replaySaveFile.Write( BitConverter.GetBytes( (int)0 ) );
|
||||
replaySaveFile.Write( BitConverter.GetBytes( (int)syncData.Length ) );
|
||||
replaySaveFile.Write( syncData );
|
||||
}
|
||||
|
||||
void WriteImmediateToReplay( List<Pair<int, byte[]>> immediatePackets )
|
||||
{
|
||||
if( replaySaveFile == null ) return;
|
||||
|
||||
foreach( var i in immediatePackets )
|
||||
{
|
||||
replaySaveFile.Write( BitConverter.GetBytes( i.First ) );
|
||||
replaySaveFile.Write( BitConverter.GetBytes( i.Second.Length ) );
|
||||
replaySaveFile.Write( i.Second );
|
||||
}
|
||||
++NetFrameNumber;
|
||||
}
|
||||
|
||||
bool disposed;
|
||||
@@ -230,11 +181,7 @@ namespace OpenRA.Network
|
||||
{
|
||||
if (disposed) return;
|
||||
|
||||
if (replaySaveFile != null)
|
||||
replaySaveFile.Dispose();
|
||||
|
||||
var disposableConnection = Connection as IDisposable;
|
||||
if (disposableConnection != null) disposableConnection.Dispose();
|
||||
Connection.Dispose();
|
||||
|
||||
disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
110
OpenRA.Game/Network/ReplayConnection.cs
Executable file
110
OpenRA.Game/Network/ReplayConnection.cs
Executable file
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
class ReplayConnection : IConnection
|
||||
{
|
||||
//uint nextFrame = 1;
|
||||
FileStream replayStream;
|
||||
|
||||
public ReplayConnection( string replayFilename )
|
||||
{
|
||||
replayStream = File.OpenRead( replayFilename );
|
||||
}
|
||||
|
||||
public int LocalClientId
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
public ConnectionState ConnectionState
|
||||
{
|
||||
get { return ConnectionState.Connected; }
|
||||
}
|
||||
|
||||
// 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 )
|
||||
{
|
||||
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 );
|
||||
while( replayStream.Position < replayStream.Length )
|
||||
{
|
||||
var client = reader.ReadInt32();
|
||||
var packetLen = reader.ReadInt32();
|
||||
var packet = reader.ReadBytes( packetLen );
|
||||
packetFn( client, packet );
|
||||
}
|
||||
replayStream = null;
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
}
|
||||
|
||||
class ReplayRecorderConnection : IConnection
|
||||
{
|
||||
IConnection inner;
|
||||
BinaryWriter writer;
|
||||
|
||||
public ReplayRecorderConnection( IConnection inner, FileStream replayFile )
|
||||
{
|
||||
this.inner = inner;
|
||||
this.writer = new BinaryWriter( replayFile );
|
||||
}
|
||||
|
||||
public int LocalClientId { get { return inner.LocalClientId; } }
|
||||
public ConnectionState ConnectionState { get { return inner.ConnectionState; } }
|
||||
|
||||
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 )
|
||||
{
|
||||
inner.Receive( ( client, data ) =>
|
||||
{
|
||||
writer.Write( client );
|
||||
writer.Write( data.Length );
|
||||
writer.Write( data );
|
||||
packetFn( client, data );
|
||||
} );
|
||||
}
|
||||
|
||||
bool disposed;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if( disposed )
|
||||
return;
|
||||
|
||||
writer.Close();
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
~ReplayRecorderConnection()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
|
||||
namespace OpenRA.Network
|
||||
@@ -20,10 +21,21 @@ namespace OpenRA.Network
|
||||
public List<Slot> Slots = new List<Slot>();
|
||||
public Global GlobalSettings = new Global();
|
||||
|
||||
public Client ClientWithIndex(int clientID)
|
||||
{
|
||||
return Clients.SingleOrDefault(c => c.Index == clientID);
|
||||
}
|
||||
|
||||
public Client ClientInSlot(Slot slot)
|
||||
{
|
||||
return Clients.SingleOrDefault(c => c.Slot == slot.Index);
|
||||
}
|
||||
|
||||
public enum ClientState
|
||||
{
|
||||
NotReady,
|
||||
Ready
|
||||
Ready,
|
||||
Disconnected = 1000
|
||||
}
|
||||
|
||||
public class Client
|
||||
@@ -44,13 +56,14 @@ 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?
|
||||
}
|
||||
|
||||
public class Global
|
||||
{
|
||||
public string ServerName;
|
||||
public string Map;
|
||||
public string[] Mods = { "ra" }; // mod names
|
||||
public int OrderLatency = 3;
|
||||
@@ -59,32 +72,36 @@ namespace OpenRA.Network
|
||||
public bool AllowCheats = false;
|
||||
}
|
||||
|
||||
public Session(string[] mods)
|
||||
{
|
||||
this.GlobalSettings.Mods = mods.ToArray();
|
||||
}
|
||||
|
||||
public string Serialize()
|
||||
{
|
||||
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();
|
||||
session.GlobalSettings.Mods = 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);
|
||||
@@ -95,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,61 +3,90 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
class SyncReport
|
||||
{
|
||||
Queue<Pair<int, string>> syncReports = new Queue<Pair<int, string>>();
|
||||
readonly OrderManager orderManager;
|
||||
const int numSyncReports = 5;
|
||||
Report[] syncReports = new Report[numSyncReports];
|
||||
int curIndex = 0;
|
||||
|
||||
public SyncReport( OrderManager orderManager )
|
||||
{
|
||||
this.orderManager = orderManager;
|
||||
for (var i = 0; i < numSyncReports; i++)
|
||||
syncReports[i] = new SyncReport.Report();
|
||||
}
|
||||
|
||||
internal void UpdateSyncReport()
|
||||
{
|
||||
if (!Game.Settings.Debug.RecordSyncReports)
|
||||
return;
|
||||
|
||||
while (syncReports.Count >= numSyncReports) syncReports.Dequeue();
|
||||
syncReports.Enqueue(Pair.New(Game.orderManager.FrameNumber, GenerateSyncReport()));
|
||||
|
||||
GenerateSyncReport(syncReports[curIndex]);
|
||||
curIndex = ++curIndex % numSyncReports;
|
||||
}
|
||||
|
||||
string GenerateSyncReport()
|
||||
|
||||
void GenerateSyncReport(Report report)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("Actors:");
|
||||
foreach (var a in Game.world.Actors)
|
||||
sb.AppendLine("\t {0} {1} {2} ({3})".F(
|
||||
a.ActorID,
|
||||
a.Info.Name,
|
||||
(a.Owner == null) ? "null" : a.Owner.InternalName,
|
||||
Sync.CalculateSyncHash(a)));
|
||||
|
||||
sb.AppendLine("Tick Actors:");
|
||||
foreach (var a in Game.world.Queries.WithTraitMultiple<object>())
|
||||
report.Frame = orderManager.NetFrameNumber;
|
||||
report.SyncedRandom = orderManager.world.SharedRandom.Last;
|
||||
report.Traits.Clear();
|
||||
foreach (var a in orderManager.world.Queries.WithTraitMultiple<object>())
|
||||
{
|
||||
var sync = Sync.CalculateSyncHash(a.Trait);
|
||||
if (sync != 0)
|
||||
sb.AppendLine("\t {0} {1} {2} {3} ({4})".F(
|
||||
a.Actor.ActorID,
|
||||
a.Actor.Info.Name,
|
||||
(a.Actor.Owner == null) ? "null" : a.Actor.Owner.InternalName,
|
||||
a.Trait.GetType().Name,
|
||||
sync));
|
||||
report.Traits.Add(new TraitReport()
|
||||
{
|
||||
ActorID = a.Actor.ActorID,
|
||||
Type = a.Actor.Info.Name,
|
||||
Owner = (a.Actor.Owner == null) ? "null" : a.Actor.Owner.InternalName,
|
||||
Trait = a.Trait.GetType().Name,
|
||||
Hash = sync
|
||||
});
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
internal void DumpSyncReport(int frame)
|
||||
{
|
||||
var f = syncReports.FirstOrDefault(a => a.First == frame);
|
||||
if (f == default(Pair<int, string>))
|
||||
{
|
||||
Log.Write("sync", "No sync report available!");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Write("sync", "Sync for net frame {0} -------------", f.First);
|
||||
Log.Write("sync", "{0}", f.Second);
|
||||
foreach (var r in syncReports)
|
||||
if (r.Frame == frame)
|
||||
{
|
||||
Log.Write("sync", "Sync for net frame {0} -------------", r.Frame);
|
||||
Log.Write("sync", "SharedRandom: "+r.SyncedRandom);
|
||||
Log.Write("sync", "Synced Traits:");
|
||||
foreach (var a in r.Traits)
|
||||
Log.Write("sync", "\t {0} {1} {2} {3} ({4})".F(
|
||||
a.ActorID,
|
||||
a.Type,
|
||||
a.Owner,
|
||||
a.Trait,
|
||||
a.Hash
|
||||
));
|
||||
return;
|
||||
}
|
||||
Log.Write("sync", "No sync report available!");
|
||||
}
|
||||
|
||||
class Report
|
||||
{
|
||||
public int Frame;
|
||||
public int SyncedRandom;
|
||||
public List<TraitReport> Traits = new List<TraitReport>();
|
||||
}
|
||||
|
||||
struct TraitReport
|
||||
{
|
||||
public uint ActorID;
|
||||
public string Type;
|
||||
public string Owner;
|
||||
public string Trait;
|
||||
public int Hash;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -16,92 +16,141 @@ namespace OpenRA.Network
|
||||
{
|
||||
static class UnitOrders
|
||||
{
|
||||
static Session.Client FindClientById(int id)
|
||||
static Player FindPlayerByClient(this World world, Session.Client c)
|
||||
{
|
||||
return Game.LobbyInfo.Clients.FirstOrDefault(c => c.Index == id);
|
||||
/* 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,
|
||||
* bots,.. */
|
||||
return world.players.Values.FirstOrDefault(
|
||||
p => p.ClientIndex == c.Index && p.PlayerName == c.Name);
|
||||
}
|
||||
|
||||
static Player FindPlayerByClientId(int id)
|
||||
public static void ProcessOrder(OrderManager orderManager, World world, int clientId, Order order)
|
||||
{
|
||||
/* todo: find the interactive player. */
|
||||
return Game.world.players.Values.FirstOrDefault(p => p.ClientIndex == id);
|
||||
}
|
||||
|
||||
public static void ProcessOrder( 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 = FindClientById(clientId);
|
||||
if (client != null)
|
||||
{
|
||||
var player = FindPlayerByClientId(clientId);
|
||||
if (player != null && player.WinState == WinState.Lost)
|
||||
Game.AddChatLine(client.Color1, client.Name + " (Dead)", order.TargetString);
|
||||
else
|
||||
Game.AddChatLine(client.Color1, client.Name, order.TargetString);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "TeamChat":
|
||||
{
|
||||
var client = FindClientById(clientId);
|
||||
if (client != null)
|
||||
{
|
||||
var player = FindPlayerByClientId(clientId);
|
||||
var display = (world.GameHasStarted) ?
|
||||
player != null && (world.LocalPlayer != null && player.Stances[world.LocalPlayer] == Stance.Ally
|
||||
|| player.WinState == WinState.Lost) :
|
||||
client == Game.LocalClient || (client.Team == Game.LocalClient.Team && client.Team != 0);
|
||||
|
||||
if (display)
|
||||
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
|
||||
if (client != null)
|
||||
{
|
||||
var suffix = (player != null && player.WinState == WinState.Lost) ? " (Dead)" : " (Team)";
|
||||
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)
|
||||
{
|
||||
client.State = Session.ClientState.Disconnected;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "TeamChat":
|
||||
{
|
||||
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
|
||||
|
||||
if (client != null)
|
||||
{
|
||||
if (world == null)
|
||||
{
|
||||
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;
|
||||
}
|
||||
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 (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;
|
||||
}
|
||||
|
||||
case "SetStance":
|
||||
{
|
||||
var targetPlayer = order.Player.World.players[order.TargetLocation.X];
|
||||
var newStance = (Stance)order.TargetLocation.Y;
|
||||
|
||||
SetPlayerStance(world, order.Player, targetPlayer, 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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "StartGame":
|
||||
{
|
||||
Game.AddChatLine(Color.White, "Server", "The game has started.");
|
||||
Game.StartGame(Game.LobbyInfo.GlobalSettings.Map);
|
||||
break;
|
||||
}
|
||||
case "SyncInfo":
|
||||
{
|
||||
Game.SyncLobbyInfo(order.TargetString);
|
||||
break;
|
||||
}
|
||||
case "SetStance":
|
||||
{
|
||||
var targetPlayer = order.Player.World.players[order.TargetLocation.X];
|
||||
var oldStance = order.Player.Stances[targetPlayer];
|
||||
order.Player.Stances[targetPlayer] = (Stance)order.TargetLocation.Y;
|
||||
|
||||
if (targetPlayer == world.LocalPlayer)
|
||||
world.WorldActor.Trait<Shroud>().UpdatePlayerStance(world, order.Player, oldStance, order.Player.Stances[targetPlayer]);
|
||||
|
||||
Game.Debug("{0} has set diplomatic stance vs {1} to {2}".F(
|
||||
order.Player.PlayerName, targetPlayer.PlayerName, order.Player.Stances[targetPlayer]));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if( !order.IsImmediate )
|
||||
foreach (var t in order.Subject.TraitsImplementing<IResolveOrder>())
|
||||
t.ResolveOrder(order.Subject, order);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SetPlayerStance(World w, Player p, Player target, Stance s)
|
||||
{
|
||||
var oldStance = p.Stances[target];
|
||||
p.Stances[target] = s;
|
||||
if (target == w.LocalPlayer)
|
||||
w.WorldActor.Trait<Shroud>().UpdatePlayerStance(w, p, oldStance, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +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.Linq;
|
||||
using System.Text;
|
||||
using OpenRA.FileFormats;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using OpenRA.FileFormats;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
@@ -32,19 +41,63 @@ namespace OpenRA
|
||||
public static Action<string> MissingTypeAction =
|
||||
s => { throw new InvalidOperationException("Cannot locate type: {0}".F(s)); };
|
||||
|
||||
public T CreateObject<T>(string classname)
|
||||
public T CreateObject<T>(string className)
|
||||
{
|
||||
foreach (var mod in ModAssemblies)
|
||||
{
|
||||
var fullTypeName = mod.Second + "." + classname;
|
||||
var obj = mod.First.CreateInstance(fullTypeName);
|
||||
if (obj != null)
|
||||
return (T)obj;
|
||||
}
|
||||
return CreateObject<T>( className, new Dictionary<string, object>() );
|
||||
}
|
||||
|
||||
MissingTypeAction(classname);
|
||||
public T CreateObject<T>( string className, Dictionary<string, object> args )
|
||||
{
|
||||
foreach( var mod in ModAssemblies )
|
||||
{
|
||||
var type = mod.First.GetType( mod.Second + "." + className, false );
|
||||
if( type == null ) continue;
|
||||
var ctors = type.GetConstructors( BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance ).Where( x => x.HasAttribute<UseCtorAttribute>() ).ToList();
|
||||
if( ctors.Count == 0 )
|
||||
return (T)CreateBasic( type );
|
||||
else if( ctors.Count == 1 )
|
||||
return (T)CreateUsingArgs( ctors[ 0 ], args );
|
||||
else
|
||||
throw new InvalidOperationException( "ObjectCreator: UseCtor on multiple constructors; invalid." );
|
||||
}
|
||||
MissingTypeAction(className);
|
||||
return default(T);
|
||||
}
|
||||
|
||||
public object CreateBasic( Type type )
|
||||
{
|
||||
return type.GetConstructor( new Type[ 0 ] ).Invoke( new object[ 0 ] );
|
||||
}
|
||||
|
||||
public object CreateUsingArgs( ConstructorInfo ctor, Dictionary<string, object> args )
|
||||
{
|
||||
var p = ctor.GetParameters();
|
||||
var a = new object[ p.Length ];
|
||||
for( int i = 0 ; i < p.Length ; i++ )
|
||||
{
|
||||
var attrs = p[ i ].GetCustomAttributes<ParamAttribute>();
|
||||
if( attrs.Length != 1 ) throw new InvalidOperationException( "ObjectCreator: argument in [UseCtor] doesn't have [Param]" );
|
||||
a[ i ] = args[ attrs[ 0 ].ParamName ?? p[i].Name ];
|
||||
}
|
||||
return ctor.Invoke( a );
|
||||
}
|
||||
|
||||
[AttributeUsage( AttributeTargets.Parameter )]
|
||||
public class ParamAttribute : Attribute
|
||||
{
|
||||
public string ParamName { get; private set; }
|
||||
|
||||
public ParamAttribute() { }
|
||||
|
||||
public ParamAttribute( string paramName )
|
||||
{
|
||||
ParamName = paramName;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage( AttributeTargets.Constructor )]
|
||||
public class UseCtorAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -75,31 +75,20 @@
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Effects\RepairIndicator.cs" />
|
||||
<Compile Include="GameRules\WeaponInfo.cs" />
|
||||
<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\Player\TechTreeCache.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\TechTree.cs" />
|
||||
<Compile Include="GameRules\VoiceInfo.cs" />
|
||||
<Compile Include="Effects\IEffect.cs" />
|
||||
<Compile Include="Graphics\ChromeProvider.cs" />
|
||||
@@ -109,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" />
|
||||
@@ -133,21 +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="Orders\PlaceBuildingOrderGenerator.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" />
|
||||
@@ -155,25 +133,12 @@
|
||||
<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\Buildable.cs" />
|
||||
<Compile Include="Traits\Building.cs" />
|
||||
<Compile Include="Traits\World\BuildingInfluence.cs" />
|
||||
<Compile Include="Traits\Player\PlaceBuilding.cs" />
|
||||
<Compile Include="Traits\World\PlayerColorPalette.cs" />
|
||||
<Compile Include="Traits\World\ResourceLayer.cs" />
|
||||
<Compile Include="Traits\World\ResourceType.cs" />
|
||||
<Compile Include="Traits\SupportPower.cs" />
|
||||
<Compile Include="Traits\ProvidesRadar.cs" />
|
||||
<Compile Include="Traits\Selectable.cs" />
|
||||
<Compile Include="Traits\Player\ProductionQueue.cs" />
|
||||
<Compile Include="Traits\Mobile.cs" />
|
||||
<Compile Include="Traits\Production.cs" />
|
||||
<Compile Include="Traits\RallyPoint.cs" />
|
||||
<Compile Include="Traits\Render\RenderSimple.cs" />
|
||||
<Compile Include="Traits\TraitsInterfaces.cs" />
|
||||
<Compile Include="Traits\Turreted.cs" />
|
||||
<Compile Include="Traits\World\UnitInfluence.cs" />
|
||||
<Compile Include="Network\UnitOrders.cs" />
|
||||
<Compile Include="Traits\Util.cs" />
|
||||
@@ -181,32 +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="Traits\World\BibLayer.cs" />
|
||||
<Compile Include="Traits\World\SmudgeLayer.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="Traits\SharesCell.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" />
|
||||
@@ -215,16 +170,16 @@
|
||||
<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\RepairableBuilding.cs" />
|
||||
<Compile Include="Traits\Activities\Drag.cs" />
|
||||
<Compile Include="Widgets\VqaPlayerWidget.cs" />
|
||||
<Compile Include="Widgets\Delegates\VideoPlayerDelegate.cs" />
|
||||
<Compile Include="Traits\MPStartLocations.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="Server\TraitInterfaces.cs" />
|
||||
<Compile Include="Widgets\ScrollPanelWidget.cs" />
|
||||
<Compile Include="Graphics\ShroudRenderer.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
|
||||
@@ -233,14 +188,21 @@
|
||||
</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" />
|
||||
<Compile Include="Network\ReplayConnection.cs" />
|
||||
<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\PrimaryBuilding.cs" />
|
||||
<Compile Include="Widgets\Delegates\DeveloperModeDelegate.cs" />
|
||||
<Compile Include="Traits\Activities\CancelableActivity.cs" />
|
||||
<Compile Include="Traits\SharesCell.cs" />
|
||||
<Compile Include="Widgets\PasswordFieldWidget.cs" />
|
||||
<Compile Include="Widgets\ScrollingTextWidget.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.2.0">
|
||||
@@ -270,4 +232,4 @@
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -10,38 +10,74 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Orders
|
||||
{
|
||||
public class GenericSelectTarget : IOrderGenerator
|
||||
{
|
||||
readonly Actor subject;
|
||||
readonly IEnumerable<Actor> subjects;
|
||||
readonly string order;
|
||||
readonly string cursor;
|
||||
readonly MouseButton expectedButton;
|
||||
|
||||
public GenericSelectTarget(Actor subject, string order, string cursor)
|
||||
public GenericSelectTarget(IEnumerable<Actor> subjects, string order, string cursor, MouseButton button)
|
||||
{
|
||||
this.subject = subject;
|
||||
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)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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))
|
||||
yield return new Order(order, subject, xy);
|
||||
if (mi.Button == expectedButton && world.Map.IsInMap(xy))
|
||||
{
|
||||
world.CancelInputMode();
|
||||
foreach (var subject in subjects)
|
||||
yield return new Order(order, subject, false) { TargetLocation = xy };
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Tick(World world) { }
|
||||
public void RenderAfterWorld(World world) { }
|
||||
public void RenderBeforeWorld(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) {}
|
||||
|
||||
public string GetCursor(World world, int2 xy, MouseInput mi) { return world.Map.IsInMap(xy) ? cursor : "generic-blocked"; }
|
||||
}
|
||||
@@ -53,6 +89,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]
|
||||
@@ -63,5 +102,4 @@ namespace OpenRA.Orders
|
||||
world.CancelInputMode();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.Graphics;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
@@ -16,8 +17,8 @@ namespace OpenRA
|
||||
{
|
||||
IEnumerable<Order> Order(World world, int2 xy, MouseInput mi);
|
||||
void Tick(World world);
|
||||
void RenderBeforeWorld(World world);
|
||||
void RenderAfterWorld(World world);
|
||||
void RenderBeforeWorld(WorldRenderer wr, World world);
|
||||
void RenderAfterWorld(WorldRenderer wr, World world);
|
||||
string GetCursor(World world, int2 xy, MouseInput mi);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,80 +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.Linq;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Orders
|
||||
{
|
||||
public class PlaceBuildingOrderGenerator : IOrderGenerator
|
||||
{
|
||||
readonly Actor Producer;
|
||||
readonly string Building;
|
||||
BuildingInfo BuildingInfo { get { return Rules.Info[ Building ].Traits.Get<BuildingInfo>(); } }
|
||||
|
||||
public PlaceBuildingOrderGenerator(Actor producer, string name)
|
||||
{
|
||||
Producer = producer;
|
||||
Building = name;
|
||||
}
|
||||
|
||||
public IEnumerable<Order> Order(World world, int2 xy, MouseInput mi)
|
||||
{
|
||||
if (mi.Button == MouseButton.Right)
|
||||
world.CancelInputMode();
|
||||
|
||||
return InnerOrder(world, xy, mi);
|
||||
}
|
||||
|
||||
IEnumerable<Order> InnerOrder(World world, int2 xy, MouseInput mi)
|
||||
{
|
||||
if (mi.Button == MouseButton.Left)
|
||||
{
|
||||
var topLeft = xy - Footprint.AdjustForBuildingSize( BuildingInfo );
|
||||
if (!world.CanPlaceBuilding( Building, BuildingInfo, topLeft, null)
|
||||
|| !world.IsCloseEnoughToBase(Producer.Owner, Building, BuildingInfo, topLeft))
|
||||
{
|
||||
var eva = world.WorldActor.Info.Traits.Get<EvaAlertsInfo>();
|
||||
Sound.Play(eva.BuildingCannotPlaceAudio);
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (Rules.Info[ Building ].Traits.Contains<LineBuildInfo>())
|
||||
yield return new Order("LineBuild", Producer.Owner.PlayerActor, topLeft, Building);
|
||||
else
|
||||
yield return new Order("PlaceBuilding", Producer.Owner.PlayerActor, topLeft, Building);
|
||||
}
|
||||
}
|
||||
|
||||
public void Tick( World world )
|
||||
{
|
||||
// Find the queue with the target actor
|
||||
var queue = Producer.TraitsImplementing<ProductionQueue>()
|
||||
.Where(p => p.CurrentItem() != null &&
|
||||
p.CurrentItem().Item == Building &&
|
||||
p.CurrentItem().RemainingTime == 0)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (queue == null)
|
||||
world.CancelInputMode();
|
||||
}
|
||||
|
||||
public void RenderAfterWorld( World world ) {}
|
||||
|
||||
public void RenderBeforeWorld(World world)
|
||||
{
|
||||
world.WorldRenderer.uiOverlay.DrawBuildingGrid(world, Building, BuildingInfo);
|
||||
}
|
||||
|
||||
public string GetCursor(World world, int2 xy, MouseInput mi) { return "default"; }
|
||||
}
|
||||
}
|
||||
@@ -6,66 +6,180 @@
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Orders
|
||||
{
|
||||
class UnitOrderGenerator : IOrderGenerator
|
||||
{
|
||||
public IEnumerable<Order> Order( World world, int2 xy, MouseInput mi )
|
||||
{
|
||||
var orders = world.Selection.Actors
|
||||
.Select(a => a.Order(xy, mi))
|
||||
.Where(o => o != null)
|
||||
.ToArray();
|
||||
|
||||
var actorsInvolved = orders.Select(o => o.Subject).Distinct();
|
||||
if (actorsInvolved.Any())
|
||||
yield return new Order("CreateGroup", actorsInvolved.First().Owner.PlayerActor,
|
||||
string.Join(",", actorsInvolved.Select(a => a.ActorID.ToString()).ToArray()));
|
||||
|
||||
foreach (var o in orders)
|
||||
yield return o;
|
||||
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 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 )
|
||||
{
|
||||
var custom = world.WorldActor.TraitOrDefault<ICustomUnitOrderGenerator>();
|
||||
if (custom != null)
|
||||
{
|
||||
custom.RenderBeforeWorld(wr, world);
|
||||
return;
|
||||
}
|
||||
|
||||
public void RenderBeforeWorld(World world)
|
||||
{
|
||||
foreach (var a in world.Selection.Actors)
|
||||
if (!a.Destroyed)
|
||||
foreach (var t in a.TraitsImplementing<IPreRenderSelection>())
|
||||
t.RenderBeforeWorld(a);
|
||||
t.RenderBeforeWorld( wr, a );
|
||||
|
||||
Game.Renderer.Flush();
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
bool useSelect = false;
|
||||
|
||||
Game.Renderer.Flush();
|
||||
var custom = world.WorldActor.TraitOrDefault<ICustomUnitOrderGenerator>();
|
||||
if (custom != null)
|
||||
{
|
||||
return custom.GetCursor(world, xy, mi);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
|
||||
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 (useSelect) ? "select" : "default";
|
||||
|
||||
return orders[0].cursor ?? ((useSelect) ? "select" : "default");
|
||||
}
|
||||
|
||||
public void RenderAfterWorld( World world )
|
||||
static UnitOrderResult OrderForUnit( Actor self, int2 xy, MouseInput mi, Actor underCursor )
|
||||
{
|
||||
foreach (var a in world.Selection.Actors)
|
||||
if (!a.Destroyed)
|
||||
foreach (var t in a.TraitsImplementing<IPostRenderSelection>())
|
||||
t.RenderAfterWorld(a);
|
||||
if (self.Owner != self.World.LocalPlayer)
|
||||
return null;
|
||||
|
||||
Game.Renderer.Flush();
|
||||
}
|
||||
|
||||
public string GetCursor( World world, int2 xy, MouseInput mi )
|
||||
{
|
||||
var c = Order(world, xy, mi)
|
||||
.Select(o => o.Subject.TraitsImplementing<IOrderCursor>()
|
||||
.Select(pc => pc.CursorForOrder(o.Subject, o)).FirstOrDefault(a => a != null))
|
||||
.FirstOrDefault(a => a != null);
|
||||
|
||||
return c ??
|
||||
(world.FindUnitsAtMouse(mi.Location)
|
||||
.Any(a => a.Info.Traits.Contains<SelectableInfo>())
|
||||
? "select" : "default");
|
||||
}
|
||||
|
||||
if (self.Destroyed)
|
||||
return null;
|
||||
|
||||
if( mi.Button == MouseButton.Right )
|
||||
{
|
||||
var uim = self.World.WorldActor.Trait<UnitInfluence>();
|
||||
foreach( var o in self.TraitsImplementing<IIssueOrder>()
|
||||
.SelectMany( trait => trait.Orders
|
||||
.Select( x => new { Trait = trait, Order = x } ) )
|
||||
.OrderByDescending( x => x.Order.OrderPriority ) )
|
||||
{
|
||||
var actorsAt = uim.GetUnitsAt( xy ).ToList();
|
||||
|
||||
string cursor = null;
|
||||
if( underCursor != null )
|
||||
if (o.Order.CanTargetUnit(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), mi.Modifiers.HasModifier(Modifiers.Shift), ref cursor))
|
||||
return new UnitOrderResult( self, o.Order, o.Trait, cursor, Target.FromCell( xy ) );
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static Order CheckSameOrder( IOrderTargeter iot, Order order )
|
||||
{
|
||||
if( order == null && iot.OrderID != null )
|
||||
Game.Debug( "BUG: in order targeter - decided on {0} but then didn't order", iot.OrderID );
|
||||
else if( iot.OrderID != order.OrderString )
|
||||
Game.Debug( "BUG: in order targeter - decided on {0} but ordered {1}", iot.OrderID, order.OrderString );
|
||||
return order;
|
||||
}
|
||||
|
||||
class UnitOrderResult
|
||||
{
|
||||
public readonly Actor self;
|
||||
public readonly IOrderTargeter iot;
|
||||
public readonly IIssueOrder trait;
|
||||
public readonly string cursor;
|
||||
public readonly Target target;
|
||||
|
||||
public UnitOrderResult( Actor self, IOrderTargeter iot, IIssueOrder trait, string cursor, Target target )
|
||||
{
|
||||
this.self = self;
|
||||
this.iot = iot;
|
||||
this.trait = trait;
|
||||
this.cursor = cursor;
|
||||
this.target = target;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,95 +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 ShroudRenderer Shroud;
|
||||
public World World { get; private set; }
|
||||
|
||||
public Player( World world, PlayerReference pr, int index )
|
||||
{
|
||||
World = world;
|
||||
Shroud = new ShroudRenderer(this, world.Map);
|
||||
|
||||
PlayerActor = world.CreateActor("Player", new TypeDictionary{ new OwnerInit( this ) });
|
||||
|
||||
Index = index;
|
||||
Palette = "player"+index;
|
||||
Color = pr.Color;
|
||||
public readonly PlayerReference PlayerRef;
|
||||
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;
|
||||
|
||||
RegisterPlayerColor(world, Palette);
|
||||
}
|
||||
|
||||
public Player( World world, Session.Client client, PlayerReference pr, int index )
|
||||
{
|
||||
World = world;
|
||||
Shroud = new ShroudRenderer(this, world.Map);
|
||||
|
||||
PlayerActor = world.CreateActor("Player", new TypeDictionary{ new OwnerInit( this ) });
|
||||
|
||||
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;
|
||||
|
||||
RegisterPlayerColor(world, Palette);
|
||||
}
|
||||
|
||||
public void RegisterPlayerColor(World world, string palette)
|
||||
{
|
||||
var info = Rules.Info["world"].Traits.Get<PlayerColorPaletteInfo>();
|
||||
var newpal = new Palette(world.WorldRenderer.GetPalette(info.BasePalette),
|
||||
new PlayerColorRemap(Color, Color2, info.SplitRamp));
|
||||
world.WorldRenderer.AddPalette(palette, newpal);
|
||||
}
|
||||
|
||||
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>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace OpenRA
|
||||
else
|
||||
actors = (isCombine ? oldSelection.Union(newSelection) : newSelection).ToList();
|
||||
|
||||
var voicedUnit = actors.FirstOrDefault(a => a.Owner == world.LocalPlayer && a.HasVoice());
|
||||
var voicedUnit = actors.FirstOrDefault(a => a.Owner == world.LocalPlayer && a.IsInWorld && a.HasVoice());
|
||||
if (voicedUnit != null)
|
||||
Sound.PlayVoice("Select", voicedUnit, voicedUnit.Owner.Country.Race);
|
||||
|
||||
@@ -56,6 +56,10 @@ namespace OpenRA
|
||||
public void Tick(World world)
|
||||
{
|
||||
actors.RemoveAll(a => !a.IsInWorld);
|
||||
|
||||
foreach (var cg in controlGroups.Values)
|
||||
cg.RemoveAll(a => a.Destroyed); // note: NOT `!a.IsInWorld`, since that would remove things
|
||||
// that are in transports.
|
||||
}
|
||||
|
||||
Cache<int, List<Actor>> controlGroups = new Cache<int, List<Actor>>(_ => new List<Actor>());
|
||||
|
||||
@@ -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, null);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace OpenRA.Server
|
||||
catch (SocketException e)
|
||||
{
|
||||
if (e.SocketErrorCode == SocketError.WouldBlock) break;
|
||||
Server.DropClient(this, e);
|
||||
server.DropClient(this, e);
|
||||
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 };
|
||||
}
|
||||
|
||||
1
OpenRA.Game/Server/Exts.cs
Normal file → Executable file
1
OpenRA.Game/Server/Exts.cs
Normal file → Executable file
@@ -11,6 +11,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System;
|
||||
|
||||
namespace OpenRA.Server
|
||||
{
|
||||
|
||||
69
OpenRA.Game/Server/MasterServerQuery.cs
Normal file → Executable file
69
OpenRA.Game/Server/MasterServerQuery.cs
Normal file → Executable file
@@ -14,15 +14,20 @@ using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using OpenRA.FileFormats;
|
||||
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 = _ => { };
|
||||
|
||||
static GameServer[] Games = { };
|
||||
public static string ClientVersion = "";
|
||||
public static string ServerVersion = "";
|
||||
static AutoResetEvent ev = new AutoResetEvent(false);
|
||||
static AutoResetEvent ev2 = new AutoResetEvent(false);
|
||||
|
||||
public static void Refresh(string masterServerUrl)
|
||||
{
|
||||
@@ -30,9 +35,7 @@ namespace OpenRA.Server
|
||||
{
|
||||
try
|
||||
{
|
||||
var wc = new WebClient();
|
||||
var data = wc.DownloadData(new Uri(masterServerUrl + "list.php"));
|
||||
var str = Encoding.UTF8.GetString(data);
|
||||
var str = GetData(new Uri(masterServerUrl + "list.php"));
|
||||
|
||||
var yaml = MiniYaml.FromString(str);
|
||||
|
||||
@@ -48,14 +51,70 @@ 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
|
||||
{
|
||||
string motdText = GetData(new Uri(masterServerUrl + "motd.php?v=" + ClientVersion));
|
||||
string[] p = motdText.Split('|');
|
||||
if (p.Length == 2 && p[1].Length == int.Parse(p[0]))
|
||||
{
|
||||
motd.SetText(p[1]);
|
||||
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)))
|
||||
OnComplete(Games);
|
||||
if (ev2.WaitOne(TimeSpan.FromMilliseconds(0)))
|
||||
OnVersion(ServerVersion);
|
||||
}
|
||||
|
||||
static string GetData(Uri uri)
|
||||
{
|
||||
var wc = new WebClient();
|
||||
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;
|
||||
|
||||
@@ -23,51 +23,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
|
||||
public List<Connection> conns = 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)
|
||||
public void Shutdown()
|
||||
{
|
||||
conns.Clear();
|
||||
GameStarted = false;
|
||||
foreach (var t in ServerTraits.WithInterface<INotifyServerShutdown>())
|
||||
t.ServerShutdown(this);
|
||||
|
||||
try { listener.Stop(); }
|
||||
catch { }
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
lobbyInfo = new Session();
|
||||
lobbyInfo.GlobalSettings.Mods = settings.Game.Mods;
|
||||
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,24 +88,21 @@ 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 );
|
||||
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 (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)
|
||||
{
|
||||
@@ -112,64 +112,38 @@ namespace OpenRA.Server
|
||||
}
|
||||
}
|
||||
} ) { 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++)
|
||||
for (var i = 0; i < 256; i++)
|
||||
if (conns.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).Index;
|
||||
}
|
||||
Socket newSocket = null;
|
||||
|
||||
static void AcceptConnection()
|
||||
{
|
||||
var newConn = new Connection { socket = listener.AcceptSocket() };
|
||||
try
|
||||
{
|
||||
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
|
||||
{
|
||||
if (GameStarted)
|
||||
@@ -189,32 +163,13 @@ namespace OpenRA.Server
|
||||
newConn.socket.Send(BitConverter.GetBytes(newConn.PlayerIndex));
|
||||
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(),
|
||||
});
|
||||
|
||||
Log.Write("server", "Client {0}: Accepted connection from {1}",
|
||||
newConn.PlayerIndex, newConn.socket.RemoteEndPoint);
|
||||
|
||||
SendChat(newConn, "has joined the game.");
|
||||
|
||||
SyncLobbyInfo();
|
||||
foreach (var t in ServerTraits.WithInterface<IClientJoined>())
|
||||
t.ClientJoined(this, newConn);
|
||||
}
|
||||
catch (Exception e) { DropClient(newConn, e); }
|
||||
}
|
||||
|
||||
public static void UpdateInFlightFrames(Connection conn)
|
||||
public void UpdateInFlightFrames(Connection conn)
|
||||
{
|
||||
if (conn.Frame != 0)
|
||||
{
|
||||
@@ -230,7 +185,7 @@ namespace OpenRA.Server
|
||||
}
|
||||
}
|
||||
|
||||
static void DispatchOrdersToClient(Connection c, int client, int frame, byte[] data)
|
||||
void DispatchOrdersToClient(Connection c, int client, int frame, byte[] data)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -244,7 +199,7 @@ namespace OpenRA.Server
|
||||
catch( Exception e ) { DropClient( c, e ); }
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -256,7 +211,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);
|
||||
@@ -273,294 +228,43 @@ 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))
|
||||
{
|
||||
Log.Write("server", "Bad server command: {0}", so.Data);
|
||||
SendChatTo(conn, "Bad server command.");
|
||||
};
|
||||
}
|
||||
break;
|
||||
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", "Unknown server command: {0}", so.Data);
|
||||
SendChatTo(conn, "Unknown server command: {0}".F(so.Data));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "Chat":
|
||||
case "Chat":
|
||||
case "TeamChat":
|
||||
foreach (var c in conns.Except(conn).ToArray())
|
||||
DispatchOrdersToClient(c, GetClient(conn).Index, 0, so.Serialize());
|
||||
@@ -568,16 +272,19 @@ namespace OpenRA.Server
|
||||
}
|
||||
}
|
||||
|
||||
static Session.Client GetClient(Connection conn)
|
||||
public Session.Client GetClient(Connection conn)
|
||||
{
|
||||
return lobbyInfo.Clients.First(c => c.Index == conn.PlayerIndex);
|
||||
}
|
||||
|
||||
public static void DropClient(Connection toDrop, Exception e)
|
||||
public void DropClient(Connection toDrop, Exception e)
|
||||
{
|
||||
conns.Remove(toDrop);
|
||||
SendChat(toDrop, "Connection Dropped");
|
||||
|
||||
if (GameStarted)
|
||||
SendDisconnected(toDrop); /* Report disconnection */
|
||||
|
||||
lobbyInfo.Clients.RemoveAll(c => c.Index == toDrop.PlayerIndex);
|
||||
|
||||
DispatchOrders( toDrop, toDrop.MostRecentFrame, new byte[] { 0xbf } );
|
||||
@@ -586,59 +293,28 @@ namespace OpenRA.Server
|
||||
SyncLobbyInfo();
|
||||
}
|
||||
|
||||
static void SyncLobbyInfo()
|
||||
public void SyncLobbyInfo()
|
||||
{
|
||||
DispatchOrders(null, 0,
|
||||
new ServerOrder("SyncInfo", lobbyInfo.Serialize()).Serialize());
|
||||
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;
|
||||
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()");
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user