Compare commits
314 Commits
playtest-2
...
playtest-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
b84d2f2c29 | ||
|
|
2945838eef | ||
|
|
43aa8812b3 | ||
|
|
9a2467dc21 | ||
|
|
5a75625eef | ||
|
|
2f2890596d | ||
|
|
01fa0b357e | ||
|
|
c5f0dad84e | ||
|
|
7f7f4e81a5 | ||
|
|
8b423908ca | ||
|
|
598fe9f956 | ||
|
|
06e8b530ea | ||
|
|
d5c25f73bd | ||
|
|
73fa306719 | ||
|
|
8d0cea6fe4 | ||
|
|
273b57ee69 | ||
|
|
c1578b92e1 | ||
|
|
c69a741c90 | ||
|
|
f8d537c0c5 | ||
|
|
e371159826 | ||
|
|
7d912715bc | ||
|
|
d6449c2902 | ||
|
|
8a3868abd0 | ||
|
|
112047d2b3 | ||
|
|
cd9f584729 | ||
|
|
31741a1ab3 | ||
|
|
eb6440694a | ||
|
|
2c388308c7 | ||
|
|
1c8f744719 | ||
|
|
dfdc893d63 | ||
|
|
669aaab6b4 | ||
|
|
73017d3f5c | ||
|
|
38fa809656 | ||
|
|
0ed21a4acb | ||
|
|
9ae7d98193 | ||
|
|
07e60286f2 | ||
|
|
7f918b8a69 | ||
|
|
4e0ace6ec5 | ||
|
|
dca5f8d27b | ||
|
|
6478276064 | ||
|
|
7d603f97fe | ||
|
|
112fdb32af | ||
|
|
5a78df0318 | ||
|
|
6ac4266847 | ||
|
|
91487b85d0 | ||
|
|
178af8b849 | ||
|
|
853ef12c8f | ||
|
|
62cc2fa4cb | ||
|
|
1f4991833d | ||
|
|
9fedeefdbc | ||
|
|
29a77f7c5c | ||
|
|
a751b8c49d | ||
|
|
baacfc96bb | ||
|
|
8dcd1e8fd1 | ||
|
|
2a7857570a | ||
|
|
2f92b873e8 | ||
|
|
bce52e989f | ||
|
|
a917086969 | ||
|
|
34913ab077 | ||
|
|
9de22add08 | ||
|
|
b2f535d2e0 | ||
|
|
3e7b9a7b5a | ||
|
|
29d03fb133 | ||
|
|
d4aeb157cc | ||
|
|
853e60f76e | ||
|
|
7f3e491ecc | ||
|
|
88dfbe657c | ||
|
|
226fd167e7 | ||
|
|
cedfeab63c | ||
|
|
a882735deb | ||
|
|
61ebe0d0a0 | ||
|
|
ee546750b2 | ||
|
|
267d89a459 | ||
|
|
3b59afcd4f | ||
|
|
8b98448090 | ||
|
|
1d7ca206f4 | ||
|
|
7b7b9d3319 | ||
|
|
e5d45a6961 | ||
|
|
c7f8921cea | ||
|
|
58ebd68478 | ||
|
|
6234f53e2c | ||
|
|
ce0e671ac5 | ||
|
|
739c38d3d8 | ||
|
|
5561ac458b | ||
|
|
c0d0636e08 | ||
|
|
f4da83e920 | ||
|
|
50066ec238 | ||
|
|
dbd076543b | ||
|
|
2d4ed56f76 | ||
|
|
03cdcdd4aa | ||
|
|
0cc440aa1f | ||
|
|
f635835b1c | ||
|
|
3f08f16b9b | ||
|
|
c433ca77c4 | ||
|
|
70a92b80cf | ||
|
|
b3fbefe968 | ||
|
|
f38e4d27be | ||
|
|
c35e3b4bd7 | ||
|
|
b0608936d8 | ||
|
|
56b9d1add7 | ||
|
|
5ce531f2d5 | ||
|
|
c7e76cc26d | ||
|
|
142be86934 | ||
|
|
73dadbc873 | ||
|
|
50d5936846 | ||
|
|
4e86f5b252 | ||
|
|
b6b4df703a | ||
|
|
b17e6900ec | ||
|
|
08a60ca336 | ||
|
|
d8c053253d | ||
|
|
e4271b35dc | ||
|
|
95c34c30ba | ||
|
|
8e42dd95fc | ||
|
|
f2dd0de1ea | ||
|
|
701ef05562 | ||
|
|
c3228cecd0 | ||
|
|
47eacc5b80 | ||
|
|
909152c662 | ||
|
|
0f9221dc5a | ||
|
|
cfc937e8eb | ||
|
|
def0580078 | ||
|
|
3c7015567e | ||
|
|
a00f0b18a0 | ||
|
|
703f3b8c13 | ||
|
|
bf9bcfed68 | ||
|
|
f6dac1fe83 | ||
|
|
731c64c1a7 | ||
|
|
19494c3262 | ||
|
|
cf9a2ee0ef | ||
|
|
93e8bd6644 | ||
|
|
4d0f75353b | ||
|
|
fb035756a0 | ||
|
|
e0a00940af | ||
|
|
e74c3eeb2e | ||
|
|
05bf6d83f2 | ||
|
|
e4b65256de | ||
|
|
d744cfe21b | ||
|
|
6ae7da77ae | ||
|
|
0352884205 | ||
|
|
7c6edab04e | ||
|
|
997501bb12 | ||
|
|
d4f43a399e | ||
|
|
f6ec2163de | ||
|
|
3428b3c4c2 | ||
|
|
8ec16fdbbe | ||
|
|
39f699916c | ||
|
|
4a337185f5 | ||
|
|
298f5ec24f | ||
|
|
16402f26fe | ||
|
|
825743b225 | ||
|
|
c3b3947b9d | ||
|
|
1143f496db | ||
|
|
46d0ce89e9 | ||
|
|
79ced35010 | ||
|
|
18a06eb3bf | ||
|
|
d8c5f1aed3 | ||
|
|
3880326787 | ||
|
|
09d9396123 | ||
|
|
aeccf53e97 | ||
|
|
b1b8b2c14a | ||
|
|
fd9a31168d | ||
|
|
3f11c32c4a | ||
|
|
d65626e87c | ||
|
|
ccb77dee12 | ||
|
|
952da4efec | ||
|
|
49f2bfb460 | ||
|
|
9db92ed8ba | ||
|
|
4e2d76f6ed | ||
|
|
94dd73cb48 | ||
|
|
aeb9f4e441 | ||
|
|
1a4e3053ce | ||
|
|
18914447aa | ||
|
|
7e67a5bb2d | ||
|
|
9eaa0e5765 | ||
|
|
0e16aa339d | ||
|
|
c7b650d6ec | ||
|
|
bcc3cd32ae | ||
|
|
38ffd30b28 | ||
|
|
15bd58ddce | ||
|
|
7247bd1078 | ||
|
|
ac31767aef | ||
|
|
3f68330c70 | ||
|
|
0e71af25f4 | ||
|
|
e610c4d7fc | ||
|
|
0a041fe425 | ||
|
|
1595c6a0ed | ||
|
|
db7a11e8a6 | ||
|
|
53df4c4bfb | ||
|
|
1afcd2b98a | ||
|
|
08ffe1d44d | ||
|
|
66359b7058 | ||
|
|
30ab5c33ea | ||
|
|
0c5fb4c6b0 | ||
|
|
21b0b12948 | ||
|
|
cb3f6435ad | ||
|
|
235ae1fad7 | ||
|
|
1c6470eff3 | ||
|
|
0cf51d8142 | ||
|
|
38e5f70cbe | ||
|
|
56b0da0b13 | ||
|
|
d6f0a03270 | ||
|
|
b1b75ecf2f | ||
|
|
228852a55d | ||
|
|
f6a880e524 | ||
|
|
7e82dc729b | ||
|
|
d91a7c6b35 | ||
|
|
b4068f0ac7 | ||
|
|
b336a1e478 | ||
|
|
55afcddce0 | ||
|
|
ac7bb3c71b | ||
|
|
3aa7ff5a6b | ||
|
|
0b3414c46f | ||
|
|
fdcad8710b | ||
|
|
02a7b092dc | ||
|
|
d43781c604 | ||
|
|
cfbededcc4 | ||
|
|
9ba50a9c5e | ||
|
|
6377269a14 | ||
|
|
a8be81ea2b | ||
|
|
50950cba22 | ||
|
|
210e847904 | ||
|
|
54f7b6430d | ||
|
|
26e823fc25 | ||
|
|
10ea035b42 | ||
|
|
58fb7178ad | ||
|
|
cd358eb985 | ||
|
|
39d4b80323 | ||
|
|
45a4935b01 | ||
|
|
824e2a7b0b | ||
|
|
230d59f655 | ||
|
|
7f191887ec | ||
|
|
499613f105 | ||
|
|
20af2b5c0a | ||
|
|
cbc9d66bee | ||
|
|
c338612b9f | ||
|
|
edd0a7c614 | ||
|
|
1d259e4bc7 | ||
|
|
6c651e9d02 | ||
|
|
a29e0bf4b1 | ||
|
|
9d403d3072 | ||
|
|
184d044f26 | ||
|
|
0473aed2c3 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -44,6 +44,9 @@ settings.ini
|
||||
|
||||
packaging/windows/*.exe
|
||||
|
||||
# osx crap
|
||||
.DS_Store
|
||||
|
||||
# osx build crap
|
||||
packaging/osx/launcher/build/
|
||||
packaging/osx/launcher/OpenRA.xcodeproj/*.pbxuser
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using OpenRA.FileFormats;
|
||||
using System.IO;
|
||||
|
||||
namespace FileExtractor
|
||||
{
|
||||
@@ -17,10 +17,8 @@ namespace FileExtractor
|
||||
}
|
||||
|
||||
var mods = args[0].Split(',');
|
||||
var manifest = new Manifest(mods);
|
||||
|
||||
foreach (var folder in manifest.Folders) FileSystem.Mount(folder);
|
||||
foreach (var pkg in manifest.Packages) FileSystem.Mount(pkg);
|
||||
var manifest = new Manifest(mods);
|
||||
FileSystem.LoadFromManifest( manifest );
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
2
HACKING
2
HACKING
@@ -37,7 +37,7 @@ Three renderers (SpriteRenderer, LineRenderer, Rgba?Renderer) render most stuff.
|
||||
|
||||
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://openra.res0l.net/projects/openra/issues . 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://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).
|
||||
|
||||
We also have a website at http://www.open-ra.org/ .
|
||||
|
||||
|
||||
112
INSTALL
112
INSTALL
@@ -1,44 +1,47 @@
|
||||
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
|
||||
|
||||
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,6 +49,9 @@ 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
|
||||
@@ -54,4 +60,40 @@ have very strange errors.
|
||||
* 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,52 +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-*
|
||||
21
Makefile
21
Makefile
@@ -103,18 +103,27 @@ install: all
|
||||
@-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
|
||||
@-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 *.ttf $(INSTALL_DIR)
|
||||
@cp --parents -r thirdparty/Tao $(INSTALL_DIR)
|
||||
@$(INSTALL_PROGRAM) thirdparty/WindowsBase.dll $(INSTALL_DIR)
|
||||
@-$(INSTALL_PROGRAM) VERSION $(INSTALL_DIR)
|
||||
|
||||
@echo "#!/bin/sh" > openra
|
||||
@echo "cd "$(datadir)"/openra" >> openra
|
||||
@@ -123,8 +132,8 @@ install: all
|
||||
@$(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.zip and"
|
||||
@echo "http://open-ra.org/packages/cnc-packages.zip"
|
||||
@echo "http://open-ra.org/get-dependency.php?ra-packages and"
|
||||
@echo "http://open-ra.org/get-dependency.php?cnc-packages"
|
||||
@echo "and extract their contents to"
|
||||
@echo "$(INSTALL_DIR)/mods/ra/packages and "
|
||||
@echo "$(INSTALL_DIR)/mods/cnc/packages respectively."
|
||||
@@ -155,9 +164,12 @@ OpenRA.TilesetBuilder.Form1.resources:
|
||||
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)
|
||||
$$($(1)_TARGET): $$($(1)_SRCS) Makefile $$($(1)_DEPS) fixheader
|
||||
@echo CSC $$(@)
|
||||
@$(CSC) $$($(1)_LIBS:%=-r:%) \
|
||||
-out:$$(@) $(CSFLAGS) $$($(1)_FLAGS) \
|
||||
@@ -165,6 +177,7 @@ $$($(1)_TARGET): $$($(1)_SRCS) Makefile $$($(1)_DEPS)
|
||||
-t:"$$($(1)_KIND)" \
|
||||
$$($(1)_EXTRA) \
|
||||
$$($(1)_SRCS)
|
||||
@mono fixheader.exe $$(@)
|
||||
endef
|
||||
|
||||
$(foreach prog,$(PROGRAMS),$(eval $(call BUILD_ASSEMBLY,$(prog))))
|
||||
|
||||
@@ -32,14 +32,9 @@ namespace OpenRA.Editor
|
||||
|
||||
Text = "OpenRA Editor (mod:{0})".F(currentMod);
|
||||
|
||||
var manifest = new Manifest(new[] { currentMod });
|
||||
Game.LoadModAssemblies(manifest);
|
||||
Game.modData = new ModData( currentMod );
|
||||
|
||||
FileSystem.UnmountAll();
|
||||
foreach (var folder in manifest.Folders) FileSystem.Mount(folder);
|
||||
foreach (var pkg in manifest.Packages) FileSystem.Mount(pkg);
|
||||
|
||||
Rules.LoadRules(manifest, new Map());
|
||||
Rules.LoadRules(Game.modData.Manifest, new Map());
|
||||
|
||||
folderBrowser.SelectedPath = new string[] { Environment.CurrentDirectory, "mods", currentMod, "maps" }
|
||||
.Aggregate(Path.Combine);
|
||||
@@ -62,12 +57,7 @@ namespace OpenRA.Editor
|
||||
|
||||
loadedMapName = mapname;
|
||||
|
||||
var manifest = new Manifest(new[] { currentMod });
|
||||
Game.LoadModAssemblies(manifest);
|
||||
|
||||
FileSystem.UnmountAll();
|
||||
foreach (var folder in manifest.Folders) FileSystem.Mount(folder);
|
||||
foreach (var pkg in manifest.Packages) FileSystem.Mount(pkg);
|
||||
Game.modData = new ModData( currentMod );
|
||||
|
||||
// load the map
|
||||
var map = new Map(new Folder(mapname));
|
||||
@@ -78,7 +68,7 @@ namespace OpenRA.Editor
|
||||
map.Players.Add("Neutral", new PlayerReference("Neutral",
|
||||
Rules.Info["world"].Traits.WithInterface<CountryInfo>().First().Race, true, true));
|
||||
|
||||
PrepareMapResources(manifest, map);
|
||||
PrepareMapResources(Game.modData.Manifest, map);
|
||||
|
||||
dirty = false;
|
||||
}
|
||||
@@ -91,14 +81,9 @@ namespace OpenRA.Editor
|
||||
|
||||
loadedMapName = null;
|
||||
|
||||
var manifest = new Manifest(new[] { currentMod });
|
||||
Game.LoadModAssemblies(manifest);
|
||||
Game.modData = new ModData( currentMod );
|
||||
|
||||
FileSystem.UnmountAll();
|
||||
foreach (var folder in manifest.Folders) FileSystem.Mount(folder);
|
||||
foreach (var pkg in manifest.Packages) FileSystem.Mount(pkg);
|
||||
|
||||
PrepareMapResources(manifest, map);
|
||||
PrepareMapResources(Game.modData.Manifest, map);
|
||||
|
||||
MakeDirty();
|
||||
}
|
||||
@@ -167,9 +152,8 @@ namespace OpenRA.Editor
|
||||
actorPalette.Controls.Add(ibox);
|
||||
|
||||
tt.SetToolTip(ibox,
|
||||
"{0}:{1}".F(
|
||||
info.Name,
|
||||
info.Category));
|
||||
"{0}".F(
|
||||
info.Name));
|
||||
|
||||
actorTemplates.Add(template);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using OpenRA;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
using System.Drawing;
|
||||
|
||||
namespace OpenRA.Editor
|
||||
{
|
||||
@@ -79,11 +81,26 @@ namespace OpenRA.Editor
|
||||
// {"wcrate","crate"},
|
||||
// {"scrate","crate"},
|
||||
};
|
||||
|
||||
|
||||
static Dictionary<string,Pair<Color,Color>> namedColorMapping = new Dictionary<string, Pair<Color, Color>>()
|
||||
{
|
||||
{"gold",Pair.New(Color.FromArgb(246,214,121),Color.FromArgb(40,32,8))},
|
||||
{"blue",Pair.New(Color.FromArgb(226,230,246),Color.FromArgb(8,20,52))},
|
||||
{"red",Pair.New(Color.FromArgb(255,20,0),Color.FromArgb(56,0,0))},
|
||||
{"neutral",Pair.New(Color.FromArgb(238,238,238),Color.FromArgb(44,28,24))},
|
||||
{"orange",Pair.New(Color.FromArgb(255,230,149),Color.FromArgb(56,0,0))},
|
||||
{"teal",Pair.New(Color.FromArgb(93,194,165),Color.FromArgb(0,32,32))},
|
||||
{"salmon",Pair.New(Color.FromArgb(210,153,125),Color.FromArgb(56,0,0))},
|
||||
{"green",Pair.New(Color.FromArgb(160,240,140),Color.FromArgb(20,20,20))},
|
||||
{"white",Pair.New(Color.FromArgb(255,255,255),Color.FromArgb(75,75,75))},
|
||||
{"black",Pair.New(Color.FromArgb(80,80,80),Color.FromArgb(5,5,5))},
|
||||
};
|
||||
|
||||
int MapSize;
|
||||
int ActorCount = 0;
|
||||
Map Map = new Map();
|
||||
|
||||
List<string> Players = new List<string>();
|
||||
|
||||
LegacyMapImporter(string filename)
|
||||
{
|
||||
ConvertIniMap(filename);
|
||||
@@ -96,7 +113,7 @@ namespace OpenRA.Editor
|
||||
}
|
||||
|
||||
enum IniMapFormat { RedAlert = 3, /* otherwise, cnc (2 variants exist, we don't care to differentiate) */ };
|
||||
|
||||
|
||||
public void ConvertIniMap(string iniFile)
|
||||
{
|
||||
var file = new IniFile(FileSystem.Open(iniFile));
|
||||
@@ -123,17 +140,12 @@ namespace OpenRA.Editor
|
||||
UnpackRATileData(ReadPackedSection(file.GetSection("MapPack")));
|
||||
UnpackRAOverlayData(ReadPackedSection(file.GetSection("OverlayPack")));
|
||||
ReadRATrees(file);
|
||||
// TODO: Fixme
|
||||
//tileset = new TileSet("tileSet.til","templates.ini",fileMapping[Pair.New("ra",Map.Tileset)].First);
|
||||
}
|
||||
else // CNC
|
||||
{
|
||||
UnpackCncTileData(FileSystem.Open(iniFile.Substring(0, iniFile.Length - 4) + ".bin"));
|
||||
ReadCncOverlay(file);
|
||||
ReadCncTrees(file);
|
||||
|
||||
// TODO: Fixme
|
||||
//tileset = new TileSet("tileSet.til","templates.ini",fileMapping[Pair.New("cnc",Map.Tileset)].First);
|
||||
}
|
||||
|
||||
LoadActors(file, "STRUCTURES");
|
||||
@@ -141,11 +153,13 @@ namespace OpenRA.Editor
|
||||
LoadActors(file, "INFANTRY");
|
||||
LoadSmudges(file, "SMUDGE");
|
||||
|
||||
foreach (var p in Players)
|
||||
LoadPlayer(file, p, (legacyMapFormat == IniMapFormat.RedAlert));
|
||||
|
||||
var wp = file.GetSection("Waypoints")
|
||||
.Where(kv => int.Parse(kv.Value) > 0)
|
||||
.Select(kv => Pair.New(int.Parse(kv.Key),
|
||||
LocationFromMapOffset(int.Parse(kv.Value), MapSize)))
|
||||
.Where(a => a.First < 8)
|
||||
.ToArray();
|
||||
|
||||
Map.PlayerCount = wp.Count();
|
||||
@@ -251,7 +265,7 @@ namespace OpenRA.Editor
|
||||
Map.MapResources[i, j] = new TileReference<byte, byte>(res.First, res.Second);
|
||||
|
||||
if (o != 255 && overlayActorMapping.ContainsKey(raOverlayNames[o]))
|
||||
Map.Actors.Add("Actor" + ActorCount,
|
||||
Map.Actors.Add("Actor" + ActorCount++,
|
||||
new ActorReference(overlayActorMapping[raOverlayNames[o]])
|
||||
{
|
||||
new LocationInit( new int2(i, j) ),
|
||||
@@ -269,7 +283,7 @@ namespace OpenRA.Editor
|
||||
foreach (KeyValuePair<string, string> kv in terrain)
|
||||
{
|
||||
var loc = int.Parse(kv.Key);
|
||||
Map.Actors.Add("Actor" + ActorCount,
|
||||
Map.Actors.Add("Actor" + ActorCount++,
|
||||
new ActorReference(kv.Value.ToLowerInvariant())
|
||||
{
|
||||
new LocationInit(new int2(loc % MapSize, loc / MapSize)),
|
||||
@@ -315,7 +329,7 @@ namespace OpenRA.Editor
|
||||
Map.MapResources[cell.X, cell.Y] = new TileReference<byte, byte>(res.First, res.Second);
|
||||
|
||||
if (overlayActorMapping.ContainsKey(kv.Value.ToLower()))
|
||||
Map.Actors.Add("Actor" + ActorCount,
|
||||
Map.Actors.Add("Actor" + ActorCount++,
|
||||
new ActorReference(overlayActorMapping[kv.Value.ToLower()])
|
||||
{
|
||||
new LocationInit(cell),
|
||||
@@ -333,7 +347,7 @@ namespace OpenRA.Editor
|
||||
foreach (KeyValuePair<string, string> kv in terrain)
|
||||
{
|
||||
var loc = int.Parse(kv.Key);
|
||||
Map.Actors.Add("Actor" + ActorCount,
|
||||
Map.Actors.Add("Actor" + ActorCount++,
|
||||
new ActorReference(kv.Value.Split(',')[0].ToLowerInvariant())
|
||||
{
|
||||
new LocationInit(new int2(loc % MapSize, loc / MapSize)),
|
||||
@@ -346,17 +360,57 @@ namespace OpenRA.Editor
|
||||
{
|
||||
foreach (var s in file.GetSection(section, true))
|
||||
{
|
||||
//num=owner,type,health,location,facing,...
|
||||
//Structures: num=owner,type,health,location,turret-facing,trigger
|
||||
//Units: num=owner,type,health,location,facing,action,trigger
|
||||
//Infantry: num=owner,type,health,location,subcell,action,facing,trigger
|
||||
var parts = s.Value.Split(',');
|
||||
var loc = int.Parse(parts[3]);
|
||||
if (parts[0] == "")
|
||||
parts[0] = "Neutral";
|
||||
Map.Actors.Add("Actor" + ActorCount,
|
||||
new ActorReference(parts[1].ToLowerInvariant())
|
||||
{
|
||||
new LocationInit(new int2(loc % MapSize, loc / MapSize)),
|
||||
new OwnerInit(parts[0])
|
||||
});
|
||||
|
||||
|
||||
if (!Players.Contains(parts[0]))
|
||||
Players.Add(parts[0]);
|
||||
|
||||
var stance = ActorStance.Stance.None;
|
||||
switch(parts[5])
|
||||
{
|
||||
case "Area Guard":
|
||||
case "Guard":
|
||||
stance = ActorStance.Stance.Guard;
|
||||
break;
|
||||
case "Defend Base":
|
||||
stance = ActorStance.Stance.Defend;
|
||||
break;
|
||||
case "Hunt":
|
||||
case "Rampage":
|
||||
case "Attack Base":
|
||||
case "Attack Units":
|
||||
case "Attack Civil.":
|
||||
case "Attack Tarcom":
|
||||
stance = ActorStance.Stance.Hunt;
|
||||
break;
|
||||
case "Retreat":
|
||||
case "Return":
|
||||
stance = ActorStance.Stance.Retreat;
|
||||
break;
|
||||
// do we care about `Harvest' and `Sticky'?
|
||||
}
|
||||
|
||||
var actor = new ActorReference(parts[1].ToLowerInvariant())
|
||||
{
|
||||
new LocationInit(new int2(loc % MapSize, loc / MapSize)),
|
||||
new OwnerInit(parts[0]),
|
||||
new HealthInit(float.Parse(parts[2])/256),
|
||||
new FacingInit((section == "INFANTRY") ? int.Parse(parts[6]) : int.Parse(parts[4])),
|
||||
new ActorStanceInit(stance),
|
||||
};
|
||||
|
||||
if (section == "INFANTRY")
|
||||
actor.Add(new SubcellInit(int.Parse(parts[4])));
|
||||
|
||||
Map.Actors.Add("Actor" + ActorCount++,actor);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,6 +424,42 @@ namespace OpenRA.Editor
|
||||
Map.Smudges.Add(new SmudgeReference(parts[0].ToLowerInvariant(), new int2(loc % MapSize, loc / MapSize), int.Parse(parts[2])));
|
||||
}
|
||||
}
|
||||
|
||||
void LoadPlayer(IniFile file, string section, bool isRA)
|
||||
{
|
||||
var c = (section == "BadGuy") ? "red" :
|
||||
(isRA) ? "blue" : "gold";
|
||||
|
||||
var color = namedColorMapping[c];
|
||||
|
||||
var pr = new PlayerReference
|
||||
{
|
||||
Name = section,
|
||||
OwnsWorld = (section == "Neutral"),
|
||||
NonCombatant = (section == "Neutral"),
|
||||
Race = (isRA) ? ((section == "BadGuy") ? "soviet" : "allies") : ((section == "BadGuy") ? "nod" : "gdi"),
|
||||
Color = color.First,
|
||||
Color2 = color.Second,
|
||||
};
|
||||
|
||||
var Neutral = new List<string>(){"Neutral"};
|
||||
foreach (var s in file.GetSection(section, true))
|
||||
{
|
||||
Console.WriteLine(s.Key);
|
||||
switch(s.Key)
|
||||
{
|
||||
case "Credits":
|
||||
pr.InitialCash = int.Parse(s.Value);
|
||||
break;
|
||||
case "Allies":
|
||||
pr.Allies = s.Value.Split(',').Intersect(Players).Except(Neutral).ToArray();
|
||||
pr.Enemies = s.Value.Split(',').SymmetricDifference(Players).Except(Neutral).ToArray();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Map.Players.Add(section, pr);
|
||||
}
|
||||
|
||||
static string Truncate(string s, int maxLength)
|
||||
{
|
||||
|
||||
22
OpenRA.Editor/NewMapDialog.Designer.cs
generated
Normal file → Executable file
22
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
|
||||
//
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
144
OpenRA.Editor/Surface.cs
Normal file → Executable file
144
OpenRA.Editor/Surface.cs
Normal file → Executable file
@@ -25,6 +25,8 @@ namespace OpenRA.Editor
|
||||
public Palette Palette { get; private set; }
|
||||
int2 Offset;
|
||||
|
||||
float Zoom = 1.0f;
|
||||
|
||||
BrushTemplate Brush;
|
||||
ActorTemplate Actor;
|
||||
ResourceTemplate Resource;
|
||||
@@ -80,7 +82,34 @@ namespace OpenRA.Editor
|
||||
Offset -= dx;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
|
||||
protected override void OnMouseWheel(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseWheel(e);
|
||||
|
||||
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);
|
||||
@@ -96,7 +125,7 @@ namespace OpenRA.Editor
|
||||
Erase();
|
||||
|
||||
if (e.Button == MouseButtons.Left)
|
||||
Draw();
|
||||
Draw();
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
@@ -208,18 +237,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 +265,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();
|
||||
@@ -358,28 +396,56 @@ 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 / 24, vY / 24);
|
||||
}
|
||||
|
||||
void DrawActor(System.Drawing.Graphics g, int2 p, ActorTemplate t)
|
||||
{
|
||||
g.DrawImage(t.Bitmap,
|
||||
((24 * p + Offset
|
||||
- (t.Centered
|
||||
? new int2(t.Bitmap.Width / 2 - 12, t.Bitmap.Height / 2 - 12)
|
||||
: int2.Zero)).ToPoint()));
|
||||
float OffsetX = t.Centered ? t.Bitmap.Width / 2 - 12 : 0;
|
||||
float DrawX = 24 * p.X * Zoom + Offset.X - OffsetX;
|
||||
|
||||
float OffsetY = t.Centered ? t.Bitmap.Height / 2 - 12 : 0;
|
||||
float DrawY = 24 * 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);
|
||||
g.DrawImage(t.Bitmap, destRect, sourceRect, GraphicsUnit.Pixel);
|
||||
}
|
||||
|
||||
void DrawImage(System.Drawing.Graphics g, Bitmap bmp, int2 location)
|
||||
{
|
||||
float OffsetX = bmp.Width / 2 - 12;
|
||||
float DrawX = 24 * location.X * Zoom + Offset.X - OffsetX;
|
||||
|
||||
float OffsetY = bmp.Height / 2 - 12;
|
||||
float DrawY = 24 * 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();
|
||||
float OffsetX = t.Centered ? t.Bitmap.Width / 2 - 12 : 0;
|
||||
float DrawX = 24 * p.X * Zoom + Offset.X - OffsetX;
|
||||
|
||||
float OffsetY = t.Centered ? t.Bitmap.Height / 2 - 12 : 0;
|
||||
float DrawY = 24 * 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);
|
||||
g.DrawRectangle(CordonPen,
|
||||
origin.X, origin.Y,
|
||||
t.Bitmap.Width, t.Bitmap.Height );
|
||||
DrawX, DrawY,
|
||||
t.Bitmap.Width * Zoom, t.Bitmap.Height * Zoom);
|
||||
}
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e)
|
||||
@@ -392,37 +458,49 @@ namespace OpenRA.Editor
|
||||
{
|
||||
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 = 24.0f * (float)ChunkSize * (float)x.X * Zoom + Offset.X;
|
||||
float DrawY = 24.0f * (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.XOffset * 24 * Zoom + Offset.X,
|
||||
Map.YOffset * 24 * Zoom + Offset.Y,
|
||||
Map.Width * 24 * Zoom,
|
||||
Map.Height * 24 * Zoom);
|
||||
|
||||
foreach (var ar in Map.Actors)
|
||||
DrawActor(e.Graphics, ar.Value.Location(), ActorTemplates[ar.Value.Type]);
|
||||
|
||||
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,
|
||||
24 * wp.Value.X * Zoom + Offset.X + 4,
|
||||
24 * wp.Value.Y * Zoom + Offset.Y + 4,
|
||||
16 * Zoom, 16 * Zoom);
|
||||
|
||||
if (Brush != null)
|
||||
e.Graphics.DrawImage(Brush.Bitmap,
|
||||
(24 * GetBrushLocation() + Offset).ToPoint());
|
||||
24 * GetBrushLocation().X * Zoom + Offset.X,
|
||||
24 * GetBrushLocation().Y * Zoom + Offset.Y,
|
||||
Brush.Bitmap.Width * Zoom,
|
||||
Brush.Bitmap.Height * Zoom);
|
||||
|
||||
if (Actor != null)
|
||||
DrawActor(e.Graphics, GetBrushLocation(), Actor);
|
||||
|
||||
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,
|
||||
24 * GetBrushLocation().X * Zoom + Offset.X + 4,
|
||||
24 * GetBrushLocation().Y * Zoom + Offset.Y + 4,
|
||||
16 * Zoom, 16 * Zoom);
|
||||
|
||||
if (Brush == null && Actor == null && Resource == null)
|
||||
{
|
||||
|
||||
@@ -36,5 +36,10 @@ namespace OpenRA.FileFormats
|
||||
}
|
||||
|
||||
public IEnumerable<uint> AllFileHashes() { return hashes; }
|
||||
|
||||
public bool Exists(string filename)
|
||||
{
|
||||
return hashes.Contains(PackageEntry.HashFilename(filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
throw new InvalidOperationException("FieldLoader: Cannot parse `{0}` into `{1}.{2}` ".F(s,f,t) );
|
||||
};
|
||||
|
||||
|
||||
public static Action<string,Type> UnknownFieldAction = (s,f) =>
|
||||
{
|
||||
throw new NotImplementedException( "FieldLoader: Missing field `{0}` on `{1}`".F( s, f.Name ) );
|
||||
@@ -30,13 +30,32 @@ namespace OpenRA.FileFormats
|
||||
|
||||
public static void Load( object self, MiniYaml my )
|
||||
{
|
||||
foreach( var x in my.Nodes )
|
||||
if (!x.Key.StartsWith("-"))
|
||||
LoadField(self, x.Key, x.Value.Value);
|
||||
var loadDict = typeLoadInfo[ self.GetType() ];
|
||||
|
||||
foreach( var field in self.GetType().GetFields())
|
||||
if( field.HasAttribute<FieldFromYamlKeyAttribute>() )
|
||||
field.SetValue( self, GetValue( field.Name, field.FieldType, my.Value ) );
|
||||
foreach( var kv in loadDict )
|
||||
{
|
||||
object val;
|
||||
if( kv.Value != null )
|
||||
val = kv.Value( kv.Key.Name, kv.Key.FieldType, my );
|
||||
else if( !TryGetValueFromYaml( kv.Key.Name, kv.Key.FieldType, my, out val ) )
|
||||
continue;
|
||||
|
||||
kv.Key.SetValue( self, val );
|
||||
}
|
||||
}
|
||||
|
||||
static bool TryGetValueFromYaml( string fieldName, Type fieldType, MiniYaml yaml, out object ret )
|
||||
{
|
||||
ret = null;
|
||||
var n = yaml.Nodes.Where( x=>x.Key == fieldName ).ToList();
|
||||
if( n.Count == 0 )
|
||||
return false;
|
||||
if( n.Count == 1 && n[ 0 ].Value.Nodes.Count == 0 )
|
||||
{
|
||||
ret = GetValue( fieldName, fieldType, n[ 0 ].Value.Value );
|
||||
return true;
|
||||
}
|
||||
throw new InvalidOperationException( "TryGetValueFromYaml: unable to load field {0} (of type {1})".F( fieldName, fieldType ) );
|
||||
}
|
||||
|
||||
public static T Load<T>(MiniYaml y) where T : new()
|
||||
@@ -45,15 +64,6 @@ namespace OpenRA.FileFormats
|
||||
Load(t, y);
|
||||
return t;
|
||||
}
|
||||
|
||||
public static void LoadFields( object self, Dictionary<string,MiniYaml> my, IEnumerable<string> fields )
|
||||
{
|
||||
foreach (var field in fields)
|
||||
{
|
||||
if (!my.ContainsKey(field)) continue;
|
||||
LoadField(self,field,my[field].Value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void LoadField( object self, string key, string value )
|
||||
{
|
||||
@@ -66,7 +76,7 @@ namespace OpenRA.FileFormats
|
||||
else
|
||||
field.SetValue( self, GetValue( field.Name, field.FieldType, value ) );
|
||||
}
|
||||
|
||||
|
||||
public static object GetValue( string field, Type fieldType, string x )
|
||||
{
|
||||
if (x != null) x = x.Trim();
|
||||
@@ -77,7 +87,7 @@ namespace OpenRA.FileFormats
|
||||
return res;
|
||||
return InvalidValueAction(x,fieldType, field);
|
||||
}
|
||||
|
||||
|
||||
else if( fieldType == typeof( ushort ) )
|
||||
{
|
||||
ushort res;
|
||||
@@ -96,7 +106,7 @@ namespace OpenRA.FileFormats
|
||||
|
||||
else if (fieldType == typeof(string))
|
||||
return x;
|
||||
|
||||
|
||||
else if (fieldType == typeof(Color))
|
||||
{
|
||||
var parts = x.Split(',');
|
||||
@@ -106,14 +116,14 @@ namespace OpenRA.FileFormats
|
||||
return Color.FromArgb(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), int.Parse(parts[3]));
|
||||
return InvalidValueAction(x,fieldType, field);
|
||||
}
|
||||
|
||||
|
||||
else if (fieldType.IsEnum)
|
||||
{
|
||||
if (!Enum.GetNames(fieldType).Select(a => a.ToLower()).Contains(x.ToLower()))
|
||||
return InvalidValueAction(x,fieldType, field);
|
||||
return Enum.Parse(fieldType, x, true);
|
||||
}
|
||||
|
||||
|
||||
else if (fieldType == typeof(bool))
|
||||
return ParseYesNo(x, fieldType, field);
|
||||
|
||||
@@ -134,7 +144,19 @@ namespace OpenRA.FileFormats
|
||||
var parts = x.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
return new int2(int.Parse(parts[0]), int.Parse(parts[1]));
|
||||
}
|
||||
|
||||
else if (fieldType == typeof(float2))
|
||||
{
|
||||
var parts = x.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
float xx = 0;
|
||||
float yy = 0;
|
||||
float res;
|
||||
if (float.TryParse(parts[0].Replace("%",""), out res))
|
||||
xx = res * (parts[0].Contains( '%' ) ? 0.01f : 1f);
|
||||
if (float.TryParse(parts[1].Replace("%",""), out res))
|
||||
yy = res * (parts[1].Contains( '%' ) ? 0.01f : 1f);
|
||||
return new float2(xx,yy);
|
||||
}
|
||||
|
||||
UnknownFieldAction("[Type] {0}".F(x),fieldType);
|
||||
return null;
|
||||
}
|
||||
@@ -148,13 +170,62 @@ namespace OpenRA.FileFormats
|
||||
if( p == "false" ) return false;
|
||||
return InvalidValueAction(p,fieldType, field);
|
||||
}
|
||||
|
||||
static Cache<Type, Dictionary<FieldInfo, Func<string, Type, MiniYaml, object>>> typeLoadInfo = new Cache<Type, Dictionary<FieldInfo, Func<string, Type, MiniYaml, object>>>( GetTypeLoadInfo );
|
||||
|
||||
static Dictionary<FieldInfo, Func<string, Type, MiniYaml, object>> GetTypeLoadInfo( Type type )
|
||||
{
|
||||
var ret = new Dictionary<FieldInfo, Func<string, Type, MiniYaml, object>>();
|
||||
|
||||
foreach( var field 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 );
|
||||
if( loadUsing.Length != 0 )
|
||||
ret[ field ] = ( _1, fieldType, yaml ) => loadUsing[ 0 ].LoaderFunc( field )( yaml );
|
||||
else if( fromYamlKey.Length != 0 )
|
||||
ret[ field ] = ( f, ft, yaml ) => GetValue( f, ft, yaml.Value );
|
||||
else if( load.Length != 0 )
|
||||
ret[ field ] = null;
|
||||
}
|
||||
|
||||
if( ret.Count == 0 )
|
||||
foreach( var f in type.GetFields() )
|
||||
ret.Add( f, null );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
[AttributeUsage( AttributeTargets.Field )]
|
||||
public class LoadAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage( AttributeTargets.Field )]
|
||||
public class LoadUsingAttribute : Attribute
|
||||
{
|
||||
Func<MiniYaml, object> loaderFuncCache;
|
||||
public readonly string Loader;
|
||||
|
||||
public LoadUsingAttribute( string loader )
|
||||
{
|
||||
Loader = loader;
|
||||
}
|
||||
|
||||
internal Func<MiniYaml, object> LoaderFunc( FieldInfo field )
|
||||
{
|
||||
const BindingFlags bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
|
||||
if( loaderFuncCache == null )
|
||||
loaderFuncCache = (Func<MiniYaml, object>)Delegate.CreateDelegate( typeof( Func<MiniYaml, object> ), field.DeclaringType.GetMethod( Loader, bf ) );
|
||||
return loaderFuncCache;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class FieldSaver
|
||||
{
|
||||
public static MiniYaml Save(object o)
|
||||
{
|
||||
var dict = new Dictionary<string, MiniYaml>();
|
||||
var nodes = new List<MiniYamlNode>();
|
||||
string root = null;
|
||||
|
||||
foreach( var f in o.GetType().GetFields( BindingFlags.Public | BindingFlags.Instance ) )
|
||||
@@ -162,12 +233,12 @@ namespace OpenRA.FileFormats
|
||||
if( f.HasAttribute<FieldFromYamlKeyAttribute>() )
|
||||
root = FormatValue( o, f );
|
||||
else
|
||||
dict.Add( f.Name, new MiniYaml( FormatValue( o, f ) ) );
|
||||
nodes.Add( new MiniYamlNode( f.Name, FormatValue( o, f ) ) );
|
||||
}
|
||||
|
||||
return new MiniYaml( root, dict );
|
||||
return new MiniYaml( root, nodes );
|
||||
}
|
||||
|
||||
|
||||
public static MiniYaml SaveDifferences(object o, object from)
|
||||
{
|
||||
if (o.GetType() != from.GetType())
|
||||
@@ -175,10 +246,10 @@ namespace OpenRA.FileFormats
|
||||
|
||||
var fields = o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(f => FormatValue(o,f) != FormatValue(from,f));
|
||||
|
||||
return new MiniYaml(null, fields.ToDictionary(
|
||||
f => f.Name,
|
||||
f => new MiniYaml(FormatValue(o, f))));
|
||||
|
||||
return new MiniYaml( null, fields.Select( f => new MiniYamlNode(
|
||||
f.Name,
|
||||
FormatValue( o, f ) ) ).ToList() );
|
||||
}
|
||||
|
||||
public static string FormatValue(object o, FieldInfo f)
|
||||
@@ -193,7 +264,7 @@ namespace OpenRA.FileFormats
|
||||
var c = (Color)v;
|
||||
return "{0},{1},{2},{3}".F(c.A,c.R,c.G,c.B);
|
||||
}
|
||||
|
||||
|
||||
return f.FieldType.IsArray
|
||||
? string.Join(",", ((Array)v).OfType<object>().Select(a => a.ToString()).ToArray())
|
||||
: v.ToString();
|
||||
|
||||
@@ -64,14 +64,18 @@ namespace OpenRA.FileFormats
|
||||
allFiles = new Cache<uint, List<IFolder>>( _ => new List<IFolder>() );
|
||||
}
|
||||
|
||||
public static void LoadFromManifest( Manifest manifest )
|
||||
{
|
||||
UnmountAll();
|
||||
foreach (var dir in manifest.Folders) Mount(dir);
|
||||
foreach (var pkg in manifest.Packages) Mount(pkg);
|
||||
}
|
||||
|
||||
static Stream GetFromCache( Cache<uint, List<IFolder>> index, string filename )
|
||||
{
|
||||
foreach( var folder in index[ PackageEntry.HashFilename( filename ) ] )
|
||||
{
|
||||
Stream s = folder.GetContent(filename);
|
||||
if( s != null )
|
||||
return s;
|
||||
}
|
||||
if (folder.Exists(filename))
|
||||
return folder.GetContent(filename);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -86,9 +90,8 @@ namespace OpenRA.FileFormats
|
||||
|
||||
foreach( IFolder folder in mountedFolders )
|
||||
{
|
||||
Stream s = folder.GetContent(filename);
|
||||
if( s != null )
|
||||
return s;
|
||||
if (folder.Exists(filename))
|
||||
return folder.GetContent(filename);
|
||||
}
|
||||
|
||||
throw new FileNotFoundException( string.Format( "File not found: {0}", filename ), filename );
|
||||
@@ -109,11 +112,8 @@ namespace OpenRA.FileFormats
|
||||
foreach( var ext in exts )
|
||||
{
|
||||
foreach( IFolder folder in mountedFolders )
|
||||
{
|
||||
Stream s = folder.GetContent( filename + ext );
|
||||
if( s != null )
|
||||
return s;
|
||||
}
|
||||
if (folder.Exists(filename + ext))
|
||||
return folder.GetContent( filename + ext );
|
||||
}
|
||||
|
||||
throw new FileNotFoundException( string.Format( "File not found: {0}", filename ), filename );
|
||||
@@ -122,15 +122,8 @@ namespace OpenRA.FileFormats
|
||||
public static bool Exists(string filename)
|
||||
{
|
||||
foreach (var folder in mountedFolders)
|
||||
{
|
||||
var s = folder.GetContent(filename);
|
||||
if (s != null)
|
||||
{
|
||||
s.Dispose();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (folder.Exists(filename))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,5 +30,10 @@ namespace OpenRA.FileFormats
|
||||
foreach( var filename in Directory.GetFiles( path, "*", SearchOption.TopDirectoryOnly ) )
|
||||
yield return PackageEntry.HashFilename( filename );
|
||||
}
|
||||
|
||||
public bool Exists(string filename)
|
||||
{
|
||||
return File.Exists(Path.Combine(path,filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,15 +71,9 @@ namespace OpenRA.FileFormats.Graphics
|
||||
|
||||
public interface ITexture
|
||||
{
|
||||
void SetData( Bitmap bitmap );
|
||||
void SetData(Bitmap bitmap);
|
||||
void SetData(uint[,] colors);
|
||||
}
|
||||
|
||||
public interface IFont
|
||||
{
|
||||
void DrawText( string text, int2 pos, Color c );
|
||||
|
||||
int2 Measure( string text );
|
||||
void SetData(byte[] colors, int width, int height);
|
||||
}
|
||||
|
||||
public enum PrimitiveType
|
||||
|
||||
@@ -22,7 +22,6 @@ namespace OpenRA.FileFormats
|
||||
|
||||
Stream stream;
|
||||
int currentFrame;
|
||||
ushort flags;
|
||||
ushort numColors;
|
||||
ushort blockWidth;
|
||||
ushort blockHeight;
|
||||
@@ -62,7 +61,7 @@ namespace OpenRA.FileFormats
|
||||
/* var length = */reader.ReadUInt32();
|
||||
|
||||
/*var version = */reader.ReadUInt16();
|
||||
flags = reader.ReadUInt16();
|
||||
/*var flags = */reader.ReadUInt16();
|
||||
Frames = reader.ReadUInt16();
|
||||
Width = reader.ReadUInt16();
|
||||
Height = reader.ReadUInt16();
|
||||
@@ -82,7 +81,7 @@ namespace OpenRA.FileFormats
|
||||
/*var freq = */reader.ReadUInt16();
|
||||
/*var channels = */reader.ReadByte();
|
||||
/*var bits = */reader.ReadByte();
|
||||
var unknown3 = reader.ReadChars(14);
|
||||
/*var unknown3 = */reader.ReadChars(14);
|
||||
|
||||
|
||||
var frameSize = NextPowerOf2(Math.Max(Width,Height));
|
||||
|
||||
60
OpenRA.FileFormats/Manifest.cs
Normal file
60
OpenRA.FileFormats/Manifest.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
#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;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
/* describes what is to be loaded in order to run a set of mods */
|
||||
|
||||
public class Manifest
|
||||
{
|
||||
public readonly string[]
|
||||
Folders, Packages, Rules,
|
||||
Sequences, Cursors, Chrome, Assemblies, ChromeLayout,
|
||||
Weapons, Voices, Music, Movies, TileSets;
|
||||
|
||||
public readonly string ShellmapUid, LoadScreen;
|
||||
|
||||
public Manifest(string[] mods)
|
||||
{
|
||||
var yaml = mods
|
||||
.Select(m => MiniYaml.FromFile("mods/" + m + "/mod.yaml"))
|
||||
.Aggregate(MiniYaml.Merge);
|
||||
|
||||
Folders = YamlList(yaml, "Folders");
|
||||
Packages = YamlList(yaml, "Packages");
|
||||
Rules = YamlList(yaml, "Rules");
|
||||
Sequences = YamlList(yaml, "Sequences");
|
||||
Cursors = YamlList(yaml, "Cursors");
|
||||
Chrome = YamlList(yaml, "Chrome");
|
||||
Assemblies = YamlList(yaml, "Assemblies");
|
||||
ChromeLayout = YamlList(yaml, "ChromeLayout");
|
||||
Weapons = YamlList(yaml, "Weapons");
|
||||
Voices = YamlList(yaml, "Voices");
|
||||
Music = YamlList(yaml, "Music");
|
||||
Movies = YamlList(yaml, "Movies");
|
||||
TileSets = YamlList(yaml, "TileSets");
|
||||
|
||||
ShellmapUid = yaml.First( x => x.Key == "ShellmapUid" ).Value.Value;
|
||||
LoadScreen = yaml.First( x => x.Key == "LoadScreen" ).Value.Value;
|
||||
}
|
||||
|
||||
static string[] YamlList(List<MiniYamlNode> ys, string key)
|
||||
{
|
||||
var y = ys.FirstOrDefault( x => x.Key == key );
|
||||
if( y == null )
|
||||
return new string[ 0 ];
|
||||
|
||||
return y.Value.NodesDict.Keys.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,39 +21,41 @@ namespace OpenRA.FileFormats
|
||||
|
||||
// Yaml map data
|
||||
public readonly string Uid;
|
||||
public bool Selectable;
|
||||
[FieldLoader.Load] public bool Selectable;
|
||||
|
||||
public string Title;
|
||||
public string Description;
|
||||
public string Author;
|
||||
public int PlayerCount;
|
||||
public string Tileset;
|
||||
[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;
|
||||
|
||||
[FieldLoader.LoadUsing( "LoadWaypoints" )]
|
||||
public Dictionary<string, int2> Waypoints = new Dictionary<string, int2>();
|
||||
public IEnumerable<int2> SpawnPoints { get { return Waypoints.Select(kv => kv.Value); } }
|
||||
|
||||
public int2 TopLeft;
|
||||
public int2 BottomRight;
|
||||
[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; } }
|
||||
|
||||
static List<string> Fields = new List<string>() {
|
||||
"Selectable", "Title", "Description", "Author", "PlayerCount", "Tileset", "TopLeft", "BottomRight"
|
||||
};
|
||||
|
||||
public MapStub(IFolder package)
|
||||
{
|
||||
Package = package;
|
||||
var yaml = MiniYaml.FromStream(Package.GetContent("map.yaml"));
|
||||
FieldLoader.LoadFields(this, yaml, Fields);
|
||||
FieldLoader.Load( this, new MiniYaml( null, yaml ) );
|
||||
|
||||
// Waypoints
|
||||
foreach (var wp in yaml["Waypoints"].Nodes)
|
||||
{
|
||||
string[] loc = wp.Value.Value.Split(',');
|
||||
Waypoints.Add(wp.Key, new int2(int.Parse(loc[0]), int.Parse(loc[1])));
|
||||
}
|
||||
|
||||
Uid = Package.GetContent("map.uid").ReadAllText();
|
||||
}
|
||||
|
||||
static object LoadWaypoints( MiniYaml y )
|
||||
{
|
||||
var ret = new Dictionary<string, int2>();
|
||||
foreach( var wp in y.NodesDict[ "Waypoints" ].NodesDict )
|
||||
{
|
||||
string[] loc = wp.Value.Value.Split( ',' );
|
||||
ret.Add( wp.Key, new int2( int.Parse( loc[ 0 ] ), int.Parse( loc[ 1 ] ) ) );
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,21 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
public class PlayerReference
|
||||
{
|
||||
public readonly string Name;
|
||||
public readonly string Palette;
|
||||
public readonly string Race;
|
||||
public readonly bool OwnsWorld = false;
|
||||
public readonly bool NonCombatant = false;
|
||||
public readonly Color Color = Color.FromArgb(238,238,238);
|
||||
public readonly Color Color2 = Color.FromArgb(44,28,24);
|
||||
|
||||
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 Color Color = Color.FromArgb(238,238,238);
|
||||
public Color Color2 = Color.FromArgb(44,28,24);
|
||||
|
||||
public int InitialCash = 0;
|
||||
public string[] Allies = {};
|
||||
public string[] Enemies = {};
|
||||
|
||||
public PlayerReference() {}
|
||||
public PlayerReference(MiniYaml my)
|
||||
{
|
||||
FieldLoader.Load(this, my);
|
||||
|
||||
@@ -31,38 +31,39 @@ namespace OpenRA.FileFormats
|
||||
|
||||
public class TileTemplate
|
||||
{
|
||||
public ushort Id;
|
||||
public string Image;
|
||||
public int2 Size;
|
||||
public bool PickAny;
|
||||
[FieldLoader.Load] public ushort Id;
|
||||
[FieldLoader.Load] public string Image;
|
||||
[FieldLoader.Load] public int2 Size;
|
||||
[FieldLoader.Load] public bool PickAny;
|
||||
|
||||
[FieldLoader.LoadUsing( "LoadTiles" )]
|
||||
public Dictionary<byte, string> Tiles = new Dictionary<byte, string>();
|
||||
|
||||
static List<string> fields = new List<string>() {"Id", "Image", "Size", "PickAny"};
|
||||
|
||||
public TileTemplate() {}
|
||||
public TileTemplate(MiniYaml my)
|
||||
{
|
||||
FieldLoader.LoadFields(this, my.Nodes, fields);
|
||||
FieldLoader.Load( this, my );
|
||||
}
|
||||
|
||||
Tiles = my.Nodes["Tiles"].Nodes.ToDictionary(
|
||||
static object LoadTiles( MiniYaml y )
|
||||
{
|
||||
return y.NodesDict["Tiles"].NodesDict.ToDictionary(
|
||||
t => byte.Parse(t.Key),
|
||||
t => t.Value.Value);
|
||||
t => t.Value.Value );
|
||||
}
|
||||
|
||||
public MiniYaml Save()
|
||||
{
|
||||
var root = new Dictionary<string, MiniYaml>();
|
||||
foreach (var field in fields)
|
||||
var root = new List<MiniYamlNode>();
|
||||
foreach (var field in new string[] {"Id", "Image", "Size", "PickAny"})
|
||||
{
|
||||
FieldInfo f = this.GetType().GetField(field);
|
||||
if (f.GetValue(this) == null) continue;
|
||||
root.Add(field, new MiniYaml(FieldSaver.FormatValue(this, f), null));
|
||||
root.Add( new MiniYamlNode( field, FieldSaver.FormatValue( this, f ) ) );
|
||||
}
|
||||
|
||||
root.Add("Tiles",
|
||||
new MiniYaml(null, Tiles.ToDictionary(
|
||||
p => p.Key.ToString(),
|
||||
p => new MiniYaml(p.Value))));
|
||||
root.Add( new MiniYamlNode( "Tiles", null,
|
||||
Tiles.Select( x => new MiniYamlNode( x.Key.ToString(), x.Value ) ).ToList() ) );
|
||||
|
||||
return new MiniYaml(null, root);
|
||||
}
|
||||
@@ -82,17 +83,17 @@ namespace OpenRA.FileFormats
|
||||
public TileSet() {}
|
||||
public TileSet( string filepath )
|
||||
{
|
||||
var yaml = MiniYaml.FromFile(filepath);
|
||||
var yaml = MiniYaml.DictFromFile( filepath );
|
||||
|
||||
// General info
|
||||
FieldLoader.Load(this, yaml["General"]);
|
||||
|
||||
// TerrainTypes
|
||||
Terrain = yaml["Terrain"].Nodes.Values
|
||||
Terrain = yaml["Terrain"].NodesDict.Values
|
||||
.Select(y => new TerrainTypeInfo(y)).ToDictionary(t => t.Type);
|
||||
|
||||
// Templates
|
||||
Templates = yaml["Templates"].Nodes.Values
|
||||
Templates = yaml["Templates"].NodesDict.Values
|
||||
.Select(y => new TileTemplate(y)).ToDictionary(t => t.Id);
|
||||
}
|
||||
|
||||
@@ -108,32 +109,32 @@ namespace OpenRA.FileFormats
|
||||
|
||||
public void Save(string filepath)
|
||||
{
|
||||
var root = new Dictionary<string, MiniYaml>();
|
||||
var root = new List<MiniYamlNode>();
|
||||
foreach (var field in fields)
|
||||
{
|
||||
FieldInfo f = this.GetType().GetField(field);
|
||||
if (f.GetValue(this) == null) continue;
|
||||
root.Add(field, new MiniYaml(FieldSaver.FormatValue(this, f), null));
|
||||
root.Add( new MiniYamlNode( field, FieldSaver.FormatValue( this, f ) ) );
|
||||
}
|
||||
|
||||
var gen = new Dictionary<string, MiniYaml>();
|
||||
var gen = new List<MiniYamlNode>();
|
||||
foreach (var field in fields)
|
||||
{
|
||||
FieldInfo f = this.GetType().GetField(field);
|
||||
if (f.GetValue(this) == null) continue;
|
||||
gen.Add(field, new MiniYaml(FieldSaver.FormatValue(this, f), null));
|
||||
gen.Add( new MiniYamlNode( field, FieldSaver.FormatValue( this, f ) ) );
|
||||
}
|
||||
root.Add("General", new MiniYaml(null, gen));
|
||||
|
||||
root.Add("Terrain",
|
||||
new MiniYaml(null, Terrain.ToDictionary(
|
||||
t => "TerrainType@{0}".F(t.Value.Type),
|
||||
t => t.Value.Save())));
|
||||
|
||||
root.Add("Templates",
|
||||
new MiniYaml(null, Templates.ToDictionary(
|
||||
t => "Template@{0}".F(t.Value.Id),
|
||||
t => t.Value.Save())));
|
||||
root.Add( new MiniYamlNode( "General", null, gen ) );
|
||||
|
||||
root.Add( new MiniYamlNode( "Terrain", null,
|
||||
Terrain.Select( t => new MiniYamlNode(
|
||||
"TerrainType@{0}".F( t.Value.Type ),
|
||||
t.Value.Save() ) ).ToList() ) );
|
||||
|
||||
root.Add( new MiniYamlNode( "Templates", null,
|
||||
Templates.Select( t => new MiniYamlNode(
|
||||
"Template@{0}".F( t.Value.Id ),
|
||||
t.Value.Save() ) ).ToList() ) );
|
||||
root.WriteToFile(filepath);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,38 +15,80 @@ using System.Linq;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
using MiniYamlNodes = Dictionary<string, MiniYaml>;
|
||||
using MiniYamlNodes = List<MiniYamlNode>;
|
||||
|
||||
public class MiniYamlNode
|
||||
{
|
||||
public struct SourceLocation
|
||||
{
|
||||
public string Filename; public int Line;
|
||||
}
|
||||
|
||||
public SourceLocation Location;
|
||||
public string Key;
|
||||
public MiniYaml Value;
|
||||
|
||||
public MiniYamlNode( string k, MiniYaml v )
|
||||
{
|
||||
Key = k;
|
||||
Value = v;
|
||||
}
|
||||
|
||||
public MiniYamlNode( string k, MiniYaml v, SourceLocation loc )
|
||||
: this( k, v )
|
||||
{
|
||||
Location = loc;
|
||||
}
|
||||
|
||||
public MiniYamlNode( string k, string v )
|
||||
: this( k, v, null )
|
||||
{
|
||||
}
|
||||
public MiniYamlNode( string k, string v, List<MiniYamlNode> n )
|
||||
: this( k, new MiniYaml( v, n ) )
|
||||
{
|
||||
}
|
||||
|
||||
public MiniYamlNode( string k, string v, List<MiniYamlNode> n, SourceLocation loc )
|
||||
: this( k, new MiniYaml( v, n ), loc )
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class MiniYaml
|
||||
{
|
||||
public string Value;
|
||||
public Dictionary<string, MiniYaml> Nodes = new Dictionary<string,MiniYaml>();
|
||||
public List<MiniYamlNode> Nodes;
|
||||
|
||||
public MiniYaml( string value ) : this( value, new Dictionary<string, MiniYaml>() ) { }
|
||||
public Dictionary<string, MiniYaml> NodesDict { get { return Nodes.ToDictionary( x => x.Key, x => x.Value ); } }
|
||||
|
||||
public MiniYaml( string value, Dictionary<string, MiniYaml> nodes )
|
||||
public MiniYaml( string value ) : this( value, null ) { }
|
||||
|
||||
public MiniYaml( string value, List<MiniYamlNode> nodes )
|
||||
{
|
||||
Value = value;
|
||||
Nodes = nodes;
|
||||
Nodes = nodes ?? new List<MiniYamlNode>();
|
||||
}
|
||||
|
||||
public static MiniYaml FromDictionary<K,V>(Dictionary<K,V>dict)
|
||||
{
|
||||
return new MiniYaml( null, dict.ToDictionary( x=>x.Key.ToString(), x=>new MiniYaml(x.Value.ToString())));
|
||||
}
|
||||
|
||||
public static MiniYaml FromList<T>(List<T>list)
|
||||
{
|
||||
return new MiniYaml( null, list.ToDictionary( x=>x.ToString(), x=>new MiniYaml(null)));
|
||||
}
|
||||
|
||||
static Dictionary<string, MiniYaml> FromLines(string[] lines)
|
||||
{
|
||||
var levels = new List<Dictionary<string, MiniYaml>>();
|
||||
levels.Add(new Dictionary<string, MiniYaml>());
|
||||
|
||||
public static MiniYaml FromDictionary<K, V>( Dictionary<K, V> dict )
|
||||
{
|
||||
return new MiniYaml( null, dict.Select( x => new MiniYamlNode( x.Key.ToString(), new MiniYaml( x.Value.ToString() ) ) ).ToList() );
|
||||
}
|
||||
|
||||
public static MiniYaml FromList<T>( List<T> list )
|
||||
{
|
||||
return new MiniYaml( null, list.Select( x => new MiniYamlNode( x.ToString(), new MiniYaml( null ) ) ).ToList() );
|
||||
}
|
||||
|
||||
static List<MiniYamlNode> FromLines(string[] lines, string filename)
|
||||
{
|
||||
var levels = new List<List<MiniYamlNode>>();
|
||||
levels.Add(new List<MiniYamlNode>());
|
||||
|
||||
var lineNo = 0;
|
||||
foreach (var line in lines)
|
||||
{
|
||||
++lineNo;
|
||||
var t = line.TrimStart(' ', '\t');
|
||||
if (t.Length == 0 || t[0] == '#')
|
||||
continue;
|
||||
@@ -57,28 +99,28 @@ namespace OpenRA.FileFormats
|
||||
while (levels.Count > level + 1)
|
||||
levels.RemoveAt(levels.Count - 1);
|
||||
|
||||
var colon = t.IndexOf(':');
|
||||
var d = new Dictionary<string, MiniYaml>();
|
||||
try
|
||||
{
|
||||
if (colon == -1)
|
||||
levels[level].Add(t.Trim(), new MiniYaml(null, d));
|
||||
else
|
||||
{
|
||||
var value = t.Substring(colon + 1).Trim();
|
||||
if (value.Length == 0)
|
||||
value = null;
|
||||
levels[level].Add(t.Substring(0, colon).Trim(), new MiniYaml(value, d));
|
||||
}
|
||||
}
|
||||
catch (ArgumentException) { throw new InvalidDataException("Duplicate Identifier:`{0}`".F(t)); }
|
||||
var d = new List<MiniYamlNode>();
|
||||
var rhs = SplitAtColon( ref t );
|
||||
levels[ level ].Add( new MiniYamlNode( t, rhs, d, new MiniYamlNode.SourceLocation { Filename = filename, Line = lineNo } ) );
|
||||
|
||||
levels.Add(d);
|
||||
}
|
||||
return levels[0];
|
||||
return levels[ 0 ];
|
||||
}
|
||||
|
||||
public static Dictionary<string, MiniYaml> FromFileInPackage( string path )
|
||||
static string SplitAtColon( ref string t )
|
||||
{
|
||||
var colon = t.IndexOf(':');
|
||||
if( colon == -1 )
|
||||
return null;
|
||||
var ret = t.Substring( colon + 1 ).Trim();
|
||||
if( ret.Length == 0 )
|
||||
ret = null;
|
||||
t = t.Substring( 0, colon ).Trim();
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static List<MiniYamlNode> FromFileInPackage( string path )
|
||||
{
|
||||
StreamReader reader = new StreamReader( FileSystem.Open(path) );
|
||||
List<string> lines = new List<string>();
|
||||
@@ -87,54 +129,67 @@ namespace OpenRA.FileFormats
|
||||
lines.Add(reader.ReadLine());
|
||||
reader.Close();
|
||||
|
||||
return FromLines(lines.ToArray());
|
||||
}
|
||||
|
||||
public static Dictionary<string, MiniYaml> FromFile( string path )
|
||||
{
|
||||
return FromLines(File.ReadAllLines( path ));
|
||||
return FromLines(lines.ToArray(), path);
|
||||
}
|
||||
|
||||
public static Dictionary<string, MiniYaml> FromStream(Stream s)
|
||||
public static Dictionary<string, MiniYaml> DictFromFile( string path )
|
||||
{
|
||||
return FromFile( path ).ToDictionary( x => x.Key, x => x.Value );
|
||||
}
|
||||
|
||||
public static Dictionary<string, MiniYaml> DictFromStream( Stream stream )
|
||||
{
|
||||
return FromStream( stream ).ToDictionary( x => x.Key, x => x.Value );
|
||||
}
|
||||
|
||||
public static List<MiniYamlNode> FromFile( string path )
|
||||
{
|
||||
return FromLines(File.ReadAllLines( path ), path);
|
||||
}
|
||||
|
||||
public static List<MiniYamlNode> FromStream(Stream s)
|
||||
{
|
||||
using (var reader = new StreamReader(s))
|
||||
return FromString(reader.ReadToEnd());
|
||||
}
|
||||
|
||||
public static Dictionary<string, MiniYaml> FromString(string text)
|
||||
public static List<MiniYamlNode> FromString(string text)
|
||||
{
|
||||
return FromLines(text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries));
|
||||
return FromLines(text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries), "<no filename available>");
|
||||
}
|
||||
|
||||
public static Dictionary<string, MiniYaml> Merge( Dictionary<string, MiniYaml> a, Dictionary<string, MiniYaml> b )
|
||||
public static List<MiniYamlNode> Merge( List<MiniYamlNode> a, List<MiniYamlNode> b )
|
||||
{
|
||||
if( a.Count == 0 )
|
||||
return b;
|
||||
if( b.Count == 0 )
|
||||
return a;
|
||||
|
||||
var ret = new Dictionary<string, MiniYaml>();
|
||||
var ret = new List<MiniYamlNode>();
|
||||
|
||||
var keys = a.Keys.Union( b.Keys ).ToList();
|
||||
var aDict = a.ToDictionary( x => x.Key );
|
||||
var bDict = b.ToDictionary( x => x.Key );
|
||||
var keys = aDict.Keys.Union( bDict.Keys ).ToList();
|
||||
|
||||
var noInherit = keys.Where( x => x.Length > 0 && x[ 0 ] == '-' ).Select( x => x.Substring( 1 ) ).ToList();
|
||||
|
||||
foreach( var key in keys )
|
||||
{
|
||||
MiniYaml aa, bb;
|
||||
a.TryGetValue( key, out aa );
|
||||
b.TryGetValue( key, out bb );
|
||||
MiniYamlNode aa, bb;
|
||||
aDict.TryGetValue( key, out aa );
|
||||
bDict.TryGetValue( key, out bb );
|
||||
|
||||
// if( key.Length > 0 && key[ 0 ] == '-' )
|
||||
// continue;
|
||||
// else
|
||||
if( noInherit.Contains( key ) )
|
||||
{
|
||||
if( aa != null )
|
||||
ret.Add( key, aa );
|
||||
ret.Add( aa );
|
||||
}
|
||||
else
|
||||
ret.Add( key, Merge( aa, bb ) );
|
||||
{
|
||||
var loc = aa == null ? default( MiniYamlNode.SourceLocation ) : aa.Location;
|
||||
var merged = ( aa == null || bb == null ) ? aa ?? bb : new MiniYamlNode( key, Merge( aa.Value, bb.Value ), loc );
|
||||
ret.Add( merged );
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
@@ -62,6 +62,7 @@
|
||||
<Compile Include="Folder.cs" />
|
||||
<Compile Include="Graphics\IGraphicsDevice.cs" />
|
||||
<Compile Include="Graphics\Vertex.cs" />
|
||||
<Compile Include="Manifest.cs" />
|
||||
<Compile Include="MiniYaml.cs" />
|
||||
<Compile Include="PackageEntry.cs" />
|
||||
<Compile Include="Package.cs" />
|
||||
@@ -71,7 +72,6 @@
|
||||
<Compile Include="Primitives\DisposableAction.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Thirdparty\Random.cs" />
|
||||
<Compile Include="Session.cs" />
|
||||
<Compile Include="Support\Log.cs" />
|
||||
<Compile Include="Support\Stopwatch.cs" />
|
||||
<Compile Include="Support\Timer.cs" />
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace OpenRA.FileFormats
|
||||
public interface IFolder
|
||||
{
|
||||
Stream GetContent(string filename);
|
||||
bool Exists(string filename);
|
||||
IEnumerable<uint> AllFileHashes();
|
||||
}
|
||||
|
||||
@@ -143,6 +144,11 @@ namespace OpenRA.FileFormats
|
||||
{
|
||||
return index.Keys;
|
||||
}
|
||||
|
||||
public bool Exists(string filename)
|
||||
{
|
||||
return index.ContainsKey(PackageEntry.HashFilename(filename));
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
|
||||
@@ -56,6 +56,11 @@ namespace OpenRA.FileFormats
|
||||
public static U AsSecond(Pair<T, U> p) { return p.Second; }
|
||||
|
||||
public Pair<U, T> Swap() { return Pair.New(Second, First); }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "({0},{1})".F(First, Second);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Pair
|
||||
|
||||
@@ -1,90 +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;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
// todo: ship most of this back to the Game assembly;
|
||||
// it was only in FileFormats due to the original server model,
|
||||
// in a sep. process.
|
||||
|
||||
public class Session
|
||||
{
|
||||
public List<Client> Clients = new List<Client>();
|
||||
public Global GlobalSettings = new Global();
|
||||
|
||||
public enum ClientState
|
||||
{
|
||||
NotReady,
|
||||
Ready
|
||||
}
|
||||
|
||||
public class Client
|
||||
{
|
||||
public int Index;
|
||||
public System.Drawing.Color Color1;
|
||||
public System.Drawing.Color Color2;
|
||||
public string Country;
|
||||
public int SpawnPoint;
|
||||
public string Name;
|
||||
public ClientState State;
|
||||
public int Team;
|
||||
}
|
||||
|
||||
public class Global
|
||||
{
|
||||
public string Map;
|
||||
public string[] Mods = { "ra" }; // mod names
|
||||
public int OrderLatency = 3;
|
||||
public int RandomSeed = 0;
|
||||
public bool LockTeams = false; // don't allow team changes after game start.
|
||||
public bool AllowCheats = false;
|
||||
}
|
||||
}
|
||||
|
||||
public class Manifest
|
||||
{
|
||||
public readonly string[]
|
||||
Folders, Packages, Rules,
|
||||
Sequences, Chrome, Assemblies, ChromeLayout,
|
||||
Weapons, Voices, Music, Movies, TileSets;
|
||||
|
||||
public readonly string ShellmapUid;
|
||||
|
||||
public Manifest(string[] mods)
|
||||
{
|
||||
var yaml = mods
|
||||
.Select(m => MiniYaml.FromFile("mods/" + m + "/mod.yaml"))
|
||||
.Aggregate(MiniYaml.Merge);
|
||||
|
||||
Folders = YamlList(yaml, "Folders");
|
||||
Packages = YamlList(yaml, "Packages");
|
||||
Rules = YamlList(yaml, "Rules");
|
||||
Sequences = YamlList(yaml, "Sequences");
|
||||
Chrome = YamlList(yaml, "Chrome");
|
||||
Assemblies = YamlList(yaml, "Assemblies");
|
||||
ChromeLayout = YamlList(yaml, "ChromeLayout");
|
||||
Weapons = YamlList(yaml, "Weapons");
|
||||
Voices = YamlList(yaml, "Voices");
|
||||
Music = YamlList(yaml, "Music");
|
||||
Movies = YamlList(yaml, "Movies");
|
||||
TileSets = YamlList(yaml, "TileSets");
|
||||
|
||||
ShellmapUid = yaml["ShellmapUid"].Value;
|
||||
}
|
||||
|
||||
static string[] YamlList(Dictionary<string, MiniYaml> ys, string key)
|
||||
{
|
||||
return ys.ContainsKey(key) ? ys[key].Nodes.Keys.ToArray() : new string[] { };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,8 @@ namespace OpenRA
|
||||
{
|
||||
public struct ChannelInfo
|
||||
{
|
||||
public bool Upload;
|
||||
public string Filename;
|
||||
public StreamWriter Writer;
|
||||
public bool Diff;
|
||||
}
|
||||
|
||||
public static class Log
|
||||
@@ -41,7 +39,7 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddChannel(string channelName, string filename, bool upload, bool diff)
|
||||
public static void AddChannel(string channelName, string filename)
|
||||
{
|
||||
if (channels.ContainsKey(channelName)) return;
|
||||
|
||||
@@ -52,7 +50,7 @@ namespace OpenRA
|
||||
{
|
||||
StreamWriter writer = File.CreateText(LogPathPrefix + filename);
|
||||
writer.AutoFlush = true;
|
||||
channels.Add(channelName, new ChannelInfo() { Upload = upload, Filename = filename, Writer = writer, Diff = diff });
|
||||
channels.Add(channelName, new ChannelInfo() { Filename = filename, Writer = writer });
|
||||
return;
|
||||
}
|
||||
catch (IOException) { filename = f + ".{0}".F(++i); }
|
||||
@@ -60,7 +58,7 @@ namespace OpenRA
|
||||
//if no logs exist, just make it
|
||||
StreamWriter w = File.CreateText(LogPathPrefix + filename);
|
||||
w.AutoFlush = true;
|
||||
channels.Add(channelName, new ChannelInfo() { Upload = upload, Filename = filename, Writer = w, Diff = diff });
|
||||
channels.Add(channelName, new ChannelInfo() { Filename = filename, Writer = w });
|
||||
|
||||
}
|
||||
|
||||
@@ -72,37 +70,5 @@ namespace OpenRA
|
||||
|
||||
info.Writer.WriteLine(format, args);
|
||||
}
|
||||
|
||||
public static void Upload(int gameId)
|
||||
{
|
||||
foreach (var kvp in channels.Where(x => x.Value.Upload))
|
||||
{
|
||||
kvp.Value.Writer.Close();
|
||||
var logfile = File.OpenRead(Log.LogPathPrefix + kvp.Value.Filename);
|
||||
byte[] fileContents = logfile.ReadAllBytes();
|
||||
var ms = new MemoryStream();
|
||||
|
||||
using (var gzip = new GZipStream(ms, CompressionMode.Compress, true))
|
||||
gzip.Write(fileContents, 0, fileContents.Length);
|
||||
|
||||
ms.Position = 0;
|
||||
byte[] buffer = ms.ReadAllBytes();
|
||||
|
||||
WebRequest request = WebRequest.Create("http://open-ra.org/logs/upload.php");
|
||||
request.ContentType = "application/x-gzip";
|
||||
request.ContentLength = buffer.Length;
|
||||
request.Method = "POST";
|
||||
request.Headers.Add("Game-ID", gameId.ToString());
|
||||
request.Headers.Add("Channel", kvp.Key);
|
||||
// request.Headers.Add("Diff", kvp.Value.Diff ? "1" : "0");
|
||||
|
||||
try
|
||||
{
|
||||
using (var requestStream = request.GetRequestStream())
|
||||
requestStream.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
catch (Exception){}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace OpenRA.FileFormats
|
||||
}
|
||||
}
|
||||
|
||||
static class TypeExts
|
||||
public static class TypeExts
|
||||
{
|
||||
public static IEnumerable<Type> BaseTypes( this Type t )
|
||||
{
|
||||
|
||||
@@ -21,8 +21,6 @@ namespace OpenRA
|
||||
{
|
||||
public class Actor
|
||||
{
|
||||
[Sync]
|
||||
readonly TypeDictionary traits = new TypeDictionary();
|
||||
public readonly ActorInfo Info;
|
||||
|
||||
public readonly World World;
|
||||
@@ -33,8 +31,8 @@ namespace OpenRA
|
||||
public Player Owner;
|
||||
|
||||
IActivity currentActivity;
|
||||
public Group Group;
|
||||
|
||||
public Group Group;
|
||||
|
||||
internal Actor(World world, string name, TypeDictionary initDict )
|
||||
{
|
||||
var init = new ActorInitializer( this, initDict );
|
||||
@@ -71,7 +69,7 @@ namespace OpenRA
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
{
|
||||
var wasIdle = currentActivity is Idle;
|
||||
while (currentActivity != null)
|
||||
{
|
||||
@@ -113,10 +111,13 @@ namespace OpenRA
|
||||
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<SelectableInfo>())
|
||||
.Where(a => a.Info.Traits.Contains<TargetableInfo>())
|
||||
.OrderByDescending(a => a.Info.Traits.Contains<SelectableInfo>() ? a.Info.Traits.Get<SelectableInfo>().Priority : int.MinValue)
|
||||
.FirstOrDefault();
|
||||
|
||||
@@ -144,7 +145,7 @@ namespace OpenRA
|
||||
return new RectangleF(loc.X, loc.Y, size.X, size.Y);
|
||||
}
|
||||
|
||||
public bool IsInWorld { get; set; }
|
||||
public bool IsInWorld { get; internal set; }
|
||||
|
||||
public void QueueActivity( IActivity nextActivity )
|
||||
{
|
||||
@@ -191,27 +192,39 @@ namespace OpenRA
|
||||
|
||||
public T Trait<T>()
|
||||
{
|
||||
return traits.Get<T>();
|
||||
return World.traitDict.Get<T>( this );
|
||||
}
|
||||
|
||||
public T TraitOrDefault<T>()
|
||||
{
|
||||
return traits.GetOrDefault<T>();
|
||||
return World.traitDict.GetOrDefault<T>( this );
|
||||
}
|
||||
|
||||
public IEnumerable<T> TraitsImplementing<T>()
|
||||
{
|
||||
return traits.WithInterface<T>();
|
||||
return World.traitDict.WithInterface<T>( this );
|
||||
}
|
||||
|
||||
public bool HasTrait<T>()
|
||||
{
|
||||
return traits.Contains<T>();
|
||||
return World.traitDict.Contains<T>( this );
|
||||
}
|
||||
|
||||
public void AddTrait( object t )
|
||||
public void AddTrait( object trait )
|
||||
{
|
||||
traits.Add( t );
|
||||
World.traitDict.AddTrait( this, trait );
|
||||
}
|
||||
|
||||
public bool Destroyed { get; private set; }
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
World.AddFrameEndTask( w =>
|
||||
{
|
||||
World.Remove( this );
|
||||
World.traitDict.RemoveActor( this );
|
||||
Destroyed = true;
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace OpenRA
|
||||
{
|
||||
if( player != null )
|
||||
return player;
|
||||
return world.players.First( x => x.Value.InternalName == PlayerName ).Value;
|
||||
return world.players.Values.First( x => x.InternalName == PlayerName );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace OpenRA.FileFormats
|
||||
{
|
||||
@@ -22,6 +23,9 @@ namespace OpenRA.FileFormats
|
||||
|
||||
public ActorReference( string type, Dictionary<string, MiniYaml> inits )
|
||||
{
|
||||
if (Rules.Info != null && !Rules.Info.ContainsKey(type))
|
||||
throw new InvalidDataException("Unknown actor: `{0}'".F(type));
|
||||
|
||||
Type = type;
|
||||
InitDict = new TypeDictionary();
|
||||
foreach( var i in inits )
|
||||
@@ -41,7 +45,7 @@ namespace OpenRA.FileFormats
|
||||
foreach( var init in InitDict )
|
||||
{
|
||||
var initName = init.GetType().Name;
|
||||
ret.Nodes.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,7 +17,7 @@ namespace OpenRA
|
||||
CursorSequence sequence;
|
||||
public Cursor(string cursor)
|
||||
{
|
||||
sequence = SequenceProvider.GetCursorSequence(cursor);
|
||||
sequence = CursorProvider.GetCursorSequence(cursor);
|
||||
}
|
||||
|
||||
public void Draw(int frame, float2 pos)
|
||||
|
||||
@@ -28,12 +28,15 @@ namespace OpenRA.Effects
|
||||
|
||||
public void Tick( World world )
|
||||
{
|
||||
if (--remainingTicks == 0)
|
||||
if (--remainingTicks == 0 || !target.IsInWorld)
|
||||
world.AddFrameEndTask(w => w.Remove(this));
|
||||
}
|
||||
|
||||
public IEnumerable<Renderable> Render()
|
||||
{
|
||||
if (!target.IsInWorld)
|
||||
yield break;
|
||||
|
||||
if (remainingTicks % 2 == 0)
|
||||
foreach (var r in target.Render())
|
||||
yield return r.WithPalette("highlight");
|
||||
|
||||
@@ -8,16 +8,12 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.FileFormats.Graphics;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Network;
|
||||
@@ -35,121 +31,27 @@ namespace OpenRA
|
||||
{
|
||||
public static readonly int CellSize = 24;
|
||||
|
||||
public static ModData modData;
|
||||
public static World world;
|
||||
public static Viewport viewport;
|
||||
public static UserSettings Settings;
|
||||
public static Settings Settings;
|
||||
|
||||
internal static OrderManager orderManager;
|
||||
|
||||
public static bool skipMakeAnims = true;
|
||||
|
||||
public static XRandom CosmeticRandom = new XRandom(); // not synced
|
||||
|
||||
public static Renderer Renderer;
|
||||
static int2 clientSize;
|
||||
static string mapName;
|
||||
public static Session LobbyInfo = new Session();
|
||||
static bool packageChangePending;
|
||||
static bool mapChangePending;
|
||||
static Pair<Assembly, string>[] ModAssemblies;
|
||||
|
||||
static void LoadModPackages()
|
||||
{
|
||||
FileSystem.UnmountAll();
|
||||
Timer.Time("reset: {0}");
|
||||
|
||||
foreach (var dir in Manifest.Folders) FileSystem.Mount(dir);
|
||||
foreach (var pkg in Manifest.Packages) FileSystem.Mount(pkg);
|
||||
|
||||
Timer.Time("mount temporary packages: {0}");
|
||||
}
|
||||
|
||||
public static void LoadModAssemblies(Manifest m)
|
||||
{
|
||||
// All the core namespaces
|
||||
var asms = typeof(Game).Assembly.GetNamespaces()
|
||||
.Select(c => Pair.New(typeof(Game).Assembly, c))
|
||||
.ToList();
|
||||
|
||||
// Namespaces from each mod assembly
|
||||
foreach (var a in m.Assemblies)
|
||||
{
|
||||
var asm = Assembly.LoadFile(Path.GetFullPath(a));
|
||||
asms.AddRange(asm.GetNamespaces().Select(ns => Pair.New(asm, ns)));
|
||||
}
|
||||
|
||||
ModAssemblies = asms.ToArray();
|
||||
}
|
||||
|
||||
public static Action<string> MissingTypeAction =
|
||||
s => { throw new InvalidOperationException("Cannot locate type: {0}".F(s)); };
|
||||
|
||||
public static 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;
|
||||
}
|
||||
|
||||
MissingTypeAction(classname);
|
||||
return default(T);
|
||||
}
|
||||
|
||||
public static Dictionary<string, MapStub> AvailableMaps;
|
||||
|
||||
// TODO: Do this nicer
|
||||
static Dictionary<string, MapStub> FindMaps(string[] mods)
|
||||
{
|
||||
var paths = new[] { "maps/" }.Concat(mods.Select(m => "mods/" + m + "/maps/"))
|
||||
.Where(p => Directory.Exists(p))
|
||||
.SelectMany(p => Directory.GetDirectories(p)).ToList();
|
||||
|
||||
return paths.Select(p => new MapStub(new Folder(p))).ToDictionary(m => m.Uid);
|
||||
}
|
||||
|
||||
static void ChangeMods()
|
||||
{
|
||||
Timer.Time("----ChangeMods");
|
||||
Manifest = new Manifest(LobbyInfo.GlobalSettings.Mods);
|
||||
Timer.Time("manifest: {0}");
|
||||
LoadModAssemblies(Manifest);
|
||||
SheetBuilder.Initialize();
|
||||
LoadModPackages();
|
||||
Timer.Time("load assemblies, packages: {0}");
|
||||
packageChangePending = false;
|
||||
}
|
||||
|
||||
public static Manifest Manifest;
|
||||
|
||||
static void LoadMap(string mapName)
|
||||
static void LoadMap(string uid)
|
||||
{
|
||||
Timer.Time("----LoadMap");
|
||||
SheetBuilder.Initialize();
|
||||
Manifest = new Manifest(LobbyInfo.GlobalSettings.Mods);
|
||||
Timer.Time("manifest: {0}");
|
||||
|
||||
if (!Game.AvailableMaps.ContainsKey(mapName))
|
||||
throw new InvalidDataException("Cannot find map with Uid {0}".F(mapName));
|
||||
|
||||
var map = new Map(Game.AvailableMaps[mapName].Package);
|
||||
|
||||
viewport = new Viewport(clientSize, map.TopLeft, map.BottomRight, Renderer);
|
||||
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.
|
||||
ChromeProvider.Initialize(Manifest.Chrome);
|
||||
Timer.Time("viewport, ChromeProvider: {0}");
|
||||
world = new World(Manifest, map);
|
||||
Timer.Time("world: {0}");
|
||||
|
||||
SequenceProvider.Initialize(Manifest.Sequences);
|
||||
Timer.Time("ChromeProv, SeqProv: {0}");
|
||||
|
||||
Timer.Time("----end LoadMap");
|
||||
Debug("Map change {0} -> {1}".F(Game.mapName, mapName));
|
||||
world = new World(modData.Manifest, map);
|
||||
}
|
||||
|
||||
|
||||
public static void MoveViewport(int2 loc)
|
||||
{
|
||||
viewport.Center(loc);
|
||||
@@ -167,7 +69,7 @@ namespace OpenRA
|
||||
|
||||
lastConnectionState = ConnectionState.PreConnecting;
|
||||
ConnectionStateChanged();
|
||||
|
||||
|
||||
orderManager = new OrderManager(new NetworkConnection(host, port), ChooseReplayFilename());
|
||||
}
|
||||
|
||||
@@ -180,7 +82,7 @@ namespace OpenRA
|
||||
{
|
||||
lastConnectionState = ConnectionState.PreConnecting;
|
||||
ConnectionStateChanged();
|
||||
|
||||
|
||||
if (orderManager != null) orderManager.Dispose();
|
||||
orderManager = new OrderManager(new EchoConnection());
|
||||
}
|
||||
@@ -195,94 +97,26 @@ namespace OpenRA
|
||||
internal static int RenderFrame = 0;
|
||||
internal static int LocalTick = 0;
|
||||
const int NetTickScale = 3; // 120ms net tick for 40ms local tick
|
||||
|
||||
static Queue<Pair<int, string>> syncReports = new Queue<Pair<int, string>>();
|
||||
const int numSyncReports = 5;
|
||||
|
||||
internal static void UpdateSyncReport()
|
||||
{
|
||||
if (!Settings.RecordSyncReports)
|
||||
return;
|
||||
|
||||
while (syncReports.Count >= numSyncReports) syncReports.Dequeue();
|
||||
syncReports.Enqueue(Pair.New(orderManager.FrameNumber, GenerateSyncReport()));
|
||||
}
|
||||
|
||||
static string GenerateSyncReport()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("Actors:");
|
||||
foreach (var a in 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 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));
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
internal static 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);
|
||||
}
|
||||
|
||||
public static event Action ConnectionStateChanged = () => { };
|
||||
static ConnectionState lastConnectionState = ConnectionState.PreConnecting;
|
||||
|
||||
public static int LocalClientId { get { return orderManager.Connection.LocalClientId; } }
|
||||
|
||||
static void Tick()
|
||||
{
|
||||
if (packageChangePending)
|
||||
{
|
||||
// TODO: Only do this on mod change
|
||||
Timer.Time("----begin maplist");
|
||||
AvailableMaps = FindMaps(LobbyInfo.GlobalSettings.Mods);
|
||||
Timer.Time("maplist: {0}");
|
||||
ChangeMods();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mapChangePending)
|
||||
{
|
||||
mapName = LobbyInfo.GlobalSettings.Map;
|
||||
mapChangePending = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (orderManager.Connection.ConnectionState != lastConnectionState)
|
||||
{
|
||||
lastConnectionState = orderManager.Connection.ConnectionState;
|
||||
ConnectionStateChanged();
|
||||
}
|
||||
|
||||
|
||||
int t = Environment.TickCount;
|
||||
int dt = t - lastTime;
|
||||
if (dt >= Settings.Timestep)
|
||||
if (dt >= Settings.Game.Timestep)
|
||||
{
|
||||
using (new PerfSample("tick_time"))
|
||||
{
|
||||
lastTime += Settings.Timestep;
|
||||
lastTime += Settings.Game.Timestep;
|
||||
Widget.DoTick(world);
|
||||
|
||||
orderManager.TickImmediate(world);
|
||||
@@ -326,33 +160,10 @@ namespace OpenRA
|
||||
|
||||
internal static void SyncLobbyInfo(string data)
|
||||
{
|
||||
var oldLobbyInfo = LobbyInfo;
|
||||
LobbyInfo = Session.Deserialize(data);
|
||||
|
||||
var session = new Session();
|
||||
session.GlobalSettings.Mods = Settings.InitialMods;
|
||||
|
||||
var ys = MiniYaml.FromString(data);
|
||||
foreach (var y in ys)
|
||||
{
|
||||
if (y.Key == "GlobalSettings")
|
||||
{
|
||||
FieldLoader.Load(session.GlobalSettings, y.Value);
|
||||
continue;
|
||||
}
|
||||
|
||||
int index;
|
||||
if (!int.TryParse(y.Key, out index))
|
||||
continue; // not a player.
|
||||
|
||||
var client = new Session.Client();
|
||||
FieldLoader.Load(client, y.Value);
|
||||
session.Clients.Add(client);
|
||||
}
|
||||
|
||||
LobbyInfo = session;
|
||||
|
||||
if (!world.GameHasStarted)
|
||||
world.SharedRandom = new OpenRA.Thirdparty.Random(LobbyInfo.GlobalSettings.RandomSeed);
|
||||
if( !world.GameHasStarted )
|
||||
world.SharedRandom = new XRandom( LobbyInfo.GlobalSettings.RandomSeed );
|
||||
|
||||
if (orderManager.Connection.ConnectionState == ConnectionState.Connected)
|
||||
world.SetLocalPlayer(orderManager.Connection.LocalClientId);
|
||||
@@ -364,69 +175,31 @@ namespace OpenRA
|
||||
Debug("Order lag is now {0} frames.".F(LobbyInfo.GlobalSettings.OrderLatency));
|
||||
}
|
||||
|
||||
if (mapName != LobbyInfo.GlobalSettings.Map)
|
||||
mapChangePending = true;
|
||||
|
||||
if (string.Join(",", oldLobbyInfo.GlobalSettings.Mods)
|
||||
!= string.Join(",", LobbyInfo.GlobalSettings.Mods))
|
||||
{
|
||||
Debug("Mods list changed, reloading: {0}".F(string.Join(",", LobbyInfo.GlobalSettings.Mods)));
|
||||
packageChangePending = true;
|
||||
}
|
||||
|
||||
LobbyInfoChanged();
|
||||
}
|
||||
|
||||
public static void IssueOrder(Order o) { orderManager.IssueOrder(o); } /* avoid exposing the OM to mod code */
|
||||
|
||||
static void LoadShellMap(string map)
|
||||
{
|
||||
LoadMap(map);
|
||||
world.Queries = new World.AllQueries(world);
|
||||
|
||||
foreach (var gs in world.WorldActor.TraitsImplementing<IGameStarted>())
|
||||
gs.GameStarted(world);
|
||||
orderManager.StartGame();
|
||||
}
|
||||
|
||||
public static event Action OnGameStart = () => { };
|
||||
internal static void StartGame()
|
||||
public static event Action AfterGameStart = () => {};
|
||||
public static event Action BeforeGameStart = () => {};
|
||||
internal static void StartGame(string map)
|
||||
{
|
||||
LoadMap(LobbyInfo.GlobalSettings.Map);
|
||||
BeforeGameStart();
|
||||
LoadMap(map);
|
||||
if (orderManager.GameStarted) return;
|
||||
Widget.SelectedWidget = null;
|
||||
|
||||
world.Queries = new World.AllQueries(world);
|
||||
|
||||
foreach (var gs in world.WorldActor.TraitsImplementing<IGameStarted>())
|
||||
gs.GameStarted(world);
|
||||
|
||||
viewport.GoToStartLocation(world.LocalPlayer);
|
||||
orderManager.StartGame();
|
||||
OnGameStart();
|
||||
}
|
||||
|
||||
public static Stance ChooseInitialStance(Player p, Player q)
|
||||
{
|
||||
if (p == q) return Stance.Ally;
|
||||
|
||||
// Hack: All map players are neutral wrt everyone else
|
||||
if (p.Index < 0 || q.Index < 0) return Stance.Neutral;
|
||||
|
||||
var pc = GetClientForPlayer(p);
|
||||
var qc = GetClientForPlayer(q);
|
||||
|
||||
return pc.Team != 0 && pc.Team == qc.Team
|
||||
? Stance.Ally : Stance.Enemy;
|
||||
}
|
||||
|
||||
static Session.Client GetClientForPlayer(Player p)
|
||||
{
|
||||
return LobbyInfo.Clients.Single(c => c.Index == p.Index);
|
||||
viewport.RefreshPalette();
|
||||
AfterGameStart();
|
||||
}
|
||||
|
||||
public static void DispatchMouseInput(MouseInputEvent ev, MouseEventArgs e, Modifiers modifierKeys)
|
||||
{
|
||||
if (world == null)
|
||||
return;
|
||||
|
||||
int sync = world.SyncHash();
|
||||
var initialWorld = world;
|
||||
|
||||
@@ -438,12 +211,12 @@ namespace OpenRA
|
||||
Modifiers = modifierKeys,
|
||||
};
|
||||
Widget.HandleInput(world, mi);
|
||||
|
||||
|
||||
if (sync != world.SyncHash() && world == initialWorld)
|
||||
throw new InvalidOperationException("Desync in DispatchMouseInput");
|
||||
}
|
||||
|
||||
internal static bool IsHost
|
||||
public static bool IsHost
|
||||
{
|
||||
get { return orderManager.Connection.LocalClientId == 0; }
|
||||
}
|
||||
@@ -455,8 +228,11 @@ namespace OpenRA
|
||||
|
||||
public static void HandleKeyEvent(KeyInput e)
|
||||
{
|
||||
if (world == null)
|
||||
return;
|
||||
|
||||
int sync = world.SyncHash();
|
||||
|
||||
|
||||
if (Widget.HandleKeyPress(e))
|
||||
return;
|
||||
|
||||
@@ -468,67 +244,39 @@ namespace OpenRA
|
||||
public static Modifiers GetModifierKeys() { return modifiers; }
|
||||
public static void HandleModifierKeys(Modifiers mods) { modifiers = mods; }
|
||||
|
||||
static Size GetResolution(Settings settings, WindowMode windowmode)
|
||||
{
|
||||
var desktopResolution = Screen.PrimaryScreen.Bounds.Size;
|
||||
var customSize = (windowmode == WindowMode.Windowed) ? Settings.WindowedSize : Settings.FullscreenSize;
|
||||
|
||||
if (customSize.X > 0 && customSize.Y > 0)
|
||||
{
|
||||
desktopResolution.Width = customSize.X;
|
||||
desktopResolution.Height = customSize.Y;
|
||||
}
|
||||
return new Size(
|
||||
desktopResolution.Width,
|
||||
desktopResolution.Height);
|
||||
}
|
||||
|
||||
internal static void Initialize(Settings settings)
|
||||
internal static void Initialize(Arguments args)
|
||||
{
|
||||
AppDomain.CurrentDomain.AssemblyResolve += FileSystem.ResolveAssembly;
|
||||
|
||||
|
||||
var defaultSupport = Environment.GetFolderPath(Environment.SpecialFolder.Personal)
|
||||
+ Path.DirectorySeparatorChar + "OpenRA";
|
||||
|
||||
SupportDir = settings.GetValue("SupportDir", defaultSupport);
|
||||
Settings = new UserSettings(settings);
|
||||
|
||||
|
||||
SupportDir = args.GetValue("SupportDir", defaultSupport);
|
||||
Settings = new Settings(SupportDir + "settings.yaml", args);
|
||||
|
||||
Log.LogPath = SupportDir + "Logs" + Path.DirectorySeparatorChar;
|
||||
Log.AddChannel("perf", "perf.log", false, false);
|
||||
Log.AddChannel("debug", "debug.log", false, false);
|
||||
Log.AddChannel("sync", "syncreport.log", true, true);
|
||||
|
||||
LobbyInfo.GlobalSettings.Mods = Settings.InitialMods;
|
||||
Manifest = new Manifest(LobbyInfo.GlobalSettings.Mods);
|
||||
|
||||
// Load the default mod to access required files
|
||||
LoadModPackages();
|
||||
|
||||
Renderer.SheetSize = Settings.SheetSize;
|
||||
|
||||
var resolution = GetResolution(settings, Game.Settings.WindowMode);
|
||||
Renderer = new Renderer(resolution, Game.Settings.WindowMode);
|
||||
resolution = Renderer.Resolution;
|
||||
clientSize = new int2(resolution);
|
||||
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 );
|
||||
|
||||
Sound.Initialize();
|
||||
PerfHistory.items["render"].hasNormalTick = false;
|
||||
PerfHistory.items["batches"].hasNormalTick = false;
|
||||
PerfHistory.items["text"].hasNormalTick = false;
|
||||
PerfHistory.items["cursor"].hasNormalTick = false;
|
||||
AvailableMaps = FindMaps(LobbyInfo.GlobalSettings.Mods);
|
||||
|
||||
ChangeMods();
|
||||
|
||||
if (Settings.Replay != null)
|
||||
orderManager = new OrderManager(new ReplayConnection(Settings.Replay));
|
||||
else
|
||||
JoinLocal();
|
||||
|
||||
LoadShellMap(Manifest.ShellmapUid);
|
||||
PerfHistory.items["cursor"].hasNormalTick = false;
|
||||
|
||||
|
||||
JoinLocal();
|
||||
StartGame(modData.Manifest.ShellmapUid);
|
||||
|
||||
|
||||
ResetTimer();
|
||||
}
|
||||
|
||||
@@ -542,10 +290,10 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
AddChatLine(Color.White, "Debug", s);
|
||||
@@ -553,67 +301,38 @@ namespace OpenRA
|
||||
|
||||
public static void Disconnect()
|
||||
{
|
||||
var shellmap = Manifest.ShellmapUid;
|
||||
orderManager.Dispose();
|
||||
var shellmap = modData.Manifest.ShellmapUid;
|
||||
LobbyInfo.GlobalSettings.Mods = Settings.InitialMods;
|
||||
LobbyInfo = new Session();
|
||||
LobbyInfo.GlobalSettings.Mods = Settings.Game.Mods;
|
||||
LoadShellMap(shellmap);
|
||||
JoinLocal();
|
||||
StartGame(shellmap);
|
||||
|
||||
Widget.RootWidget.CloseWindow();
|
||||
Widget.RootWidget.OpenWindow("MAINMENU_BG");
|
||||
|
||||
}
|
||||
|
||||
static string baseSupportDir = null;
|
||||
public static string SupportDir
|
||||
set {
|
||||
{
|
||||
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);
|
||||
|
||||
}
|
||||
get {return baseSupportDir;}
|
||||
}
|
||||
|
||||
|
||||
internal static int GetGameId()
|
||||
{
|
||||
try
|
||||
{
|
||||
string s = File.ReadAllText(SupportDir + "currentgameid");
|
||||
return int.Parse(s);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return 0;
|
||||
baseSupportDir = dir + Path.DirectorySeparatorChar;
|
||||
}
|
||||
get { return baseSupportDir; }
|
||||
}
|
||||
internal static void SetGameId(int id)
|
||||
|
||||
public static T CreateObject<T>( string name )
|
||||
var file = File.CreateText(SupportDir + "currentgameid");
|
||||
file.Write(id);
|
||||
file.Flush();
|
||||
file.Close();
|
||||
}
|
||||
|
||||
public static void InitializeEngineWithMods(string[] mods)
|
||||
{
|
||||
AppDomain.CurrentDomain.AssemblyResolve += FileSystem.ResolveAssembly;
|
||||
Manifest = new Manifest(mods);
|
||||
LoadModAssemblies(Manifest);
|
||||
|
||||
FileSystem.UnmountAll();
|
||||
foreach (var folder in Manifest.Folders) FileSystem.Mount(folder);
|
||||
foreach (var pkg in Manifest.Packages) FileSystem.Mount(pkg);
|
||||
|
||||
Rules.LoadRules(Manifest, new Map());
|
||||
{
|
||||
return modData.ObjectCreator.CreateObject<T>( name );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,27 +19,22 @@ namespace OpenRA
|
||||
public class ActorInfo
|
||||
{
|
||||
public readonly string Name;
|
||||
public readonly string Category;
|
||||
public readonly TypeDictionary Traits = new TypeDictionary();
|
||||
|
||||
public ActorInfo( string name, MiniYaml node, Dictionary<string, MiniYaml> allUnits )
|
||||
{
|
||||
var mergedNode = MergeWithParent( node, allUnits ).Nodes;
|
||||
var mergedNode = MergeWithParent( node, allUnits ).NodesDict;
|
||||
|
||||
Name = name;
|
||||
MiniYaml categoryNode;
|
||||
if( mergedNode.TryGetValue( "Category", out categoryNode ) )
|
||||
Category = categoryNode.Value;
|
||||
|
||||
foreach( var t in mergedNode )
|
||||
if( t.Key != "Inherits" && t.Key != "Category" && !t.Key.StartsWith("-") )
|
||||
if( t.Key != "Inherits" && !t.Key.StartsWith("-") )
|
||||
Traits.Add( LoadTraitInfo( t.Key.Split('@')[0], t.Value ) );
|
||||
}
|
||||
|
||||
static MiniYaml GetParent( MiniYaml node, Dictionary<string, MiniYaml> allUnits )
|
||||
{
|
||||
MiniYaml inherits;
|
||||
node.Nodes.TryGetValue( "Inherits", out inherits );
|
||||
node.NodesDict.TryGetValue( "Inherits", out inherits );
|
||||
if( inherits == null || string.IsNullOrEmpty( inherits.Value ) )
|
||||
return null;
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@ namespace OpenRA.GameRules
|
||||
{
|
||||
public class MusicInfo
|
||||
{
|
||||
public readonly string Filename = null;
|
||||
public readonly string Title = null;
|
||||
public readonly int Length = 0; // seconds
|
||||
[FieldLoader.Load] public readonly string Filename = null;
|
||||
[FieldLoader.Load] public readonly string Title = null;
|
||||
[FieldLoader.Load] public readonly int Length = 0; // seconds
|
||||
|
||||
public MusicInfo( string key, MiniYaml value )
|
||||
{
|
||||
|
||||
@@ -30,10 +30,10 @@ namespace OpenRA
|
||||
public static void LoadRules(Manifest m, Map map)
|
||||
{
|
||||
Info = LoadYamlRules(m.Rules, map.Rules, (k, y) => new ActorInfo(k.Key.ToLowerInvariant(), k.Value, y));
|
||||
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, map.Music, (k, _) => new MusicInfo(k.Key, k.Value));
|
||||
Movies = LoadYamlRules(m.Movies, new Dictionary<string,MiniYaml>(), (k, v) => k.Value.Value);
|
||||
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));
|
||||
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);
|
||||
|
||||
TileSets = new Dictionary<string, TileSet>();
|
||||
foreach (var file in m.TileSets)
|
||||
@@ -45,15 +45,11 @@ namespace OpenRA
|
||||
TechTree = new TechTree();
|
||||
}
|
||||
|
||||
static Dictionary<string, T> LoadYamlRules<T>(string[] files, Dictionary<string,MiniYaml>dict, Func<KeyValuePair<string, MiniYaml>, Dictionary<string, MiniYaml>, T> f)
|
||||
static Dictionary<string, T> LoadYamlRules<T>(string[] files, List<MiniYamlNode> dict, Func<MiniYamlNode, Dictionary<string, MiniYaml>, T> f)
|
||||
{
|
||||
var y = files.Select(a => MiniYaml.FromFile(a)).Aggregate(dict,MiniYaml.Merge);
|
||||
return y.ToDictionary(kv => kv.Key.ToLowerInvariant(), kv => f(kv, y));
|
||||
}
|
||||
|
||||
public static IEnumerable<string> Categories()
|
||||
{
|
||||
return Info.Values.Select( x => x.Category ).Distinct().Where( g => g != null ).ToList();
|
||||
var yy = y.ToDictionary( x => x.Key, x => x.Value );
|
||||
return y.ToDictionary(kv => kv.Key.ToLowerInvariant(), kv => f(kv, yy));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
156
OpenRA.Game/GameRules/Settings.cs
Executable file
156
OpenRA.Game/GameRules/Settings.cs
Executable file
@@ -0,0 +1,156 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.FileFormats.Graphics;
|
||||
using System;
|
||||
|
||||
namespace OpenRA.GameRules
|
||||
{
|
||||
public class ServerSettings
|
||||
{
|
||||
public string Name = "OpenRA Game";
|
||||
public int ListenPort = 1234;
|
||||
public int ExternalPort = 1234;
|
||||
public bool AdvertiseOnline = true;
|
||||
public string MasterServer = "http://open-ra.org/master/";
|
||||
public bool AllowCheats = false;
|
||||
}
|
||||
|
||||
public class DebugSettings
|
||||
{
|
||||
public bool PerfGraph = false;
|
||||
public bool RecordSyncReports = true;
|
||||
public bool ShowCollisions = false;
|
||||
}
|
||||
|
||||
public class GraphicSettings
|
||||
{
|
||||
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 readonly int2 MinResolution = new int2(800, 600);
|
||||
}
|
||||
|
||||
public class SoundSettings
|
||||
{
|
||||
public float SoundVolume = 0.5f;
|
||||
public float MusicVolume = 0.5f;
|
||||
public float VideoVolume = 0.5f;
|
||||
}
|
||||
|
||||
public class PlayerSettings
|
||||
{
|
||||
public string Name = "Newbie";
|
||||
public Color Color1 = Color.FromArgb(255,160,238);
|
||||
public Color Color2 = Color.FromArgb(68,0,56);
|
||||
public string LastServer = "localhost:1234";
|
||||
}
|
||||
|
||||
public class GameSettings
|
||||
{
|
||||
public string[] Mods = { "ra" };
|
||||
public bool MatchTimer = true;
|
||||
|
||||
// Behaviour settings
|
||||
public bool ViewportEdgeScroll = true;
|
||||
public bool InverseDragScroll = false;
|
||||
|
||||
// Internal game settings
|
||||
public int Timestep = 40;
|
||||
public int SheetSize = 2048;
|
||||
}
|
||||
|
||||
public class Settings
|
||||
{
|
||||
string SettingsFile;
|
||||
|
||||
public PlayerSettings Player = new PlayerSettings();
|
||||
public GameSettings Game = new GameSettings();
|
||||
public SoundSettings Sound = new SoundSettings();
|
||||
public GraphicSettings Graphics = new GraphicSettings();
|
||||
public ServerSettings Server = new ServerSettings();
|
||||
public DebugSettings Debug = new DebugSettings();
|
||||
|
||||
Dictionary<string, object> Sections;
|
||||
public Settings(string file, Arguments args)
|
||||
{
|
||||
SettingsFile = file;
|
||||
Sections = new Dictionary<string, object>()
|
||||
{
|
||||
{"Player", Player},
|
||||
{"Game", Game},
|
||||
{"Sound", Sound},
|
||||
{"Graphics", Graphics},
|
||||
{"Server", Server},
|
||||
{"Debug", Debug}
|
||||
};
|
||||
|
||||
// Override fieldloader to ignore invalid entries
|
||||
var err1 = FieldLoader.UnknownFieldAction;
|
||||
var err2 = FieldLoader.InvalidValueAction;
|
||||
|
||||
FieldLoader.UnknownFieldAction = (s,f) =>
|
||||
{
|
||||
System.Console.WriteLine( "Ignoring unknown field `{0}` on `{1}`".F( s, f.Name ) );
|
||||
};
|
||||
|
||||
if (File.Exists(SettingsFile))
|
||||
{
|
||||
System.Console.WriteLine("Loading settings file {0}",SettingsFile);
|
||||
var yaml = MiniYaml.DictFromFile(SettingsFile);
|
||||
|
||||
foreach (var kv in Sections)
|
||||
if (yaml.ContainsKey(kv.Key))
|
||||
LoadSectionYaml(yaml[kv.Key], kv.Value);
|
||||
}
|
||||
|
||||
// Override with commandline args
|
||||
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.UnknownFieldAction = err1;
|
||||
FieldLoader.InvalidValueAction = err2;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var root = new List<MiniYamlNode>();
|
||||
foreach( var kv in Sections )
|
||||
root.Add( new MiniYamlNode( kv.Key, SectionYaml( kv.Value ) ) );
|
||||
|
||||
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());
|
||||
FieldLoader.InvalidValueAction = (s,t,f) =>
|
||||
{
|
||||
object ret = defaults.GetType().GetField(f).GetValue(defaults);
|
||||
System.Console.WriteLine("FieldLoader: Cannot parse `{0}` into `{2}:{1}`; substituting default `{3}`".F(s,t.Name,f,ret) );
|
||||
return ret;
|
||||
};
|
||||
|
||||
FieldLoader.Load(section, yaml);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,9 +39,9 @@ namespace OpenRA.GameRules
|
||||
foreach( var b in player.World.Queries.OwnedBy[player].Where( x=>x.Info.Traits.Contains<BuildingInfo>() ) )
|
||||
{
|
||||
ret[ b.Info.Name ].Add( b );
|
||||
var buildable = b.Info.Traits.GetOrDefault<BuildableInfo>();
|
||||
if( buildable != null )
|
||||
foreach( var alt in buildable.AlternateName )
|
||||
var tt = b.Info.Traits.GetOrDefault<TooltipInfo>();
|
||||
if( tt != null )
|
||||
foreach( var alt in tt.AlternateName )
|
||||
ret[ alt ].Add( b );
|
||||
}
|
||||
return ret;
|
||||
@@ -62,7 +62,7 @@ namespace OpenRA.GameRules
|
||||
if( playerBuildings[ p ].Count == 0 )
|
||||
return false;
|
||||
|
||||
if( producesIndex[ info.Category ].All( x => playerBuildings[ x.Name ].Count == 0 ) )
|
||||
if( producesIndex[ bi.Queue ].All( x => playerBuildings[ x.Name ].Count == 0 ) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -83,17 +83,18 @@ namespace OpenRA.GameRules
|
||||
{
|
||||
return Rules.Info.Values
|
||||
.Where( x => x.Name[ 0 ] != '^' )
|
||||
.Where( x => categories.Contains( x.Category ) )
|
||||
.Where( x => x.Traits.Contains<BuildableInfo>() );
|
||||
.Where( x => x.Traits.Contains<BuildableInfo>() )
|
||||
.Where( x => categories.Contains(x.Traits.Get<BuildableInfo>().Queue) );
|
||||
}
|
||||
|
||||
public IEnumerable<ActorInfo> UnitBuiltAt( ActorInfo info )
|
||||
{
|
||||
var builtAt = info.Traits.Get<BuildableInfo>().BuiltAt;
|
||||
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[ info.Category ];
|
||||
return producesIndex[ bi.Queue ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,112 +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.Drawing;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.FileFormats.Graphics;
|
||||
|
||||
namespace OpenRA.GameRules
|
||||
{
|
||||
public class UserSettings
|
||||
{
|
||||
// Behaviour settings
|
||||
public bool ViewportEdgeScroll = true;
|
||||
|
||||
// Debug settings
|
||||
public bool PerfDebug = false;
|
||||
public bool RecordSyncReports = true;
|
||||
public bool ShowGameTimer = true;
|
||||
public bool DeveloperMode = false;
|
||||
public bool UnitDebug = false;
|
||||
|
||||
// Window settings
|
||||
public WindowMode WindowMode = WindowMode.PseudoFullscreen;
|
||||
public int2 FullscreenSize = new int2(Screen.PrimaryScreen.Bounds.Width,Screen.PrimaryScreen.Bounds.Height);
|
||||
public int2 WindowedSize = new int2(1024,768);
|
||||
public readonly static int2 MinResolution = new int2(800, 600);
|
||||
|
||||
//Sound Settings
|
||||
public float SoundVolume = 0.5f;
|
||||
public float MusicVolume = 0.5f;
|
||||
public float VideoVolume = 0.5f;
|
||||
public bool MusicPlayer = false;
|
||||
|
||||
// Internal game settings
|
||||
public int Timestep = 40;
|
||||
public int SheetSize = 2048;
|
||||
|
||||
// External game settings
|
||||
public string LastServer = "localhost:1234";
|
||||
public string Replay = null;
|
||||
public string PlayerName = "Newbie";
|
||||
public Color PlayerColor1 = Color.FromArgb(255,160,238);
|
||||
public Color PlayerColor2 = Color.FromArgb(68,0,56);
|
||||
|
||||
public string[] InitialMods = { "ra" };
|
||||
|
||||
// Server settings
|
||||
public string LastServerTitle = "OpenRA Game";
|
||||
public int ListenPort = 1234;
|
||||
public int ExternalPort = 1234;
|
||||
public bool AdvertiseOnline = true;
|
||||
public string MasterServer = "http://open-ra.org/master/";
|
||||
public bool AllowCheats = false;
|
||||
|
||||
string SettingsFile;
|
||||
UserSettings defaults;
|
||||
|
||||
public UserSettings() {}
|
||||
public UserSettings(Settings args)
|
||||
{
|
||||
defaults = new UserSettings();
|
||||
SettingsFile = Game.SupportDir + "settings.yaml";
|
||||
|
||||
// Override settings loading to not crash
|
||||
var err1 = FieldLoader.UnknownFieldAction;
|
||||
var err2 = FieldLoader.InvalidValueAction;
|
||||
|
||||
FieldLoader.InvalidValueAction = (s,t,f) =>
|
||||
{
|
||||
object ret = defaults.GetType().GetField(f).GetValue(defaults);
|
||||
System.Console.WriteLine("FieldLoader: Cannot parse `{0}` into `{2}:{1}`; substituting default `{3}`".F(s,t.Name,f,ret) );
|
||||
return ret;
|
||||
};
|
||||
|
||||
FieldLoader.UnknownFieldAction = (s,f) =>
|
||||
{
|
||||
System.Console.WriteLine( "Ignoring unknown field `{0}` on `{1}`".F( s, f.Name ) );
|
||||
};
|
||||
|
||||
if (File.Exists(SettingsFile))
|
||||
{
|
||||
System.Console.WriteLine("Loading settings file {0}",SettingsFile);
|
||||
var yaml = MiniYaml.FromFile(SettingsFile);
|
||||
FieldLoader.Load(this, yaml["Settings"]);
|
||||
}
|
||||
|
||||
foreach (var f in this.GetType().GetFields())
|
||||
if (args.Contains(f.Name))
|
||||
OpenRA.FileFormats.FieldLoader.LoadField( this, f.Name, args.GetValue(f.Name, "") );
|
||||
|
||||
FieldLoader.UnknownFieldAction = err1;
|
||||
FieldLoader.InvalidValueAction = err2;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
Dictionary<string, MiniYaml> root = new Dictionary<string, MiniYaml>();
|
||||
root.Add("Settings", FieldSaver.SaveDifferences(this, defaults));
|
||||
root.WriteToFile(SettingsFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,17 +20,22 @@ namespace OpenRA.GameRules
|
||||
public readonly Dictionary<string,string[]> Variants;
|
||||
public readonly Dictionary<string,string[]> Voices;
|
||||
public readonly string DefaultVariant = ".aud" ;
|
||||
public readonly string[] DisableVariants = { };
|
||||
|
||||
Func<MiniYaml, string, Dictionary<string, string[]>> Load = (y,name) => (y.Nodes.ContainsKey(name))? y.Nodes[name].Nodes.ToDictionary(a => a.Key,
|
||||
a => (string[])FieldLoader.GetValue( "(value)", typeof(string[]), a.Value.Value ))
|
||||
: new Dictionary<string, string[]>();
|
||||
[FieldLoader.Load] public readonly string[] DisableVariants = { };
|
||||
|
||||
static Dictionary<string, string[]> Load( MiniYaml y, string name )
|
||||
{
|
||||
return y.NodesDict.ContainsKey( name )
|
||||
? y.NodesDict[ name ].NodesDict.ToDictionary(
|
||||
a => a.Key,
|
||||
a => (string[])FieldLoader.GetValue( "(value)", typeof( string[] ), a.Value.Value ) )
|
||||
: new Dictionary<string, string[]>();
|
||||
}
|
||||
|
||||
public readonly Lazy<Dictionary<string, VoicePool>> Pools;
|
||||
|
||||
public VoiceInfo( MiniYaml y )
|
||||
{
|
||||
FieldLoader.LoadFields(this, y.Nodes, new string[] { "DisableVariants" });
|
||||
FieldLoader.Load( this, y );
|
||||
Variants = Load(y, "Variants");
|
||||
Voices = Load(y, "Voices");
|
||||
|
||||
|
||||
@@ -12,42 +12,54 @@ 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
|
||||
|
||||
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
|
||||
{
|
||||
@@ -65,55 +77,48 @@ namespace OpenRA.GameRules
|
||||
public Target target;
|
||||
public int2 dest;
|
||||
public int destAltitude;
|
||||
public float firepowerModifier = 1.0f;
|
||||
}
|
||||
|
||||
public interface IProjectileInfo { IEffect Create(ProjectileArgs args); }
|
||||
|
||||
public class WeaponInfo
|
||||
{
|
||||
public readonly float Range = 0;
|
||||
public readonly string Report = null;
|
||||
public readonly int ROF = 1;
|
||||
public readonly int Burst = 1;
|
||||
public readonly bool Charges = false;
|
||||
public readonly bool Underwater = false;
|
||||
public readonly string[] ValidTargets = { "Ground" };
|
||||
public readonly int BurstDelay = 5;
|
||||
[FieldLoader.Load] public readonly float Range = 0;
|
||||
[FieldLoader.Load] public readonly string Report = null;
|
||||
[FieldLoader.Load] public readonly int ROF = 1;
|
||||
[FieldLoader.Load] public readonly int Burst = 1;
|
||||
[FieldLoader.Load] public readonly bool Charges = false;
|
||||
[FieldLoader.Load] public readonly bool Underwater = false;
|
||||
[FieldLoader.Load] public readonly string[] ValidTargets = { "Ground" };
|
||||
[FieldLoader.Load] public readonly int BurstDelay = 5;
|
||||
|
||||
public IProjectileInfo Projectile;
|
||||
public List<WarheadInfo> Warheads = new List<WarheadInfo>();
|
||||
[FieldLoader.LoadUsing( "LoadProjectile" )] public IProjectileInfo Projectile;
|
||||
[FieldLoader.LoadUsing( "LoadWarheads" )] public List<WarheadInfo> Warheads;
|
||||
|
||||
public WeaponInfo(string name, MiniYaml content)
|
||||
{
|
||||
foreach (var kv in content.Nodes)
|
||||
{
|
||||
var key = kv.Key.Split('@')[0];
|
||||
switch (key)
|
||||
{
|
||||
case "Range": FieldLoader.LoadField(this, "Range", content.Nodes["Range"].Value); break;
|
||||
case "ROF": FieldLoader.LoadField(this, "ROF", content.Nodes["ROF"].Value); break;
|
||||
case "Report": FieldLoader.LoadField(this, "Report", content.Nodes["Report"].Value); break;
|
||||
case "Burst": FieldLoader.LoadField(this, "Burst", content.Nodes["Burst"].Value); break;
|
||||
case "Charges": FieldLoader.LoadField(this, "Charges", content.Nodes["Charges"].Value); break;
|
||||
case "ValidTargets": FieldLoader.LoadField(this, "ValidTargets", content.Nodes["ValidTargets"].Value); break;
|
||||
case "Underwater": FieldLoader.LoadField(this, "Underwater", content.Nodes["Underwater"].Value); break;
|
||||
case "BurstDelay": FieldLoader.LoadField(this, "BurstDelay", content.Nodes["BurstDelay"].Value); break;
|
||||
FieldLoader.Load( this, content );
|
||||
}
|
||||
|
||||
case "Warhead":
|
||||
{
|
||||
var warhead = new WarheadInfo();
|
||||
FieldLoader.Load(warhead, kv.Value);
|
||||
Warheads.Add(warhead);
|
||||
} break;
|
||||
static object LoadProjectile( MiniYaml yaml )
|
||||
{
|
||||
MiniYaml proj;
|
||||
if( !yaml.NodesDict.TryGetValue( "Projectile", out proj ) )
|
||||
return null;
|
||||
var ret = Game.CreateObject<IProjectileInfo>( proj.Value + "Info" );
|
||||
FieldLoader.Load( ret, proj );
|
||||
return ret;
|
||||
}
|
||||
|
||||
// in this case, it's an implementation of IProjectileInfo
|
||||
default:
|
||||
{
|
||||
Projectile = Game.CreateObject<IProjectileInfo>(key + "Info");
|
||||
FieldLoader.Load(Projectile, kv.Value);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
static object LoadWarheads( MiniYaml yaml )
|
||||
{
|
||||
var ret = new List<WarheadInfo>();
|
||||
foreach( var w in yaml.Nodes )
|
||||
if( w.Key.Split( '@' )[ 0 ] == "Warhead" )
|
||||
ret.Add( new WarheadInfo( w.Value ) );
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
public CursorSequence(string cursorSrc, string palette, XmlElement e)
|
||||
{
|
||||
sprites = CursorSheetBuilder.LoadAllSprites(cursorSrc);
|
||||
sprites = Game.modData.CursorSheetBuilder.LoadAllSprites(cursorSrc);
|
||||
|
||||
start = int.Parse(e.GetAttribute("start"));
|
||||
this.palette = palette;
|
||||
|
||||
@@ -14,25 +14,32 @@ using OpenRA.FileFormats;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
static class CursorSheetBuilder
|
||||
public class CursorSheetBuilder
|
||||
{
|
||||
static Cache<string, Sprite[]> cursors = new Cache<string, Sprite[]>(LoadCursors);
|
||||
static readonly string[] exts = { ".shp" };
|
||||
ModData modData;
|
||||
Cache<string, Sprite[]> cursors;
|
||||
readonly string[] exts = { ".shp" };
|
||||
|
||||
static Sprite[] LoadCursors(string filename)
|
||||
public CursorSheetBuilder( ModData modData )
|
||||
{
|
||||
this.modData = modData;
|
||||
this.cursors = new Cache<string, Sprite[]>( LoadCursors );
|
||||
}
|
||||
|
||||
Sprite[] LoadCursors(string filename)
|
||||
{
|
||||
try
|
||||
{
|
||||
var shp = new Dune2ShpReader(FileSystem.OpenWithExts(filename, exts));
|
||||
return shp.Select(a => SheetBuilder.SharedInstance.Add(a.Image, a.Size)).ToArray();
|
||||
return shp.Select(a => modData.SheetBuilder.Add(a.Image, a.Size)).ToArray();
|
||||
}
|
||||
catch (IndexOutOfRangeException) // This will occur when loading a custom (RA-format) .shp
|
||||
{
|
||||
var shp = new ShpReader(FileSystem.OpenWithExts(filename, exts));
|
||||
return shp.Select(a => SheetBuilder.SharedInstance.Add(a.Image, shp.Size)).ToArray();
|
||||
return shp.Select(a => modData.SheetBuilder.Add(a.Image, shp.Size)).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static Sprite[] LoadAllSprites(string filename) { return cursors[filename]; }
|
||||
public Sprite[] LoadAllSprites(string filename) { return cursors[filename]; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,10 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
using OpenRA.FileFormats.Graphics;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.FileFormats.Graphics;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
foreach (var t in world.Queries.WithTraitMultiple<IRadarSignature>())
|
||||
{
|
||||
if (!t.Actor.IsVisible())
|
||||
if (!t.Actor.IsVisible(world.LocalPlayer))
|
||||
continue;
|
||||
|
||||
var color = t.Trait.RadarSignatureColor(t.Actor);
|
||||
|
||||
@@ -15,6 +15,7 @@ using System.Reflection;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.FileFormats.Graphics;
|
||||
using OpenRA.Support;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
@@ -22,8 +23,6 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
internal static int SheetSize;
|
||||
|
||||
readonly IGraphicsDevice device;
|
||||
|
||||
public IShader SpriteShader { get; private set; } /* note: shared shader params */
|
||||
public IShader LineShader { get; private set; }
|
||||
public IShader RgbaSpriteShader { get; private set; }
|
||||
@@ -38,12 +37,8 @@ namespace OpenRA.Graphics
|
||||
|
||||
public readonly SpriteFont RegularFont, BoldFont, TitleFont;
|
||||
|
||||
public Size Resolution { get { return device.WindowSize; } }
|
||||
|
||||
public Renderer(Size resolution, OpenRA.FileFormats.Graphics.WindowMode windowMode)
|
||||
public Renderer()
|
||||
{
|
||||
device = CreateDevice( Assembly.LoadFile( Path.GetFullPath( "OpenRA.Gl.dll" ) ), resolution.Width, resolution.Height, windowMode, false );
|
||||
|
||||
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"));
|
||||
@@ -59,23 +54,16 @@ namespace OpenRA.Graphics
|
||||
TitleFont = new SpriteFont("titles.ttf", 48);
|
||||
}
|
||||
|
||||
IGraphicsDevice CreateDevice( Assembly rendererDll, int width, int height, WindowMode window, bool vsync )
|
||||
{
|
||||
foreach( RendererAttribute r in rendererDll.GetCustomAttributes( typeof( RendererAttribute ), false ) )
|
||||
{
|
||||
return (IGraphicsDevice)r.Type.GetConstructor( new Type[] { typeof( int ), typeof( int ), typeof( WindowMode ), typeof( bool ) } )
|
||||
.Invoke( new object[] { width, height, window, vsync } );
|
||||
}
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IGraphicsDevice Device { get { return device; } }
|
||||
|
||||
public void BeginFrame(float2 r1, float2 r2, float2 scroll)
|
||||
public void BeginFrame(float2 scroll)
|
||||
{
|
||||
device.Begin();
|
||||
device.Clear(Color.Black);
|
||||
|
||||
float2 r1 = new float2(2f/Resolution.Width, -2f/Resolution.Height);
|
||||
float2 r2 = new float2(-1, 1);
|
||||
|
||||
SetShaderParams( SpriteShader, r1, r2, scroll );
|
||||
SetShaderParams( LineShader, r1, r2, scroll );
|
||||
SetShaderParams( RgbaSpriteShader, r1, r2, scroll );
|
||||
@@ -127,5 +115,44 @@ namespace OpenRA.Graphics
|
||||
RgbaSpriteRenderer.Flush();
|
||||
LineRenderer.Flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static IGraphicsDevice device;
|
||||
|
||||
public static Size Resolution { get { return device.WindowSize; } }
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
static Size GetResolution(WindowMode windowmode)
|
||||
{
|
||||
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;
|
||||
desktopResolution.Height = customSize.Y;
|
||||
}
|
||||
return new Size(
|
||||
desktopResolution.Width,
|
||||
desktopResolution.Height);
|
||||
}
|
||||
|
||||
static IGraphicsDevice CreateDevice( Assembly rendererDll, int width, int height, WindowMode window, bool vsync )
|
||||
{
|
||||
foreach( RendererAttribute r in rendererDll.GetCustomAttributes( typeof( RendererAttribute ), false ) )
|
||||
{
|
||||
return (IGraphicsDevice)r.Type.GetConstructor( new Type[] { typeof( int ), typeof( int ), typeof( WindowMode ), typeof( bool ) } )
|
||||
.Invoke( new object[] { width, height, window, vsync } );
|
||||
}
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,51 +20,39 @@ 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)
|
||||
{
|
||||
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(MiniYaml.Merge);
|
||||
|
||||
foreach (var s in sequences)
|
||||
LoadSequencesForUnit(s.Key, s.Value);
|
||||
}
|
||||
|
||||
static void LoadSequenceSource(string filename)
|
||||
static void LoadSequencesForUnit(string unit, MiniYaml sequences)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
string unitName = eUnit.GetAttribute("name");
|
||||
Game.modData.LoadScreen.Display();
|
||||
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]; }
|
||||
@@ -79,20 +67,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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,18 +16,21 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
public class Sheet
|
||||
{
|
||||
protected readonly Bitmap bitmap;
|
||||
Bitmap bitmap;
|
||||
ITexture texture;
|
||||
bool dirty;
|
||||
byte[] data;
|
||||
public readonly Size Size;
|
||||
|
||||
public Sheet(Size size)
|
||||
{
|
||||
this.bitmap = new Bitmap(size.Width, size.Height);
|
||||
Size = size;
|
||||
}
|
||||
|
||||
internal Sheet(string filename)
|
||||
public Sheet(string filename)
|
||||
{
|
||||
this.bitmap = (Bitmap)Image.FromStream(FileSystem.Open(filename));
|
||||
bitmap = (Bitmap)Image.FromStream(FileSystem.Open(filename));
|
||||
Size = bitmap.Size;
|
||||
}
|
||||
|
||||
public ITexture Texture
|
||||
@@ -35,28 +38,30 @@ namespace OpenRA.Graphics
|
||||
get
|
||||
{
|
||||
if (texture == null)
|
||||
texture = Game.Renderer.Device.CreateTexture(bitmap);
|
||||
{
|
||||
texture = Game.Renderer.Device.CreateTexture();
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if (dirty)
|
||||
{
|
||||
texture.SetData(bitmap);
|
||||
dirty = false;
|
||||
if (data != null)
|
||||
{
|
||||
texture.SetData(data, Size.Width, Size.Height);
|
||||
dirty = false;
|
||||
}
|
||||
else if (bitmap != null)
|
||||
{
|
||||
texture.SetData(bitmap);
|
||||
dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
|
||||
public Size Size { get { return bitmap.Size; } }
|
||||
|
||||
protected Color this[Point p]
|
||||
{
|
||||
get { return bitmap.GetPixel(p.X, p.Y); }
|
||||
set { bitmap.SetPixel(p.X, p.Y, value); }
|
||||
}
|
||||
|
||||
public Bitmap Bitmap { get { return bitmap; } } // for perf
|
||||
|
||||
public byte[] Data { get { if (data == null) data = new byte[4 * Size.Width * Size.Height]; return data; } }
|
||||
public void MakeDirty() { dirty = true; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,6 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
public class SheetBuilder
|
||||
{
|
||||
public static SheetBuilder SharedInstance;
|
||||
internal static void Initialize()
|
||||
{
|
||||
SharedInstance = new SheetBuilder(TextureChannel.Red);
|
||||
}
|
||||
|
||||
internal SheetBuilder(TextureChannel ch)
|
||||
{
|
||||
current = null;
|
||||
|
||||
@@ -64,12 +64,9 @@ namespace OpenRA.Graphics
|
||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite,
|
||||
new float2(
|
||||
(int)Math.Round(p.X + g.Offset.X, 0),
|
||||
p.Y + g.Offset.Y),
|
||||
"chrome");
|
||||
p.Y + g.Offset.Y));
|
||||
p.X += g.Advance;
|
||||
}
|
||||
|
||||
// r.Flush();
|
||||
}
|
||||
|
||||
public int2 Measure(string text)
|
||||
@@ -100,13 +97,20 @@ namespace OpenRA.Graphics
|
||||
unsafe
|
||||
{
|
||||
var p = (byte*)_glyph.bitmap.buffer;
|
||||
var dest = s.sheet.Data;
|
||||
var destStride = s.sheet.Size.Width * 4;
|
||||
|
||||
for (var j = 0; j < s.size.Y; j++)
|
||||
{
|
||||
for (var i = 0; i < s.size.X; i++)
|
||||
if (p[i] != 0)
|
||||
s.sheet.Bitmap.SetPixel(i + s.bounds.Left, j + s.bounds.Top,
|
||||
Color.FromArgb(p[i], c.Second.R, c.Second.G, c.Second.B));
|
||||
{
|
||||
var q = destStride * (j + s.bounds.Top) + 4 * (i + s.bounds.Left);
|
||||
dest[q] = c.Second.B;
|
||||
dest[q + 1] = c.Second.G;
|
||||
dest[q + 2] = c.Second.R;
|
||||
dest[q + 3] = p[i];
|
||||
}
|
||||
|
||||
p += _glyph.bitmap.pitch;
|
||||
}
|
||||
|
||||
@@ -60,22 +60,39 @@ namespace OpenRA.Graphics
|
||||
sprites = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void DrawSprite(Sprite s, float2 location, string palette)
|
||||
{
|
||||
DrawSprite(s, location, palette, s.size);
|
||||
DrawSprite(s, location, Game.world.WorldRenderer.GetPaletteIndex(palette), s.size);
|
||||
}
|
||||
|
||||
|
||||
public void DrawSprite(Sprite s, float2 location, string palette, float2 size)
|
||||
{
|
||||
DrawSprite(s, location, Game.world.WorldRenderer.GetPaletteIndex(palette), size);
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, float2 location, int paletteIndex, float2 size)
|
||||
{
|
||||
if (s.sheet != currentSheet)
|
||||
Flush();
|
||||
|
||||
currentSheet = s.sheet;
|
||||
Util.FastCreateQuad(vertices, indices, location.ToInt2(), s, Game.world.WorldRenderer.GetPaletteIndex(palette), nv, ni, size);
|
||||
Util.FastCreateQuad(vertices, indices, location.ToInt2(), s, paletteIndex, nv, ni, size);
|
||||
nv += 4; ni += 6;
|
||||
if (++sprites >= spritesPerBatch)
|
||||
Flush();
|
||||
}
|
||||
|
||||
|
||||
// For RGBASpriteRenderer, which doesn't use palettes
|
||||
public void DrawSprite(Sprite s, float2 location)
|
||||
{
|
||||
DrawSprite(s, location, 0, s.size);
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, float2 location, float2 size)
|
||||
{
|
||||
DrawSprite(s, location, 0, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
public static void Initialize( TileSet tileset )
|
||||
{
|
||||
/* .tem: hack to allow incomplete theaters (interior) to work, falling back to temperate for the missing art */
|
||||
exts = tileset.Extensions;
|
||||
sprites = new Cache<string, Sprite[]>( LoadSprites );
|
||||
}
|
||||
@@ -28,7 +27,7 @@ namespace OpenRA.Graphics
|
||||
static Sprite[] LoadSprites(string filename)
|
||||
{
|
||||
var shp = new ShpReader(FileSystem.OpenWithExts(filename, exts));
|
||||
return shp.Select(a => SheetBuilder.SharedInstance.Add(a.Image, shp.Size)).ToArray();
|
||||
return shp.Select(a => Game.modData.SheetBuilder.Add(a.Image, shp.Size)).ToArray();
|
||||
}
|
||||
|
||||
public static Sprite[] LoadAllSprites(string filename) { return sprites[filename]; }
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace OpenRA.Graphics
|
||||
Size tileSize = new Size( Game.CellSize, Game.CellSize );
|
||||
|
||||
var tileMapping = new Cache<TileReference<ushort,byte>, Sprite>(
|
||||
x => SheetBuilder.SharedInstance.Add(world.TileSet.GetBytes(x), tileSize));
|
||||
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];
|
||||
|
||||
@@ -65,42 +65,23 @@ namespace OpenRA.Graphics
|
||||
|
||||
public static void FastCopyIntoChannel(Sprite dest, byte[] src)
|
||||
{
|
||||
var bitmap = dest.sheet.Bitmap;
|
||||
BitmapData bits = null;
|
||||
uint[] channelMasks = { 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 };
|
||||
int[] shifts = { 16, 8, 0, 24 };
|
||||
var masks = new int[] { 2, 1, 0, 3 }; // hack, our channel order is nuts.
|
||||
var data = dest.sheet.Data;
|
||||
var srcStride = dest.bounds.Width;
|
||||
var destStride = dest.sheet.Size.Width * 4;
|
||||
var destOffset = destStride * dest.bounds.Top + dest.bounds.Left * 4 + masks[(int)dest.channel];
|
||||
var destSkip = destStride - 4 * srcStride;
|
||||
var height = dest.bounds.Height;
|
||||
|
||||
uint mask = channelMasks[(int)dest.channel];
|
||||
int shift = shifts[(int)dest.channel];
|
||||
|
||||
try
|
||||
var srcOffset = 0;
|
||||
for (var j = 0; j < height; j++)
|
||||
{
|
||||
bits = bitmap.LockBits(dest.bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
|
||||
|
||||
int width = dest.bounds.Width;
|
||||
int height = dest.bounds.Height;
|
||||
|
||||
unsafe
|
||||
for (int i = 0; i < srcStride; i++, srcOffset++)
|
||||
{
|
||||
fixed (byte* srcbase = &src[0])
|
||||
{
|
||||
byte* s = srcbase;
|
||||
uint* t = (uint*)bits.Scan0.ToPointer();
|
||||
int stride = bits.Stride >> 2;
|
||||
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
uint* p = t;
|
||||
for (int i = 0; i < width; i++, p++)
|
||||
*p = (*p & ~mask) | ((mask & ((uint)*s++) << shift));
|
||||
t += stride;
|
||||
}
|
||||
}
|
||||
data[destOffset] = src[srcOffset];
|
||||
destOffset += 4;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
bitmap.UnlockBits(bits);
|
||||
destOffset += destSkip;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace OpenRA.Graphics
|
||||
readonly float2 screenSize;
|
||||
float2 scrollPosition;
|
||||
readonly Renderer renderer;
|
||||
readonly int2 mapStart;
|
||||
readonly int2 mapEnd;
|
||||
|
||||
public float2 Location { get { return scrollPosition; } }
|
||||
|
||||
@@ -35,42 +37,81 @@ namespace OpenRA.Graphics
|
||||
|
||||
public void Scroll(float2 delta)
|
||||
{
|
||||
scrollPosition = scrollPosition + delta;
|
||||
this.Scroll(delta, false);
|
||||
}
|
||||
|
||||
public void Scroll(float2 delta, bool ignoreBorders)
|
||||
{
|
||||
float2 newScrollPosition = scrollPosition + delta;
|
||||
|
||||
if(!ignoreBorders)
|
||||
newScrollPosition = this.NormalizeScrollPosition(newScrollPosition);
|
||||
|
||||
scrollPosition = newScrollPosition;
|
||||
}
|
||||
|
||||
private float2 NormalizeScrollPosition(float2 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;
|
||||
}
|
||||
|
||||
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)
|
||||
blockedDirections = blockedDirections.Set(ScrollDirection.Up, true);
|
||||
if(scrollPosition.X <= topLeftBorder.X - screenSize.X/2)
|
||||
blockedDirections = blockedDirections.Set(ScrollDirection.Left, true);
|
||||
if(scrollPosition.Y >= bottomRightBorder.Y - screenSize.Y/2)
|
||||
blockedDirections = blockedDirections.Set(ScrollDirection.Down, true);
|
||||
if(scrollPosition.X >= bottomRightBorder.X - screenSize.X/2)
|
||||
blockedDirections = blockedDirections.Set(ScrollDirection.Right, true);
|
||||
|
||||
return blockedDirections;
|
||||
}
|
||||
|
||||
public Viewport(float2 screenSize, int2 mapStart, int2 mapEnd, Renderer renderer)
|
||||
{
|
||||
this.screenSize = screenSize;
|
||||
this.renderer = renderer;
|
||||
this.mapStart = mapStart;
|
||||
this.mapEnd = mapEnd;
|
||||
|
||||
this.scrollPosition = Game.CellSize* mapStart;
|
||||
}
|
||||
|
||||
public void DrawRegions( World world )
|
||||
{
|
||||
Timer.Time( "DrawRegions start" );
|
||||
|
||||
float2 r1 = new float2(2, -2) / screenSize;
|
||||
float2 r2 = new float2(-1, 1);
|
||||
|
||||
renderer.BeginFrame(r1, r2, scrollPosition.ToInt2());
|
||||
renderer.BeginFrame(scrollPosition);
|
||||
world.WorldRenderer.Draw();
|
||||
Timer.Time( "worldRenderer: {0}" );
|
||||
|
||||
Widget.DoDraw(world);
|
||||
Timer.Time( "widgets: {0}" );
|
||||
|
||||
var cursorName = Widget.RootWidget.GetCursorOuter(Viewport.LastMousePos) ?? "default";
|
||||
var c = new Cursor(cursorName);
|
||||
c.Draw((int)cursorFrame, Viewport.LastMousePos + Location);
|
||||
Timer.Time( "cursors: {0}" );
|
||||
|
||||
renderer.RgbaSpriteRenderer.Flush();
|
||||
renderer.SpriteRenderer.Flush();
|
||||
renderer.WorldSpriteRenderer.Flush();
|
||||
|
||||
renderer.EndFrame();
|
||||
Timer.Time( "endFrame: {0}" );
|
||||
}
|
||||
|
||||
public void RefreshPalette()
|
||||
@@ -96,7 +137,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public void Center(int2 loc)
|
||||
{
|
||||
scrollPosition = (Game.CellSize*loc - .5f * new float2(Width, Height)).ToInt2();
|
||||
scrollPosition = this.NormalizeScrollPosition(Game.CellSize*loc - .5f * new float2(Width, Height));
|
||||
}
|
||||
|
||||
public void Center(IEnumerable<Actor> actors)
|
||||
@@ -107,12 +148,7 @@ namespace OpenRA.Graphics
|
||||
.Select(a => a.CenterLocation)
|
||||
.Aggregate((a, b) => a + b);
|
||||
|
||||
scrollPosition = (avgPos - .5f * new float2(Width, Height)).ToInt2();
|
||||
}
|
||||
|
||||
public void GoToStartLocation( Player player )
|
||||
{
|
||||
Center( player.World.Queries.OwnedBy[ player ].WithTrait<Selectable>().Select( a => a.Actor ) );
|
||||
scrollPosition = this.NormalizeScrollPosition((avgPos - .5f * new float2(Width, Height)));
|
||||
}
|
||||
|
||||
public Rectangle? ShroudBounds()
|
||||
@@ -123,4 +159,4 @@ namespace OpenRA.Graphics
|
||||
return localPlayer.Shroud.Bounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,13 +25,13 @@ namespace OpenRA
|
||||
public string Uid;
|
||||
|
||||
// Yaml map data
|
||||
public bool Selectable = true;
|
||||
public int MapFormat;
|
||||
public string Title;
|
||||
public string Description;
|
||||
public string Author;
|
||||
public int PlayerCount;
|
||||
public string Tileset;
|
||||
[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>();
|
||||
@@ -39,18 +39,14 @@ namespace OpenRA
|
||||
public Dictionary<string, int2> Waypoints = new Dictionary<string, int2>();
|
||||
|
||||
// Rules overrides
|
||||
public Dictionary<string, MiniYaml> Rules = new Dictionary<string, MiniYaml>();
|
||||
public Dictionary<string, MiniYaml> Weapons = new Dictionary<string, MiniYaml>();
|
||||
public Dictionary<string, MiniYaml> Voices = new Dictionary<string, MiniYaml>();
|
||||
public Dictionary<string, MiniYaml> Music = new Dictionary<string, MiniYaml>();
|
||||
public Dictionary<string, MiniYaml> Terrain = new Dictionary<string, MiniYaml>();
|
||||
|
||||
public List<MiniYamlNode> Rules = new List<MiniYamlNode>();
|
||||
|
||||
// Binary map data
|
||||
public byte TileFormat = 1;
|
||||
public int2 MapSize;
|
||||
[FieldLoader.Load] public int2 MapSize;
|
||||
|
||||
public int2 TopLeft;
|
||||
public int2 BottomRight;
|
||||
[FieldLoader.Load] public int2 TopLeft;
|
||||
[FieldLoader.Load] public int2 BottomRight;
|
||||
|
||||
public TileReference<ushort, byte>[,] MapTiles;
|
||||
public TileReference<byte, byte>[,] MapResources;
|
||||
@@ -65,10 +61,6 @@ namespace OpenRA
|
||||
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); } }
|
||||
|
||||
static List<string> SimpleFields = new List<string>() {
|
||||
"Selectable", "MapFormat", "Title", "Description", "Author", "PlayerCount", "Tileset", "MapSize", "TopLeft", "BottomRight"
|
||||
};
|
||||
|
||||
public Map()
|
||||
{
|
||||
MapSize = new int2(1, 1);
|
||||
@@ -90,22 +82,22 @@ namespace OpenRA
|
||||
|
||||
class Format2ActorReference
|
||||
{
|
||||
public string Id;
|
||||
public string Type;
|
||||
public int2 Location;
|
||||
public string Owner;
|
||||
public string Id = null;
|
||||
public string Type = null;
|
||||
public int2 Location = int2.Zero;
|
||||
public string Owner = null;
|
||||
}
|
||||
|
||||
public Map(IFolder package)
|
||||
{
|
||||
Package = package;
|
||||
var yaml = MiniYaml.FromStream(Package.GetContent("map.yaml"));
|
||||
var yaml = new MiniYaml( null, MiniYaml.FromStream( Package.GetContent( "map.yaml" ) ) );
|
||||
|
||||
// 'Simple' metadata
|
||||
FieldLoader.LoadFields(this, yaml, SimpleFields);
|
||||
FieldLoader.Load( this, yaml );
|
||||
|
||||
// Waypoints
|
||||
foreach (var wp in yaml["Waypoints"].Nodes)
|
||||
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])));
|
||||
@@ -124,7 +116,7 @@ namespace OpenRA
|
||||
Players.Add("Neutral", new PlayerReference("Neutral", "allies", true, true));
|
||||
|
||||
int actors = 0;
|
||||
foreach (var kv in yaml["Actors"].Nodes)
|
||||
foreach (var kv in yaml.NodesDict["Actors"].NodesDict)
|
||||
{
|
||||
string[] vals = kv.Value.Value.Split(' ');
|
||||
string[] loc = vals[2].Split(',');
|
||||
@@ -138,13 +130,13 @@ namespace OpenRA
|
||||
|
||||
case 2:
|
||||
{
|
||||
foreach (var kv in yaml["Players"].Nodes)
|
||||
foreach (var kv in yaml.NodesDict["Players"].NodesDict)
|
||||
{
|
||||
var player = new PlayerReference(kv.Value);
|
||||
Players.Add(player.Name, player);
|
||||
}
|
||||
|
||||
foreach (var kv in yaml["Actors"].Nodes)
|
||||
foreach (var kv in yaml.NodesDict["Actors"].NodesDict)
|
||||
{
|
||||
var oldActorReference = FieldLoader.Load<Format2ActorReference>(kv.Value);
|
||||
Actors.Add(oldActorReference.Id, new ActorReference(oldActorReference.Type)
|
||||
@@ -157,22 +149,38 @@ namespace OpenRA
|
||||
|
||||
case 3:
|
||||
{
|
||||
foreach (var kv in yaml["Players"].Nodes)
|
||||
foreach (var kv in yaml.NodesDict["Players"].NodesDict)
|
||||
{
|
||||
var player = new PlayerReference(kv.Value);
|
||||
Players.Add(player.Name, player);
|
||||
}
|
||||
|
||||
foreach (var kv in yaml["Actors"].Nodes)
|
||||
Actors.Add(kv.Key, new ActorReference(kv.Value.Value, kv.Value.Nodes));
|
||||
foreach (var kv in yaml.NodesDict["Actors"].NodesDict)
|
||||
Actors.Add(kv.Key, new ActorReference(kv.Value.Value, kv.Value.NodesDict));
|
||||
} break;
|
||||
|
||||
default:
|
||||
throw new InvalidDataException("Map format {0} is not supported.".F(MapFormat));
|
||||
}
|
||||
|
||||
/* hack: make some slots. */
|
||||
if (!Players.Any(p => p.Value.Playable))
|
||||
{
|
||||
for (int index = 0; index < Waypoints.Count; index++)
|
||||
{
|
||||
var p = new PlayerReference
|
||||
{
|
||||
Name = "Multi{0}".F(index),
|
||||
Race = "Random",
|
||||
Playable = true,
|
||||
DefaultStartingUnits = true
|
||||
};
|
||||
Players.Add(p.Name, p);
|
||||
}
|
||||
}
|
||||
|
||||
// Smudges
|
||||
foreach (var kv in yaml["Smudges"].Nodes)
|
||||
foreach (var kv in yaml.NodesDict["Smudges"].NodesDict)
|
||||
{
|
||||
string[] vals = kv.Key.Split(' ');
|
||||
string[] loc = vals[1].Split(',');
|
||||
@@ -180,7 +188,7 @@ namespace OpenRA
|
||||
}
|
||||
|
||||
// Rules
|
||||
Rules = yaml["Rules"].Nodes;
|
||||
Rules = yaml.NodesDict["Rules"].Nodes;
|
||||
|
||||
CustomTerrain = new string[MapSize.X, MapSize.Y];
|
||||
LoadUid();
|
||||
@@ -191,27 +199,27 @@ namespace OpenRA
|
||||
{
|
||||
MapFormat = 3;
|
||||
|
||||
var root = new Dictionary<string, MiniYaml>();
|
||||
foreach (var field in SimpleFields)
|
||||
var root = new List<MiniYamlNode>();
|
||||
foreach (var field in new string[] {"Selectable", "MapFormat", "Title", "Description", "Author", "PlayerCount", "Tileset", "MapSize", "TopLeft", "BottomRight"})
|
||||
{
|
||||
FieldInfo f = this.GetType().GetField(field);
|
||||
if (f.GetValue(this) == null) continue;
|
||||
root.Add(field, new MiniYaml(FieldSaver.FormatValue(this, f), null));
|
||||
root.Add( new MiniYamlNode( field, FieldSaver.FormatValue( this, f ) ) );
|
||||
}
|
||||
|
||||
root.Add("Players",
|
||||
new MiniYaml(null, Players.ToDictionary(
|
||||
p => "PlayerReference@{0}".F(p.Key),
|
||||
p => FieldSaver.Save(p.Value))));
|
||||
root.Add( new MiniYamlNode( "Players", null,
|
||||
Players.Select( p => new MiniYamlNode(
|
||||
"PlayerReference@{0}".F( p.Key ),
|
||||
FieldSaver.Save( p.Value ) ) ).ToList() ) );
|
||||
|
||||
root.Add("Actors",
|
||||
new MiniYaml( null, Actors.ToDictionary(
|
||||
x => x.Key,
|
||||
x => x.Value.Save() ) ) );
|
||||
root.Add( new MiniYamlNode( "Actors", null,
|
||||
Actors.Select( x => new MiniYamlNode(
|
||||
x.Key,
|
||||
x.Value.Save() ) ).ToList() ) );
|
||||
|
||||
root.Add("Waypoints", MiniYaml.FromDictionary<string, int2>(Waypoints));
|
||||
root.Add("Smudges", MiniYaml.FromList<SmudgeReference>(Smudges));
|
||||
root.Add("Rules", new MiniYaml(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 ) );
|
||||
|
||||
SaveBinaryData(Path.Combine(filepath, "map.bin"));
|
||||
root.WriteToFile(Path.Combine(filepath, "map.yaml"));
|
||||
|
||||
77
OpenRA.Game/ModData.cs
Executable file
77
OpenRA.Game/ModData.cs
Executable file
@@ -0,0 +1,77 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Support;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public class ModData
|
||||
{
|
||||
public readonly Manifest Manifest;
|
||||
public readonly ObjectCreator ObjectCreator;
|
||||
public readonly SheetBuilder SheetBuilder;
|
||||
public readonly CursorSheetBuilder CursorSheetBuilder;
|
||||
public readonly Dictionary<string, MapStub> AvailableMaps;
|
||||
public ILoadScreen LoadScreen = null;
|
||||
|
||||
public ModData( params string[] mods )
|
||||
{
|
||||
Manifest = new Manifest( mods );
|
||||
ObjectCreator = new ObjectCreator( Manifest );
|
||||
LoadScreen = ObjectCreator.CreateObject<ILoadScreen>(Manifest.LoadScreen);
|
||||
LoadScreen.Init();
|
||||
LoadScreen.Display();
|
||||
|
||||
FileSystem.LoadFromManifest( Manifest );
|
||||
ChromeProvider.Initialize( Manifest.Chrome );
|
||||
SheetBuilder = new SheetBuilder( TextureChannel.Red );
|
||||
CursorSheetBuilder = new CursorSheetBuilder( this );
|
||||
AvailableMaps = FindMaps( mods );
|
||||
}
|
||||
|
||||
// TODO: Do this nicer
|
||||
Dictionary<string, MapStub> FindMaps(string[] mods)
|
||||
{
|
||||
var paths = new[] { "maps/" }.Concat(mods.Select(m => "mods/" + m + "/maps/"))
|
||||
.Where(p => Directory.Exists(p))
|
||||
.SelectMany(p => Directory.GetDirectories(p)).ToList();
|
||||
|
||||
return paths.Select(p => new MapStub(new Folder(p))).ToDictionary(m => m.Uid);
|
||||
}
|
||||
|
||||
string cachedTheatre = null;
|
||||
public Map PrepareMap(string uid)
|
||||
{
|
||||
LoadScreen.Display();
|
||||
|
||||
if (!AvailableMaps.ContainsKey(uid))
|
||||
throw new InvalidDataException("Invalid map uid: {0}".F(uid));
|
||||
|
||||
var map = new Map(AvailableMaps[uid].Package);
|
||||
|
||||
Rules.LoadRules(Manifest, map);
|
||||
if (map.Theater != cachedTheatre)
|
||||
{
|
||||
SpriteSheetBuilder.Initialize( Rules.TileSets[map.Tileset] );
|
||||
SequenceProvider.Initialize(Manifest.Sequences);
|
||||
CursorProvider.Initialize(Manifest.Cursors);
|
||||
cachedTheatre = map.Theater;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
public interface ILoadScreen { void Display(); void Init(); }
|
||||
}
|
||||
@@ -140,8 +140,11 @@ namespace OpenRA.Network
|
||||
|
||||
}
|
||||
catch (SocketException) { /* drop this on the floor; we'll pick up the disconnect from the reader thread */ }
|
||||
catch (ObjectDisposedException) { /* ditto */ }
|
||||
}
|
||||
|
||||
bool disposed = false;
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
if (disposed) return;
|
||||
|
||||
@@ -177,19 +177,19 @@ namespace OpenRA
|
||||
return new Order("Command", null, text) { IsImmediate = true };
|
||||
}
|
||||
|
||||
public static Order StartProduction(Player subject, string item, int count)
|
||||
public static Order StartProduction(Actor subject, string item, int count)
|
||||
{
|
||||
return new Order("StartProduction", subject.PlayerActor, new int2( count, 0 ), item );
|
||||
return new Order("StartProduction", subject, new int2( count, 0 ), item );
|
||||
}
|
||||
|
||||
public static Order PauseProduction(Player subject, string item, bool pause)
|
||||
public static Order PauseProduction(Actor subject, string item, bool pause)
|
||||
{
|
||||
return new Order("PauseProduction", subject.PlayerActor, new int2( pause ? 1 : 0, 0 ), item);
|
||||
return new Order("PauseProduction", subject, new int2( pause ? 1 : 0, 0 ), item);
|
||||
}
|
||||
|
||||
public static Order CancelProduction(Player subject, string item)
|
||||
public static Order CancelProduction(Actor subject, string item)
|
||||
{
|
||||
return new Order("CancelProduction", subject.PlayerActor, item);
|
||||
return new Order("CancelProduction", subject, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace OpenRA.Network
|
||||
{
|
||||
class OrderManager : IDisposable
|
||||
{
|
||||
SyncReport syncReport = new SyncReport();
|
||||
|
||||
public int FrameNumber { get; private set; }
|
||||
|
||||
public int FramesAhead = 0;
|
||||
@@ -109,7 +111,7 @@ namespace OpenRA.Network
|
||||
{
|
||||
if (packet.Length != existingSync.Length)
|
||||
{
|
||||
Game.DumpSyncReport(frame);
|
||||
syncReport.DumpSyncReport(frame);
|
||||
OutOfSync(frame);
|
||||
}
|
||||
else
|
||||
@@ -118,7 +120,7 @@ namespace OpenRA.Network
|
||||
{
|
||||
if (packet[i] != existingSync[i])
|
||||
{
|
||||
Game.DumpSyncReport(frame);
|
||||
syncReport.DumpSyncReport(frame);
|
||||
|
||||
if (i < SyncHeaderSize)
|
||||
OutOfSync(frame, "Tick");
|
||||
@@ -189,7 +191,7 @@ namespace OpenRA.Network
|
||||
Connection.Send( ss );
|
||||
WriteToReplay( frameData, ss );
|
||||
|
||||
Game.UpdateSyncReport();
|
||||
syncReport.UpdateSyncReport();
|
||||
|
||||
CheckSync( ss );
|
||||
|
||||
|
||||
106
OpenRA.Game/Network/Session.cs
Normal file
106
OpenRA.Game/Network/Session.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using OpenRA.FileFormats;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
public class Session
|
||||
{
|
||||
public List<Client> Clients = new List<Client>();
|
||||
public List<Slot> Slots = new List<Slot>();
|
||||
public Global GlobalSettings = new Global();
|
||||
|
||||
public enum ClientState
|
||||
{
|
||||
NotReady,
|
||||
Ready
|
||||
}
|
||||
|
||||
public class Client
|
||||
{
|
||||
public int Index;
|
||||
public Color Color1;
|
||||
public Color Color2;
|
||||
public string Country;
|
||||
public int SpawnPoint;
|
||||
public string Name;
|
||||
public ClientState State;
|
||||
public int Team;
|
||||
public int Slot; // which slot we're in, or -1 for `observer`.
|
||||
}
|
||||
|
||||
public class Slot
|
||||
{
|
||||
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.
|
||||
|
||||
// todo: more stuff?
|
||||
}
|
||||
|
||||
public class Global
|
||||
{
|
||||
public string Map;
|
||||
public string[] Mods = { "ra" }; // mod names
|
||||
public int OrderLatency = 3;
|
||||
public int RandomSeed = 0;
|
||||
public bool LockTeams = false; // don't allow team changes after game start.
|
||||
public bool AllowCheats = false;
|
||||
}
|
||||
|
||||
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 slot in Slots )
|
||||
clientData.Add( new MiniYamlNode( "Slot@{0}".F( slot.Index ), FieldSaver.Save( slot ) ) );
|
||||
|
||||
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 ys = MiniYaml.FromString(data);
|
||||
foreach (var y in ys)
|
||||
{
|
||||
var yy = y.Key.Split('@');
|
||||
|
||||
switch( yy[0] )
|
||||
{
|
||||
case "GlobalSettings":
|
||||
FieldLoader.Load(session.GlobalSettings, y.Value);
|
||||
break;
|
||||
|
||||
case "Client":
|
||||
session.Clients.Add(FieldLoader.Load<Session.Client>(y.Value));
|
||||
break;
|
||||
|
||||
case "Slot":
|
||||
session.Slots.Add(FieldLoader.Load<Session.Slot>(y.Value ));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
}
|
||||
}
|
||||
63
OpenRA.Game/Network/SyncReport.cs
Executable file
63
OpenRA.Game/Network/SyncReport.cs
Executable file
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using OpenRA.FileFormats;
|
||||
|
||||
namespace OpenRA.Network
|
||||
{
|
||||
class SyncReport
|
||||
{
|
||||
Queue<Pair<int, string>> syncReports = new Queue<Pair<int, string>>();
|
||||
const int numSyncReports = 5;
|
||||
|
||||
internal void UpdateSyncReport()
|
||||
{
|
||||
if (!Game.Settings.Debug.RecordSyncReports)
|
||||
return;
|
||||
|
||||
while (syncReports.Count >= numSyncReports) syncReports.Dequeue();
|
||||
syncReports.Enqueue(Pair.New(Game.orderManager.FrameNumber, GenerateSyncReport()));
|
||||
}
|
||||
|
||||
string GenerateSyncReport()
|
||||
{
|
||||
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>())
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,16 +16,34 @@ namespace OpenRA.Network
|
||||
{
|
||||
static class UnitOrders
|
||||
{
|
||||
static Session.Client FindClientById(int id)
|
||||
{
|
||||
return Game.LobbyInfo.Clients.FirstOrDefault(c => c.Index == id);
|
||||
}
|
||||
|
||||
static Player FindPlayerByClientId(int id)
|
||||
{
|
||||
/* 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)
|
||||
{
|
||||
Game.Debug("Detected exploit order from {0}: {1}".F(clientId, order.OrderString));
|
||||
return;
|
||||
}
|
||||
|
||||
switch( order.OrderString )
|
||||
{
|
||||
case "Chat":
|
||||
{
|
||||
var client = Game.LobbyInfo.Clients.FirstOrDefault(c => c.Index == clientId);
|
||||
var client = FindClientById(clientId);
|
||||
if (client != null)
|
||||
{
|
||||
var player = world.players.Values.FirstOrDefault(p => p.Index == client.Index);
|
||||
var player = FindPlayerByClientId(clientId);
|
||||
if (player != null && player.WinState == WinState.Lost)
|
||||
Game.AddChatLine(client.Color1, client.Name + " (Dead)", order.TargetString);
|
||||
else
|
||||
@@ -35,10 +53,10 @@ namespace OpenRA.Network
|
||||
}
|
||||
case "TeamChat":
|
||||
{
|
||||
var client = Game.LobbyInfo.Clients.FirstOrDefault(c => c.Index == clientId);
|
||||
var client = FindClientById(clientId);
|
||||
if (client != null)
|
||||
{
|
||||
var player = world.players.Values.FirstOrDefault(p => p.Index == client.Index);
|
||||
var player = FindPlayerByClientId(clientId);
|
||||
var display = (world.GameHasStarted) ?
|
||||
player != null && (world.LocalPlayer != null && player.Stances[world.LocalPlayer] == Stance.Ally
|
||||
|| player.WinState == WinState.Lost) :
|
||||
@@ -55,7 +73,7 @@ namespace OpenRA.Network
|
||||
case "StartGame":
|
||||
{
|
||||
Game.AddChatLine(Color.White, "Server", "The game has started.");
|
||||
Game.StartGame();
|
||||
Game.StartGame(Game.LobbyInfo.GlobalSettings.Map);
|
||||
break;
|
||||
}
|
||||
case "SyncInfo":
|
||||
|
||||
48
OpenRA.Game/ObjectCreator.cs
Executable file
48
OpenRA.Game/ObjectCreator.cs
Executable file
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using OpenRA.FileFormats;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public class ObjectCreator
|
||||
{
|
||||
Pair<Assembly, string>[] ModAssemblies;
|
||||
|
||||
public ObjectCreator( Manifest manifest )
|
||||
{
|
||||
// All the core namespaces
|
||||
var asms = typeof(Game).Assembly.GetNamespaces()
|
||||
.Select(c => Pair.New(typeof(Game).Assembly, c))
|
||||
.ToList();
|
||||
|
||||
// Namespaces from each mod assembly
|
||||
foreach (var a in manifest.Assemblies)
|
||||
{
|
||||
var asm = Assembly.LoadFile(Path.GetFullPath(a));
|
||||
asms.AddRange(asm.GetNamespaces().Select(ns => Pair.New(asm, ns)));
|
||||
}
|
||||
|
||||
ModAssemblies = asms.ToArray();
|
||||
}
|
||||
|
||||
public static Action<string> MissingTypeAction =
|
||||
s => { throw new InvalidOperationException("Cannot locate type: {0}".F(s)); };
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
MissingTypeAction(classname);
|
||||
return default(T);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -100,7 +100,6 @@
|
||||
<Compile Include="Exts.cs" />
|
||||
<Compile Include="GameRules\ActorInfo.cs" />
|
||||
<Compile Include="GameRules\TechTree.cs" />
|
||||
<Compile Include="GameRules\UserSettings.cs" />
|
||||
<Compile Include="GameRules\VoiceInfo.cs" />
|
||||
<Compile Include="Effects\IEffect.cs" />
|
||||
<Compile Include="Graphics\ChromeProvider.cs" />
|
||||
@@ -152,7 +151,6 @@
|
||||
<Compile Include="Support\Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Graphics\Renderer.cs" />
|
||||
<Compile Include="Support\Settings.cs" />
|
||||
<Compile Include="Graphics\Sprite.cs" />
|
||||
<Compile Include="Graphics\SpriteRenderer.cs" />
|
||||
<Compile Include="Graphics\SpriteSheetBuilder.cs" />
|
||||
@@ -223,6 +221,12 @@
|
||||
<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" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
|
||||
@@ -231,7 +235,12 @@
|
||||
</ProjectReference>
|
||||
<Compile Include="ActorInitializer.cs" />
|
||||
<Compile Include="ActorReference.cs" />
|
||||
<Compile Include="ModData.cs" />
|
||||
<Compile Include="Map.cs" />
|
||||
<Compile Include="Network\Session.cs" />
|
||||
<Compile Include="ObjectCreator.cs" />
|
||||
<Compile Include="Network\SyncReport.cs" />
|
||||
<Compile Include="TraitDictionary.cs" />
|
||||
<Compile Include="Traits\PrimaryBuilding.cs" />
|
||||
<Compile Include="Widgets\Delegates\DeveloperModeDelegate.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.Traits;
|
||||
|
||||
@@ -56,8 +57,14 @@ namespace OpenRA.Orders
|
||||
|
||||
public void Tick( World world )
|
||||
{
|
||||
var producing = Producer.Trait<Traits.ProductionQueue>().CurrentItem( Rules.Info[ Building ].Category );
|
||||
if (producing == null || producing.Item != Building || producing.RemainingTime != 0)
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -38,15 +38,21 @@ namespace OpenRA.Orders
|
||||
public void RenderBeforeWorld(World world)
|
||||
{
|
||||
foreach (var a in world.Selection.Actors)
|
||||
foreach (var t in a.TraitsImplementing<IPreRenderSelection>())
|
||||
t.RenderBeforeWorld(a);
|
||||
if (!a.Destroyed)
|
||||
foreach (var t in a.TraitsImplementing<IPreRenderSelection>())
|
||||
t.RenderBeforeWorld(a);
|
||||
|
||||
Game.Renderer.Flush();
|
||||
}
|
||||
|
||||
public void RenderAfterWorld( World world )
|
||||
{
|
||||
foreach (var a in world.Selection.Actors)
|
||||
foreach (var t in a.TraitsImplementing<IPostRenderSelection>())
|
||||
t.RenderAfterWorld(a);
|
||||
foreach (var a in world.Selection.Actors)
|
||||
if (!a.Destroyed)
|
||||
foreach (var t in a.TraitsImplementing<IPostRenderSelection>())
|
||||
t.RenderAfterWorld(a);
|
||||
|
||||
Game.Renderer.Flush();
|
||||
}
|
||||
|
||||
public string GetCursor( World world, int2 xy, MouseInput mi )
|
||||
|
||||
@@ -49,11 +49,13 @@ namespace OpenRA
|
||||
cached.tick = Game.LocalTick;
|
||||
return new List<int2>(cached.result);
|
||||
}
|
||||
|
||||
var mi = self.Info.Traits.Get<MobileInfo>();
|
||||
|
||||
var pb = FindBidiPath(
|
||||
PathSearch.FromPoint(self, target, from, true)
|
||||
PathSearch.FromPoint(world, mi, target, from, true)
|
||||
.WithCustomBlocker(AvoidUnitsNear(from, 4, self)),
|
||||
PathSearch.FromPoint(self, from, target, true)
|
||||
PathSearch.FromPoint(world, mi, from, target, true)
|
||||
.WithCustomBlocker(AvoidUnitsNear(from, 4, self))
|
||||
.InReverse());
|
||||
|
||||
@@ -69,11 +71,11 @@ namespace OpenRA
|
||||
{
|
||||
using( new PerfSample( "find_unit_path_multiple_src" ) )
|
||||
{
|
||||
var mobile = self.Trait<Mobile>();
|
||||
var mobileInfo = self.Info.Traits.Get<MobileInfo>();
|
||||
var tilesInRange = world.FindTilesInCircle(target, range)
|
||||
.Where( t => mobile.CanEnterCell(t));
|
||||
.Where( t => Mobile.CanEnterCell(self.World, mobileInfo, t, null, true));
|
||||
|
||||
var path = FindPath( PathSearch.FromPoints( self, tilesInRange, src, false )
|
||||
var path = FindPath( PathSearch.FromPoints( world, mobileInfo, tilesInRange, src, false )
|
||||
.WithCustomBlocker(AvoidUnitsNear(src, 4, self))
|
||||
.InReverse());
|
||||
path.Reverse();
|
||||
|
||||
@@ -24,16 +24,19 @@ namespace OpenRA
|
||||
Func<int2, bool> customBlock;
|
||||
public bool checkForBlocked;
|
||||
public Actor ignoreBuilding;
|
||||
Actor self;
|
||||
public bool inReverse;
|
||||
Mobile mobile;
|
||||
|
||||
public PathSearch(Actor self)
|
||||
|
||||
MobileInfo mobileInfo;
|
||||
BuildingInfluence bim;
|
||||
UnitInfluence uim;
|
||||
|
||||
public PathSearch(World world, MobileInfo mobileInfo)
|
||||
{
|
||||
this.self = self;
|
||||
world = self.World;
|
||||
this.world = world;
|
||||
bim = world.WorldActor.Trait<BuildingInfluence>();
|
||||
uim = world.WorldActor.Trait<UnitInfluence>();
|
||||
cellInfo = InitCellInfo();
|
||||
mobile = self.Trait<Mobile>();
|
||||
this.mobileInfo = mobileInfo;
|
||||
queue = new PriorityQueue<PathDistance>();
|
||||
}
|
||||
|
||||
@@ -61,13 +64,19 @@ namespace OpenRA
|
||||
return this;
|
||||
}
|
||||
|
||||
public PathSearch FromPoint(int2 from)
|
||||
public PathSearch WithoutLaneBias()
|
||||
{
|
||||
AddInitialCell( self.World, from );
|
||||
LaneBias = 0f;
|
||||
return this;
|
||||
}
|
||||
|
||||
const float LaneBias = .5f;
|
||||
public PathSearch FromPoint(int2 from)
|
||||
{
|
||||
AddInitialCell( world, from );
|
||||
return this;
|
||||
}
|
||||
|
||||
float LaneBias = .5f;
|
||||
|
||||
public int2 Expand( World world )
|
||||
{
|
||||
@@ -80,7 +89,7 @@ namespace OpenRA
|
||||
|
||||
cellInfo[p.Location.X, p.Location.Y].Seen = true;
|
||||
|
||||
var thisCost = mobile.MovementCostForCell(self, p.Location);
|
||||
var thisCost = Mobile.MovementCostForCell(mobileInfo, world, p.Location);
|
||||
|
||||
if (thisCost == float.PositiveInfinity)
|
||||
return p.Location;
|
||||
@@ -93,12 +102,12 @@ namespace OpenRA
|
||||
if( cellInfo[ newHere.X, newHere.Y ].Seen )
|
||||
continue;
|
||||
|
||||
var costHere = mobile.MovementCostForCell(self, newHere);
|
||||
var costHere = Mobile.MovementCostForCell(mobileInfo, world, newHere);
|
||||
|
||||
if (costHere == float.PositiveInfinity)
|
||||
continue;
|
||||
|
||||
if (!mobile.CanEnterCell(newHere, ignoreBuilding, checkForBlocked))
|
||||
if (!Mobile.CanEnterCell(mobileInfo, world, uim, bim, newHere, ignoreBuilding, checkForBlocked))
|
||||
continue;
|
||||
|
||||
if (customBlock != null && customBlock(newHere))
|
||||
@@ -154,33 +163,33 @@ namespace OpenRA
|
||||
queue.Add( new PathDistance( heuristic( location ), location ) );
|
||||
}
|
||||
|
||||
public static PathSearch Search( Actor self, bool checkForBlocked )
|
||||
public static PathSearch Search( World world, MobileInfo mi, bool checkForBlocked )
|
||||
{
|
||||
var search = new PathSearch(self) {
|
||||
var search = new PathSearch(world, mi) {
|
||||
checkForBlocked = checkForBlocked };
|
||||
return search;
|
||||
}
|
||||
|
||||
public static PathSearch FromPoint( Actor self, int2 from, int2 target, bool checkForBlocked )
|
||||
public static PathSearch FromPoint( World world, MobileInfo mi, int2 from, int2 target, bool checkForBlocked )
|
||||
{
|
||||
var search = new PathSearch(self) {
|
||||
var search = new PathSearch(world, mi) {
|
||||
heuristic = DefaultEstimator( target ),
|
||||
checkForBlocked = checkForBlocked };
|
||||
|
||||
search.AddInitialCell( self.World, from );
|
||||
search.AddInitialCell( world, from );
|
||||
return search;
|
||||
}
|
||||
|
||||
public static PathSearch FromPoints(Actor self, IEnumerable<int2> froms, int2 target, bool checkForBlocked)
|
||||
public static PathSearch FromPoints(World world, MobileInfo mi, IEnumerable<int2> froms, int2 target, bool checkForBlocked)
|
||||
{
|
||||
var search = new PathSearch(self)
|
||||
var search = new PathSearch(world, mi)
|
||||
{
|
||||
heuristic = DefaultEstimator(target),
|
||||
checkForBlocked = checkForBlocked
|
||||
};
|
||||
|
||||
foreach (var sl in froms)
|
||||
search.AddInitialCell(self.World, sl);
|
||||
search.AddInitialCell(world, sl);
|
||||
|
||||
return search;
|
||||
}
|
||||
|
||||
@@ -8,10 +8,11 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA
|
||||
@@ -34,7 +35,9 @@ namespace OpenRA
|
||||
public readonly string InternalName;
|
||||
public readonly CountryInfo Country;
|
||||
public readonly int Index;
|
||||
public readonly bool NonCombatant = false;
|
||||
public readonly bool NonCombatant = false;
|
||||
public readonly int ClientIndex;
|
||||
public readonly PlayerReference PlayerRef;
|
||||
|
||||
public ShroudRenderer Shroud;
|
||||
public World World { get; private set; }
|
||||
@@ -49,32 +52,39 @@ namespace OpenRA
|
||||
Index = index;
|
||||
Palette = "player"+index;
|
||||
Color = pr.Color;
|
||||
Color2 = pr.Color2;
|
||||
Color2 = pr.Color2;
|
||||
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);
|
||||
.FirstOrDefault(c => pr.Race == c.Race)
|
||||
?? world.GetCountries().Random(world.SharedRandom);
|
||||
|
||||
PlayerRef = pr;
|
||||
|
||||
RegisterPlayerColor(world, Palette);
|
||||
}
|
||||
|
||||
public Player( World world, Session.Client client )
|
||||
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 ) });
|
||||
|
||||
Index = client.Index;
|
||||
Palette = "player"+client.Index;
|
||||
Index = index;
|
||||
Palette = "player"+index;
|
||||
Color = client.Color1;
|
||||
Color2 = client.Color2;
|
||||
PlayerName = client.Name;
|
||||
InternalName = "Multi{0}".F(client.Index);
|
||||
PlayerName = client.Name;
|
||||
InternalName = pr.Name;
|
||||
Country = world.GetCountries()
|
||||
.FirstOrDefault(c => client != null && client.Country == c.Race)
|
||||
?? world.GetCountries().Random(world.SharedRandom);
|
||||
?? world.GetCountries().Random(world.SharedRandom);
|
||||
|
||||
ClientIndex = client.Index;
|
||||
PlayerRef = pr;
|
||||
|
||||
RegisterPlayerColor(world, Palette);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace OpenRA
|
||||
{
|
||||
return actors.AsEnumerable().Contains(a);
|
||||
}
|
||||
|
||||
public void Combine(World world, IEnumerable<Actor> newSelection, bool isCombine, bool isClick)
|
||||
{
|
||||
var oldSelection = actors.AsEnumerable();
|
||||
|
||||
@@ -13,6 +13,6 @@ namespace OpenRA.Server
|
||||
public static class ProtocolVersion
|
||||
{
|
||||
// you *must* increment this whenever you make an incompatible protocol change
|
||||
public static readonly int Version = 5;
|
||||
public static readonly int Version = 6;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,10 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.Network;
|
||||
|
||||
namespace OpenRA.Server
|
||||
{
|
||||
@@ -31,7 +32,6 @@ namespace OpenRA.Server
|
||||
static Session lobbyInfo;
|
||||
static bool GameStarted = false;
|
||||
static string Name;
|
||||
static WebClient wc = new WebClient();
|
||||
static int ExternalPort;
|
||||
static int randomSeed;
|
||||
|
||||
@@ -44,25 +44,29 @@ namespace OpenRA.Server
|
||||
static bool isInternetServer;
|
||||
static string masterServerUrl;
|
||||
static bool isInitialPing;
|
||||
static ModData ModData;
|
||||
static Map Map;
|
||||
|
||||
public static void ServerMain(bool internetServer, string masterServerUrl, string name, int port, int extport,
|
||||
string[] mods, string map, bool cheats)
|
||||
public static void ServerMain(ModData modData, Settings settings, string map)
|
||||
{
|
||||
Log.AddChannel("server", "server.log", false, false);
|
||||
Log.AddChannel("server", "server.log");
|
||||
|
||||
isInitialPing = true;
|
||||
Server.masterServerUrl = masterServerUrl;
|
||||
isInternetServer = internetServer;
|
||||
listener = new TcpListener(IPAddress.Any, port);
|
||||
Name = name;
|
||||
ExternalPort = extport;
|
||||
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 = mods;
|
||||
lobbyInfo.GlobalSettings.Mods = settings.Game.Mods;
|
||||
lobbyInfo.GlobalSettings.RandomSeed = randomSeed;
|
||||
lobbyInfo.GlobalSettings.Map = map;
|
||||
lobbyInfo.GlobalSettings.AllowCheats = cheats;
|
||||
lobbyInfo.GlobalSettings.AllowCheats = settings.Server.AllowCheats;
|
||||
|
||||
LoadMap(); // populates the Session's slots, too.
|
||||
|
||||
Log.Write("server", "Initial mods: ");
|
||||
foreach( var m in lobbyInfo.GlobalSettings.Mods )
|
||||
@@ -108,8 +112,47 @@ 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.
|
||||
*/
|
||||
|
||||
static int ChooseFreePlayerIndex()
|
||||
{
|
||||
for (var i = 0; i < 8; i++)
|
||||
@@ -119,6 +162,11 @@ namespace OpenRA.Server
|
||||
throw new InvalidOperationException("Already got 8 players");
|
||||
}
|
||||
|
||||
static int ChooseFreeSlot()
|
||||
{
|
||||
return lobbyInfo.Slots.First(s => !s.Closed && s.Bot == null).Index;
|
||||
}
|
||||
|
||||
static void AcceptConnection()
|
||||
{
|
||||
var newConn = new Connection { socket = listener.AcceptSocket() };
|
||||
@@ -141,18 +189,19 @@ namespace OpenRA.Server
|
||||
newConn.socket.Send(BitConverter.GetBytes(newConn.PlayerIndex));
|
||||
conns.Add(newConn);
|
||||
|
||||
var defaults = new GameRules.UserSettings();
|
||||
var defaults = new GameRules.PlayerSettings();
|
||||
lobbyInfo.Clients.Add(
|
||||
new Session.Client()
|
||||
{
|
||||
Index = newConn.PlayerIndex,
|
||||
Color1 = defaults.PlayerColor1,
|
||||
Color2 = defaults.PlayerColor2,
|
||||
Name = defaults.PlayerName,
|
||||
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}",
|
||||
@@ -330,6 +379,103 @@ namespace OpenRA.Server
|
||||
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 =>
|
||||
{
|
||||
@@ -339,6 +485,8 @@ namespace OpenRA.Server
|
||||
return true;
|
||||
}
|
||||
lobbyInfo.GlobalSettings.Map = s;
|
||||
LoadMap();
|
||||
|
||||
foreach(var client in lobbyInfo.Clients)
|
||||
{
|
||||
client.SpawnPoint = 0;
|
||||
@@ -411,7 +559,9 @@ namespace OpenRA.Server
|
||||
};
|
||||
}
|
||||
break;
|
||||
case "Chat": case "TeamChat":
|
||||
|
||||
case "Chat":
|
||||
case "TeamChat":
|
||||
foreach (var c in conns.Except(conn).ToArray())
|
||||
DispatchOrdersToClient(c, GetClient(conn).Index, 0, so.Serialize());
|
||||
break;
|
||||
@@ -438,14 +588,8 @@ namespace OpenRA.Server
|
||||
|
||||
static void SyncLobbyInfo()
|
||||
{
|
||||
var clientData = lobbyInfo.Clients.ToDictionary(
|
||||
a => a.Index.ToString(),
|
||||
a => FieldSaver.Save(a));
|
||||
|
||||
clientData["GlobalSettings"] = FieldSaver.Save(lobbyInfo.GlobalSettings);
|
||||
|
||||
DispatchOrders(null, 0,
|
||||
new ServerOrder("SyncInfo", clientData.WriteToString()).Serialize());
|
||||
new ServerOrder("SyncInfo", lobbyInfo.Serialize()).Serialize());
|
||||
|
||||
PingMasterServer();
|
||||
}
|
||||
@@ -468,7 +612,7 @@ namespace OpenRA.Server
|
||||
|
||||
using (var wc = new WebClient())
|
||||
{
|
||||
var result = wc.DownloadData(
|
||||
wc.DownloadData(
|
||||
masterServerUrl + url.F(
|
||||
ExternalPort, Uri.EscapeUriString(Name),
|
||||
GameStarted ? 2 : 1, // todo: post-game states, etc.
|
||||
@@ -479,14 +623,8 @@ namespace OpenRA.Server
|
||||
if (isInitialPing)
|
||||
{
|
||||
isInitialPing = false;
|
||||
|
||||
var s = Encoding.UTF8.GetString(result);
|
||||
int gameId;
|
||||
if (int.TryParse(s.Trim(), out gameId))
|
||||
Game.SetGameId(gameId);
|
||||
|
||||
lock (masterServerMessages)
|
||||
masterServerMessages.Enqueue("Master server communication established. Game ID = {0}".F(gameId));
|
||||
masterServerMessages.Enqueue("Master server communication established.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ namespace OpenRA
|
||||
soundEngine.StopSound(video);
|
||||
}
|
||||
|
||||
public static bool MusicPlaying { get; private set; }
|
||||
public static void PlayMusic(string name)
|
||||
{
|
||||
if (name == "" || name == null)
|
||||
@@ -115,22 +116,35 @@ namespace OpenRA
|
||||
StopMusic();
|
||||
|
||||
currentMusic = name;
|
||||
MusicPlaying = true;
|
||||
var sound = sounds[name];
|
||||
music = soundEngine.Play2D(sound, true, true, float2.Zero, MusicVolume);
|
||||
}
|
||||
|
||||
public static void PlayMusic()
|
||||
{
|
||||
if (music == null)
|
||||
return;
|
||||
MusicPlaying = true;
|
||||
soundEngine.PauseSound(music, false);
|
||||
}
|
||||
|
||||
public static void StopMusic()
|
||||
{
|
||||
if (music != null)
|
||||
soundEngine.StopSound(music);
|
||||
|
||||
MusicPlaying = false;
|
||||
currentMusic = null;
|
||||
}
|
||||
|
||||
public static void PauseMusic()
|
||||
{
|
||||
if (music != null)
|
||||
soundEngine.PauseSound(music, true);
|
||||
if (music == null)
|
||||
return;
|
||||
|
||||
MusicPlaying = false;
|
||||
soundEngine.PauseSound(music, true);
|
||||
}
|
||||
|
||||
public static float GlobalVolume
|
||||
@@ -141,20 +155,20 @@ namespace OpenRA
|
||||
|
||||
public static float SoundVolume
|
||||
{
|
||||
get { return Game.Settings.SoundVolume; }
|
||||
get { return Game.Settings.Sound.SoundVolume; }
|
||||
set
|
||||
{
|
||||
Game.Settings.SoundVolume = value;
|
||||
Game.Settings.Sound.SoundVolume = value;
|
||||
soundEngine.SetSoundVolume(value, music, video);
|
||||
}
|
||||
}
|
||||
|
||||
public static float MusicVolume
|
||||
{
|
||||
get { return Game.Settings.MusicVolume; }
|
||||
get { return Game.Settings.Sound.MusicVolume; }
|
||||
set
|
||||
{
|
||||
Game.Settings.MusicVolume = value;
|
||||
Game.Settings.Sound.MusicVolume = value;
|
||||
if (music != null)
|
||||
music.Volume = value;
|
||||
}
|
||||
@@ -162,10 +176,10 @@ namespace OpenRA
|
||||
|
||||
public static float VideoVolume
|
||||
{
|
||||
get { return Game.Settings.VideoVolume; }
|
||||
get { return Game.Settings.Sound.VideoVolume; }
|
||||
set
|
||||
{
|
||||
Game.Settings.VideoVolume = value;
|
||||
Game.Settings.Sound.VideoVolume = value;
|
||||
if (video != null)
|
||||
video.Volume = value;
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@ using System.Text.RegularExpressions;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
public class Settings
|
||||
public class Arguments
|
||||
{
|
||||
Dictionary<string, string> settings = new Dictionary<string, string>();
|
||||
Dictionary<string, string> args = new Dictionary<string, string>();
|
||||
|
||||
public Settings(IEnumerable<string> src)
|
||||
public Arguments(IEnumerable<string> src)
|
||||
{
|
||||
Regex regex = new Regex("([^=]+)=(.*)");
|
||||
foreach (string s in src)
|
||||
@@ -26,13 +26,13 @@ namespace OpenRA
|
||||
if (m == null || !m.Success)
|
||||
continue;
|
||||
|
||||
settings.Add(m.Groups[1].Value, m.Groups[2].Value);
|
||||
args.Add(m.Groups[1].Value, m.Groups[2].Value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(string key) { return settings.ContainsKey(key); }
|
||||
public bool Contains(string key) { return args.ContainsKey(key); }
|
||||
|
||||
public string GetValue(string key, string defaultValue) { return Contains(key) ? settings[key] : defaultValue; }
|
||||
public string GetValue(string key, string defaultValue) { return Contains(key) ? args[key] : defaultValue; }
|
||||
|
||||
public int GetValue(string key, int defaultValue)
|
||||
{
|
||||
@@ -36,17 +36,16 @@ namespace OpenRA
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
Log.AddChannel("exception", "openra.exception.txt", true, false);
|
||||
Log.AddChannel("exception", "exception.log");
|
||||
Log.Write("exception", "{0}", e.ToString());
|
||||
if (!Game.Settings.DeveloperMode || ( Game.Settings.DeveloperMode && Game.GetGameId() != 0) )
|
||||
Log.Upload(Game.GetGameId());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
static void Run( string[] args )
|
||||
{
|
||||
Game.Initialize( new Settings( args ) );
|
||||
Game.Initialize( new Arguments(args) );
|
||||
GC.Collect();
|
||||
Game.Run();
|
||||
}
|
||||
}
|
||||
|
||||
175
OpenRA.Game/TraitDictionary.cs
Executable file
175
OpenRA.Game/TraitDictionary.cs
Executable file
@@ -0,0 +1,175 @@
|
||||
#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 OpenRA.FileFormats;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
class TraitDictionary
|
||||
{
|
||||
Dictionary<Type, ITraitContainer> traits = new Dictionary<Type, ITraitContainer>();
|
||||
|
||||
ITraitContainer InnerGet( Type t )
|
||||
{
|
||||
return traits.GetOrAdd( t, CreateTraitContainer );
|
||||
}
|
||||
|
||||
static ITraitContainer CreateTraitContainer( Type t )
|
||||
{
|
||||
return (ITraitContainer)typeof( TraitContainer<> ).MakeGenericType( t )
|
||||
.GetConstructor( new Type[ 0 ] ).Invoke( new object[ 0 ] );
|
||||
}
|
||||
|
||||
public void AddTrait( Actor actor, object val )
|
||||
{
|
||||
var t = val.GetType();
|
||||
|
||||
foreach( var i in t.GetInterfaces() )
|
||||
InnerAdd( actor, i, val );
|
||||
foreach( var tt in t.BaseTypes() )
|
||||
InnerAdd( actor, tt, val );
|
||||
}
|
||||
|
||||
void InnerAdd( Actor actor, Type t, object val )
|
||||
{
|
||||
InnerGet( t ).Add( actor, val );
|
||||
}
|
||||
|
||||
public bool Contains<T>( Actor actor )
|
||||
{
|
||||
if( actor.Destroyed )
|
||||
throw new InvalidOperationException( "Attempted to get trait from destroyed object ({0})".F( actor.ToString() ) );
|
||||
return ( (TraitContainer<T>)InnerGet( typeof( T ) ) ).GetMultiple( actor.ActorID ).Count() != 0;
|
||||
}
|
||||
|
||||
public T Get<T>( Actor actor )
|
||||
{
|
||||
if( actor.Destroyed )
|
||||
throw new InvalidOperationException( "Attempted to get trait from destroyed object ({0})".F( actor.ToString() ) );
|
||||
return ( (TraitContainer<T>)InnerGet( typeof( T ) ) ).Get( actor.ActorID );
|
||||
}
|
||||
|
||||
public T GetOrDefault<T>( Actor actor )
|
||||
{
|
||||
if( actor.Destroyed )
|
||||
throw new InvalidOperationException( "Attempted to get trait from destroyed object ({0})".F( actor.ToString() ) );
|
||||
return ( (TraitContainer<T>)InnerGet( typeof( T ) ) ).GetOrDefault( actor.ActorID );
|
||||
}
|
||||
|
||||
public IEnumerable<T> WithInterface<T>( Actor actor )
|
||||
{
|
||||
if( actor.Destroyed )
|
||||
throw new InvalidOperationException( "Attempted to get trait from destroyed object ({0})".F( actor.ToString() ) );
|
||||
return ( (TraitContainer<T>)InnerGet( typeof( T ) ) ).GetMultiple( actor.ActorID );
|
||||
}
|
||||
|
||||
public IEnumerable<TraitPair<T>> ActorsWithTraitMultiple<T>( World world )
|
||||
{
|
||||
return ( (TraitContainer<T>)InnerGet( typeof( T ) ) ).All();//.Where( x => x.Actor.IsInWorld );
|
||||
}
|
||||
|
||||
public void RemoveActor( Actor a )
|
||||
{
|
||||
foreach( var t in traits )
|
||||
t.Value.RemoveActor( a.ActorID );
|
||||
}
|
||||
|
||||
interface ITraitContainer
|
||||
{
|
||||
void Add( Actor actor, object trait );
|
||||
void RemoveActor( uint actor );
|
||||
}
|
||||
|
||||
class TraitContainer<T> : ITraitContainer
|
||||
{
|
||||
List<Actor> actors = new List<Actor>();
|
||||
List<T> traits = new List<T>();
|
||||
|
||||
public void Add( Actor actor, object trait )
|
||||
{
|
||||
var insertIndex = actors.BinarySearchMany( actor.ActorID + 1 );
|
||||
actors.Insert( insertIndex, actor );
|
||||
traits.Insert( insertIndex, (T)trait );
|
||||
}
|
||||
|
||||
public T Get( uint actor )
|
||||
{
|
||||
var index = actors.BinarySearchMany( actor );
|
||||
if( index >= actors.Count || actors[ index ].ActorID != actor )
|
||||
throw new InvalidOperationException( string.Format( "TraitDictionary does not contain instance of type `{0}`", typeof( T ) ) );
|
||||
else if( index + 1 < actors.Count && actors[ index + 1 ].ActorID == actor )
|
||||
throw new InvalidOperationException( string.Format( "TraitDictionary contains multiple instance of type `{0}`", typeof( T ) ) );
|
||||
else
|
||||
return traits[ index ];
|
||||
}
|
||||
|
||||
public T GetOrDefault( uint actor )
|
||||
{
|
||||
var index = actors.BinarySearchMany( actor );
|
||||
if( index >= actors.Count || actors[ index ].ActorID != actor )
|
||||
return default( T );
|
||||
else if( index + 1 < actors.Count && actors[ index + 1 ].ActorID == actor )
|
||||
throw new InvalidOperationException( string.Format( "TraitDictionary contains multiple instance of type `{0}`", typeof( T ) ) );
|
||||
else return traits[ index ];
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetMultiple( uint actor )
|
||||
{
|
||||
var index = actors.BinarySearchMany( actor );
|
||||
while( index < actors.Count && actors[ index ].ActorID == actor )
|
||||
{
|
||||
yield return traits[ index ];
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<TraitPair<T>> All()
|
||||
{
|
||||
for( int i = 0 ; i < actors.Count ; i++ )
|
||||
yield return new TraitPair<T> { Actor = actors[ i ], Trait = traits[ i ] };
|
||||
}
|
||||
|
||||
public void RemoveActor( uint actor )
|
||||
{
|
||||
for( int i = actors.Count - 1 ; i >= 0 ; i-- )
|
||||
{
|
||||
if( actors[ i ].ActorID == actor )
|
||||
{
|
||||
actors.RemoveAt( i );
|
||||
traits.RemoveAt( i );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class ListExts
|
||||
{
|
||||
public static int BinarySearchMany( this List<Actor> list, uint searchFor )
|
||||
{
|
||||
int start = 0;
|
||||
int end = list.Count;
|
||||
int mid = 0;
|
||||
while( start != end )
|
||||
{
|
||||
mid = ( start + end ) / 2;
|
||||
var c = list[ mid ].ActorID.CompareTo( searchFor );
|
||||
if( c < 0 )
|
||||
start = mid + 1;
|
||||
else
|
||||
end = mid;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,8 @@ namespace OpenRA.Traits.Activities
|
||||
public List<int2> path;
|
||||
Func<Actor, Mobile, List<int2>> getPath;
|
||||
public Actor ignoreBuilding;
|
||||
|
||||
bool cancellable = true;
|
||||
|
||||
MovePart move;
|
||||
int ticksBeforePathing;
|
||||
|
||||
@@ -37,21 +38,34 @@ namespace OpenRA.Traits.Activities
|
||||
Game.world.SharedRandom.Next(-spreadTicksBeforePathing, spreadTicksBeforePathing);
|
||||
}
|
||||
|
||||
// Scriptable move order
|
||||
// Ignores lane bias and nearby units
|
||||
public Move( int2 destination )
|
||||
: this()
|
||||
{
|
||||
this.getPath = (self,mobile) =>
|
||||
self.World.PathFinder.FindPath(
|
||||
PathSearch.FromPoint( self.World, mobile.Info, mobile.toCell, destination, false )
|
||||
.WithoutLaneBias());
|
||||
this.destination = destination;
|
||||
this.nearEnough = 0;
|
||||
this.cancellable = false;
|
||||
}
|
||||
|
||||
public Move( int2 destination, int nearEnough )
|
||||
: this()
|
||||
{
|
||||
this.getPath = (self,mobile) => self.World.PathFinder.FindUnitPath(
|
||||
mobile.toCell, destination, self );
|
||||
this.getPath = (self,mobile) => self.World.PathFinder.FindUnitPath( mobile.toCell, destination, self );
|
||||
this.destination = destination;
|
||||
this.nearEnough = nearEnough;
|
||||
}
|
||||
|
||||
|
||||
public Move(int2 destination, Actor ignoreBuilding)
|
||||
: this()
|
||||
{
|
||||
this.getPath = (self,mobile) =>
|
||||
self.World.PathFinder.FindPath(
|
||||
PathSearch.FromPoint( self, mobile.toCell, destination, false )
|
||||
PathSearch.FromPoint( self.World, mobile.Info, mobile.toCell, destination, false )
|
||||
.WithCustomBlocker( self.World.PathFinder.AvoidUnitsNear( mobile.toCell, 4, self ))
|
||||
.WithIgnoredBuilding( ignoreBuilding ));
|
||||
|
||||
@@ -218,6 +232,8 @@ namespace OpenRA.Traits.Activities
|
||||
|
||||
public void Cancel( Actor self )
|
||||
{
|
||||
if (!cancellable) return;
|
||||
|
||||
path = new List<int2>();
|
||||
NextActivity = null;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace OpenRA.Traits.Activities
|
||||
public IActivity Tick(Actor self)
|
||||
{
|
||||
if (isCanceled) return NextActivity;
|
||||
self.World.AddFrameEndTask(w => w.Remove(self));
|
||||
self.Destroy();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace OpenRA.Traits.Activities
|
||||
|
||||
foreach (var ns in self.TraitsImplementing<INotifySold>())
|
||||
ns.Sold(self);
|
||||
self.World.AddFrameEndTask( _ => self.World.Remove( self ) );
|
||||
self.Destroy();
|
||||
}
|
||||
|
||||
public IActivity Tick(Actor self)
|
||||
|
||||
52
OpenRA.Game/Traits/ActorStance.cs
Normal file
52
OpenRA.Game/Traits/ActorStance.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 OpenRA.FileFormats;
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
class ActorStanceInfo : ITraitInfo
|
||||
{
|
||||
public object Create(ActorInitializer init) { return new ActorStance(init); }
|
||||
}
|
||||
|
||||
public class ActorStance
|
||||
{
|
||||
// Stances modify default actor behavior
|
||||
public enum Stance
|
||||
{
|
||||
None,
|
||||
Guard, // Stay near an actor/area; attack anything that comes near
|
||||
Defend, // Come running if a bad guy comes in range of the defendee
|
||||
Hunt, // Go searching for things to kill; will stray from move orders etc to follow target
|
||||
Retreat // Actively avoid things which might kill me
|
||||
}
|
||||
|
||||
// Doesn't do anything... yet
|
||||
public ActorStance(ActorInitializer init) {}
|
||||
}
|
||||
|
||||
public class ActorStanceInit : IActorInit<ActorStance.Stance>
|
||||
{
|
||||
[FieldFromYamlKey]
|
||||
public readonly ActorStance.Stance value = ActorStance.Stance.None;
|
||||
|
||||
public ActorStanceInit() { }
|
||||
|
||||
public ActorStanceInit( ActorStance.Stance init )
|
||||
{
|
||||
value = init;
|
||||
}
|
||||
|
||||
public ActorStance.Stance Value( World world )
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
26
OpenRA.Game/Traits/Armor.cs
Normal file
26
OpenRA.Game/Traits/Armor.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
#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.Traits;
|
||||
using OpenRA.GameRules;
|
||||
using System.Collections.Generic;
|
||||
using OpenRA.FileFormats;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
public class ArmorInfo : ITraitInfo
|
||||
{
|
||||
public readonly string Type = null;
|
||||
public object Create (ActorInitializer init) { return new Armor(); }
|
||||
}
|
||||
public class Armor {}
|
||||
}
|
||||
|
||||
@@ -13,26 +13,37 @@ namespace OpenRA.Traits
|
||||
public class ValuedInfo : ITraitInfo
|
||||
{
|
||||
public readonly int Cost = 0;
|
||||
public readonly string Description = "";
|
||||
public readonly string LongDesc = "";
|
||||
public readonly string[] Owner = { };
|
||||
|
||||
public virtual object Create(ActorInitializer init) { return new Valued(); }
|
||||
public object Create(ActorInitializer init) { return new Valued(); }
|
||||
}
|
||||
|
||||
public class BuildableInfo : ValuedInfo
|
||||
|
||||
public class TooltipInfo : ITraitInfo
|
||||
{
|
||||
public readonly string Description = "";
|
||||
public readonly string Name = "";
|
||||
public readonly string Icon = null;
|
||||
public readonly string[] AlternateName = { };
|
||||
public object Create(ActorInitializer init) { return new Tooltip(); }
|
||||
}
|
||||
|
||||
public class BuildableInfo : ITraitInfo
|
||||
{
|
||||
[ActorReference]public readonly string[] Prerequisites = { };
|
||||
[ActorReference] public readonly string[] BuiltAt = { };
|
||||
|
||||
public readonly string Icon = null;
|
||||
public readonly string[] AlternateName = { };
|
||||
[ActorReference]
|
||||
public readonly string[] Prerequisites = { };
|
||||
[ActorReference]
|
||||
public readonly string[] BuiltAt = { };
|
||||
|
||||
public readonly string[] Owner = { };
|
||||
|
||||
public readonly string Queue;
|
||||
public readonly bool Hidden = false;
|
||||
|
||||
// todo: UI fluff; doesn't belong here
|
||||
public readonly int BuildPaletteOrder = 9999;
|
||||
public readonly string Hotkey = null;
|
||||
|
||||
public override object Create(ActorInitializer init) { return new Buildable(); }
|
||||
public object Create(ActorInitializer init) { return new Buildable(); }
|
||||
}
|
||||
|
||||
class Valued { } /* halfway to buildable */
|
||||
class Buildable { }
|
||||
class Valued { }
|
||||
class Buildable { }
|
||||
class Tooltip { }
|
||||
}
|
||||
|
||||
@@ -63,7 +63,6 @@ namespace OpenRA.Traits
|
||||
Game.Renderer.LineRenderer.DrawLine(p + new float2(1, 1), p + new float2(1, -1), c, c);
|
||||
Game.Renderer.LineRenderer.DrawLine(p + new float2(1, -1), p + new float2(-1, -1), c, c);
|
||||
}
|
||||
Game.Renderer.LineRenderer.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ namespace OpenRA.Traits
|
||||
public class HealthInfo : ITraitInfo
|
||||
{
|
||||
public readonly int HP = 0;
|
||||
public readonly ArmorType Armor = ArmorType.none;
|
||||
public virtual object Create(ActorInitializer init) { return new Health(init, this); }
|
||||
}
|
||||
|
||||
@@ -88,20 +87,6 @@ namespace OpenRA.Traits
|
||||
damage = (int)(damage * modifier);
|
||||
|
||||
hp -= damage;
|
||||
if (hp <= 0)
|
||||
{
|
||||
hp = 0;
|
||||
|
||||
attacker.Owner.Kills++;
|
||||
self.Owner.Deaths++;
|
||||
|
||||
if (RemoveOnDeath)
|
||||
self.World.AddFrameEndTask(w => w.Remove(self));
|
||||
|
||||
Log.Write("debug", "{0} #{1} killed by {2} #{3}", self.Info.Name, self.ActorID, attacker.Info.Name, attacker.ActorID);
|
||||
}
|
||||
|
||||
if (hp > MaxHP) hp = MaxHP;
|
||||
|
||||
foreach (var nd in self.TraitsImplementing<INotifyDamage>())
|
||||
nd.Damaged(self, new AttackInfo
|
||||
@@ -113,6 +98,21 @@ namespace OpenRA.Traits
|
||||
DamageStateChanged = this.DamageState != oldState,
|
||||
Warhead = warhead
|
||||
});
|
||||
|
||||
if (hp <= 0)
|
||||
{
|
||||
hp = 0;
|
||||
|
||||
attacker.Owner.Kills++;
|
||||
self.Owner.Deaths++;
|
||||
|
||||
if( RemoveOnDeath )
|
||||
self.Destroy();
|
||||
|
||||
Log.Write("debug", "{0} #{1} killed by {2} #{3}", self.Info.Name, self.ActorID, attacker.Info.Name, attacker.ActorID);
|
||||
}
|
||||
|
||||
if (hp > MaxHP) hp = MaxHP;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,74 +1,85 @@
|
||||
#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;
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.RA
|
||||
{
|
||||
class SpawnDefaultUnitsInfo : TraitInfo<SpawnDefaultUnits>
|
||||
{
|
||||
public readonly int InitialExploreRange = 5;
|
||||
}
|
||||
|
||||
class SpawnDefaultUnits : IGameStarted
|
||||
{
|
||||
public void GameStarted(World world)
|
||||
{
|
||||
var taken = Game.LobbyInfo.Clients.Where(c => c.SpawnPoint != 0)
|
||||
.Select(c => world.Map.SpawnPoints.ElementAt(c.SpawnPoint - 1)).ToList();
|
||||
|
||||
var available = world.Map.SpawnPoints.Except(taken).ToList();
|
||||
|
||||
foreach (var client in Game.LobbyInfo.Clients)
|
||||
using OpenRA.Network;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
public class MPStartLocationsInfo : TraitInfo<MPStartLocations>
|
||||
{
|
||||
public readonly int InitialExploreRange = 5;
|
||||
}
|
||||
|
||||
public class MPStartLocations : IWorldLoaded
|
||||
{
|
||||
public Dictionary<Player, int2> Start = new Dictionary<Player, int2>();
|
||||
|
||||
public void WorldLoaded(World world)
|
||||
{
|
||||
var taken = Game.LobbyInfo.Clients.Where(c => c.SpawnPoint != 0)
|
||||
.Select(c => world.Map.SpawnPoints.ElementAt(c.SpawnPoint - 1)).ToList();
|
||||
var available = world.Map.SpawnPoints.Except(taken).ToList();
|
||||
|
||||
// Set spawn
|
||||
foreach (var slot in Game.LobbyInfo.Slots)
|
||||
{
|
||||
SpawnUnitsForPlayer(world.players[client.Index],
|
||||
(client.SpawnPoint == 0)
|
||||
var client = Game.LobbyInfo.Clients.FirstOrDefault(c => c.Slot == slot.Index);
|
||||
var player = FindPlayerInSlot(world, slot);
|
||||
|
||||
if (player == null) continue;
|
||||
|
||||
var spid = (client == null || client.SpawnPoint == 0)
|
||||
? ChooseSpawnPoint(world, available, taken)
|
||||
: world.Map.SpawnPoints.ElementAt(client.SpawnPoint - 1));
|
||||
}
|
||||
: world.Map.SpawnPoints.ElementAt(client.SpawnPoint - 1);
|
||||
|
||||
Start.Add(player, spid);
|
||||
}
|
||||
|
||||
// Explore allied shroud
|
||||
foreach (var p in Start)
|
||||
if (p.Key == world.LocalPlayer || p.Key.Stances[world.LocalPlayer] == Stance.Ally)
|
||||
world.WorldActor.Trait<Shroud>().Explore(world, p.Value,
|
||||
world.WorldActor.Info.Traits.Get<MPStartLocationsInfo>().InitialExploreRange);
|
||||
|
||||
// Set viewport
|
||||
if (world.LocalPlayer != null && Start.ContainsKey(world.LocalPlayer))
|
||||
Game.viewport.Center(Start[world.LocalPlayer]);
|
||||
}
|
||||
|
||||
void SpawnUnitsForPlayer(Player p, int2 sp)
|
||||
static Player FindPlayerInSlot(World world, Session.Slot slot)
|
||||
{
|
||||
p.World.CreateActor("mcv", new TypeDictionary
|
||||
{
|
||||
new LocationInit( sp ),
|
||||
new OwnerInit( p ),
|
||||
});
|
||||
|
||||
if (p == p.World.LocalPlayer || p.Stances[p.World.LocalPlayer] == Stance.Ally)
|
||||
p.World.WorldActor.Trait<Shroud>().Explore(p.World, sp,
|
||||
p.World.WorldActor.Info.Traits.Get<SpawnDefaultUnitsInfo>().InitialExploreRange);
|
||||
}
|
||||
|
||||
static int2 ChooseSpawnPoint(World world, List<int2> available, List<int2> taken)
|
||||
{
|
||||
if (available.Count == 0)
|
||||
throw new InvalidOperationException("No free spawnpoint.");
|
||||
|
||||
var n = taken.Count == 0
|
||||
? world.SharedRandom.Next(available.Count)
|
||||
: available // pick the most distant spawnpoint from everyone else
|
||||
.Select((k, i) => Pair.New(k, i))
|
||||
.OrderByDescending(a => taken.Sum(t => (t - a.First).LengthSquared))
|
||||
.Select(a => a.Second)
|
||||
.First();
|
||||
|
||||
var sp = available[n];
|
||||
available.RemoveAt(n);
|
||||
taken.Add(sp);
|
||||
return sp;
|
||||
}
|
||||
}
|
||||
}
|
||||
return world.players.Values.FirstOrDefault(p => p.PlayerRef.Name == slot.MapPlayer);
|
||||
}
|
||||
|
||||
static int2 ChooseSpawnPoint(World world, List<int2> available, List<int2> taken)
|
||||
{
|
||||
if (available.Count == 0)
|
||||
throw new InvalidOperationException("No free spawnpoint.");
|
||||
|
||||
var n = taken.Count == 0
|
||||
? world.SharedRandom.Next(available.Count)
|
||||
: available // pick the most distant spawnpoint from everyone else
|
||||
.Select((k, i) => Pair.New(k, i))
|
||||
.OrderByDescending(a => taken.Sum(t => (t - a.First).LengthSquared))
|
||||
.Select(a => a.Second)
|
||||
.First();
|
||||
|
||||
var sp = available[n];
|
||||
available.RemoveAt(n);
|
||||
taken.Add(sp);
|
||||
return sp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,32 +14,49 @@ using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Effects;
|
||||
using OpenRA.Traits.Activities;
|
||||
using OpenRA.FileFormats;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
public class MobileInfo : ITraitInfo
|
||||
{
|
||||
public readonly string[] TerrainTypes;
|
||||
public readonly float[] TerrainSpeeds;
|
||||
public readonly string[] TerrainCostOverrides;
|
||||
public readonly float[] TerrainCosts;
|
||||
public readonly string[] Crushes;
|
||||
public readonly int WaitAverage = 60;
|
||||
public readonly int WaitSpread = 20;
|
||||
public readonly int InitialFacing = 128;
|
||||
public readonly int ROT = 255;
|
||||
public readonly int Speed = 1;
|
||||
[FieldLoader.LoadUsing( "LoadSpeeds" )]
|
||||
public readonly Dictionary<string, TerrainInfo> TerrainSpeeds;
|
||||
[FieldLoader.Load] public readonly string[] Crushes;
|
||||
[FieldLoader.Load] public readonly int WaitAverage = 60;
|
||||
[FieldLoader.Load] public readonly int WaitSpread = 20;
|
||||
[FieldLoader.Load] public readonly int InitialFacing = 128;
|
||||
[FieldLoader.Load] public readonly int ROT = 255;
|
||||
[FieldLoader.Load] public readonly int Speed = 1;
|
||||
[FieldLoader.Load] public readonly bool OnRails = false;
|
||||
|
||||
public virtual object Create(ActorInitializer init) { return new Mobile(init, this); }
|
||||
|
||||
static object LoadSpeeds( MiniYaml y )
|
||||
{
|
||||
Dictionary<string,TerrainInfo> ret = new Dictionary<string, TerrainInfo>();
|
||||
foreach (var t in y.NodesDict["TerrainSpeeds"].Nodes)
|
||||
{
|
||||
var speed = (float)FieldLoader.GetValue("speed", typeof(float),t.Value.Value);
|
||||
var cost = t.Value.NodesDict.ContainsKey("PathingCost") ? (float)FieldLoader.GetValue("cost", typeof(float), t.Value.NodesDict["PathingCost"].Value) : 1f/speed;
|
||||
ret.Add(t.Key, new TerrainInfo{Speed = speed, Cost = cost});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public class TerrainInfo
|
||||
{
|
||||
public float Cost = float.PositiveInfinity;
|
||||
public float Speed = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class Mobile : IIssueOrder, IResolveOrder, IOrderCursor, IOrderVoice, IOccupySpace, IMove, IFacing, INudge
|
||||
{
|
||||
public readonly Actor self;
|
||||
public readonly MobileInfo Info;
|
||||
public readonly Dictionary<string,float> TerrainCost;
|
||||
public readonly Dictionary<string,float> TerrainSpeed;
|
||||
|
||||
|
||||
[Sync]
|
||||
public int Facing { get; set; }
|
||||
[Sync]
|
||||
@@ -71,8 +88,6 @@ namespace OpenRA.Traits
|
||||
|
||||
Shroud shroud;
|
||||
UnitInfluence uim;
|
||||
BuildingInfluence bim;
|
||||
bool canShareCell;
|
||||
|
||||
public Mobile(ActorInitializer init, MobileInfo info)
|
||||
{
|
||||
@@ -81,8 +96,6 @@ namespace OpenRA.Traits
|
||||
|
||||
shroud = self.World.WorldActor.Trait<Shroud>();
|
||||
uim = self.World.WorldActor.Trait<UnitInfluence>();
|
||||
bim = self.World.WorldActor.Trait<BuildingInfluence>();
|
||||
canShareCell = self.HasTrait<SharesCell>();
|
||||
|
||||
if (init.Contains<LocationInit>())
|
||||
{
|
||||
@@ -92,26 +105,6 @@ namespace OpenRA.Traits
|
||||
|
||||
this.Facing = init.Contains<FacingInit>() ? init.Get<FacingInit,int>() : info.InitialFacing;
|
||||
this.Altitude = init.Contains<AltitudeInit>() ? init.Get<AltitudeInit,int>() : 0;
|
||||
|
||||
TerrainCost = new Dictionary<string, float>();
|
||||
TerrainSpeed = new Dictionary<string, float>();
|
||||
|
||||
if (info.TerrainTypes.Count() != info.TerrainSpeeds.Count())
|
||||
throw new InvalidOperationException("Mobile TerrainType/TerrainSpeed length mismatch");
|
||||
|
||||
if (info.TerrainCostOverrides != null)
|
||||
for (int i = 0; i < info.TerrainCostOverrides.Count(); i++)
|
||||
{
|
||||
TerrainCost.Add(info.TerrainCostOverrides[i], info.TerrainCosts[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < info.TerrainTypes.Count(); i++)
|
||||
{
|
||||
if (!TerrainCost.ContainsKey(info.TerrainTypes[i]))
|
||||
TerrainCost.Add(info.TerrainTypes[i], 1f/info.TerrainSpeeds[i]);
|
||||
|
||||
TerrainSpeed.Add(info.TerrainTypes[i], info.TerrainSpeeds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPosition(Actor self, int2 cell)
|
||||
@@ -122,11 +115,13 @@ namespace OpenRA.Traits
|
||||
|
||||
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
|
||||
{
|
||||
if (Info.OnRails) return null;
|
||||
|
||||
if (mi.Button == MouseButton.Left) return null;
|
||||
|
||||
// force-fire should *always* take precedence over move.
|
||||
if (mi.Modifiers.HasModifier(Modifiers.Ctrl)) return null;
|
||||
|
||||
|
||||
if (underCursor != null && underCursor.Owner != null)
|
||||
{
|
||||
// force-move
|
||||
@@ -139,24 +134,45 @@ namespace OpenRA.Traits
|
||||
return new Order("Move", self, xy, mi.Modifiers.HasModifier(Modifiers.Shift));
|
||||
}
|
||||
|
||||
public int2 NearestMoveableCell(int2 target)
|
||||
{
|
||||
if (CanEnterCell(target))
|
||||
return target;
|
||||
|
||||
var searched = new List<int2>(){};
|
||||
// Limit search to a radius of 10 tiles
|
||||
for (int r = 1; r < 10; r++)
|
||||
foreach (var tile in self.World.FindTilesInCircle(target,r).Except(searched))
|
||||
{
|
||||
if (CanEnterCell(tile))
|
||||
return tile;
|
||||
|
||||
searched.Add(tile);
|
||||
}
|
||||
|
||||
// Couldn't find a cell
|
||||
return target;
|
||||
}
|
||||
|
||||
public void ResolveOrder(Actor self, Order order)
|
||||
{
|
||||
if (order.OrderString == "Move")
|
||||
{
|
||||
if (CanEnterCell(order.TargetLocation))
|
||||
{
|
||||
if (self.Owner == self.World.LocalPlayer)
|
||||
self.World.AddFrameEndTask(w =>
|
||||
{
|
||||
w.Add(new MoveFlash(self.World, order.TargetLocation));
|
||||
var line = self.TraitOrDefault<DrawLineToTarget>();
|
||||
if (line != null)
|
||||
line.SetTarget(self, Target.FromOrder(order), Color.Green);
|
||||
});
|
||||
|
||||
if( !order.Queued ) self.CancelActivity();
|
||||
self.QueueActivity(new Activities.Move(order.TargetLocation, 8));
|
||||
}
|
||||
int2 currentLocation = NearestMoveableCell(order.TargetLocation);
|
||||
if (!CanEnterCell(currentLocation))
|
||||
return;
|
||||
|
||||
if( !order.Queued ) self.CancelActivity();
|
||||
self.QueueActivity(new Activities.Move(currentLocation, 8));
|
||||
|
||||
if (self.Owner == self.World.LocalPlayer)
|
||||
self.World.AddFrameEndTask(w =>
|
||||
{
|
||||
w.Add(new MoveFlash(self.World, order.TargetLocation));
|
||||
var line = self.TraitOrDefault<DrawLineToTarget>();
|
||||
if (line != null)
|
||||
line.SetTarget(self, Target.FromCell(currentLocation), Color.Green);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +193,7 @@ namespace OpenRA.Traits
|
||||
|
||||
public int2 TopLeft { get { return toCell; } }
|
||||
|
||||
public virtual IEnumerable<int2> OccupiedCells()
|
||||
public IEnumerable<int2> OccupiedCells()
|
||||
{
|
||||
return (fromCell == toCell)
|
||||
? new[] { fromCell }
|
||||
@@ -188,58 +204,60 @@ namespace OpenRA.Traits
|
||||
|
||||
public bool CanEnterCell(int2 p)
|
||||
{
|
||||
return CanEnterCell(p, null, true);
|
||||
return CanEnterCell( p, null, true);
|
||||
}
|
||||
|
||||
public virtual bool CanEnterCell(int2 cell, Actor ignoreActor, bool checkTransientActors)
|
||||
public static bool CanEnterCell( World world, MobileInfo mi, int2 cell, Actor ignoreActor, bool checkTransientActors )
|
||||
{
|
||||
if (MovementCostForCell(self, cell) == float.PositiveInfinity)
|
||||
var bim = world.WorldActor.Trait<BuildingInfluence>();
|
||||
var uim = world.WorldActor.Trait<UnitInfluence>();
|
||||
return Mobile.CanEnterCell( mi, world, uim, bim, cell, ignoreActor, checkTransientActors );
|
||||
}
|
||||
|
||||
public bool CanEnterCell( int2 cell, Actor ignoreActor, bool checkTransientActors )
|
||||
{
|
||||
var bim = self.World.WorldActor.Trait<BuildingInfluence>();
|
||||
var uim = self.World.WorldActor.Trait<UnitInfluence>();
|
||||
return CanEnterCell( Info, self.World, uim, bim, cell, ignoreActor, checkTransientActors );
|
||||
}
|
||||
|
||||
public static bool CanEnterCell( MobileInfo mobileInfo, World world, UnitInfluence uim, BuildingInfluence bim, int2 cell, Actor ignoreActor, bool checkTransientActors )
|
||||
{
|
||||
if (MovementCostForCell(mobileInfo, world, cell) == float.PositiveInfinity)
|
||||
return false;
|
||||
|
||||
// Check for buildings
|
||||
var building = bim.GetBuildingBlocking(cell);
|
||||
if (building != null && building != ignoreActor)
|
||||
{
|
||||
if (Info.Crushes == null)
|
||||
if (mobileInfo.Crushes == null)
|
||||
return false;
|
||||
|
||||
var crushable = building.TraitsImplementing<ICrushable>();
|
||||
if (crushable.Count() == 0)
|
||||
return false;
|
||||
|
||||
if (!crushable.Any(b => b.CrushClasses.Intersect(Info.Crushes).Any()))
|
||||
if (!crushable.Any(b => b.CrushClasses.Intersect(mobileInfo.Crushes).Any()))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check mobile actors
|
||||
if (checkTransientActors && uim.AnyUnitsAt(cell))
|
||||
var blockingActors = uim.GetUnitsAt( cell ).Where( x => x != ignoreActor ).ToList();
|
||||
if (checkTransientActors && blockingActors.Count > 0)
|
||||
{
|
||||
var actors = uim.GetUnitsAt(cell).Where(a => a != self && a != ignoreActor).ToArray();
|
||||
var nonshareable = canShareCell ? actors : actors.Where(a => !a.HasTrait<SharesCell>()).ToArray();
|
||||
|
||||
if (canShareCell)
|
||||
{
|
||||
var shareable = actors.Where(a => a.HasTrait<SharesCell>());
|
||||
|
||||
// only allow 5 in a cell
|
||||
if (shareable.Count() >= 5)
|
||||
return false;
|
||||
}
|
||||
|
||||
// We can enter a cell with nonshareable units only if we can crush all of them
|
||||
if (Info.Crushes == null && nonshareable.Length > 0)
|
||||
if (mobileInfo.Crushes == null)
|
||||
return false;
|
||||
|
||||
if (nonshareable.Length > 0 && nonshareable.Any(a => !(a.HasTrait<ICrushable>() &&
|
||||
a.TraitsImplementing<ICrushable>().Any(b => b.CrushClasses.Intersect(Info.Crushes).Any()))))
|
||||
if (blockingActors.Any(a => !(a.HasTrait<ICrushable>() &&
|
||||
a.TraitsImplementing<ICrushable>().Any(b => b.CrushClasses.Intersect(mobileInfo.Crushes).Any()))))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual void FinishedMoving(Actor self)
|
||||
public void FinishedMoving(Actor self)
|
||||
{
|
||||
var crushable = uim.GetUnitsAt(toCell).Where(a => a != self && a.HasTrait<ICrushable>());
|
||||
foreach (var a in crushable)
|
||||
@@ -249,25 +267,36 @@ namespace OpenRA.Traits
|
||||
b.OnCrush(self);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual float MovementCostForCell(Actor self, int2 cell)
|
||||
{
|
||||
if (!self.World.Map.IsInMap(cell.X,cell.Y))
|
||||
return float.PositiveInfinity;
|
||||
|
||||
var type = self.World.GetTerrainType(cell);
|
||||
return TerrainCost[type];
|
||||
public float MovementCostForCell( Actor self, int2 cell )
|
||||
{
|
||||
return MovementCostForCell( Info, self.World, cell );
|
||||
}
|
||||
|
||||
public virtual float MovementSpeedForCell(Actor self, int2 cell)
|
||||
public static float MovementCostForCell(MobileInfo info, World world, int2 cell)
|
||||
{
|
||||
if (!world.Map.IsInMap(cell.X,cell.Y))
|
||||
return float.PositiveInfinity;
|
||||
|
||||
var type = world.GetTerrainType(cell);
|
||||
if (!info.TerrainSpeeds.ContainsKey(type))
|
||||
return float.PositiveInfinity;
|
||||
|
||||
return info.TerrainSpeeds[type].Cost;
|
||||
}
|
||||
|
||||
public float MovementSpeedForCell(Actor self, int2 cell)
|
||||
{
|
||||
var type = self.World.GetTerrainType(cell);
|
||||
|
||||
|
||||
if (!Info.TerrainSpeeds.ContainsKey(type))
|
||||
return 0;
|
||||
|
||||
var modifier = self
|
||||
.TraitsImplementing<ISpeedModifier>()
|
||||
.Select(t => t.GetSpeedModifier())
|
||||
.Product();
|
||||
return Info.Speed * TerrainSpeed[type] * modifier;
|
||||
return Info.Speed * Info.TerrainSpeeds[type].Speed * modifier;
|
||||
}
|
||||
|
||||
public IEnumerable<float2> GetCurrentPath(Actor self)
|
||||
@@ -278,12 +307,12 @@ namespace OpenRA.Traits
|
||||
return Enumerable.Reverse(move.path).Select( c => Util.CenterOfCell(c) );
|
||||
}
|
||||
|
||||
public virtual void AddInfluence()
|
||||
public void AddInfluence()
|
||||
{
|
||||
uim.Add( self, this );
|
||||
}
|
||||
|
||||
public virtual void RemoveInfluence()
|
||||
public void RemoveInfluence()
|
||||
{
|
||||
uim.Remove( self, this );
|
||||
}
|
||||
|
||||
@@ -25,11 +25,11 @@ namespace OpenRA.Traits
|
||||
public class DeveloperMode : IResolveOrder
|
||||
{
|
||||
DeveloperModeInfo Info;
|
||||
[Sync]
|
||||
public bool FastCharge;
|
||||
public bool FastBuild;
|
||||
public bool DisableShroud;
|
||||
public bool PathDebug;
|
||||
[Sync] public bool FastCharge;
|
||||
[Sync] public bool AllTech;
|
||||
[Sync] public bool FastBuild;
|
||||
[Sync] public bool DisableShroud;
|
||||
[Sync] public bool PathDebug;
|
||||
|
||||
public DeveloperMode(DeveloperModeInfo info)
|
||||
{
|
||||
@@ -46,6 +46,11 @@ namespace OpenRA.Traits
|
||||
|
||||
switch(order.OrderString)
|
||||
{
|
||||
case "DevEnableTech":
|
||||
{
|
||||
AllTech ^= true;
|
||||
break;
|
||||
}
|
||||
case "DevFastCharge":
|
||||
{
|
||||
FastCharge ^= true;
|
||||
@@ -79,8 +84,14 @@ namespace OpenRA.Traits
|
||||
case "DevUnitDebug":
|
||||
{
|
||||
if (self.World.LocalPlayer == self.Owner)
|
||||
Game.Settings.UnitDebug ^= true;
|
||||
Game.Settings.Debug.ShowCollisions ^= true;
|
||||
break;
|
||||
}
|
||||
case "DevGiveExploration":
|
||||
{
|
||||
if (self.World.LocalPlayer == self.Owner)
|
||||
self.World.WorldActor.Trait<Shroud>().ExploreAll(self.World);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,14 +26,20 @@ namespace OpenRA.Traits
|
||||
self.World.AddFrameEndTask(w =>
|
||||
{
|
||||
var prevItems = GetNumBuildables(self.Owner);
|
||||
|
||||
var queue = self.Trait<ProductionQueue>();
|
||||
var unit = Rules.Info[order.TargetString];
|
||||
var producing = queue.CurrentItem(unit.Category);
|
||||
|
||||
if (producing == null || producing.Item != order.TargetString || producing.RemainingTime != 0)
|
||||
|
||||
// Find the queue with the target actor
|
||||
var queue = w.Queries.WithTraitMultiple<ProductionQueue>()
|
||||
.Where(p => p.Actor.Owner == self.Owner &&
|
||||
p.Trait.CurrentItem() != null &&
|
||||
p.Trait.CurrentItem().Item == order.TargetString &&
|
||||
p.Trait.CurrentItem().RemainingTime == 0)
|
||||
.Select(p => p.Trait)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (queue == null)
|
||||
return;
|
||||
|
||||
|
||||
var unit = Rules.Info[order.TargetString];
|
||||
var buildingInfo = unit.Traits.Get<BuildingInfo>();
|
||||
|
||||
if (order.OrderString == "LineBuild")
|
||||
@@ -66,7 +72,7 @@ namespace OpenRA.Traits
|
||||
|
||||
PlayBuildAnim( self, unit );
|
||||
|
||||
queue.FinishProduction(unit.Category);
|
||||
queue.FinishProduction();
|
||||
|
||||
if (GetNumBuildables(self.Owner) > prevItems)
|
||||
w.Add(new DelayedAction(10,
|
||||
@@ -79,8 +85,12 @@ namespace OpenRA.Traits
|
||||
// finds a construction yard (or equivalent) and runs its "build" animation.
|
||||
static void PlayBuildAnim( Actor self, ActorInfo unit )
|
||||
{
|
||||
var bi = unit.Traits.GetOrDefault<BuildableInfo>();
|
||||
if (bi == null)
|
||||
return;
|
||||
|
||||
var producers = self.World.Queries.OwnedBy[ self.Owner ].WithTrait<Production>()
|
||||
.Where( x => x.Actor.Info.Traits.Get<ProductionInfo>().Produces.Contains( unit.Category ) )
|
||||
.Where( x => x.Actor.Info.Traits.Get<ProductionInfo>().Produces.Contains( bi.Queue ) )
|
||||
.ToList();
|
||||
var producer = producers.Where( x => x.Actor.IsPrimaryBuilding() ).Concat( producers )
|
||||
.FirstOrDefault();
|
||||
@@ -92,7 +102,10 @@ namespace OpenRA.Traits
|
||||
static int GetNumBuildables(Player p)
|
||||
{
|
||||
if (p != p.World.LocalPlayer) return 0; // this only matters for local players.
|
||||
return Rules.TechTree.BuildableItems(p, Rules.Categories().ToArray()).Count();
|
||||
|
||||
return p.World.Queries.WithTraitMultiple<ProductionQueue>()
|
||||
.Where(a => a.Actor.Owner == p)
|
||||
.SelectMany(a => a.Trait.BuildableItems()).Distinct().Count();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,10 +54,11 @@ namespace OpenRA.Traits
|
||||
const float displayCashFracPerFrame = .07f;
|
||||
const int displayCashDeltaPerFrame = 37;
|
||||
int nextSiloAdviceTime = 0;
|
||||
|
||||
void TickOre(Actor self)
|
||||
{
|
||||
OreCapacity = self.World.Queries.OwnedBy[Owner].WithTrait<IStoreOre>()
|
||||
.Sum(a => a.Actor.TraitsImplementing<IStoreOre>().Sum(b => b.Capacity));
|
||||
.Sum(a => a.Trait.Capacity);
|
||||
|
||||
if (Ore > OreCapacity)
|
||||
Ore = OreCapacity;
|
||||
|
||||
@@ -17,32 +17,124 @@ namespace OpenRA.Traits
|
||||
{
|
||||
public class ProductionQueueInfo : ITraitInfo
|
||||
{
|
||||
public readonly string Type = null;
|
||||
public float BuildSpeed = 0.4f;
|
||||
public readonly int LowPowerSlowdown = 3;
|
||||
public object Create(ActorInitializer init) { return new ProductionQueue(init.self); }
|
||||
public object Create(ActorInitializer init) { return new ProductionQueue(init.self, this); }
|
||||
}
|
||||
|
||||
public class ProductionQueue : IResolveOrder, ITick
|
||||
public class ProductionQueue : IResolveOrder, ITick, ITechTreeElement
|
||||
{
|
||||
Actor self;
|
||||
public readonly Actor self;
|
||||
public ProductionQueueInfo Info;
|
||||
|
||||
// TODO: sync these
|
||||
// A list of things we are currently building
|
||||
List<ProductionItem> Queue = new List<ProductionItem>();
|
||||
|
||||
// A list of things we could possibly build, even if our race doesn't normally get it
|
||||
Dictionary<ActorInfo, ProductionState> Produceable = new Dictionary<ActorInfo, ProductionState>();
|
||||
|
||||
public ProductionQueue( Actor self )
|
||||
public ProductionQueue( Actor self, ProductionQueueInfo info )
|
||||
{
|
||||
this.self = self;
|
||||
this.Info = info;
|
||||
}
|
||||
|
||||
// Trait initialization bites us when queue lives on PlayerActor; delay init until first tick
|
||||
bool initialized = false;
|
||||
void Initialize()
|
||||
{
|
||||
initialized = true;
|
||||
var ttc = self.Owner.PlayerActor.Trait<TechTreeCache>();
|
||||
foreach (var a in Rules.TechTree.AllBuildables(Info.Type))
|
||||
{
|
||||
var bi = a.Traits.Get<BuildableInfo>();
|
||||
// Can our race build this by satisfying normal prereqs?
|
||||
var buildable = bi.Owner.Contains(self.Owner.Country.Race);
|
||||
Produceable.Add( a, new ProductionState(){ Visible = buildable && !bi.Hidden } );
|
||||
if (buildable)
|
||||
ttc.Add( a.Name, a.Traits.Get<BuildableInfo>().Prerequisites.ToList(), this );
|
||||
}
|
||||
}
|
||||
|
||||
public void OverrideProduction(ActorInfo type, bool buildable)
|
||||
{
|
||||
Produceable[type].Buildable = buildable;
|
||||
Produceable[type].Sticky = true;
|
||||
}
|
||||
|
||||
public void PrerequisitesAvailable(string key)
|
||||
{
|
||||
var ps = Produceable[ Rules.Info[key] ];
|
||||
if (!ps.Sticky)
|
||||
ps.Buildable = true;
|
||||
}
|
||||
|
||||
public void PrerequisitesUnavailable(string key)
|
||||
{
|
||||
var ps = Produceable[ Rules.Info[key] ];
|
||||
if (!ps.Sticky)
|
||||
ps.Buildable = false;
|
||||
}
|
||||
|
||||
public void Tick( Actor self )
|
||||
public ProductionItem CurrentItem()
|
||||
{
|
||||
foreach( var p in production.OrderBy( p => p.Key ) )
|
||||
return Queue.ElementAtOrDefault(0);
|
||||
}
|
||||
|
||||
public IEnumerable<ProductionItem> AllQueued()
|
||||
{
|
||||
return Queue;
|
||||
}
|
||||
|
||||
ActorInfo[] None = new ActorInfo[]{};
|
||||
public IEnumerable<ActorInfo> AllItems()
|
||||
{
|
||||
if (!QueueActive)
|
||||
return None;
|
||||
|
||||
if (Game.LobbyInfo.GlobalSettings.AllowCheats && self.Owner.PlayerActor.Trait<DeveloperMode>().AllTech)
|
||||
return Produceable.Select(a => a.Key);
|
||||
|
||||
return Produceable.Where(a => a.Value.Buildable || a.Value.Visible).Select(a => a.Key);
|
||||
}
|
||||
|
||||
public IEnumerable<ActorInfo> BuildableItems()
|
||||
{
|
||||
if (!QueueActive)
|
||||
return None;
|
||||
|
||||
if (Game.LobbyInfo.GlobalSettings.AllowCheats && self.Owner.PlayerActor.Trait<DeveloperMode>().AllTech)
|
||||
return Produceable.Select(a => a.Key);
|
||||
|
||||
return Produceable.Where(a => a.Value.Buildable).Select(a => a.Key);
|
||||
}
|
||||
|
||||
public bool CanBuild(ActorInfo actor)
|
||||
{
|
||||
var buildings = Rules.TechTree.GatherBuildings( self.Owner );
|
||||
return Rules.TechTree.CanBuild(actor, self.Owner, buildings);
|
||||
}
|
||||
|
||||
[Sync] bool QueueActive = true;
|
||||
public void Tick( Actor self )
|
||||
{
|
||||
if (!initialized)
|
||||
Initialize();
|
||||
|
||||
if (self == self.Owner.PlayerActor)
|
||||
QueueActive = self.World.Queries.OwnedBy[self.Owner].WithTrait<Production>()
|
||||
.Where(x => x.Trait.Info.Produces.Contains(Info.Type))
|
||||
.Any();
|
||||
|
||||
while( Queue.Count > 0 && !BuildableItems().Any(b => b.Name == Queue[ 0 ].Item) )
|
||||
{
|
||||
while( p.Value.Count > 0 && !Rules.TechTree.BuildableItems( self.Owner, p.Key ).Contains( p.Value[ 0 ].Item ) )
|
||||
{
|
||||
self.Owner.PlayerActor.Trait<PlayerResources>().GiveCash(p.Value[0].TotalCost - p.Value[0].RemainingCost); // refund what's been paid so far.
|
||||
FinishProduction(p.Key);
|
||||
}
|
||||
if( p.Value.Count > 0 )
|
||||
( p.Value )[ 0 ].Tick( self.Owner );
|
||||
self.Owner.PlayerActor.Trait<PlayerResources>().GiveCash(Queue[0].TotalCost - Queue[0].RemainingCost); // refund what's been paid so far.
|
||||
FinishProduction();
|
||||
}
|
||||
if( Queue.Count > 0 )
|
||||
Queue[ 0 ].Tick( self.Owner );
|
||||
}
|
||||
|
||||
public void ResolveOrder( Actor self, Order order )
|
||||
@@ -51,19 +143,22 @@ namespace OpenRA.Traits
|
||||
{
|
||||
case "StartProduction":
|
||||
{
|
||||
var unit = Rules.Info[order.TargetString];
|
||||
var bi = unit.Traits.Get<BuildableInfo>();
|
||||
if (bi.Queue != Info.Type)
|
||||
return; /* Not built by this queue */
|
||||
|
||||
var cost = unit.Traits.Contains<ValuedInfo>() ? unit.Traits.Get<ValuedInfo>().Cost : 0;
|
||||
var time = GetBuildTime(order.TargetString);
|
||||
|
||||
if (!BuildableItems().Any(b => b.Name == order.TargetString))
|
||||
return; /* you can't build that!! */
|
||||
|
||||
bool hasPlayedSound = false;
|
||||
|
||||
for (var n = 0; n < order.TargetLocation.X; n++) // repeat count
|
||||
{
|
||||
var unit = Rules.Info[order.TargetString];
|
||||
var ui = unit.Traits.Get<BuildableInfo>();
|
||||
var time = GetBuildTime(self, order.TargetString);
|
||||
|
||||
if (!Rules.TechTree.BuildableItems(order.Player, unit.Category).Contains(order.TargetString))
|
||||
return; /* you can't build that!! */
|
||||
|
||||
bool hasPlayedSound = false;
|
||||
|
||||
BeginProduction(unit.Category,
|
||||
new ProductionItem(order.TargetString, (int)time, ui.Cost,
|
||||
BeginProduction(new ProductionItem(this, order.TargetString, (int)time, cost,
|
||||
() => self.World.AddFrameEndTask(
|
||||
_ =>
|
||||
{
|
||||
@@ -82,9 +177,8 @@ namespace OpenRA.Traits
|
||||
}
|
||||
case "PauseProduction":
|
||||
{
|
||||
var producing = CurrentItem( Rules.Info[ order.TargetString ].Category );
|
||||
if( producing != null && producing.Item == order.TargetString )
|
||||
producing.Paused = ( order.TargetLocation.X != 0 );
|
||||
if( Queue.Count > 0 && Queue[0].Item == order.TargetString )
|
||||
Queue[0].Paused = ( order.TargetLocation.X != 0 );
|
||||
break;
|
||||
}
|
||||
case "CancelProduction":
|
||||
@@ -95,65 +189,46 @@ namespace OpenRA.Traits
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetBuildTime(Actor self, String unitString)
|
||||
public int GetBuildTime(String unitString)
|
||||
{
|
||||
var unit = Rules.Info[unitString];
|
||||
if (unit == null || ! unit.Traits.Contains<BuildableInfo>())
|
||||
return 0;
|
||||
|
||||
if (Game.LobbyInfo.GlobalSettings.AllowCheats && self.Trait<DeveloperMode>().FastBuild) return 0;
|
||||
var ui = unit.Traits.Get<BuildableInfo>();
|
||||
var time = ui.Cost
|
||||
* self.Owner.PlayerActor.Info.Traits.Get<ProductionQueueInfo>().BuildSpeed /* todo: country-specific build speed bonus */
|
||||
if (Game.LobbyInfo.GlobalSettings.AllowCheats && self.Owner.PlayerActor.Trait<DeveloperMode>().FastBuild) return 0;
|
||||
var cost = unit.Traits.Contains<ValuedInfo>() ? unit.Traits.Get<ValuedInfo>().Cost : 0;
|
||||
var time = cost
|
||||
* Info.BuildSpeed
|
||||
* (25 * 60) /* frames per min */ /* todo: build acceleration, if we do that */
|
||||
/ 1000;
|
||||
return (int) time;
|
||||
}
|
||||
|
||||
// Key: Production category.
|
||||
// TODO: sync this
|
||||
readonly Cache<string, List<ProductionItem>> production
|
||||
= new Cache<string, List<ProductionItem>>( _ => new List<ProductionItem>() );
|
||||
|
||||
public ProductionItem CurrentItem(string category)
|
||||
{
|
||||
return production[category].ElementAtOrDefault(0);
|
||||
}
|
||||
|
||||
public IEnumerable<ProductionItem> AllItems(string category)
|
||||
{
|
||||
return production[category];
|
||||
}
|
||||
|
||||
void CancelProduction( string itemName )
|
||||
{
|
||||
var category = Rules.Info[itemName].Category;
|
||||
var queue = production[ category ];
|
||||
if (queue.Count == 0) return;
|
||||
|
||||
var lastIndex = queue.FindLastIndex( a => a.Item == itemName );
|
||||
{
|
||||
if (Queue.Count == 0)
|
||||
return; // Nothing to do here
|
||||
|
||||
var lastIndex = Queue.FindLastIndex( a => a.Item == itemName );
|
||||
if (lastIndex > 0)
|
||||
{
|
||||
queue.RemoveAt(lastIndex);
|
||||
}
|
||||
Queue.RemoveAt(lastIndex);
|
||||
else if( lastIndex == 0 )
|
||||
{
|
||||
var item = queue[0];
|
||||
var item = Queue[0];
|
||||
self.Owner.PlayerActor.Trait<PlayerResources>().GiveCash(item.TotalCost - item.RemainingCost); // refund what's been paid so far.
|
||||
FinishProduction(category);
|
||||
FinishProduction();
|
||||
}
|
||||
}
|
||||
|
||||
public void FinishProduction( string category )
|
||||
public void FinishProduction()
|
||||
{
|
||||
var queue = production[category];
|
||||
if (queue.Count == 0) return;
|
||||
queue.RemoveAt(0);
|
||||
if (Queue.Count == 0) return;
|
||||
Queue.RemoveAt(0);
|
||||
}
|
||||
|
||||
void BeginProduction( string group, ProductionItem item )
|
||||
void BeginProduction( ProductionItem item )
|
||||
{
|
||||
production[group].Add(item);
|
||||
Queue.Add(item);
|
||||
}
|
||||
|
||||
static bool IsDisabledBuilding(Actor a)
|
||||
@@ -163,39 +238,54 @@ namespace OpenRA.Traits
|
||||
}
|
||||
|
||||
void BuildUnit( string name )
|
||||
{
|
||||
var newUnitType = Rules.Info[ name ];
|
||||
var producerTypes = Rules.TechTree.UnitBuiltAt( newUnitType );
|
||||
|
||||
var producers = self.World.Queries.OwnedBy[self.Owner]
|
||||
{
|
||||
if (self == self.Owner.PlayerActor)
|
||||
{
|
||||
// original ra behavior; queue lives on PlayerActor, need to find a production structure
|
||||
var producers = self.World.Queries.OwnedBy[self.Owner]
|
||||
.WithTrait<Production>()
|
||||
.Where(x => producerTypes.Contains(x.Actor.Info))
|
||||
.Where(x => x.Trait.Info.Produces.Contains(Info.Type))
|
||||
.OrderByDescending(x => x.Actor.IsPrimaryBuilding() ? 1 : 0 ) // prioritize the primary.
|
||||
.ToArray();
|
||||
|
||||
if (producers.Length == 0)
|
||||
{
|
||||
CancelProduction(name);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var p in producers)
|
||||
{
|
||||
if (IsDisabledBuilding(p.Actor)) continue;
|
||||
|
||||
if (p.Trait.Produce(p.Actor, newUnitType))
|
||||
if (producers.Length == 0)
|
||||
{
|
||||
FinishProduction(newUnitType.Category);
|
||||
break;
|
||||
CancelProduction(name);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var p in producers)
|
||||
{
|
||||
if (IsDisabledBuilding(p.Actor)) continue;
|
||||
|
||||
if (p.Trait.Produce(p.Actor, Rules.Info[ name ]))
|
||||
{
|
||||
FinishProduction();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// queue lives on actor; is produced at same actor
|
||||
var sp = self.TraitsImplementing<Production>().Where(p => p.Info.Produces.Contains(Info.Type)).FirstOrDefault();
|
||||
if (sp != null && !IsDisabledBuilding(self) && sp.Produce(self, Rules.Info[ name ]))
|
||||
FinishProduction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ProductionState
|
||||
{
|
||||
public bool Visible = false;
|
||||
public bool Buildable = false;
|
||||
public bool Sticky = false;
|
||||
}
|
||||
|
||||
public class ProductionItem
|
||||
{
|
||||
public readonly string Item;
|
||||
|
||||
public readonly ProductionQueue Queue;
|
||||
public readonly int TotalTime;
|
||||
public readonly int TotalCost;
|
||||
public int RemainingTime { get; private set; }
|
||||
@@ -206,13 +296,14 @@ namespace OpenRA.Traits
|
||||
|
||||
int slowdown = 0;
|
||||
|
||||
public ProductionItem(string item, int time, int cost, Action onComplete)
|
||||
public ProductionItem(ProductionQueue queue, string item, int time, int cost, Action onComplete)
|
||||
{
|
||||
if (time <= 0) time = 1;
|
||||
Item = item;
|
||||
RemainingTime = TotalTime = time;
|
||||
RemainingCost = TotalCost = cost;
|
||||
OnComplete = onComplete;
|
||||
Queue = queue;
|
||||
|
||||
Log.Write("debug", "new ProductionItem: {0} time={1} cost={2}", item, time, cost);
|
||||
}
|
||||
@@ -230,7 +321,7 @@ namespace OpenRA.Traits
|
||||
if (player.PlayerActor.Trait<PlayerResources>().GetPowerState() != PowerState.Normal)
|
||||
{
|
||||
if (--slowdown <= 0)
|
||||
slowdown = player.PlayerActor.Info.Traits.Get<ProductionQueueInfo>().LowPowerSlowdown;
|
||||
slowdown = Queue.Info.LowPowerSlowdown;
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -21,12 +21,15 @@ namespace OpenRA.Traits
|
||||
{
|
||||
class Watcher
|
||||
{
|
||||
public readonly List<ActorInfo> prerequisites;
|
||||
public readonly string key;
|
||||
// strings may be either actor type, or "alternate name" key
|
||||
public readonly List<string> prerequisites;
|
||||
public readonly ITechTreeElement watcher;
|
||||
bool hasPrerequisites;
|
||||
|
||||
public Watcher(List<ActorInfo> prerequisites, ITechTreeElement watcher)
|
||||
public Watcher(string key, List<string> prerequisites, ITechTreeElement watcher)
|
||||
{
|
||||
this.key = key;
|
||||
this.prerequisites = prerequisites;
|
||||
this.watcher = watcher;
|
||||
this.hasPrerequisites = false;
|
||||
@@ -34,15 +37,13 @@ namespace OpenRA.Traits
|
||||
|
||||
public void Tick( Player owner, Cache<string, List<Actor>> buildings )
|
||||
{
|
||||
var effectivePrereq = prerequisites.Where( a => a.Traits.Get<BuildableInfo>().Owner.Contains( owner.Country.Race ) );
|
||||
var nowHasPrerequisites = effectivePrereq.Any() &&
|
||||
effectivePrereq.All( a => buildings[ a.Name ].Any( b => !b.Trait<Building>().Disabled ) );
|
||||
var nowHasPrerequisites = prerequisites.All( a => buildings[ a ].Any( b => !b.Trait<Building>().Disabled ) );
|
||||
|
||||
if( nowHasPrerequisites && !hasPrerequisites )
|
||||
watcher.Available();
|
||||
watcher.PrerequisitesAvailable(key);
|
||||
|
||||
if( !nowHasPrerequisites && hasPrerequisites )
|
||||
watcher.Unavailable();
|
||||
watcher.PrerequisitesUnavailable(key);
|
||||
|
||||
hasPrerequisites = nowHasPrerequisites;
|
||||
}
|
||||
@@ -58,20 +59,20 @@ namespace OpenRA.Traits
|
||||
w.Tick( self.Owner, buildings );
|
||||
}
|
||||
|
||||
public void Add( List<ActorInfo> prerequisites, ITechTreeElement tte )
|
||||
public void Add( string key, List<string> prerequisites, ITechTreeElement tte )
|
||||
{
|
||||
watchers.Add( new Watcher( prerequisites, tte ) );
|
||||
watchers.Add( new Watcher( key, prerequisites, tte ) );
|
||||
}
|
||||
|
||||
public void Remove( ITechTreeElement tte )
|
||||
public void Remove( string key )
|
||||
{
|
||||
watchers.RemoveAll( x => x.watcher == tte );
|
||||
watchers.RemoveAll( x => x.key == key );
|
||||
}
|
||||
}
|
||||
|
||||
interface ITechTreeElement
|
||||
{
|
||||
void Available();
|
||||
void Unavailable();
|
||||
void PrerequisitesAvailable(string key);
|
||||
void PrerequisitesUnavailable(string key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,30 +16,37 @@ namespace OpenRA.Traits
|
||||
{
|
||||
public class ProductionInfo : ITraitInfo
|
||||
{
|
||||
public readonly float[] SpawnOffsets; // in px relative to CenterLocation
|
||||
public readonly int[] ExitCells; // in cells relative to TopLeft, supports a list for multiple exits
|
||||
public readonly string[] Produces = { };
|
||||
|
||||
public virtual object Create(ActorInitializer init) { return new Production(this); }
|
||||
}
|
||||
|
||||
public class ExitInfo : TraitInfo<Exit>
|
||||
{
|
||||
public readonly float2 SpawnOffset = float2.Zero; // in px relative to CenterLocation
|
||||
public readonly int2 ExitCell = int2.Zero; // in cells relative to TopLeft
|
||||
public readonly int Facing = -1;
|
||||
}
|
||||
public class Exit {}
|
||||
|
||||
public class Production
|
||||
{
|
||||
public readonly List<Pair<float2, int2>> Spawns = new List<Pair<float2, int2>>();
|
||||
{
|
||||
public ProductionInfo Info;
|
||||
public Production(ProductionInfo info)
|
||||
{
|
||||
if (info.SpawnOffsets == null || info.ExitCells == null)
|
||||
return;
|
||||
|
||||
if (info.SpawnOffsets.Length != info.ExitCells.Length)
|
||||
throw new System.InvalidOperationException("SpawnOffset, ExitCells length mismatch");
|
||||
|
||||
for (int i = 0; i < info.ExitCells.Length; i+=2)
|
||||
Spawns.Add(Pair.New(new float2(info.SpawnOffsets[i],info.SpawnOffsets[i+1]), new int2(info.ExitCells[i], info.ExitCells[i+1])));
|
||||
Info = info;
|
||||
}
|
||||
|
||||
public void DoProduction(Actor self, Actor newUnit, int2 exit, float2 spawn)
|
||||
public void DoProduction(Actor self, ActorInfo producee, ExitInfo exitinfo)
|
||||
{
|
||||
var newUnit = self.World.CreateActor(false, producee.Name, new TypeDictionary
|
||||
{
|
||||
new OwnerInit( self.Owner ),
|
||||
});
|
||||
|
||||
var exit = self.Location + exitinfo.ExitCell;
|
||||
var spawn = self.CenterLocation + exitinfo.SpawnOffset;
|
||||
|
||||
var move = newUnit.Trait<IMove>();
|
||||
var facing = newUnit.TraitOrDefault<IFacing>();
|
||||
|
||||
@@ -48,7 +55,7 @@ namespace OpenRA.Traits
|
||||
var to = Util.CenterOfCell(exit);
|
||||
newUnit.CenterLocation = spawn;
|
||||
if (facing != null)
|
||||
facing.Facing = Util.GetFacing(to - spawn, facing.Facing);
|
||||
facing.Facing = exitinfo.Facing < 0 ? Util.GetFacing(to - spawn, facing.Facing) : exitinfo.Facing;
|
||||
self.World.Add(newUnit);
|
||||
|
||||
// Animate the spawn -> exit transition
|
||||
@@ -84,27 +91,21 @@ namespace OpenRA.Traits
|
||||
|
||||
public virtual bool Produce( Actor self, ActorInfo producee )
|
||||
{
|
||||
var newUnit = self.World.CreateActor(false, producee.Name, new TypeDictionary
|
||||
{
|
||||
new OwnerInit( self.Owner ),
|
||||
});
|
||||
|
||||
// Todo: remove assumption on Mobile;
|
||||
// required for 3-arg CanEnterCell
|
||||
var mobile = newUnit.Trait<Mobile>();
|
||||
|
||||
//var mobile = newUnit.Trait<Mobile>();
|
||||
var mobileInfo = producee.Traits.Get<MobileInfo>();
|
||||
var bim = self.World.WorldActor.Trait<BuildingInfluence>();
|
||||
var uim = self.World.WorldActor.Trait<UnitInfluence>();
|
||||
|
||||
// Pick a spawn/exit point pair
|
||||
// Todo: Reorder in a synced random way
|
||||
foreach (var s in Spawns)
|
||||
{
|
||||
var exit = self.Location + s.Second;
|
||||
var spawn = self.CenterLocation + s.First;
|
||||
if (mobile.CanEnterCell(exit,self,true))
|
||||
foreach (var s in self.Info.Traits.WithInterface<ExitInfo>())
|
||||
if( Mobile.CanEnterCell( mobileInfo, self.World, uim, bim, self.Location + s.ExitCell,self,true ) )
|
||||
{
|
||||
DoProduction(self, newUnit, exit, spawn);
|
||||
DoProduction(self, producee, s);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,11 @@ namespace OpenRA.Traits
|
||||
|
||||
foreach (var pips in self.TraitsImplementing<IPips>())
|
||||
{
|
||||
foreach (var pip in pips.GetPips(self))
|
||||
var thisRow = pips.GetPips(self);
|
||||
if (thisRow == null)
|
||||
continue;
|
||||
|
||||
foreach (var pip in thisRow)
|
||||
{
|
||||
if (pipxyOffset.X+5 > self.GetBounds(false).Width)
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using OpenRA.FileFormats;
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
@@ -10,11 +11,19 @@
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
class SharesCellInfo : TraitInfo<SharesCell> {}
|
||||
class SharesCellInfo : ITraitInfo
|
||||
{
|
||||
public object Create(ActorInitializer init) { return new SharesCell(init); }
|
||||
}
|
||||
public class SharesCell : IOffsetCenterLocation
|
||||
{
|
||||
[Sync]
|
||||
public int Position;
|
||||
|
||||
public SharesCell(ActorInitializer init)
|
||||
{
|
||||
Position = init.Contains<SubcellInit>() ? init.Get<SubcellInit,int>() : 0;
|
||||
}
|
||||
|
||||
public float2 CenterOffset
|
||||
{ get {
|
||||
@@ -32,5 +41,23 @@ namespace OpenRA.Traits
|
||||
return new float2(-5f, -5f);
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
public class SubcellInit : IActorInit<int>
|
||||
{
|
||||
[FieldFromYamlKey]
|
||||
public readonly int value = 0;
|
||||
|
||||
public SubcellInit() { }
|
||||
|
||||
public SubcellInit( int init )
|
||||
{
|
||||
value = init;
|
||||
}
|
||||
|
||||
public int Value( World world )
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,9 @@ namespace OpenRA.Traits
|
||||
public bool IsUsed;
|
||||
public bool IsAvailable;
|
||||
public bool IsReady { get { return IsAvailable && RemainingTime == 0; } }
|
||||
public readonly Player Owner;
|
||||
|
||||
protected readonly Actor Self;
|
||||
protected readonly Player Owner;
|
||||
|
||||
bool notifiedCharging;
|
||||
bool notifiedReady;
|
||||
@@ -54,9 +56,10 @@ namespace OpenRA.Traits
|
||||
{
|
||||
Info = info;
|
||||
RemainingTime = TotalTime;
|
||||
Self = self;
|
||||
Owner = self.Owner;
|
||||
|
||||
self.Trait<TechTreeCache>().Add( Info.Prerequisites.Select( a => Rules.Info[ a.ToLowerInvariant() ] ).ToList(), this );
|
||||
self.Trait<TechTreeCache>().Add( Info.OrderName, Info.Prerequisites.Select( a => a.ToLowerInvariant() ).ToList(), this );
|
||||
}
|
||||
|
||||
public void Tick(Actor self)
|
||||
@@ -92,8 +95,7 @@ namespace OpenRA.Traits
|
||||
{
|
||||
var buildings = Rules.TechTree.GatherBuildings(Owner);
|
||||
var effectivePrereq = Info.Prerequisites
|
||||
.Select(a => a.ToLowerInvariant())
|
||||
.Where(a => Rules.Info[a].Traits.Get<ValuedInfo>().Owner.Contains(Owner.Country.Race));
|
||||
.Select(a => a.ToLowerInvariant());
|
||||
|
||||
if (Info.Prerequisites.Count() == 0)
|
||||
return Owner.PlayerActor.Trait<PlayerResources>().GetPowerState() == PowerState.Normal;
|
||||
@@ -143,12 +145,12 @@ namespace OpenRA.Traits
|
||||
|
||||
bool hasPrerequisites;
|
||||
|
||||
public void Available()
|
||||
public void PrerequisitesAvailable(string key)
|
||||
{
|
||||
hasPrerequisites = true;
|
||||
}
|
||||
|
||||
public void Unavailable()
|
||||
public void PrerequisitesUnavailable(string key)
|
||||
{
|
||||
hasPrerequisites = false;
|
||||
}
|
||||
|
||||
@@ -14,13 +14,23 @@ using System.Linq;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
public class TargetableInfo : TraitInfo<Targetable>
|
||||
public class TargetableInfo : ITraitInfo
|
||||
{
|
||||
public readonly string[] TargetTypes = {};
|
||||
public object Create( ActorInitializer init ) { return new Targetable(this); }
|
||||
}
|
||||
|
||||
public class Targetable
|
||||
public class Targetable : ITargetable
|
||||
{
|
||||
TargetableInfo Info;
|
||||
public Targetable(TargetableInfo info)
|
||||
{
|
||||
Info = info;
|
||||
}
|
||||
|
||||
public string[] TargetTypes
|
||||
{
|
||||
get { return Info.TargetTypes;}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user