Compare commits

...

726 Commits

Author SHA1 Message Date
Paul Chote
5c6a6ee350 Add snow.mix 2010-09-10 11:51:05 +12:00
Matthew
bbf2d68e9d Small fix to Windows installer. 2010-09-10 11:34:57 +12:00
Matthew
59cf76b4ae Retry dependency downloads in Windows installer. 2010-09-10 11:34:54 +12:00
Matthew
e97440819d Bring makefile and Windows installer in to line with pchote's mod folder structure changes. 2010-09-10 11:34:50 +12:00
Matthew
87cd37a30b Back up packages and remove mods dirs before installing. 2010-09-10 11:34:47 +12:00
Paul Chote
f406dfa277 scores.mix is optional 2010-09-10 11:27:58 +12:00
Paul Chote
24039f593c Copy the RA loadscreen for cnc 2010-09-09 23:43:29 +12:00
Paul Chote
cceb4401aa Disable unfinished default test map. 2010-09-09 23:36:30 +12:00
Paul Chote
f9445cb282 Pathfinder uses a MobileInfo instead of a specific actor 2010-09-09 23:25:58 +12:00
Paul Chote
706adb6d0b Remove cruft 2010-09-09 23:10:14 +12:00
Chris Forbes
dd7e578fe6 nearly finished laying out shellmap 2010-09-09 23:02:54 +12:00
Chris Forbes
83d1df19ba half a sensible cnc shellmap (no script yet) 2010-09-09 23:02:53 +12:00
Chris Forbes
975fa28f62 start of shellmap (dropped its actors :(); fix saving of actors from the editor 2010-09-09 23:02:52 +12:00
Paul Chote
452f7f7bff Add a local packaging script for osx 2010-09-09 20:51:10 +12:00
Paul Chote
626db661ab Remove syntax cruft and redundant comments from manifests 2010-09-09 20:48:02 +12:00
Paul Chote
9ea3a404a3 Tidy ra dir 2010-09-09 20:31:54 +12:00
Paul Chote
55c3e9d5bb Package the bits into a mix 2010-09-09 20:31:54 +12:00
Paul Chote
f616a527ee Reorganise cnc mod dir. Exposes a bug in folder/package priority. 2010-09-09 20:31:54 +12:00
Paul Chote
a594e9f830 Move the sequences files into their own dir. 2010-09-09 20:31:53 +12:00
Paul Chote
45d712d390 Fix defaultmod. 2010-09-09 20:31:53 +12:00
Paul Chote
dbbdc171d2 Use the new-format sequences. 2010-09-09 20:31:53 +12:00
Paul Chote
befe22e170 Convert sequences to yaml 2010-09-09 20:31:53 +12:00
Caleb Anderson
b284e82aa7 Added zoom to editor. Fixed crashes. Added sane tabbing to new map dialog. Added selection on tab - new map dialog 2010-09-09 20:29:42 +12:00
Matthew Bowra-Dean
ed5a3338fe Minor Windows installer fixes. 2010-09-09 20:29:40 +12:00
Bob
68dcec87c7 right-clicking a passenger with a transport loads cargo 2010-09-09 20:26:03 +12:00
Chris Forbes
39d7e54e7f fix one crash when server drops 2010-09-09 18:59:49 +12:00
Chris Forbes
404a4ad578 kill cargo when the transport dies 2010-09-09 18:52:12 +12:00
Chris Forbes
c9d0a7a301 tighten bounds on ra vehicles 2010-09-09 18:52:11 +12:00
Paul Chote
54ffdc51b4 Fix crashes with actors dying mid-projectile-flight 2010-09-09 17:20:05 +12:00
Bob
9e53774299 fix bug wrt tiberium infantry damage splashing; fix bug where Mobile+Production units didn't work right 2010-09-09 15:26:12 +12:00
Bob
f70a6aafb1 fix actor leak bug 2010-09-09 13:40:24 +12:00
Chris Forbes
c3fa9f7aa8 #120 fixed: crash in DeliverResources.Tick 2010-09-09 12:34:35 +12:00
Matthew Bowra-Dean
a73ef3affd Windows installer remembers previous install location. 2010-09-08 22:22:28 +12:00
Matthew Bowra-Dean
4e5d26a2d1 Windows installer launches browser to download .NET with user's consent. 2010-09-08 22:22:27 +12:00
Chris Forbes
4b1bc7acd4 fix bounding boxes on cnc vehicles 2010-09-08 22:19:33 +12:00
Chris Forbes
95d29c6910 fix toplevel exception handler in ralint; update cnc to use new armortypes 2010-09-08 22:11:19 +12:00
alzeih
c88ea2bd7c Fix #51 2010-09-08 21:30:32 +12:00
Paul Chote
af157867d8 merge the two INSTALL files 2010-09-08 12:24:09 +12:00
Paul Chote
9476e16df6 Update INSTALL.Ubuntu (fixes bugs 116, 117) 2010-09-08 12:19:55 +12:00
Paul Chote
9c148bf3f0 Remove unused mix files from cnc 2010-09-08 12:19:26 +12:00
Paul Chote
c8c96b9f6b Fix crash when losing fact with building ready to place 2010-09-08 03:22:05 +12:00
Paul Chote
108d7764ec Add nuke flash to cnc 2010-09-08 03:16:44 +12:00
Paul Chote
5396cad2b9 Beefier building explosions (replicates real-cnc) 2010-09-08 03:13:26 +12:00
Paul Chote
2bae3d9d2f Remove cruft from yaml; disable video player by default 2010-09-08 02:40:50 +12:00
Paul Chote
9cbaa9679d Revert to the old proc model - has 1500 local tib storage. Silos moved to defence queue. (fixes "can build on / drive through proc" bug). 2010-09-08 02:10:14 +12:00
Paul Chote
6228a962f6 Idle animations for the rest of the cnc infantry; fix a potential desync. 2010-09-08 01:55:50 +12:00
Paul Chote
1baff27553 Added snow theater for cnc mode by Jafet Kackur, aka 'Jk' and Nyerguds (katzsmile) 2010-09-08 00:49:37 +12:00
Paul Chote
4855b6bca6 Use the correct palette for sidebar icons (fixes icon glitch in cnc desert). 2010-09-08 00:34:04 +12:00
Paul Chote
ff0c66e38c Idle animations for infantry. Enabled for cnc e3. 2010-09-07 23:30:49 +12:00
alzeih
83d2ca07f1 update mirror lists 2010-09-07 21:26:15 +12:00
Chris Forbes
26152489f3 #66 fixed: attacking helipads 2010-09-07 20:57:26 +12:00
Caleb Anderson
4ca4d7b5dd GL error logging 2010-09-07 20:22:40 +12:00
Chris Forbes
0dddf12831 give gtwr and gun to both sides in cnc 2010-09-07 20:20:39 +12:00
Chris Forbes
25c098bad6 fix tesla accuracy 2010-09-07 20:17:08 +12:00
Chris Forbes
be9528dcb2 beef up hind; give it muzzle flashes 2010-09-07 20:08:45 +12:00
Chris Forbes
965385980f add muzzle flashes to yak 2010-09-07 20:01:43 +12:00
Chris Forbes
ca92cd03db slightly increase yak ammo 2010-09-07 19:23:58 +12:00
Chris Forbes
cdec95b73a fix dodgy targeting of FIX 2010-09-07 19:23:13 +12:00
Chris Forbes
76bf3077e9 give mig a sensible bounding box 2010-09-07 19:17:57 +12:00
Chris Forbes
53858a7789 give hind a sensible bounding box 2010-09-07 19:14:15 +12:00
Chris Forbes
b98e25c9a7 reduce pip counts on some units 2010-09-07 19:10:32 +12:00
Chris Forbes
be46f44bc9 polish multiple rows of pips (RA proc) 2010-09-07 19:09:18 +12:00
Chris Forbes
fee6d450e4 add ability to emit a muzzleflash from multiple weapons; make ftnk somewhat nicer 2010-09-07 19:05:14 +12:00
Chris Forbes
173dc59039 fix crash on engineer repair if the target building goes away 2010-09-07 17:37:18 +12:00
Chris Forbes
950dd4e85c fix #99 chronoshift-deploy didnt need to be charged 2010-09-07 17:33:11 +12:00
Paul Chote
6632feb696 Less perf, but avoids reinventing the wheel and searches a wider area 2010-09-07 15:58:04 +12:00
Paolo Chiodi
7b2622e67b if ordered to move to unreachable cell, find a new nearby target location 2010-09-07 15:32:28 +12:00
Chris Forbes
9e5528ef83 fix crash on shooting down aircraft which have an afld reserved. 2010-09-07 08:16:40 +12:00
Chris Forbes
3d920670e5 delay mig until afld+stek 2010-09-07 00:00:46 +12:00
Chris Forbes
4d2afd827f (for now) cut kenn, shift dog to barr 2010-09-07 00:00:45 +12:00
Matthew Bowra-Dean
9a4d0dfe5e Fix RPM package build. 2010-09-06 22:41:26 +12:00
Matthew Bowra-Dean
2a240e62aa VERSION file generation and distribution. 2010-09-06 22:20:11 +12:00
Chris Forbes
7a88adcf96 include the VERSION file in the package. 2010-09-06 21:57:14 +12:00
Paul Chote
b84d2f2c29 Allow a small amount of over-scroll so ui doesn't get in the way 2010-09-06 20:12:47 +12:00
Paolo Chiodi
2945838eef some more locking into map borders 2010-09-06 20:12:47 +12:00
Matthew Bowra-Dean
43aa8812b3 Windows installer checks for .NET v3.5 SP1 2010-09-06 20:09:27 +12:00
Matthew Bowra-Dean
9a2467dc21 .NET framework detection added to Windows install script. 2010-09-06 20:09:26 +12:00
Chris Forbes
5a75625eef don't thrash the GC quite so hard in SpatialBins 2010-09-06 18:46:47 +12:00
Chris Forbes
2f2890596d fix bug in HackyAI debug 2010-09-06 18:44:12 +12:00
Chris Forbes
01fa0b357e fix crash in prev on destroying a building (oops) 2010-09-06 18:16:32 +12:00
Chris Forbes
c5f0dad84e fix smudgelayer deepening; make biblayer work the same way (not optimal, but better) 2010-09-06 18:11:58 +12:00
Chris Forbes
7f7f4e81a5 add Give Exploration devmode option 2010-09-06 17:56:12 +12:00
Matthew Bowra-Dean
8b423908ca Added forum links. 2010-09-06 17:43:41 +12:00
Paul Chote
598fe9f956 gut perf logging - only record expensive ticks. perf.log is small enough to be useful now. 2010-09-06 15:29:34 +12:00
Paul Chote
06e8b530ea Use a dictionary for smudges 2010-09-06 14:40:22 +12:00
Matthew Bowra-Dean
d5c25f73bd Mods page. 2010-09-05 21:57:20 +12:00
Matthew
73fa306719 Bringing the docs up to date. 2010-09-05 21:52:47 +12:00
Chris Forbes
8d0cea6fe4 (RobotCaleb) fix shok death crash 2010-09-05 18:32:06 +12:00
Matthew
273b57ee69 Fixing screenshots on index page. 2010-09-05 17:36:32 +12:00
Matthew
c1578b92e1 Made mirroring script parameters more specific. 2010-09-05 16:42:30 +12:00
Matthew
c69a741c90 Expanded mirroring script to all dependencies, added new mirror (mirumu.jp) and updated download scripts. 2010-09-05 16:34:44 +12:00
Chris Forbes
f8d537c0c5 notes 2010-09-05 16:18:10 +12:00
Chris Forbes
e371159826 minor tidy-up in CrateAction 2010-09-05 16:07:35 +12:00
Chris Forbes
7d912715bc add ToString() for Pair, to make debug saner. 2010-09-05 15:20:01 +12:00
Matthew
d6449c2902 Stupid arguments ordering. 2010-09-05 00:27:28 +12:00
Matthew Bowra-Dean
8a3868abd0 Updated web pages and fixed OS X download script. 2010-09-05 00:10:07 +12:00
Matthew
112047d2b3 Changed downloading scripts to point to mirror script. Added open-ra.org to mirror list. 2010-09-04 22:42:55 +12:00
Matthew
cd9f584729 Mod package mirroring scripts. 2010-09-04 22:26:11 +12:00
Matthew
31741a1ab3 Fix rebuilding of project in packaging. 2010-09-04 20:57:34 +12:00
Matthew
eb6440694a Packaging script updated to use sub-packaging scripts in freshly checkedout copy of repo. 2010-09-04 20:57:34 +12:00
Paolo Chiodi
2c388308c7 avoid calling Viewport.Scroll on every ScreenShaker.Tick 2010-09-04 10:45:09 +02:00
Paolo Chiodi
1c8f744719 allow camera shaking to exit map borders 2010-09-04 10:45:09 +02:00
Paul Chote
dfdc893d63 Fix incorrect sequence + cnc support 2010-09-04 11:46:15 +12:00
Paolo Chiodi
669aaab6b4 change mouse sequence to blocked whene map borders are reached 2010-09-04 11:46:14 +12:00
Paolo Chiodi
73017d3f5c lock viewport scrolling into map size 2010-09-04 11:46:14 +12:00
Paolo Chiodi
38fa809656 added .DS_Store to gitignore 2010-09-04 11:46:13 +12:00
Paul Chote
0ed21a4acb Fix ra defenses 2010-09-03 21:47:25 +12:00
Paul Chote
9ae7d98193 Fix ra spawns 2010-09-03 21:47:25 +12:00
Paul Chote
07e60286f2 whoops 2010-09-03 21:47:25 +12:00
Paul Chote
7f918b8a69 fix FIX footprint 2010-09-03 21:47:24 +12:00
Paul Chote
4e0ace6ec5 Tidy production exits, cnc only 2010-09-03 21:47:24 +12:00
Paul Chote
dca5f8d27b Tidy up Mobile yaml / loading 2010-09-03 21:47:24 +12:00
alzeih
6478276064 make cnc nuke leave full area of smudge 2010-09-03 21:47:24 +12:00
alzeih
7d603f97fe cnc bib and footprint fixes (some of #83) 2010-09-03 21:47:24 +12:00
Paul Chote
112fdb32af Another dead actor crashfix 2010-09-03 17:20:30 +12:00
Paul Chote
5a78df0318 Fix flashtarget crash 2010-09-03 16:37:02 +12:00
Paul Chote
6ac4266847 Allow people to join mp games 2010-09-03 16:13:03 +12:00
Paul Chote
91487b85d0 gunboat wake 2010-08-30 22:29:47 +12:00
Paul Chote
178af8b849 cnc afld polish 2010-08-30 21:56:21 +12:00
alzeih
853ef12c8f Don't flash if already dead 2010-08-30 13:57:37 +12:00
alzeih
62cc2fa4cb unbitrot cnc wrt Projectile 2010-08-30 13:20:51 +12:00
Bob
1f4991833d use LoadAttribute in WeaponInfo 2010-08-30 12:22:13 +12:00
Bob
9fedeefdbc attribute individual fields, not the class 2010-08-30 12:22:12 +12:00
Bob
29a77f7c5c removed FieldLoader.LoadFields 2010-08-30 12:22:11 +12:00
Bob
a751b8c49d FieldLoader can run arbitrary code while loading fields 2010-08-30 12:22:11 +12:00
Bob
baacfc96bb remove unused map overrides 2010-08-30 12:22:10 +12:00
Bob
8dcd1e8fd1 line numbers work in MiniYaml.Merge, too 2010-08-30 12:22:10 +12:00
Bob
2a7857570a line numbers in yaml 2010-08-30 12:22:09 +12:00
Bob
2f92b873e8 make yaml into a list, rather than a dict 2010-08-30 12:22:09 +12:00
Chris Forbes
bce52e989f Revert "make WeaponInfo suck a bit less"
This reverts commit 7b7b9d3319.
2010-08-30 12:21:53 +12:00
Chris Forbes
a917086969 cnc: fix htnk not being able to collect crates 2010-08-28 13:56:32 +12:00
Chris Forbes
34913ab077 polish both rocket trucks 2010-08-28 13:54:03 +12:00
Paul Chote
9de22add08 Fix ra queue visibility 2010-08-28 01:29:23 +12:00
Paul Chote
b2f535d2e0 Fix ProductionQueue-on-player init 2010-08-28 00:56:15 +12:00
Paul Chote
3e7b9a7b5a Fix developer menu yaml 2010-08-28 00:55:46 +12:00
Paul Chote
29d03fb133 ra buildable/tooltip/valued fixes 2010-08-28 00:45:12 +12:00
Paul Chote
d4aeb157cc ra Category fixes 2010-08-28 00:03:51 +12:00
Paul Chote
853e60f76e Tech tree changes to support non-buildable prerequisites, capturable superweapons, "hidden tech" for tech buildings, scripted tech unlocks, "build everything" developer toggle. 2010-08-27 23:51:12 +12:00
Paul Chote
7f3e491ecc Fix AI attacking 2010-08-27 11:46:54 +12:00
Paul Chote
88dfbe657c Route "Can i build X" queries via the appropriate ProductionQueue 2010-08-27 01:18:09 +12:00
Paul Chote
226fd167e7 Category dies in a fire 2010-08-27 00:32:00 +12:00
Paul Chote
cedfeab63c PrimaryBuilding is no longer necessary in cnc; remove cruft from BuildPaletteWidget 2010-08-26 23:49:14 +12:00
Paul Chote
a882735deb Fix build tab selection 2010-08-26 23:47:03 +12:00
Paul Chote
61ebe0d0a0 Per-structure queues for cnc 2010-08-26 23:11:54 +12:00
Paul Chote
ee546750b2 Remove some duplication 2010-08-26 23:11:54 +12:00
Paul Chote
267d89a459 Remove BS from ProductionQueue. Move ProductionQueue onto a structure for queue-per-building. 2010-08-26 23:11:53 +12:00
Paul Chote
3b59afcd4f Fix ralint 2010-08-26 23:11:53 +12:00
Paul Chote
8b98448090 Restricted GDI01 tech tree 2010-08-26 23:11:53 +12:00
Paul Chote
1d7ca206f4 Clean up Buildable vs Tooltip vs Valued. cnc only. 2010-08-26 23:11:53 +12:00
Chris Forbes
7b7b9d3319 make WeaponInfo suck a bit less 2010-08-26 22:12:08 +12:00
Chris Forbes
e5d45a6961 fix spurious extra spawn in ra:Equal Opportunity 2010-08-26 21:33:07 +12:00
Chris Forbes
c7f8921cea fix crash in Missile.Tick when the target is killed while the missile is enroute 2010-08-26 21:22:23 +12:00
Chris Forbes
58ebd68478 fail to deliver ore to a proc which has just died. 2010-08-26 20:30:44 +12:00
Chris Forbes
6234f53e2c hack around the buggy AI. 2010-08-26 20:12:29 +12:00
Chris Forbes
ce0e671ac5 fix obvious failures. 2010-08-26 19:53:21 +12:00
Bob
739c38d3d8 more. doesn't run. may not compile 2010-08-26 19:33:56 +12:00
Bob
5561ac458b finished TraitDictionary. Refactored permanent actor-removal. 2010-08-26 19:33:56 +12:00
Bob
c0d0636e08 store traits differently (index on trait class rather than actor) 2010-08-26 19:32:59 +12:00
Chris Forbes
f4da83e920 start cleaning up IWorldLoadHook vs IGameStarted -- IGameStarted dies. 2010-08-25 21:53:54 +12:00
Chris Forbes
50066ec238 merge CreateMapPlayers into CreateMPPlayers 2010-08-25 21:44:54 +12:00
Paul Chote
dbd076543b Low level loadscreen displays earlier 2010-08-25 21:17:05 +12:00
Chris Forbes
2d4ed56f76 make it work, but there's some issues 2010-08-25 20:35:22 +12:00
Paul Chote
03cdcdd4aa Start cleaning up sp player init; doesn't work 2010-08-25 20:15:49 +12:00
Paul Chote
0cc440aa1f Fix ralint 2010-08-25 20:15:49 +12:00
Paul Chote
f635835b1c Refactor common fmv playing code 2010-08-25 20:15:49 +12:00
alzeih
3f08f16b9b some Nod triggers 2010-08-25 20:15:49 +12:00
alzeih
c433ca77c4 Scripted lose condition 2010-08-25 20:15:49 +12:00
alzeih
70a92b80cf Gunboat non-orderable (move)
- Give Mobile OnRails property
- Make hax Move not cancellable
2010-08-25 20:15:49 +12:00
Paul Chote
b3fbefe968 Victory conditions 2010-08-25 20:15:48 +12:00
Paul Chote
f38e4d27be Disable lane bias on gunboat and lst 2010-08-25 20:15:48 +12:00
Paul Chote
c35e3b4bd7 Fix victory conditions 2010-08-25 20:15:48 +12:00
Paul Chote
b0608936d8 Let Gunboat autotarget 2010-08-25 20:15:48 +12:00
Paul Chote
56b9d1add7 Fullscreen video 2010-08-25 20:15:48 +12:00
Paul Chote
5ce531f2d5 Begin fleshing out mission scriping 2010-08-25 20:15:48 +12:00
Paul Chote
c7e76cc26d Visible cargo for LST 2010-08-25 20:15:48 +12:00
Paul Chote
142be86934 Import infantry subcell and actor stance 2010-08-25 20:15:47 +12:00
Paul Chote
73dadbc873 Fix gunboat 2010-08-25 20:15:47 +12:00
Paul Chote
50d5936846 Scripted gdi1 mission intro 2010-08-25 20:15:47 +12:00
Paul Chote
4e86f5b252 Unbreak cnc, defaultmod 2010-08-25 20:15:46 +12:00
Paul Chote
b6b4df703a Shift loadscreen into mods 2010-08-25 20:15:46 +12:00
Paul Chote
b17e6900ec Awesome loadscreen for ra; crashfixes 2010-08-25 20:15:45 +12:00
Paul Chote
08a60ca336 Initial loadscreen impl; draws a black screen. 2010-08-25 20:15:45 +12:00
Paul Chote
d8c053253d wtf? 2010-08-25 20:15:44 +12:00
Paul Chote
e4271b35dc shader params can stay inside Renderer 2010-08-25 20:15:44 +12:00
Paul Chote
95c34c30ba Don't die after render if the world doesn't exist 2010-08-25 20:15:44 +12:00
Paul Chote
8e42dd95fc RGBASpriteRender doesn't care about palettes 2010-08-25 20:15:44 +12:00
Chris Forbes
f2dd0de1ea working game with bots; stop bots from trying to order husks around 2010-08-25 20:05:02 +12:00
Chris Forbes
701ef05562 fix one stupid crash on using bots 2010-08-25 19:36:22 +12:00
Chris Forbes
c3228cecd0 helps to get the command syntax right 2010-08-25 19:35:14 +12:00
Chris Forbes
47eacc5b80 make bot slot usage actually work 2010-08-25 19:34:20 +12:00
Chris Forbes
909152c662 move HackyAI bot from world to player context 2010-08-25 19:25:19 +12:00
Chris Forbes
0f9221dc5a mostly sensible init for real players and bots 2010-08-25 19:23:25 +12:00
Chris Forbes
cfc937e8eb add PlayerReference.DefaultStartingUnits flag (set for autogenerated playerrefs; don't set this if you have custom stuff. 2010-08-25 18:28:36 +12:00
Chris Forbes
def0580078 don't create players for playable slots which are empty 2010-08-25 18:24:23 +12:00
alzeih
3c7015567e Open/Closed toggle includes Bot now 2010-08-25 00:13:41 +12:00
alzeih
a00f0b18a0 smite some compile errors 2010-08-24 23:44:22 +12:00
alzeih
703f3b8c13 right beep in ra 2010-08-24 23:30:41 +12:00
mgatland
bf9bcfed68 bot builds units and sends them out. Crude + fragile... 2010-08-24 23:19:33 +12:00
alzeih
f6dac1fe83 Fix rebase 2010-08-24 22:56:31 +12:00
mgatland
731c64c1a7 add a bot player, make ai apply to the bot not the local player 2010-08-24 22:56:31 +12:00
Chris Forbes
19494c3262 much better ai bases 2010-08-24 22:52:41 +12:00
Chris Forbes
cf9a2ee0ef tune base fractions a bit 2010-08-24 22:52:41 +12:00
Chris Forbes
93e8bd6644 build a base successfully. 2010-08-24 22:52:40 +12:00
Chris Forbes
4d0f75353b spam powr 2010-08-24 22:52:40 +12:00
Chris Forbes
fb035756a0 better handling of 'needing power' 2010-08-24 22:52:40 +12:00
Chris Forbes
e0a00940af initial buildqueue ai 2010-08-24 22:52:40 +12:00
Chris Forbes
e74c3eeb2e hook in an ai stub. all it does for now is deploy the mcv. 2010-08-24 22:52:40 +12:00
Chris Forbes
05bf6d83f2 clean up one silly use of world.players 2010-08-24 20:02:20 +12:00
Chris Forbes
e4b65256de remove a bit of duplication from UnitOrders 2010-08-24 20:02:19 +12:00
Chris Forbes
d744cfe21b change exploit order check to cope with PlayerId not matching ClientId, because it doesnt. 2010-08-24 20:02:19 +12:00
Chris Forbes
6ae7da77ae fix wtf in PlayerResources 2010-08-24 20:02:18 +12:00
Chris Forbes
0352884205 blah 2010-08-24 20:02:18 +12:00
Chris Forbes
7c6edab04e work for ra too 2010-08-24 20:02:17 +12:00
Chris Forbes
997501bb12 ui for opening and closing slots 2010-08-24 20:02:17 +12:00
Chris Forbes
d4f43a399e add slot open/close server commands with boot on close 2010-08-24 20:02:16 +12:00
Chris Forbes
f6ec2163de fix some dumb issues 2010-08-24 20:02:16 +12:00
Chris Forbes
3428b3c4c2 add slot command 2010-08-24 20:02:15 +12:00
Chris Forbes
8ec16fdbbe fail fail fail 2010-08-24 20:02:15 +12:00
Chris Forbes
39f699916c start hacking up lobby to work with slots (cnc only) 2010-08-24 20:02:14 +12:00
Chris Forbes
4a337185f5 if there are no Playable playerrefs in the map, make as many as there are spawnpoints (hack) 2010-08-24 20:02:14 +12:00
Chris Forbes
298f5ec24f some notes 2010-08-24 20:02:13 +12:00
Chris Forbes
16402f26fe start breaking things 2010-08-24 20:02:13 +12:00
Paul Chote
825743b225 Fix packaged startup on windows and osx. 2010-08-24 19:53:05 +12:00
Paul Chote
c3b3947b9d Rename some settings 2010-08-24 19:53:05 +12:00
Paul Chote
1143f496db Part 2 of 3: Split Settings into logical units.
Syntax for command line overrides is now <section>.<setting>=<value>
eg `General.InitialMods=cnc'
2010-08-24 19:53:05 +12:00
Paul Chote
46d0ce89e9 Fix silly naming conventions 2010-08-24 19:53:05 +12:00
Chris Forbes
79ced35010 make InfiltrateForSonarPulse work for any support power 2010-08-24 17:37:06 +12:00
Chris Forbes
18a06eb3bf wtf 2010-08-23 20:58:56 +12:00
Chris Forbes
d8c5f1aed3 extract serialize code from Server; deserialize code from Game; add Slot collection. 2010-08-23 19:08:09 +12:00
Chris Forbes
3880326787 ship out ChooseInitialStance from Game 2010-08-23 18:34:47 +12:00
Chris Forbes
09d9396123 move Session back to Game 2010-08-23 18:31:43 +12:00
Chris Forbes
aeccf53e97 squash some spurious warnings 2010-08-23 18:23:55 +12:00
Chris Forbes
b1b8b2c14a remove N params from ServerMain 2010-08-23 18:19:36 +12:00
Chris Forbes
fd9a31168d server loads the map (part 1) 2010-08-23 18:09:37 +12:00
Chris Forbes
3f11c32c4a blah 2010-08-23 17:58:25 +12:00
Chris Forbes
d65626e87c move Manifest into its own file 2010-08-23 17:51:08 +12:00
Chris Forbes
ccb77dee12 rename ChoosePaletteOnSelect 2010-08-23 17:47:46 +12:00
Paul Chote
952da4efec Fix wrong-palette in first tick 2010-08-21 20:39:15 +12:00
Paul Chote
49f2bfb460 Only reload sprites on theatre change; kill cruft 2010-08-21 20:34:59 +12:00
Paul Chote
9db92ed8ba Shift the nasty bits of map loading into ModData 2010-08-21 20:23:14 +12:00
Paul Chote
4e2d76f6ed Move AvailableMaps into ModData 2010-08-21 20:11:05 +12:00
Bob
94dd73cb48 fixed world to run ALL of the frameEndActions 2010-08-21 19:24:17 +12:00
Bob
aeb9f4e441 moved call to FindMaps into ChangeMods 2010-08-21 19:20:06 +12:00
Bob
1a4e3053ce inlined InitializeEngineWithMods; cleaned trailing whitespace in Game; removed non-working Replay code 2010-08-21 19:16:43 +12:00
Bob
18914447aa fixed Renderer init (separated device creation from shader load) 2010-08-21 18:59:05 +12:00
Bob
7e67a5bb2d move calls to manifest ctor into ModData 2010-08-21 18:26:16 +12:00
Bob
9eaa0e5765 extract SyncReport class from Game 2010-08-21 17:59:17 +12:00
Bob
0e16aa339d move SheetBuilder singleton onto ModData 2010-08-21 17:44:06 +12:00
Bob
c7b650d6ec de-static'd CursorSheetBuilder 2010-08-21 17:36:58 +12:00
Bob
bcc3cd32ae merge call of FileSystem.LoadFromManifest into modData ctor 2010-08-21 17:28:51 +12:00
Bob
38ffd30b28 removing duplication wrt mounting FS packages 2010-08-21 17:27:54 +12:00
Bob
15bd58ddce extracting world-specific data from Game 2010-08-21 17:11:26 +12:00
Bob
7247bd1078 changed UIM to use less memory 2010-08-21 15:10:54 +12:00
Chris Forbes
ac31767aef some random housekeeping (ScreenShaker) 2010-08-21 15:10:53 +12:00
Bob
3f68330c70 pruning refs to Game.world 2010-08-20 17:46:58 +12:00
Bob
0e71af25f4 remove Game.skipMakeAnims 2010-08-20 17:14:20 +12:00
Chris Forbes
e610c4d7fc ffs, fix beedee's upload script so it uploads. 2010-08-19 21:07:31 +12:00
Chris Forbes
0a041fe425 use byte[] backing for Sheet. bitmap backing is still available for Chrome RGBA hack. 2010-08-19 19:22:03 +12:00
Chris Forbes
1595c6a0ed interim hack to support use of raw data in Sheet 2010-08-19 18:42:45 +12:00
Chris Forbes
db7a11e8a6 some random bits 2010-08-19 18:33:52 +12:00
Chris Forbes
53df4c4bfb remove batch fail 2010-08-19 18:10:38 +12:00
Paul Chote
1afcd2b98a Move rules and SSB init out of world 2010-08-19 00:09:48 +12:00
Paul Chote
08ffe1d44d Reload chrome images only on mod change 2010-08-18 23:57:18 +12:00
Paul Chote
66359b7058 Speed up FileSystem 2010-08-18 23:40:05 +12:00
Paul Chote
30ab5c33ea Fast checking for file existance 2010-08-18 22:56:16 +12:00
Paul Chote
0c5fb4c6b0 Kill LoadShellMap 2010-08-18 22:41:00 +12:00
Paul Chote
21b0b12948 Fix MP gamestart 2010-08-18 22:21:08 +12:00
Paul Chote
cb3f6435ad Kill broken log uploading 2010-08-18 20:45:30 +12:00
Paul Chote
235ae1fad7 osx packaging fixes: Runs when name contains spaces, fixes "Cannot connect to master server". 2010-08-18 20:35:55 +12:00
Chris Forbes
1c6470eff3 add movement-free autotarget option 2010-08-18 17:47:38 +12:00
Paul Chote
0cf51d8142 Skip movies with `esc' 2010-08-17 22:59:41 +12:00
Paul Chote
38e5f70cbe Ingame briefing support 2010-08-17 22:59:41 +12:00
Paul Chote
56b0da0b13 Singleplayer campaign support: player/world init 2010-08-17 22:59:41 +12:00
Paul Chote
d6f0a03270 Ignore orders sent to other players' actors (fixes an entire class of exploits). 2010-08-17 22:59:41 +12:00
Chris Forbes
b1b75ecf2f fix wtf in spy 2010-08-17 21:42:25 +12:00
Caleb Anderson
228852a55d Inverse Drag Scrolling 2010-08-17 21:02:51 +12:00
Chris Forbes
f6a880e524 line way too long. 2010-08-17 20:32:10 +12:00
Chris Forbes
7e82dc729b fix mouse/tree and mouse/crate interactions. 2010-08-17 19:58:51 +12:00
Matthew
d91a7c6b35 Increasing robustness of packaging script. 2010-08-16 01:03:14 +12:00
Chris Forbes
b4068f0ac7 msam polish -- contains some dirty hacks 2010-08-15 21:11:38 +12:00
Chris Forbes
b336a1e478 make cnc fact significantly harder to kill 2010-08-15 20:53:36 +12:00
Chris Forbes
55afcddce0 fix alignment on gdi rocket truck 2010-08-15 20:50:21 +12:00
Chris Forbes
ac7bb3c71b use activity cursor rather than deploy for disguise 2010-08-15 20:36:29 +12:00
Chris Forbes
3aa7ff5a6b fix various bugs in prev 2 2010-08-15 20:29:09 +12:00
Chris Forbes
0b3414c46f disguise on self to lose it. 2010-08-15 20:22:59 +12:00
Chris Forbes
fdcad8710b first cut at disguise 2010-08-15 20:21:27 +12:00
Paul Chote
02a7b092dc Use map-defined stances 2010-08-15 20:15:45 +12:00
Paul Chote
d43781c604 Import legacy players 2010-08-15 20:15:45 +12:00
Chris Forbes
cfbededcc4 a quick hack to set the 32bit flag in corheader 2010-08-15 18:26:58 +12:00
Paul Chote
9ba50a9c5e Set turret facings correctly 2010-08-15 18:03:13 +12:00
Paul Chote
6377269a14 BOAT and LST work ingame (need special render hax, but don't crash) 2010-08-15 18:00:27 +12:00
Paul Chote
a8be81ea2b Import actor facing and health from legacy maps 2010-08-15 17:47:04 +12:00
Paul Chote
50950cba22 Scripted map format docs 2010-08-15 17:23:19 +12:00
Paul Chote
210e847904 cnc BOAT template 2010-08-15 16:59:14 +12:00
Paul Chote
54f7b6430d Fix map importer 2010-08-15 16:58:46 +12:00
Paul Chote
26e823fc25 EVW2 basically playable. Needs tweaking (too much tib?) and tech structures. 2010-08-15 16:14:14 +12:00
Paul Chote
10ea035b42 Fix #41 2010-08-15 15:34:27 +12:00
Paul Chote
58fb7178ad cnc Balance tweaks 2010-08-15 07:14:10 +12:00
alzeih
cd358eb985 Fix #24 2010-08-15 06:32:12 +12:00
Paul Chote
39d4b80323 Tweak sub stats 2010-08-15 06:18:47 +12:00
Paul Chote
45a4935b01 Cannot cloak if critical health 2010-08-15 06:18:47 +12:00
Paul Chote
824e2a7b0b Auto-target detected subs 2010-08-15 06:18:47 +12:00
Paul Chote
230d59f655 Tweak visibility; hopefully allows targeting of detected units 2010-08-15 06:18:46 +12:00
Paul Chote
7f191887ec Revamp cloak model 2010-08-15 06:18:46 +12:00
Paul Chote
499613f105 Depthcharges only work against subs 2010-08-15 06:18:46 +12:00
Paul Chote
20af2b5c0a Heli's don't land when idle 2010-08-15 06:18:46 +12:00
alzeih
cbc9d66bee SYRD + SPEN have rally points 2010-08-15 05:39:45 +12:00
alzeih
c338612b9f Fully laden harv's are too slow now 2010-08-15 04:38:53 +12:00
Paul Chote
edd0a7c614 Marooned tweaks 2010-08-15 03:16:26 +12:00
Paul Chote
1d259e4bc7 Nowhere to hide tweaks 2010-08-15 03:13:47 +12:00
Paul Chote
6c651e9d02 Halve fact/mcv value 2010-08-15 03:03:05 +12:00
Paul Chote
a29e0bf4b1 Lower proc cost 2010-08-15 03:03:05 +12:00
Paul Chote
9d403d3072 Unfail `Marooned' map slightly 2010-08-15 03:03:04 +12:00
Paul Chote
184d044f26 Run packaged version with --debug on osx 2010-08-15 03:03:04 +12:00
Chris Forbes
0473aed2c3 fix crash when civilians die 2010-08-15 02:45:20 +12:00
Matthew
db5920e83a Minor fixes to packaging. 2010-08-15 02:08:49 +12:00
Matthew
80555235d7 Fixes for Windows packaging. 2010-08-15 02:08:49 +12:00
Matthew
acd842f178 Windows support added to general packager. 2010-08-15 02:08:49 +12:00
Matthew
a5dacd52e8 OSX support added to general packager. 2010-08-15 02:08:49 +12:00
Matthew
6e045e864a Further RPM fixes 2010-08-15 02:08:49 +12:00
Matthew
0541b04c3a RPM packaging fixes. 2010-08-15 02:08:49 +12:00
Matthew
07b2bcbfdf RPM support in general packaging script. 2010-08-15 02:08:49 +12:00
Matthew
0e2b4bb721 General package script works for Arch-Linux packages. 2010-08-15 02:08:49 +12:00
Paul Chote
ab3c06cd04 Fail 2010-08-15 01:59:23 +12:00
Chris Forbes
299ee7f82c remove redundant devmode checkbox in settings -- 'enable cheats' does it 2010-08-15 01:44:38 +12:00
Paul Chote
ddd56e6f9b Reveal-map crate 2010-08-15 01:43:24 +12:00
Paul Chote
839043ea1b Disable music player until its finished 2010-08-15 01:13:49 +12:00
Paul Chote
1e8aea616b Hook up music seek 2010-08-15 01:07:05 +12:00
Paul Chote
727a88d82a Listbox content shouldn't steal clicks if they're clipped 2010-08-15 00:44:19 +12:00
alzeih
1f54ad3238 Oops 2010-08-15 00:38:31 +12:00
Paul Chote
694fd84188 Clean up custom color palette 2010-08-15 00:18:06 +12:00
alzeih
271be551b0 Add Voices and DisableVariants to VoiceInfo, remove the ".aud" for die sounds. 2010-08-15 00:07:27 +12:00
alzeih
fe481d7445 Fix #36
- add to ra
- add default variant
2010-08-15 00:07:27 +12:00
alzeih
7a10ae3dea Some of fixing #36 2010-08-15 00:07:27 +12:00
Chris Forbes
8c39e372e3 throttle mouse motion events to one per frame 2010-08-14 23:48:12 +12:00
Paul Chote
af6660d6ca Fix palette remaps 2010-08-14 23:36:41 +12:00
Paul Chote
d0da9d11bf Fix palettes and all palettemods. Remap palettes broken. 2010-08-14 23:29:01 +12:00
Paul Chote
0a2d39f15b More correct, still doesn't work. 2010-08-14 22:23:22 +12:00
Paul Chote
8118a17e3c foo 2010-08-14 21:53:24 +12:00
Paul Chote
0f6564f31a more or less fail 2010-08-14 21:36:13 +12:00
Paul Chote
dda6556e17 non-working palette fail 2010-08-14 21:07:19 +12:00
Paul Chote
5c1408d4d7 cnc water palette rotation 2010-08-14 17:52:59 +12:00
Chris Forbes
aa6e671d89 cnc tran unloadfacing=initialfacing 2010-08-14 17:44:36 +12:00
alzeih
e0de1427e9 Die sanely when a widget doesn't exist. 2010-08-14 17:39:50 +12:00
alzeih
db16ab4cdc Add VideoVolume 2010-08-14 17:39:50 +12:00
Bob
ae703d50b2 Actor.traits is implementation detail 2010-08-14 17:39:49 +12:00
Paul Chote
f6c6255f64 Custom tiberium pathing cost 2010-08-14 17:37:36 +12:00
Paul Chote
a569c712f0 Video pausing support; sync video to audio nicer. 2010-08-14 17:21:54 +12:00
Paul Chote
5c8c8d5e6e Support more video types. 2010-08-14 15:59:40 +12:00
alzeih
3f4f1ff75e MusicSeekPosition 2010-08-14 14:24:25 +12:00
Paul Chote
1aa4f57a68 Prototype new music player 2010-08-14 14:05:32 +12:00
Paul Chote
3f3ac377b2 Backend changes for new music player 2010-08-13 19:46:13 +12:00
Paul Chote
de12233edc cnc movie list 2010-08-13 00:30:46 +12:00
Paul Chote
b36b101392 Soviet movie definitions 2010-08-12 21:07:38 +12:00
Chris Forbes
726449b6fb fix it so it works when have no movies 2010-08-12 20:47:38 +12:00
Paul Chote
a0d7435550 Overlay with black lines for authentic ra feel 2010-08-12 20:47:38 +12:00
Paul Chote
11aed465a8 Populate the list from yaml; Define ra-allies movies; Additional polish. 2010-08-12 20:47:37 +12:00
Paul Chote
0f953226a6 Populate and use the listbox 2010-08-12 20:47:37 +12:00
Chris Forbes
ad181544cd give cnc towers significantly more hp 2010-08-12 20:44:03 +12:00
Chris Forbes
df19163ce5 fix #18 -- harvs built at weap choose a proc when trying to return now 2010-08-12 19:54:37 +12:00
Chris Forbes
4328fbc350 fix color picker again 2010-08-12 19:35:36 +12:00
Chris Forbes
7cd9afb6d2 remove some sillyness from the masterserver -- it shouldnt hold up the works. 2010-08-12 19:27:11 +12:00
Chris Forbes
1ad8b58f1b blah 2010-08-12 18:34:48 +12:00
Matthew
62e3d548c8 Makefile generated start-up script altered to allow command line switches to be passed to executable. 2010-08-12 18:11:06 +12:00
Matthew
ea7abcb585 Makefile install target fix. 2010-08-12 18:11:06 +12:00
Chris Forbes
441bffecfc fix #262 -- harv should move slower when full 2010-08-12 18:10:23 +12:00
Paul Chote
2c1ab33893 Add a "Video Player" menu with basic impl; kill the music player widget. 2010-08-12 18:06:12 +12:00
Paul Chote
74500c369c Stop video soundtrack when video stops 2010-08-12 00:44:53 +12:00
Paul Chote
6e0158039a Support uncompressed audio (ally1.vqa) 2010-08-12 00:37:19 +12:00
Paul Chote
c600239c54 Begin making video player widget useful; steal (othewise useless) musicplayer controls for now 2010-08-12 00:31:30 +12:00
Paul Chote
93a48c0cf1 Tweaks 2010-08-12 00:06:08 +12:00
Paul Chote
b88512df43 Keep video in sync with audio 2010-08-11 23:58:30 +12:00
Chris Forbes
ca6debde66 buffer up all the audio upfront 2010-08-11 22:56:32 +12:00
Chris Forbes
8dd9848636 preserve aud codec state across blocks, so the gain isnt completely bogus 2010-08-11 22:19:49 +12:00
Chris Forbes
ec9da154ad add crap broken sound support for vqa 2010-08-11 22:04:55 +12:00
Chris Forbes
46fc0ef563 add ignore for vqas; disable the vqaplayer in the mainmenu for now 2010-08-11 21:44:42 +12:00
Paul Chote
514c92a998 FAST vqa render 2010-08-11 19:54:24 +12:00
Paul Chote
96f9f33776 Tweaks 2010-08-11 19:04:03 +12:00
Paul Chote
20553a112d Don't hide the original crash if the master server is inaccessible 2010-08-11 18:20:50 +12:00
Paul Chote
9e49cad1c2 .vqp format doc 2010-08-11 14:56:16 +12:00
Paul Chote
5c6e2a5e42 Fix crash when cnc videos loop 2010-08-11 11:53:01 +12:00
Paul Chote
44d7aa14a4 Hacky ingame video widget 2010-08-11 11:45:37 +12:00
Paul Chote
bf8e3645cb cnc support (doesn't use format80) 2010-08-11 01:34:05 +12:00
Paul Chote
6ea8d4e6d3 Test a different video; fix a stupid crash. All works except for sound. 2010-08-11 00:57:49 +12:00
Paul Chote
16c3223f3e Fix 8-frame glitch 2010-08-11 00:57:49 +12:00
Paul Chote
b98e180361 Dump the animation to a set of bitmaps; glitches every 8 frames. 2010-08-11 00:57:49 +12:00
Paul Chote
80aade857e Start removing fail 2010-08-11 00:57:49 +12:00
Paul Chote
58700e4c07 Save the hax pixels too 2010-08-11 00:57:49 +12:00
Paul Chote
d364472862 Write the pixels that don't need further hax to a bitmap 2010-08-11 00:57:49 +12:00
Paul Chote
5d688a2e27 CBFZ, CBPZ, CPL0 chunks 2010-08-11 00:57:49 +12:00
Paul Chote
f4fa053a35 Add a null parser for VQFR chunk 2010-08-11 00:57:49 +12:00
Paul Chote
90373529b0 Parse FINF and (skip) SND2 2010-08-11 00:57:49 +12:00
Paul Chote
0416b6b429 Format docs 2010-08-11 00:57:48 +12:00
Paul Chote
bd991e33ca Begin vqa support; load and display FORM header 2010-08-11 00:57:48 +12:00
Chris Forbes
f162c5956e cleanup wtf 2010-08-10 21:51:06 +12:00
Chris Forbes
e6f717ff04 FAST 2010-08-10 21:26:50 +12:00
Chris Forbes
eb0c895e2c fix prev 2010-08-10 21:26:44 +12:00
Chris Forbes
3af0d2f6a4 noise reduction 2010-08-09 17:29:50 +12:00
Chris Forbes
8f6fe8344b split PrimaryBuilding from Production 2010-08-09 17:20:34 +12:00
Paul Chote
cb5307bb17 nerf e1 (stole ra damage stats) 2010-08-08 21:47:31 +12:00
Paul Chote
d9acf1a3e5 Don't teleport into transport's. 2010-08-08 19:30:49 +12:00
Paul Chote
6fc945ac91 Fix cargo/passenger interaction 2010-08-08 19:16:37 +12:00
Paul Chote
e5878e8207 Fix Color Picker 2010-08-08 18:45:05 +12:00
Paul Chote
b982a52086 stop .net clients taking out the server on drop 2010-08-08 18:26:55 +12:00
Matthew
f28ec846e7 WindowsBase.dll added to thirdparty DLLs to work around distributions with outdated versions of Mono. 2010-08-07 16:11:18 +12:00
Chris Forbes
551814fc32 fix crash when a bridge is destroyed, and has a husk on it. 2010-08-06 21:02:46 +12:00
Chris Forbes
f64a6e273e fix sub -> ground crash 2010-08-06 20:11:49 +12:00
Matthew
1a428186e9 Minor fix to arch-linux post install script. 2010-08-06 14:15:53 +12:00
Matthew
609abb7719 Fixes to include extra mod dirs in Makefile and correct paths in post install scripts. 2010-08-06 14:03:37 +12:00
Matthew
aee0728a73 SupportDir path fixed. 2010-08-06 12:37:21 +12:00
Matthew
120a5fa66b Packaging and Makefile fixes. 2010-08-06 12:28:09 +12:00
Chris Forbes
71792a8f09 fix crash on telling {sam,agun} to attack ground 2010-08-04 18:23:06 +12:00
Chris Forbes
0f20133af0 fix crash in retry 2010-08-03 20:40:27 +12:00
Paul Chote
40b16e33ba Fix and polish all ra production structures; remove some obsoleted Production subclasses 2010-08-03 18:29:32 +12:00
Paul Chote
8e82f6fa1a Fix a retarded crash with husks; remove some debug 2010-08-03 01:16:24 +12:00
Paul Chote
053f0f5c7f Refactoring + cnc weap 2010-08-03 01:03:31 +12:00
Paul Chote
7c562059c2 Fix cnc afld 2010-08-03 00:17:11 +12:00
Paul Chote
cf5cb80c61 Restore cnc helicopters (reservable etc changes deferred) 2010-08-02 23:57:19 +12:00
Paul Chote
711378d352 Polish cnc pyle & hand inf production; lays groundwork for future polish. Likely breaks every other production structure (untested). 2010-08-02 23:35:35 +12:00
Paul Chote
7d044a9e1b Remove custom cnc behavior 2010-08-02 20:43:56 +12:00
Paul Chote
bc93e8cac0 Health as an ActorInit 2010-08-02 20:12:04 +12:00
alzeih
578d42614b Convert all CreateActor calls to use TypeDictionary 2010-08-02 01:51:13 +12:00
alzeih
4ea66ea309 Added FacingInit and AltitudeInit. Used in Mobile/Aircraft/Husks. Refactored Production, SpyPlane, Paratroopers and LeavesHusk 2010-08-02 00:49:26 +12:00
alzeih
2cc6e197fa fix makefile 2010-08-02 00:36:18 +12:00
Chris Forbes
2ea0907d66 convert ra maps to format3 2010-08-01 23:46:16 +12:00
Chris Forbes
d50d26350e upgrade caffeinated as a test. 2010-08-01 21:02:12 +12:00
Chris Forbes
b77bddddbc fix maploader to be backwards compatible. InitDict-based format is now 3. Formats 1,2 are translated on load, and will be saved back as Format 3 if saved in the editor. 2010-08-01 21:00:17 +12:00
Chris Forbes
3521319095 editor compiles again 2010-08-01 20:40:23 +12:00
Bob
dfc24233f7 fix broken rebase 2010-08-01 19:38:44 +12:00
Bob
970eb4d6e1 use the new actorinit stuff when loading map yaml 2010-08-01 19:38:42 +12:00
Bob
10b7ece62e new mapactor init stuff 2010-08-01 19:38:39 +12:00
Bob
f789aef892 map format change: move mapactor Type onto header line 2010-08-01 19:38:37 +12:00
Bob
bceaecf031 change map format to remove redundant crap 2010-08-01 19:38:35 +12:00
Bob
74155812d0 don't merge yaml in chrome; just load sequentially 2010-08-01 19:38:32 +12:00
Bob
f49ec8d6b9 don't mention rootwidget in chrome yaml 2010-08-01 19:38:30 +12:00
Chris Forbes
fd5d441086 fix all the broken maps 2010-08-01 18:46:46 +12:00
Chris Forbes
eb85189c24 transparently upgrade maps produced by buggy converter 2010-08-01 18:42:48 +12:00
Chris Forbes
9bd808fcb8 generate a neutral player 2010-08-01 18:38:38 +12:00
Chris Forbes
968387eab2 converted all the cnc maps 2010-08-01 18:28:45 +12:00
Chris Forbes
f8f1e2a531 add warning on window close with modified map 2010-08-01 18:12:03 +12:00
Chris Forbes
8707bbd569 remove standalone MapConverter 2010-08-01 18:03:03 +12:00
Chris Forbes
724df5ad54 add central-conflict and marooned-ii via new importer 2010-08-01 18:02:02 +12:00
Chris Forbes
0f33913bf6 force x86 on MapConverter project 2010-08-01 17:15:51 +12:00
Chris Forbes
d856eb5905 move bits around in MapConverter 2010-08-01 17:10:05 +12:00
Chris Forbes
ab9e7ce672 replace editor toolbar with menus 2010-08-01 16:59:19 +12:00
Chris Forbes
741e43ab14 disable vshost for editor; move tabs back to top edge 2010-08-01 16:19:35 +12:00
Chris Forbes
4b0f66496c blah 2010-08-01 16:11:47 +12:00
Chris Forbes
260fe70e5f remove 'Middle of Hell' map, it's garbage 2010-08-01 16:07:55 +12:00
Chris Forbes
ae3d7bbe24 disable vshost 2010-08-01 16:02:32 +12:00
Chris Forbes
990eeb844a ChooseAirfield chooses closest, rather than first. 2010-08-01 15:49:52 +12:00
Chris Forbes
4567bad251 make Activities.Land work with Targets. 2010-08-01 14:57:07 +12:00
Chris Forbes
aafbeed703 ffs, doing it the hard way as usual 2010-08-01 14:36:53 +12:00
Chris Forbes
002bc20d50 some improvement to alfd/plane interaction 2010-08-01 14:32:59 +12:00
Chris Forbes
19c5c56ab2 ra.ss, ra.msub, cnc.stnk all spawn cloaked/submerged 2010-08-01 14:07:15 +12:00
Chris Forbes
718b0e7613 fix mslo nuke offset 2010-08-01 13:03:53 +12:00
Chris Forbes
5b5378ae6c make tmpl capturable 2010-08-01 12:54:39 +12:00
Chris Forbes
67418e0b9d fix capturing of neutral buildings 2010-08-01 12:23:45 +12:00
Chris Forbes
ca986688a1 fix primary building & building fallback when busy 2010-08-01 12:11:48 +12:00
Chris Forbes
97e43cfe40 add ReservableProduction, to start fixing afld behavior. 2010-08-01 11:57:33 +12:00
Chris Forbes
a0ca52491e add some debug to Reservable so we can see what retardage is happening 2010-08-01 11:49:55 +12:00
Chris Forbes
039a42cfef fix spyplane (it doesnt want Mobile) 2010-08-01 11:49:23 +12:00
Chris Forbes
e2ab99c0cc remove dead crap from Plane 2010-08-01 11:49:00 +12:00
Chris Forbes
5daaa99108 start cleaning up reservations 2010-08-01 11:35:26 +12:00
Chris Forbes
13244d17f1 format and clean up DrawLineToTarget 2010-08-01 11:32:09 +12:00
Chris Forbes
e8604d172c add levelup crates to cnc 2010-08-01 11:17:27 +12:00
Chris Forbes
9e0f0d2074 fix cloaking crate 2010-08-01 10:40:26 +12:00
Chris Forbes
3dae071f41 drop aftermath support from the installer 2010-08-01 09:59:53 +12:00
Paul Chote
d5dbd1b7da Fix Monodevelop build 2010-08-01 01:48:12 +12:00
Chris Forbes
b1736e5efd fix VS build 2010-08-01 01:40:37 +12:00
Paul Chote
bd979f65b8 Kill Unit trait; Radar signature tweaks 2010-08-01 01:26:59 +12:00
Paul Chote
d416f0be5c Shift "XXX Lost" notification onto a trait. Hook up "Structure Lost" (cnc) and "Airborne Unit Lost" (ra). 2010-08-01 01:01:31 +12:00
Paul Chote
45e54e1ca8 Kill some spurious references to Unit 2010-08-01 01:01:31 +12:00
Paul Chote
d29e3f3f0e Split facing into its own interface; fix husks 2010-08-01 01:01:31 +12:00
Paul Chote
207ee49da3 Move Facing and Altitude onto IMove impls, with associated pile of cleanups 2010-08-01 01:01:31 +12:00
Paul Chote
88cb942430 Cleaner access to InitialFacing and ROT 2010-08-01 01:01:31 +12:00
Chris Forbes
765bedb592 add cloak crate action to cnc 2010-08-01 00:21:20 +12:00
Chris Forbes
2c73d8a48d remove some dead files 2010-07-31 23:37:56 +12:00
Chris Forbes
750275d08f reduce crap a little more 2010-07-31 23:24:32 +12:00
Chris Forbes
491d38cafe move Weapon & friends into own file 2010-07-31 23:21:22 +12:00
Chris Forbes
6c6da12dd6 fix local offset -> screen space transform to work as advertised 2010-07-31 23:01:56 +12:00
Chris Forbes
0b20d1366a eliminate old form of GetTurretPosition 2010-07-31 22:33:54 +12:00
Chris Forbes
fa245aaa08 introduce collection of Turrets on AttackBase, too -- turret sharing w/ recoil works again 2010-07-31 22:19:29 +12:00
Chris Forbes
9a218f7b52 GetPrimaryWeapon() dies too. 2010-07-31 21:34:24 +12:00
Chris Forbes
d202e27f79 more tesla hax? 2010-07-31 21:34:24 +12:00
Chris Forbes
960dddc048 AttackTesla hax 2010-07-31 21:34:23 +12:00
Chris Forbes
9fcc739cd5 more... Combat.GetSecondaryWeapon() dies, etc 2010-07-31 21:34:23 +12:00
Chris Forbes
449cc4a42d remove old buff crates 2010-07-31 21:34:22 +12:00
Chris Forbes
b8421ba655 move more combat bits around 2010-07-31 21:34:22 +12:00
Chris Forbes
2ac261c15b remove other param spam from CheckFire 2010-07-31 21:34:22 +12:00
Chris Forbes
2bd9cd32f6 remove WeaponInfo param from CheckFire 2010-07-31 21:34:21 +12:00
Chris Forbes
693c8d96b1 introduce a Barrel object, unhacking the local offsets. 2010-07-31 21:34:21 +12:00
Chris Forbes
42683f5966 start refactoring AttackBase 2010-07-31 21:34:20 +12:00
Chris Forbes
2478b26a05 GetOrDefault can die. 2010-07-31 12:41:11 +12:00
Chris Forbes
2608ab3bf6 fix crash on building ttnk 2010-07-31 12:23:09 +12:00
Chris Forbes
9fc8b81362 disable speed/armor/firepower crates in favour of levelup crate 2010-07-31 12:23:09 +12:00
Chris Forbes
5093d531bc tweak unit crate shares 2010-07-31 12:23:08 +12:00
Chris Forbes
08d75cb55c add GiveMcvCrateAction, and wire unit crates / mcv crates / etc 2010-07-31 12:23:08 +12:00
Chris Forbes
4c24547c26 add GiveUnitCrateAction 2010-07-31 12:23:07 +12:00
Bob
53b9b63ae5 change Mobile, Move to use Mobile.toCell instead of Actor.Location 2010-07-31 12:23:07 +12:00
Chris Forbes
cd40b27357 fix bitrot in cnc and ra 2010-07-31 12:23:06 +12:00
Chris Forbes
3c10d14f60 add shok 2010-07-31 09:27:13 +12:00
Chris Forbes
035bc96b37 various bits 2010-07-31 09:27:09 +12:00
Chris Forbes
357e74573c fix .gitignore, which banned .aud by default for some reason 2010-07-31 09:25:41 +12:00
Chris Forbes
668c5c7bdd add missing sounds 2010-07-31 09:25:41 +12:00
Chris Forbes
7a0d898b1a integrate ttnk into ra; also art for shok; all am pieces are now in ra/extras 2010-07-31 09:25:40 +12:00
alzeih
33b6f590e9 move ROT, InitialFacing and Speed to Mobile and Aircraft. 2010-07-31 02:06:30 +12:00
Paul Chote
dd7008d7ee Make crates collectable; make cnc walls targetable. 2010-07-31 01:50:14 +12:00
Paul Chote
9b051a6624 Remove some bs from buildings with multiple damage states; cnc civilian structure rubble persists after death. 2010-07-31 01:50:09 +12:00
Paul Chote
62c2c3a1c6 Fix damage transition sounds 2010-07-30 22:24:43 +12:00
Paul Chote
8528c5d3a3 Correct the ordering on DamageState 2010-07-30 22:24:43 +12:00
Paul Chote
4cf0610fd9 Change DamageState names 2010-07-30 22:24:43 +12:00
Paul Chote
1459ec483c Rename ExtendedDamageState -> DamageState 2010-07-30 22:24:43 +12:00
Paul Chote
028c5b5201 Merge the two DamageState types 2010-07-30 22:24:43 +12:00
Chris Forbes
41c0d175bd fix paradrop being broken (again!) 2010-07-30 21:20:13 +12:00
Paul Chote
9280dd9f2f Add some polish 2010-07-30 21:00:23 +12:00
Paul Chote
bde16e4e15 Remove a pile of duplication on mcv undeploy 2010-07-30 21:00:23 +12:00
Paul Chote
119f85dbf4 Unfail mcv deploy 2010-07-30 21:00:23 +12:00
Bob
ede160f1b6 move some more (gameplay related) widgets into mods/ 2010-07-30 20:55:03 +12:00
Bob
b4c84a11f2 changes to allow widgets to be in mods/; moved BuildPaletteWidget into mods/ 2010-07-30 20:15:14 +12:00
Bob
2dd558a065 move LastMousePosition et al from Widget to Viewport 2010-07-30 20:07:39 +12:00
Chris Forbes
3cd9a1e0e4 fix player color blocks not showing up in MapPreviewWidget 2010-07-30 18:54:46 +12:00
Chris Forbes
ef92e004c0 set the correct template on destroying long bridges 2010-07-30 18:48:59 +12:00
Chris Forbes
290a44440c cnc should run 'ralint cnc' after build, not 'ralint ra'. ralint no longer allows exceptions to escape. 2010-07-30 18:41:09 +12:00
Chris Forbes
5d1ee145e1 make (some) RALint errors nonfatal 2010-07-30 18:41:08 +12:00
Chris Forbes
aba9942a85 selling should not cause the building to explode first. 2010-07-30 18:39:21 +12:00
Chris Forbes
3aa2bab99f crate needs Unit. 2010-07-30 18:39:03 +12:00
Chris Forbes
d6b8b327d9 fix Bridge init 2010-07-30 18:24:12 +12:00
alzeih
e1a0d40530 RA Yaml changes 2010-07-30 18:14:00 +12:00
Paul Chote
48c5803198 Zombie prevention 2010-07-30 10:21:19 +12:00
Paul Chote
0580dc4adf Fix damaged-building artwork and don't show healthbar for dead units (cnc critical-building state) 2010-07-30 10:11:33 +12:00
Paul Chote
98ac5a036f Add an `Undamaged' damagestate to simplify things related to healing. 2010-07-30 01:22:41 +12:00
Paul Chote
87d2071007 Fix health bar colors. Todo: Merge DamageState & ExtendedDamageState 2010-07-30 00:46:00 +12:00
Paul Chote
bce9791b56 Fix building repair; Kill GlobalDefaults. 2010-07-30 00:41:55 +12:00
Paul Chote
6fba888d45 Shift Actor.Health onto a trait.
Known regressions:
 - cnc only
 - health bar colors
 - can't repair buildings
2010-07-30 00:33:44 +12:00
Chris Forbes
1e08dc6301 fix crate weirdness 2010-07-29 19:31:05 +12:00
Chris Forbes
2d4efc7942 crate should only choose water/land type on landing, not every tick. 2010-07-29 18:49:41 +12:00
Chris Forbes
bb0531b80f convert Crate to use crushable support again 2010-07-29 18:48:25 +12:00
Chris Forbes
e325b5954c split ITeleportable off as a base interface from IMove; Crate no longer pretends to be moveable. 2010-07-29 18:16:26 +12:00
Paul Chote
2be6cf35ac Add extra spawnpoints to "Middle Mayhem" 2010-07-28 23:23:39 +12:00
Paul Chote
7a4fa93ce9 Introduce a Targetable trait; Subs can attack bridges; May introduce subtle bugs due to previous stupid assumption that !selectable == !attackable. 2010-07-28 22:57:14 +12:00
Paul Chote
f9f6720437 Hook up Building.Capturable and remove unused field 2010-07-28 22:28:52 +12:00
Paul Chote
cfee4405c7 ra too 2010-07-28 22:26:30 +12:00
Paul Chote
eaa80fab74 Shift Sight onto its own trait. Only cnc for now. 2010-07-28 21:37:43 +12:00
Paul Chote
8fab45ae39 Move Cloak, HiddenUnderFog, FrozenUnderFog into Mods.RA; Simplify a pile of related stuff. 2010-07-28 21:11:50 +12:00
Paul Chote
6854d9853b Kill Crewed and move WaterBound onto building 2010-07-28 20:48:50 +12:00
Chris Forbes
5ca7218eb6 integrate scrollbar and checkbox art. fixes #215. 2010-07-28 18:16:47 +12:00
Chris Forbes
72d6a211af disable vshost 2010-07-28 11:26:46 +12:00
Paul Chote
f10e3a4d2d Always update player info on server join 2010-07-27 21:52:21 +12:00
Paul Chote
cfd0e045cb Add Edge Scroll setting 2010-07-27 21:28:40 +12:00
Chris Forbes
4386970bd3 add standalone PNGs for checkbox & scrollbar use 2010-07-27 21:19:17 +12:00
Paul Chote
c6f0b792d3 Do the right thing when stances change 2010-07-27 21:19:17 +12:00
Paul Chote
966f607230 Only show the diplomacy window if its useful 2010-07-27 21:19:17 +12:00
Paul Chote
9a704e69b2 Shift RevealsShroud onto Unit; fix "vehicles don't update shroud" regression in last 2010-07-27 21:19:16 +12:00
Paul Chote
a91ebb4eda Allies share sight 2010-07-27 11:34:28 +12:00
Paul Chote
9fe6671ace Fix retarded crash on server create; redirect server console spam into a logfile. 2010-07-27 10:48:37 +12:00
Paul Chote
386211df8c Update correct download size for deps-v2. 2010-07-26 22:40:25 +12:00
alzeih
ce513ed61a oops 2010-07-26 22:23:25 +12:00
alzeih
3f75e655ce Set Allow Cheats on server creation 2010-07-26 22:23:25 +12:00
alzeih
d9a423b596 Smite Indexdebug, tidy up dev mode 2010-07-26 22:23:24 +12:00
alzeih
30241ddccc Locate devmode code largely in the trait, reduce exploitability. 2010-07-26 22:23:24 +12:00
alzeih
10c7674433 Add to cnc, tidy up ingame menus 2010-07-26 22:23:24 +12:00
alzeih
c749fcfce3 Nearly There 2010-07-26 22:23:24 +12:00
alzeih
fe527c1297 Some of DeveloperMode trait (not working yet) 2010-07-26 22:23:24 +12:00
alzeih
3b72af2e9b AllowCheats 2010-07-26 22:23:24 +12:00
alzeih
2d916e60d9 Remove Developer Settings from Settings > Debug. Adjust Widget layout in DeveloperMode. In preparation for DeveloperMode trait. 2010-07-26 22:23:23 +12:00
Paul Chote
d8d731b321 Don't draw target line when idle 2010-07-26 22:18:40 +12:00
Chris Forbes
4cc021adfe fix dumb crash in minimap -- actors outside the map accessed memory that wasnt part of the bitmap. 2010-07-26 22:16:23 +12:00
Chris Forbes
56e929014d slightly faster again (map.IsInMap isnt free) -- but be careful 2010-07-26 21:42:11 +12:00
Chris Forbes
e0a5350ed6 slightly faster again 2010-07-26 21:39:23 +12:00
Chris Forbes
d774817b0e remaining dumbness removed. 2010-07-26 21:35:00 +12:00
Chris Forbes
3526c05dc8 a bit more 2010-07-26 21:30:23 +12:00
Chris Forbes
69a74425d9 a free 40% or so. 2010-07-26 21:23:19 +12:00
Paul Chote
eb1e325fe2 More caching 2010-07-26 20:44:47 +12:00
Paul Chote
ffa0c16ab9 Ignore opendiff files 2010-07-26 20:36:48 +12:00
Paul Chote
30f16e1a7a Fix cnc 2010-07-26 20:36:48 +12:00
Chris Forbes
0d0353f8f3 a little faster -- don't look up the Mobile every expand either. 2010-07-26 20:33:42 +12:00
Paul Chote
637fae87cd Remove a bunch of unused stuff. May help pathfinder perf a little. 2010-07-26 20:27:56 +12:00
Paul Chote
c39116df84 DefaultInputController -> WorldInteractionController; Tooltip tweaks. 2010-07-26 20:14:36 +12:00
Paul Chote
dcef8770a7 Edge scrolling 2010-07-26 20:14:36 +12:00
Paul Chote
816722256b Split viewport scrolling into its own widget 2010-07-26 20:14:36 +12:00
Chris Forbes
2b890bdb2f make backspace 'cycle bases' binding more obvious. not all of us know the whole character set, or care to. 2010-07-26 20:14:36 +12:00
Chris Forbes
882d94a22a DefaultInputControllerWidget should not ever consume input it doesnt want 2010-07-26 20:14:36 +12:00
Paul Chote
43a488ed6d Rebase fixes 2010-07-26 20:14:35 +12:00
Paul Chote
305fa03355 Kill Controller 2010-07-26 20:14:35 +12:00
Paul Chote
e677be7908 Remove more cruft from Game.controller 2010-07-26 20:14:35 +12:00
Paul Chote
eac49ca641 Refactor existing hodgepodge of hardcoded mouse/keyboard events into DefaultInputController. 2010-07-26 20:14:35 +12:00
Paul Chote
2248320af7 Move Game.Controller.HandleInput into a widget; works but is hacky 2010-07-26 20:14:35 +12:00
Chris Forbes
f61421edd0 gain ~20% faster pathing perf 2010-07-26 19:14:54 +12:00
Chris Forbes
9bb5e49058 draw building grid under actors too 2010-07-26 18:32:29 +12:00
Chris Forbes
16c9f2b873 render range circle and detection circle under actors 2010-07-26 18:27:02 +12:00
Chris Forbes
69d30ac71b RenderBeforeWorld/RenderAfterWorld split. still need to sort out which behavior belongs where. 2010-07-26 18:19:39 +12:00
Chris Forbes
9cf8ac0c3d remove dead IVictoryConditions interface 2010-07-26 18:07:48 +12:00
Chris Forbes
f1a5998049 remove dead ChooseFreePalette crap 2010-07-26 17:54:18 +12:00
Chris Forbes
41b76144da force the socket closed so the client thread will die pretty fast. dirty hack, but teh read timeout is nuts otherwise. 2010-07-26 17:49:08 +12:00
Chris Forbes
e30e97037f squash warning 2010-07-26 17:40:36 +12:00
Chris Forbes
102f1dd858 blah 2010-07-26 17:33:56 +12:00
alzeih
925ca2bb73 SetTargetSilently for DrawLineToTarget 2010-07-26 13:57:53 +12:00
alzeih
526bb3e042 Use Targets 2010-07-26 12:33:54 +12:00
Paul Chote
e8adc357e9 Draw targeting lines for player-issued orders. Can force-display targets with [alt]. 2010-07-26 12:01:13 +12:00
alzeih
130b4d29b4 Move SelectedUnit drawing stuff out of WorldRenderer into Selectable with IRenderSelection 2010-07-26 02:25:52 +12:00
alzeih
5f357288ee layout tidyup - start game now forces game start, ready checkboxes behave as before. 2010-07-25 22:16:21 +12:00
alzeih
26b5fbe9bb startgame order and lobby changes 2010-07-25 21:31:17 +12:00
Paul Chote
4650642311 Keep selection on mcv deploy/undeploy. Bonus: build tab opens on mcv deploy 2010-07-25 20:02:23 +12:00
Chris Forbes
e850cb7ff8 assign a palette to the cursor for basemod 2010-07-25 16:36:29 +12:00
alzeih
9fcb0577a5 default mod (pchote: squashed + amended) 2010-07-25 16:10:35 +12:00
Chris Forbes
df7dad8aaa add ra_perf mod, which exposes the performance test maps 2010-07-25 15:57:54 +12:00
Chris Forbes
3080e19bf2 add visualization of minefields when MNLY is selected 2010-07-25 15:56:40 +12:00
Chris Forbes
f2bfa8e2ee reinstate range circles; IRenderSelection allows arb. plugging in of these things 2010-07-25 15:56:40 +12:00
alzeih
2254e48f65 little bit better threading [pchote: picked/amended] 2010-07-25 15:56:40 +12:00
Chris Forbes
443f2bb6b9 fix msbuild project files. thanks, monodevelop: what was there worked JUST FINE 2010-07-25 15:52:09 +12:00
Paul Chote
c82cc7b301 Update osx packaging script for WindowsBase.dll and mono 2.6.7 2010-07-25 15:34:13 +12:00
Paul Chote
63951ac134 Run ralint after building 2010-07-25 15:11:08 +12:00
Paul Chote
301007e225 Fix compile errors/warnings 2010-07-25 15:02:01 +12:00
Paul Chote
5776d351b4 Fix Monodevelop build; remove Aftermath project. 2010-07-25 14:55:44 +12:00
alzeih
484bea106b OpenRA works nicely with monodevelop 2010-07-24 16:19:54 +12:00
alzeih
4fd5d9e0c3 Cloak Changes: appear semitransparent to localplayer, not at all to others. Same for mines.
( Also fix compile warning in LimitedAmmo )
2010-07-24 15:17:29 +12:00
Chris Forbes
5375692f1f remove spam on planes doing stuff 2010-07-24 00:09:54 +12:00
Paul Chote
20c0419782 Don't leak information on the underlying terrain via the mouse cursor. Cursor and voice response always "move" for terrain under shroud.
Needs pathfinder changes before being mergeable
2010-07-23 22:53:38 +12:00
Paul Chote
c74fe87fc8 Fix voice on unpathable terrain 2010-07-23 22:45:11 +12:00
Paul Chote
d5c43c9fda Remove unused code 2010-07-23 22:44:59 +12:00
Paul Chote
5344e60e3d Forgot a file 2010-07-23 22:44:48 +12:00
Paul Chote
52c8c93b30 Building capture eva + wtf on silo capture 2010-07-23 22:29:27 +12:00
Chris Forbes
31cb2081c6 rude hack to make planes associate with the airfield which spawned them 2010-07-23 21:40:07 +12:00
alzeih
cad1418199 use Range as a *range*, not as a nearenough. 2010-07-23 21:40:06 +12:00
alzeih
984e081f3b threadsafe 2010-07-23 21:40:06 +12:00
alzeih
282d26b844 Remember to disconnect ingame too, and mark the game as not started.
Also fix a compiler warning
2010-07-23 21:40:05 +12:00
alzeih
9516235707 Chat lines vanish after a set time in game
Also, Clear chat on join server
2010-07-23 21:40:05 +12:00
alzeih
409416b55c Host closing server closes all connections and closes the listener. 2010-07-23 21:40:04 +12:00
Chris Forbes
05d0e825fd unfail reservations for planes a bit 2010-07-23 21:40:04 +12:00
Paul Chote
c14f1aa996 Chronotank 2010-07-23 21:37:43 +12:00
Paul Chote
05953d0d6b Building capture 2010-07-23 21:26:26 +12:00
Paul Chote
fdf57b1e63 Cargo 2010-07-23 21:17:21 +12:00
Paul Chote
7f640fd701 MCV deploy and FIX. 2010-07-23 21:08:05 +12:00
Paul Chote
4016b81208 c4 and goldwrench voices / flashes / fixed movement 2010-07-23 20:13:41 +12:00
Paul Chote
7a06ddb8d1 Harvester voices / cursors / flashes 2010-07-23 19:56:22 +12:00
Paul Chote
c90317fb35 Aircraft/Helicopter move/dock flashing and cursor fixes 2010-07-23 19:10:10 +12:00
Paul Chote
55e59e0b53 Unhardcode voice/order interaction; reimplement for move and attack 2010-07-23 18:41:35 +12:00
Chris Forbes
1186d40fda quick hack to fix unclickable tabs on buildpalette 2010-07-23 18:26:59 +12:00
Paul Chote
1fc3ca284e Fix "Unit Lost" in cnc; remove related cruft 2010-07-23 17:33:39 +12:00
Paul Chote
f4e66a3d7a Fix power bar 2010-07-23 17:28:21 +12:00
Paul Chote
71420df0f3 Fix CursorForOrderString to work with orders + preliminary e6 fixes 2010-07-23 17:16:02 +12:00
Paul Chote
5019bb8b6e Animated move cursors for cnc 2010-07-23 16:01:53 +12:00
Paul Chote
61006ee73b For now, use ra cursors to fill some of the gaps in cnc 2010-07-23 15:15:21 +12:00
Paul Chote
9ebdef043c Allow custom/multiple cursor palettes 2010-07-23 14:49:45 +12:00
Paul Chote
93e629584b Use GoldWrench cursor for engineer repair 2010-07-23 00:29:46 +12:00
Paul Chote
1061f1138f Fix ra minimap cursors 2010-07-23 00:21:45 +12:00
Paul Chote
5c7e278b93 Draw viewport rect on radar 2010-07-23 00:03:04 +12:00
Paul Chote
a42ef6b3ea Update Minimap at ~2Hz, staggering layers. Minimap is faster than ever before. 2010-07-22 23:01:27 +12:00
Paul Chote
ac526f9762 Split the radar into multiple bitmaps 2010-07-22 22:54:12 +12:00
Paul Chote
d21e9fe093 Kill ITerrainTypeModifier for explicit updating of a custom layer in the map. Functionally equivalent, but MUCH faster. 2010-07-22 22:22:53 +12:00
Paul Chote
3e493cb93c Fix cursors over closed radarbin 2010-07-22 21:44:45 +12:00
Paul Chote
37a55715d0 Shift the hard work into Tick 2010-07-22 21:41:30 +12:00
Paul Chote
5198ced5aa Remove unnecessary check 2010-07-22 20:44:14 +12:00
Paul Chote
5ce8a643a7 ... 2010-07-22 20:40:24 +12:00
Paul Chote
53304b3de2 Cloaked units (not) on radar; untested but is simple enough that it should work 2010-07-22 20:33:54 +12:00
Paul Chote
a09a6997b4 Radar takes into account visibility under fog 2010-07-22 20:26:19 +12:00
Paul Chote
f7212ef757 Correct bridge / tree / mine color 2010-07-22 19:50:41 +12:00
Paul Chote
5f36933837 Fix custom terrain location 2010-07-22 19:50:41 +12:00
Paul Chote
a234dd4382 New interface for things that show on radar; groundwork for future patches. 2010-07-22 19:50:41 +12:00
Chris Forbes
31d5d18d65 undo ProjectileArgs stuff 2010-07-22 18:59:41 +12:00
Chris Forbes
6d527c3668 unbreak Target 2010-07-22 18:36:56 +12:00
alzeih
1497c31908 some more hax 2010-07-22 18:01:32 +12:00
alzeih
6be4e5c266 Combat.DoExplosion now takes a Target
(needs more refactoring)
2010-07-22 16:00:14 +12:00
alzeih
4e22e37192 Fix the in range check 2010-07-22 15:54:42 +12:00
alzeih
3395f97b20 Attacking is working again 2010-07-22 13:07:15 +12:00
Chris Forbes
73b6eb568b more of attack-ground (order wiring, etc); doesn't work. 2010-07-22 08:26:52 +12:00
Chris Forbes
5c61c9d3a9 migrating most things to use the Target struct rather than Actor directly. 2010-07-22 08:26:51 +12:00
Chris Forbes
88b705c8ef add Target type, in prep for allowing attack-ground 2010-07-22 08:26:51 +12:00
Chris Forbes
a78001a5cc strip some redundancy from UnitOrders, but it still sucks; fix nudges breaking everything 2010-07-22 08:03:44 +12:00
alzeih
b2454c8b8d instead, fix DumpSyncReport warning properly
(? maybe)
2010-07-22 01:40:44 +12:00
alzeih
3f9ffbac80 smite some compiler warnings, hide those that are to stay.
Hide some makefile output too
2010-07-22 01:39:44 +12:00
Paul Chote
55fd5c3b9b Halve the perf fail of custom terrain (down to ~4ms) 2010-07-22 01:21:36 +12:00
Paul Chote
db9cfb9ddd More refactoring and a TODO on custom terrain perf 2010-07-22 01:13:03 +12:00
Paul Chote
2d2362a1a6 Minimap refactoring; don't render the minimap unless we have radar. 2010-07-22 00:15:33 +12:00
Paul Chote
fdc033a63f Fix teamchat in lobby 2010-07-22 00:02:30 +12:00
Paul Chote
b8eab0614f Fix spawn point rendering 2010-07-21 23:49:24 +12:00
Paul Chote
78aff37fe4 Minimap sanity; part 1: rewrite the core radar logic 2010-07-21 23:44:23 +12:00
Paul Chote
58aed632a1 Add sanity to minimap; part 0 2010-07-21 20:50:40 +12:00
Paul Chote
3969dc9fb6 Tweak cnc walls; less extreme than ra 2010-07-21 20:50:40 +12:00
alzeih
ac27b40f7a Oops 2010-07-21 20:44:58 +12:00
alzeih
8e2d422054 Improvements to VictoryConditions, and Dead = Chat to all 2010-07-21 20:21:02 +12:00
Paul Chote
d69267f71c Remove Minimap from world 2010-07-21 19:48:43 +12:00
Chris Forbes
3a7bec1eef implement INudge on Mobile 2010-07-21 19:37:24 +12:00
Chris Forbes
8006e20dd3 however, we *do* need to know who nudged us, so we can tell them where to shove it if required. 2010-07-21 19:27:49 +12:00
Chris Forbes
5ef651ce6d return value not required. 2010-07-21 19:25:32 +12:00
Chris Forbes
e426d50cc2 add INudge interface; Move tries to nudge things that are in the way as a good first option 2010-07-21 19:24:45 +12:00
alzeih
2f9f539857 big pile of Render refactoring 2010-07-21 18:59:33 +12:00
Chris Forbes
84e1ed1cf9 fix helis landing where they shouldnt 2010-07-21 18:52:24 +12:00
Chris Forbes
8cb6e0b229 make walls sortof worth using 2010-07-21 18:42:28 +12:00
Chris Forbes
2584aad0c7 make UOG actually use FindUnitsAtMouse rather than homebrewing it 2010-07-21 18:24:29 +12:00
alzeih
4065a4f6a2 WorldRenderer now uses the global renderers too 2010-07-21 18:17:43 +12:00
Paul Chote
c2197b0adb Hopefully fix some of our pathing issues. Untested. 2010-07-21 18:01:15 +12:00
alzeih
222b29cec6 Bye Bye Chrome! 2010-07-21 17:57:06 +12:00
alzeih
0ac91d3c5e Widget IHandleInputs now 2010-07-21 16:51:00 +12:00
alzeih
d0b4761e07 actually remove the rootWidget from Chrome 2010-07-21 16:18:45 +12:00
alzeih
647252b0cb Chrome.rootWidget -> Widget.RootWidget 2010-07-21 16:14:31 +12:00
alzeih
795da1b088 unhacking IChromeButton. 2010-07-21 16:00:26 +12:00
alzeih
846971836a adjust path debug for altitude 2010-07-21 14:17:14 +12:00
Paul Chote
578f47d3b2 Fix crates. Untested but should work. 2010-07-21 12:21:56 +12:00
Paul Chote
d53a765971 Fix default cursor (cnc) 2010-07-21 11:53:47 +12:00
Paul Chote
ca2c9991fe Kill PostGameWidget 2010-07-21 11:51:15 +12:00
Paul Chote
47ca19e37d Fix cnc minimap cursors and document needed artwork 2010-07-21 11:20:42 +12:00
Paul Chote
86b66b91dd Fix seqed under mono 2010-07-21 11:20:42 +12:00
Chris Forbes
06bc14f690 fix a bunch of bad cursor hotspots in ra 2010-07-21 08:21:45 +12:00
Paul Chote
d3df8e6abd Remove debug spam 2010-07-21 00:28:14 +12:00
Paul Chote
7d02a801ad minicursors for minimap.
Bugs: ra doesn't define a sequence for move-blocked-minimap; cursor hotspot is wrong.
2010-07-21 00:26:32 +12:00
Paul Chote
bd69047e8c Fix cursor interaction with widgets 2010-07-20 23:51:55 +12:00
Paul Chote
5abe52607d Apply the same hack to BuildPaletteWidget 2010-07-20 23:30:05 +12:00
Paul Chote
46d502e053 Remove blatant abuse of RenderBounds 2010-07-20 23:22:09 +12:00
alzeih
0d489e638e send message to EVERYONE... 2010-07-20 21:48:09 +12:00
alzeih
b8a335a88f put back in --just-die 2010-07-20 21:38:21 +12:00
alzeih
0d671ae407 layout tweaks 2010-07-20 21:28:58 +12:00
alzeih
e10074d153 Actually give cash, add cheating message 2010-07-20 21:20:03 +12:00
alzeih
f92b59e6db Developer Mode (pchote: amended) 2010-07-20 21:20:03 +12:00
Chris Forbes
f39f7fc23e fix mouse input breakage on radarbin 2010-07-20 20:49:10 +12:00
Chris Forbes
02e4de61eb only play 'unit lost' etc to owner 2010-07-20 20:17:30 +12:00
Chris Forbes
cedcb3d84f fix stupid crash on husks dying 2010-07-20 19:48:54 +12:00
Chris Forbes
80d1b46c04 improve ftur a bit 2010-07-20 18:30:58 +12:00
Chris Forbes
19fda0b98e kill most of GlobalDefaults 2010-07-20 18:26:09 +12:00
Chris Forbes
1918c125ec ^Ship was a better place to put those. 2010-07-20 18:17:49 +12:00
Chris Forbes
b3aaacf16b don't drop crates in stupid places 2010-07-20 18:05:20 +12:00
alzeih
416e892897 it compiles again 2010-07-20 18:04:16 +12:00
Chris Forbes
1cbc3dd60f paradrop now asks its *cargo* whether a cell is suitable, rather than itself. 2010-07-20 17:55:10 +12:00
Chris Forbes
2684284c27 unit-lost sounds through voice 2010-07-20 17:39:35 +12:00
Chris Forbes
cb44844639 fix impotent mig 2010-07-19 19:56:11 +12:00
Chris Forbes
fa0d6b3e72 move LimitedAmmo to RA dll; reduce pips for YAK 2010-07-19 19:44:18 +12:00
705 changed files with 38074 additions and 18488 deletions

10
.gitignore vendored
View File

@@ -11,7 +11,6 @@ mods/*/*.dll
# Red Alert binary files
*.[mM][iI][xX]
*.[aA][uU][dD]
# Crap generated by OpenRa
sheet-*.png
@@ -23,12 +22,14 @@ log.txt
/*.dll
*.pdb
*.mdb
/*.exe
*.exe
OpenRA
OpenRA.app
*.vqa
# backup files by various editors
*~
*.orig
# dependency DLLs (different for every platform!)
cg.dll
cgGL.dll
@@ -43,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
@@ -53,4 +57,4 @@ temp.o
temp.s
*.config
*.resources
*.resources

View File

@@ -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
{

View File

@@ -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
View File

@@ -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 games 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 shouldnt
be too hard to find a legal copy of the CD images. Unfortunately the
installer is 16-bit and so wont 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 wont 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

View File

@@ -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 games 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 shouldnt
be too hard to find a legal copy of the CD images. Unfortunately the
installer is 16-bit and so wont 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-*

View File

@@ -1,7 +1,7 @@
CSC = gmcs
CSFLAGS = -nologo -warn:4 -debug:+ -debug:full -optimize- -codepage:utf8 -unsafe
DEFINE = DEBUG;TRACE
PROGRAMS =fileformats gl game ra cnc aftermath seqed mapcvtr editor ralint filex tsbuild
PROGRAMS =fileformats gl game ra cnc seqed editor ralint filex tsbuild
prefix = /usr/local
datarootdir = $(prefix)/share
datadir = $(datarootdir)
@@ -16,7 +16,7 @@ COMMON_LIBS = System.dll System.Core.dll System.Drawing.dll System.Xml.dll
fileformats_SRCS = $(shell find OpenRA.FileFormats/ -iname '*.cs')
fileformats_TARGET = OpenRA.FileFormats.dll
fileformats_KIND = library
fileformats_LIBS = $(COMMON_LIBS) thirdparty/Tao/Tao.Sdl.dll
fileformats_LIBS = $(COMMON_LIBS) thirdparty/Tao/Tao.Sdl.dll thirdparty/WindowsBase.dll
gl_SRCS = $(shell find OpenRA.Gl/ -iname '*.cs')
gl_TARGET = OpenRA.Gl.dll
@@ -46,17 +46,12 @@ cnc_KIND = library
cnc_DEPS = $(fileformats_TARGET) $(game_TARGET) $(ra_TARGET)
cnc_LIBS = $(COMMON_LIBS) $(cnc_DEPS)
aftermath_SRCS = $(shell find OpenRA.Mods.Aftermath/ -iname '*.cs')
aftermath_TARGET = mods/aftermath/OpenRA.Mods.Aftermath.dll
aftermath_KIND = library
aftermath_DEPS = $(fileformats_TARGET) $(game_TARGET) $(ra_TARGET)
aftermath_LIBS = $(COMMON_LIBS) $(aftermath_DEPS)
seqed_SRCS = $(shell find SequenceEditor/ -iname '*.cs')
seqed_TARGET = SequenceEditor.exe
seqed_KIND = winexe
seqed_DEPS = $(fileformats_TARGET)
seqed_LIBS = $(COMMON_LIBS) System.Windows.Forms.dll $(seqed_DEPS)
seqed_EXTRA = -resource:SequenceEditor.Form1.resources
editor_SRCS = $(shell find OpenRA.Editor/ -iname '*.cs')
editor_TARGET = OpenRA.Editor.exe
@@ -65,12 +60,6 @@ editor_DEPS = $(fileformats_TARGET) $(game_TARGET)
editor_LIBS = $(COMMON_LIBS) System.Windows.Forms.dll System.Data.dll $(editor_DEPS)
editor_EXTRA = -resource:OpenRA.Editor.Form1.resources
mapcvtr_SRCS = $(shell find MapConverter/ -iname '*.cs')
mapcvtr_TARGET = MapConverter.exe
mapcvtr_KIND = winexe
mapcvtr_DEPS = $(fileformats_TARGET) $(game_TARGET)
mapcvtr_LIBS = $(COMMON_LIBS) $(mapcvtr_DEPS)
ralint_SRCS = $(shell find RALint/ -iname '*.cs')
ralint_TARGET = RALint.exe
ralint_KIND = winexe
@@ -93,51 +82,58 @@ tsbuild_EXTRA = -resource:OpenRA.TilesetBuilder.Form1.resources
# -platform:x86
.SUFFIXES:
.PHONY: clean all game tool default mods mod_ra mod_aftermath mod_cnc install uninstall editor_res editor tsbuild ralint seqed mapcvtr filex
.PHONY: clean all game tool default mods mod_ra mod_cnc install uninstall editor_res editor tsbuild ralint seqed filex
game: $(fileformats_TARGET) $(gl_TARGET) $(game_TARGET) $(ra_TARGET) $(cnc_TARGET) $(aftermath_TARGET)
game: $(fileformats_TARGET) $(gl_TARGET) $(game_TARGET) $(ra_TARGET) $(cnc_TARGET)
clean:
@-rm *.exe *.dll *.mdb mods/**/*.dll mods/**/*.mdb *.resources
distclean: clean
CORE = fileformats gl game seqed mapcvtr
CORE = fileformats gl game seqed
install: all
@-echo "Installing OpenRA to $(INSTALL_DIR)"
@$(INSTALL_PROGRAM) -d $(INSTALL_DIR)
@$(INSTALL_PROGRAM) $(foreach prog,$(CORE),$($(prog)_TARGET)) $(INSTALL_DIR)
@$(INSTALL_PROGRAM) -d $(INSTALL_DIR)/mods/aftermath
@$(INSTALL_PROGRAM) $(aftermath_TARGET) $(INSTALL_DIR)/mods/aftermath
@-cp $(foreach f,$(shell ls mods/aftermath --hide=*.dll),mods/aftermath/$(f)) $(INSTALL_DIR)/mods/aftermath
@cp -r mods/aftermath/packages $(INSTALL_DIR)/mods/aftermath
@$(INSTALL_PROGRAM) -d $(INSTALL_DIR)/mods/cnc
@$(INSTALL_PROGRAM) $(cnc_TARGET) $(INSTALL_DIR)/mods/cnc
@-cp $(foreach f,$(shell ls mods/cnc --hide=*.dll),mods/cnc/$(f)) $(INSTALL_DIR)/mods/cnc
@cp -r mods/cnc/maps $(INSTALL_DIR)/mods/cnc
@cp -r mods/cnc/chrome $(INSTALL_DIR)/mods/cnc
@cp -r mods/cnc/bits $(INSTALL_DIR)/mods/cnc
@cp -r mods/cnc/rules $(INSTALL_DIR)/mods/cnc
@cp -r mods/cnc/sequences $(INSTALL_DIR)/mods/cnc
@cp -r mods/cnc/tilesets $(INSTALL_DIR)/mods/cnc
@cp -r mods/cnc/uibits $(INSTALL_DIR)/mods/cnc
@$(INSTALL_PROGRAM) -d $(INSTALL_DIR)/mods/ra
@$(INSTALL_PROGRAM) $(ra_TARGET) $(INSTALL_DIR)/mods/ra
@-cp $(foreach f,$(shell ls mods/ra --hide=*.dll),mods/ra/$(f)) $(INSTALL_DIR)/mods/ra
@cp -r mods/ra/maps $(INSTALL_DIR)/mods/ra
@cp -r mods/ra/bits $(INSTALL_DIR)/mods/ra
@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 *.ini $(INSTALL_DIR)
@cp -r thirdparty $(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
@echo "mono "$(datadir)"/openra/OpenRA.Game.exe" >> openra
@echo "mono "$(datadir)"/openra/OpenRA.Game.exe SupportDir=~/.openra \"$$""@\"" >> openra
@$(INSTALL_PROGRAM) -d $(BIN_INSTALL_DIR)
@$(INSTALL_PROGRAM) -m +rx openra $(BIN_INSTALL_DIR)
@echo "OpenRA is now installed. You will now want to download"
@echo "http://open-ra.org/packages/ra-packages.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."
@@ -150,28 +146,30 @@ uninstall:
mod_ra: $(ra_TARGET) $(ralint_TARGET)
mono RALint.exe ra
mod_aftermath: $(aftermath_TARGET) $(ralint_TARGET)
mono RALint.exe ra aftermath
mod_cnc: $(cnc_TARGET) $(ralint_TARGET)
mono RALint.exe cnc
mods: mod_ra mod_cnc
editor_res:
resgen2 OpenRA.Editor/Form1.resx OpenRA.Editor.Form1.resources
editor: editor_res $(editor_TARGET)
OpenRA.Editor.Form1.resources:
resgen2 OpenRA.Editor/Form1.resx OpenRA.Editor.Form1.resources 1> /dev/null
editor: OpenRA.Editor.Form1.resources $(editor_TARGET)
ralint: $(ralint_TARGET)
seqed: $(seqed_TARGET)
mapcvtr: $(mapcvtr_TARGET)
seqed: SequenceEditor.Form1.resources $(seqed_TARGET)
SequenceEditor.Form1.resources:
resgen2 SequenceEditor/Form1.resx SequenceEditor.Form1.resources 1> /dev/null
filex: $(filex_TARGET)
tsbuild: tsbuild_res $(tsbuild_TARGET)
tsbuild_res:
resgen2 OpenRA.TilesetBuilder/Form1.resx OpenRA.TilesetBuilder.Form1.resources
tools: editor ralint seqed mapcvtr filex tsbuild
tsbuild: OpenRA.TilesetBuilder.Form1.resources $(tsbuild_TARGET)
OpenRA.TilesetBuilder.Form1.resources:
resgen2 OpenRA.TilesetBuilder/Form1.resx OpenRA.TilesetBuilder.Form1.resources 1> /dev/null
tools: editor ralint seqed filex tsbuild
all: game tools
fixheader: packaging/fixheader.cs
@$(CSC) packaging/fixheader.cs $(CSFLAGS) -out:fixheader.exe -t:exe $(COMMON_LIBS:%=-r:%)
define BUILD_ASSEMBLY
$$($(1)_TARGET): $$($(1)_SRCS) Makefile $$($(1)_DEPS)
$$($(1)_TARGET): $$($(1)_SRCS) Makefile $$($(1)_DEPS) fixheader
@echo CSC $$(@)
@$(CSC) $$($(1)_LIBS:%=-r:%) \
-out:$$(@) $(CSFLAGS) $$($(1)_FLAGS) \
@@ -179,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))))

View File

@@ -1,363 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see LICENSE.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using OpenRA;
using OpenRA.FileFormats;
namespace MapConverter
{
public class MapConverter
{
// Mapping from ra overlay index to type string
static string[] raOverlayNames =
{
"sbag", "cycl", "brik", "fenc", "wood",
"gold01", "gold02", "gold03", "gold04",
"gem01", "gem02", "gem03", "gem04",
"v12", "v13", "v14", "v15", "v16", "v17", "v18",
"fpls", "wcrate", "scrate", "barb", "sbag",
};
static Dictionary< string, Pair<byte,byte> > overlayResourceMapping = new Dictionary<string, Pair<byte, byte>>()
{
// RA Gems, Gold
{ "gold01", new Pair<byte,byte>(1,0) },
{ "gold02", new Pair<byte,byte>(1,1) },
{ "gold03", new Pair<byte,byte>(1,2) },
{ "gold04", new Pair<byte,byte>(1,3) },
{ "gem01", new Pair<byte,byte>(2,0) },
{ "gem02", new Pair<byte,byte>(2,1) },
{ "gem03", new Pair<byte,byte>(2,2) },
{ "gem04", new Pair<byte,byte>(2,3) },
// cnc tiberium
{ "ti1", new Pair<byte,byte>(1,0) },
{ "ti2", new Pair<byte,byte>(1,1) },
{ "ti3", new Pair<byte,byte>(1,2) },
{ "ti4", new Pair<byte,byte>(1,3) },
{ "ti5", new Pair<byte,byte>(1,4) },
{ "ti6", new Pair<byte,byte>(1,5) },
{ "ti7", new Pair<byte,byte>(1,6) },
{ "ti8", new Pair<byte,byte>(1,7) },
{ "ti9", new Pair<byte,byte>(1,8) },
{ "ti10", new Pair<byte,byte>(1,9) },
{ "ti11", new Pair<byte,byte>(1,10) },
{ "ti12", new Pair<byte,byte>(1,11) },
};
static Dictionary<string, string> overlayActorMapping = new Dictionary<string,string>() {
// Fences
{"sbag","sbag"},
{"cycl","cycl"},
{"brik","brik"},
{"fenc","fenc"},
{"wood","wood"},
// Fields
{"v12","v12"},
{"v13","v13"},
{"v14","v14"},
{"v15","v15"},
{"v16","v16"},
{"v17","v17"},
{"v18","v18"},
// Crates
{"wcrate","crate"},
{"scrate","crate"},
};
int MapSize;
int ActorCount = 0;
Map Map = new Map();
public MapConverter(string[] args)
{
if (args.Length != 3)
{
Console.WriteLine("usage: MapConverter mod[,mod]* input-map.ini output-map.yaml");
return;
}
Game.InitializeEngineWithMods(args[0].Split(','));
ConvertIniMap(args[1]);
Save(args[2]);
}
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));
var basic = file.GetSection("Basic");
var map = file.GetSection("Map");
var legacyMapFormat = (IniMapFormat)int.Parse(basic.GetValue("NewINIFormat", "0"));
var XOffset = int.Parse(map.GetValue("X", "0"));
var YOffset = int.Parse(map.GetValue("Y", "0"));
var Width = int.Parse(map.GetValue("Width", "0"));
var Height = int.Parse(map.GetValue("Height", "0"));
MapSize = (legacyMapFormat == IniMapFormat.RedAlert) ? 128 : 64;
Map.Title = basic.GetValue("Name", "(null)");
Map.Author = "Westwood Studios";
Map.Tileset = Truncate(map.GetValue("Theater", "TEMPERAT"), 8);
Map.MapSize.X = MapSize;
Map.MapSize.Y = MapSize;
Map.TopLeft = new int2 (XOffset, YOffset);
Map.BottomRight = new int2(XOffset+Width,YOffset+Height);
Map.Selectable = true;
if (legacyMapFormat == IniMapFormat.RedAlert)
{
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");
LoadActors(file, "UNITS");
LoadActors(file, "INFANTRY");
LoadSmudges(file, "SMUDGE");
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();
foreach (var kv in wp)
Map.Waypoints.Add("spawn"+kv.First, kv.Second);
}
static int2 LocationFromMapOffset(int offset, int mapSize)
{
return new int2(offset % mapSize, offset / mapSize);
}
static MemoryStream ReadPackedSection(IniSection mapPackSection)
{
StringBuilder sb = new StringBuilder();
for (int i = 1; ; i++)
{
string line = mapPackSection.GetValue(i.ToString(), null);
if (line == null)
break;
sb.Append(line.Trim());
}
byte[] data = Convert.FromBase64String(sb.ToString());
List<byte[]> chunks = new List<byte[]>();
BinaryReader reader = new BinaryReader(new MemoryStream(data));
try
{
while (true)
{
uint length = reader.ReadUInt32() & 0xdfffffff;
byte[] dest = new byte[8192];
byte[] src = reader.ReadBytes((int)length);
/*int actualLength =*/ Format80.DecodeInto(src, dest);
chunks.Add(dest);
}
}
catch (EndOfStreamException) { }
MemoryStream ms = new MemoryStream();
foreach (byte[] chunk in chunks)
ms.Write(chunk, 0, chunk.Length);
ms.Position = 0;
return ms;
}
static byte ReadByte( Stream s )
{
int ret = s.ReadByte();
if( ret == -1 )
throw new NotImplementedException();
return (byte)ret;
}
static ushort ReadWord(Stream s)
{
ushort ret = ReadByte(s);
ret |= (ushort)(ReadByte(s) << 8);
return ret;
}
void UnpackRATileData( MemoryStream ms )
{
Map.MapTiles = new TileReference<ushort, byte>[ MapSize, MapSize ];
for( int i = 0 ; i < MapSize ; i++ )
for( int j = 0 ; j < MapSize ; j++ )
Map.MapTiles[i,j] = new TileReference<ushort,byte>();
for( int j = 0 ; j < MapSize ; j++ )
for( int i = 0 ; i < MapSize ; i++ )
Map.MapTiles[i,j].type = ReadWord(ms);
for( int j = 0 ; j < MapSize ; j++ )
for( int i = 0 ; i < MapSize ; i++ )
{
Map.MapTiles[i,j].index = ReadByte(ms);
if( Map.MapTiles[i,j].type == 0xff || Map.MapTiles[i,j].type == 0xffff )
Map.MapTiles[i,j].index = byte.MaxValue;
}
}
void UnpackRAOverlayData( MemoryStream ms )
{
Map.MapResources = new TileReference<byte, byte>[ MapSize, MapSize ];
for( int j = 0 ; j < MapSize ; j++ )
for( int i = 0 ; i < MapSize ; i++ )
{
byte o = ReadByte( ms );
var res = Pair.New((byte)0,(byte)0);
if (o != 255 && overlayResourceMapping.ContainsKey(raOverlayNames[o]))
res = overlayResourceMapping[raOverlayNames[o]];
Map.MapResources[i,j] = new TileReference<byte,byte>(res.First, res.Second);
if (o != 255 && overlayActorMapping.ContainsKey(raOverlayNames[o]))
Map.Actors.Add("Actor"+ActorCount, new ActorReference("Actor"+ActorCount++, overlayActorMapping[raOverlayNames[o]], new int2(i,j), "Neutral"));
}
}
void ReadRATrees( IniFile file )
{
IniSection terrain = file.GetSection( "TERRAIN", true );
if( terrain == null )
return;
foreach( KeyValuePair<string, string> kv in terrain )
{
var loc = int.Parse( kv.Key );
Map.Actors.Add("Actor"+ActorCount, new ActorReference("Actor"+ActorCount++,kv.Value.ToLowerInvariant(), new int2(loc % MapSize, loc / MapSize), "Neutral" ) );
}
}
void UnpackCncTileData( Stream ms )
{
Map.MapTiles = new TileReference<ushort, byte>[ MapSize, MapSize ];
for( int i = 0 ; i < MapSize ; i++ )
for( int j = 0 ; j < MapSize ; j++ )
Map.MapTiles[i,j] = new TileReference<ushort,byte>();
for( int j = 0 ; j < MapSize ; j++ )
for( int i = 0 ; i < MapSize ; i++ )
{
Map.MapTiles[i,j].type = ReadByte(ms);
Map.MapTiles[i,j].index = ReadByte(ms);
if( Map.MapTiles[i,j].type == 0xff )
Map.MapTiles[i,j].index = byte.MaxValue;
}
}
void ReadCncOverlay( IniFile file )
{
IniSection overlay = file.GetSection( "OVERLAY", true );
if( overlay == null )
return;
Map.MapResources = new TileReference<byte, byte>[ MapSize, MapSize ];
foreach( KeyValuePair<string, string> kv in overlay )
{
var loc = int.Parse( kv.Key );
int2 cell = new int2(loc % MapSize, loc / MapSize);
var res = Pair.New((byte)0,(byte)0);
if (overlayResourceMapping.ContainsKey(kv.Value.ToLower()))
res = overlayResourceMapping[kv.Value.ToLower()];
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, new ActorReference("Actor"+ActorCount++, overlayActorMapping[kv.Value.ToLower()], new int2(cell.X,cell.Y), "Neutral"));
}
}
void ReadCncTrees( IniFile file )
{
IniSection terrain = file.GetSection( "TERRAIN", true );
if( terrain == null )
return;
foreach( KeyValuePair<string, string> kv in terrain )
{
var loc = int.Parse( kv.Key );
Map.Actors.Add("Actor"+ActorCount, new ActorReference("Actor"+ActorCount++, kv.Value.Split(',')[0].ToLowerInvariant(), new int2(loc % MapSize, loc / MapSize),"Neutral"));
}
}
void LoadActors(IniFile file, string section)
{
foreach (var s in file.GetSection(section, true))
{
//num=owner,type,health,location,facing,...
var parts = s.Value.Split( ',' );
var loc = int.Parse(parts[3]);
if (parts[0] == "")
parts[0] = "Neutral";
Map.Actors.Add("Actor"+ActorCount, new ActorReference("Actor"+ActorCount++, parts[1].ToLowerInvariant(), new int2(loc % MapSize, loc / MapSize), parts[0]));
}
}
void LoadSmudges(IniFile file, string section)
{
foreach (var s in file.GetSection(section, true))
{
//loc=type,loc,depth
var parts = s.Value.Split( ',' );
var loc = int.Parse(parts[1]);
Map.Smudges.Add(new SmudgeReference(parts[0].ToLowerInvariant(),new int2(loc % MapSize, loc / MapSize),int.Parse(parts[2])));
}
}
static string Truncate( string s, int maxLength )
{
return s.Length <= maxLength ? s : s.Substring(0,maxLength );
}
public void Save(string filepath)
{
Directory.CreateDirectory(filepath);
Map.Package = new Folder(filepath);
Map.Save(filepath);
}
}
}

View File

@@ -1,51 +0,0 @@
<Project DefaultTargets="Build" ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{BA2B4C61-D5EE-4C3E-9BA1-EB32C531FA36}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>MapConverter</RootNamespace>
<AssemblyName>MapConverter</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\</OutputPath>
<DefineConstants>DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Drawing" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Main.cs" />
<Compile Include="MapConverter.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
<Project>{BDAEAB25-991E-46A7-AF1E-4F0E03358DAA}</Project>
<Name>OpenRA.FileFormats</Name>
</ProjectReference>
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj">
<Project>{0DFB103F-2962-400F-8C6D-E2C28CCBA633}</Project>
<Name>OpenRA.Game</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>

View File

@@ -39,14 +39,24 @@
this.actorPalette = new System.Windows.Forms.FlowLayoutPanel();
this.tabPage3 = new System.Windows.Forms.TabPage();
this.resourcePalette = new System.Windows.Forms.FlowLayoutPanel();
this.toolStrip1 = new System.Windows.Forms.ToolStrip();
this.toolStripButton3 = new System.Windows.Forms.ToolStripButton();
this.toolStripButton5 = new System.Windows.Forms.ToolStripButton();
this.toolStripButton4 = new System.Windows.Forms.ToolStripButton();
this.toolStripButton1 = new System.Windows.Forms.ToolStripButton();
this.toolStripButton6 = new System.Windows.Forms.ToolStripButton();
this.toolStripButton7 = new System.Windows.Forms.ToolStripButton();
this.toolStripButton2 = new System.Windows.Forms.ToolStripButton();
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.newToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.saveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.saveAsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
this.cCRedAlertMapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.bitmapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
this.exotToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.mapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.propertiesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.resizeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator();
this.spawnpointsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.tt = new System.Windows.Forms.ToolTip(this.components);
this.folderBrowser = new System.Windows.Forms.FolderBrowserDialog();
this.surface1 = new OpenRA.Editor.Surface();
@@ -60,7 +70,7 @@
this.tabPage1.SuspendLayout();
this.tabPage2.SuspendLayout();
this.tabPage3.SuspendLayout();
this.toolStrip1.SuspendLayout();
this.menuStrip1.SuspendLayout();
this.SuspendLayout();
//
// toolStripContainer1
@@ -69,7 +79,7 @@
// toolStripContainer1.ContentPanel
//
this.toolStripContainer1.ContentPanel.Controls.Add(this.splitContainer1);
this.toolStripContainer1.ContentPanel.Size = new System.Drawing.Size(985, 680);
this.toolStripContainer1.ContentPanel.Size = new System.Drawing.Size(985, 681);
this.toolStripContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
this.toolStripContainer1.Location = new System.Drawing.Point(0, 0);
this.toolStripContainer1.Name = "toolStripContainer1";
@@ -79,7 +89,7 @@
//
// toolStripContainer1.TopToolStripPanel
//
this.toolStripContainer1.TopToolStripPanel.Controls.Add(this.toolStrip1);
this.toolStripContainer1.TopToolStripPanel.Controls.Add(this.menuStrip1);
//
// splitContainer1
//
@@ -94,13 +104,12 @@
// splitContainer1.Panel2
//
this.splitContainer1.Panel2.Controls.Add(this.surface1);
this.splitContainer1.Size = new System.Drawing.Size(985, 680);
this.splitContainer1.Size = new System.Drawing.Size(985, 681);
this.splitContainer1.SplitterDistance = 198;
this.splitContainer1.TabIndex = 0;
//
// tabControl1
//
this.tabControl1.Alignment = System.Windows.Forms.TabAlignment.Left;
this.tabControl1.Controls.Add(this.tabPage1);
this.tabControl1.Controls.Add(this.tabPage2);
this.tabControl1.Controls.Add(this.tabPage3);
@@ -109,16 +118,16 @@
this.tabControl1.Multiline = true;
this.tabControl1.Name = "tabControl1";
this.tabControl1.SelectedIndex = 0;
this.tabControl1.Size = new System.Drawing.Size(198, 680);
this.tabControl1.Size = new System.Drawing.Size(198, 681);
this.tabControl1.TabIndex = 0;
//
// tabPage1
//
this.tabPage1.Controls.Add(this.tilePalette);
this.tabPage1.Location = new System.Drawing.Point(23, 4);
this.tabPage1.Location = new System.Drawing.Point(4, 22);
this.tabPage1.Name = "tabPage1";
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
this.tabPage1.Size = new System.Drawing.Size(171, 672);
this.tabPage1.Size = new System.Drawing.Size(190, 655);
this.tabPage1.TabIndex = 0;
this.tabPage1.Text = "Templates";
this.tabPage1.UseVisualStyleBackColor = true;
@@ -130,16 +139,16 @@
this.tilePalette.Dock = System.Windows.Forms.DockStyle.Fill;
this.tilePalette.Location = new System.Drawing.Point(3, 3);
this.tilePalette.Name = "tilePalette";
this.tilePalette.Size = new System.Drawing.Size(165, 666);
this.tilePalette.Size = new System.Drawing.Size(184, 649);
this.tilePalette.TabIndex = 1;
//
// tabPage2
//
this.tabPage2.Controls.Add(this.actorPalette);
this.tabPage2.Location = new System.Drawing.Point(23, 4);
this.tabPage2.Location = new System.Drawing.Point(4, 22);
this.tabPage2.Name = "tabPage2";
this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
this.tabPage2.Size = new System.Drawing.Size(171, 672);
this.tabPage2.Size = new System.Drawing.Size(190, 655);
this.tabPage2.TabIndex = 1;
this.tabPage2.Text = "Actors";
this.tabPage2.UseVisualStyleBackColor = true;
@@ -151,15 +160,15 @@
this.actorPalette.Dock = System.Windows.Forms.DockStyle.Fill;
this.actorPalette.Location = new System.Drawing.Point(3, 3);
this.actorPalette.Name = "actorPalette";
this.actorPalette.Size = new System.Drawing.Size(165, 666);
this.actorPalette.Size = new System.Drawing.Size(184, 649);
this.actorPalette.TabIndex = 2;
//
// tabPage3
//
this.tabPage3.Controls.Add(this.resourcePalette);
this.tabPage3.Location = new System.Drawing.Point(23, 4);
this.tabPage3.Location = new System.Drawing.Point(4, 22);
this.tabPage3.Name = "tabPage3";
this.tabPage3.Size = new System.Drawing.Size(171, 672);
this.tabPage3.Size = new System.Drawing.Size(190, 655);
this.tabPage3.TabIndex = 2;
this.tabPage3.Text = "Resources";
this.tabPage3.UseVisualStyleBackColor = true;
@@ -171,87 +180,154 @@
this.resourcePalette.Dock = System.Windows.Forms.DockStyle.Fill;
this.resourcePalette.Location = new System.Drawing.Point(0, 0);
this.resourcePalette.Name = "resourcePalette";
this.resourcePalette.Size = new System.Drawing.Size(171, 672);
this.resourcePalette.Size = new System.Drawing.Size(190, 655);
this.resourcePalette.TabIndex = 3;
//
// toolStrip1
// menuStrip1
//
this.toolStrip1.Dock = System.Windows.Forms.DockStyle.None;
this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.toolStripButton3,
this.toolStripButton5,
this.toolStripButton4,
this.toolStripButton1,
this.toolStripButton6,
this.toolStripButton7,
this.toolStripButton2});
this.toolStrip1.Location = new System.Drawing.Point(3, 0);
this.toolStrip1.Name = "toolStrip1";
this.toolStrip1.Size = new System.Drawing.Size(480, 25);
this.toolStrip1.TabIndex = 0;
this.menuStrip1.Dock = System.Windows.Forms.DockStyle.None;
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.fileToolStripMenuItem,
this.mapToolStripMenuItem});
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
this.menuStrip1.Name = "menuStrip1";
this.menuStrip1.Size = new System.Drawing.Size(985, 24);
this.menuStrip1.TabIndex = 1;
this.menuStrip1.Text = "menuStrip1";
//
// toolStripButton3
// fileToolStripMenuItem
//
this.toolStripButton3.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton3.Image")));
this.toolStripButton3.ImageTransparentColor = System.Drawing.Color.Magenta;
this.toolStripButton3.Name = "toolStripButton3";
this.toolStripButton3.Size = new System.Drawing.Size(51, 22);
this.toolStripButton3.Text = "New";
this.toolStripButton3.Click += new System.EventHandler(this.NewClicked);
this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.newToolStripMenuItem,
this.toolStripSeparator1,
this.openToolStripMenuItem,
this.saveToolStripMenuItem,
this.saveAsToolStripMenuItem,
this.toolStripSeparator2,
this.toolStripMenuItem1,
this.toolStripSeparator3,
this.exotToolStripMenuItem});
this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
this.fileToolStripMenuItem.Text = "&File";
//
// toolStripButton5
// newToolStripMenuItem
//
this.toolStripButton5.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton5.Image")));
this.toolStripButton5.ImageTransparentColor = System.Drawing.Color.Magenta;
this.toolStripButton5.Name = "toolStripButton5";
this.toolStripButton5.Size = new System.Drawing.Size(56, 22);
this.toolStripButton5.Text = "Open";
this.toolStripButton5.Click += new System.EventHandler(this.OpenClicked);
this.newToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("newToolStripMenuItem.Image")));
this.newToolStripMenuItem.ImageTransparentColor = System.Drawing.Color.Fuchsia;
this.newToolStripMenuItem.Name = "newToolStripMenuItem";
this.newToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.newToolStripMenuItem.Text = "&New...";
this.newToolStripMenuItem.Click += new System.EventHandler(this.NewClicked);
//
// toolStripButton4
// toolStripSeparator1
//
this.toolStripButton4.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton4.Image")));
this.toolStripButton4.ImageTransparentColor = System.Drawing.Color.Magenta;
this.toolStripButton4.Name = "toolStripButton4";
this.toolStripButton4.Size = new System.Drawing.Size(51, 22);
this.toolStripButton4.Text = "Save";
this.toolStripButton4.Click += new System.EventHandler(this.SaveClicked);
this.toolStripSeparator1.Name = "toolStripSeparator1";
this.toolStripSeparator1.Size = new System.Drawing.Size(149, 6);
//
// toolStripButton1
// openToolStripMenuItem
//
this.toolStripButton1.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton1.Image")));
this.toolStripButton1.ImageTransparentColor = System.Drawing.Color.Magenta;
this.toolStripButton1.Name = "toolStripButton1";
this.toolStripButton1.Size = new System.Drawing.Size(76, 22);
this.toolStripButton1.Text = "Save As...";
this.toolStripButton1.Click += new System.EventHandler(this.SaveAsClicked);
this.openToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("openToolStripMenuItem.Image")));
this.openToolStripMenuItem.Name = "openToolStripMenuItem";
this.openToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.openToolStripMenuItem.Text = "&Open...";
this.openToolStripMenuItem.Click += new System.EventHandler(this.OpenClicked);
//
// toolStripButton6
// saveToolStripMenuItem
//
this.toolStripButton6.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton6.Image")));
this.toolStripButton6.ImageTransparentColor = System.Drawing.Color.Magenta;
this.toolStripButton6.Name = "toolStripButton6";
this.toolStripButton6.Size = new System.Drawing.Size(59, 22);
this.toolStripButton6.Text = "Resize";
this.toolStripButton6.Click += new System.EventHandler(this.ResizeClicked);
this.saveToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("saveToolStripMenuItem.Image")));
this.saveToolStripMenuItem.Name = "saveToolStripMenuItem";
this.saveToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.saveToolStripMenuItem.Text = "&Save";
this.saveToolStripMenuItem.Click += new System.EventHandler(this.SaveClicked);
//
// toolStripButton7
// saveAsToolStripMenuItem
//
this.toolStripButton7.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton7.Image")));
this.toolStripButton7.ImageTransparentColor = System.Drawing.Color.Magenta;
this.toolStripButton7.Name = "toolStripButton7";
this.toolStripButton7.Size = new System.Drawing.Size(80, 22);
this.toolStripButton7.Text = "Properties";
this.toolStripButton7.Click += new System.EventHandler(this.PropertiesClicked);
this.saveAsToolStripMenuItem.Name = "saveAsToolStripMenuItem";
this.saveAsToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.saveAsToolStripMenuItem.Text = "Save &As...";
this.saveAsToolStripMenuItem.Click += new System.EventHandler(this.SaveAsClicked);
//
// toolStripButton2
// toolStripSeparator2
//
this.toolStripButton2.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton2.Image")));
this.toolStripButton2.ImageTransparentColor = System.Drawing.Color.Magenta;
this.toolStripButton2.Name = "toolStripButton2";
this.toolStripButton2.Size = new System.Drawing.Size(95, 22);
this.toolStripButton2.Text = "Spawnpoints";
this.toolStripButton2.Click += new System.EventHandler(this.SpawnPointsClicked);
this.toolStripSeparator2.Name = "toolStripSeparator2";
this.toolStripSeparator2.Size = new System.Drawing.Size(149, 6);
//
// toolStripMenuItem1
//
this.toolStripMenuItem1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.cCRedAlertMapToolStripMenuItem,
this.bitmapToolStripMenuItem});
this.toolStripMenuItem1.Name = "toolStripMenuItem1";
this.toolStripMenuItem1.Size = new System.Drawing.Size(152, 22);
this.toolStripMenuItem1.Text = "&Import";
//
// cCRedAlertMapToolStripMenuItem
//
this.cCRedAlertMapToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("cCRedAlertMapToolStripMenuItem.Image")));
this.cCRedAlertMapToolStripMenuItem.Name = "cCRedAlertMapToolStripMenuItem";
this.cCRedAlertMapToolStripMenuItem.Size = new System.Drawing.Size(195, 22);
this.cCRedAlertMapToolStripMenuItem.Text = "&C&&C / Red Alert Map...";
this.cCRedAlertMapToolStripMenuItem.Click += new System.EventHandler(this.ImportLegacyMapClicked);
//
// bitmapToolStripMenuItem
//
this.bitmapToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("bitmapToolStripMenuItem.Image")));
this.bitmapToolStripMenuItem.Name = "bitmapToolStripMenuItem";
this.bitmapToolStripMenuItem.Size = new System.Drawing.Size(195, 22);
this.bitmapToolStripMenuItem.Text = "&Bitmap...";
//
// toolStripSeparator3
//
this.toolStripSeparator3.Name = "toolStripSeparator3";
this.toolStripSeparator3.Size = new System.Drawing.Size(149, 6);
//
// exotToolStripMenuItem
//
this.exotToolStripMenuItem.Name = "exotToolStripMenuItem";
this.exotToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.exotToolStripMenuItem.Text = "E&xit";
this.exotToolStripMenuItem.Click += new System.EventHandler(this.CloseClicked);
//
// mapToolStripMenuItem
//
this.mapToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.propertiesToolStripMenuItem,
this.resizeToolStripMenuItem,
this.toolStripSeparator4,
this.spawnpointsToolStripMenuItem});
this.mapToolStripMenuItem.Name = "mapToolStripMenuItem";
this.mapToolStripMenuItem.Size = new System.Drawing.Size(43, 20);
this.mapToolStripMenuItem.Text = "&Map";
//
// propertiesToolStripMenuItem
//
this.propertiesToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("propertiesToolStripMenuItem.Image")));
this.propertiesToolStripMenuItem.Name = "propertiesToolStripMenuItem";
this.propertiesToolStripMenuItem.Size = new System.Drawing.Size(142, 22);
this.propertiesToolStripMenuItem.Text = "&Properties...";
this.propertiesToolStripMenuItem.Click += new System.EventHandler(this.PropertiesClicked);
//
// resizeToolStripMenuItem
//
this.resizeToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("resizeToolStripMenuItem.Image")));
this.resizeToolStripMenuItem.Name = "resizeToolStripMenuItem";
this.resizeToolStripMenuItem.Size = new System.Drawing.Size(142, 22);
this.resizeToolStripMenuItem.Text = "&Resize...";
this.resizeToolStripMenuItem.Click += new System.EventHandler(this.ResizeClicked);
//
// toolStripSeparator4
//
this.toolStripSeparator4.Name = "toolStripSeparator4";
this.toolStripSeparator4.Size = new System.Drawing.Size(139, 6);
//
// spawnpointsToolStripMenuItem
//
this.spawnpointsToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("spawnpointsToolStripMenuItem.Image")));
this.spawnpointsToolStripMenuItem.Name = "spawnpointsToolStripMenuItem";
this.spawnpointsToolStripMenuItem.Size = new System.Drawing.Size(142, 22);
this.spawnpointsToolStripMenuItem.Text = "&Spawnpoints";
this.spawnpointsToolStripMenuItem.Click += new System.EventHandler(this.SpawnPointsClicked);
//
// tt
//
@@ -263,7 +339,7 @@
this.surface1.Dock = System.Windows.Forms.DockStyle.Fill;
this.surface1.Location = new System.Drawing.Point(0, 0);
this.surface1.Name = "surface1";
this.surface1.Size = new System.Drawing.Size(783, 680);
this.surface1.Size = new System.Drawing.Size(783, 681);
this.surface1.TabIndex = 5;
this.surface1.Text = "surface1";
//
@@ -274,9 +350,11 @@
this.ClientSize = new System.Drawing.Size(985, 705);
this.Controls.Add(this.toolStripContainer1);
this.KeyPreview = true;
this.MainMenuStrip = this.menuStrip1;
this.Name = "Form1";
this.Text = "OpenRA Editor";
this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyUp);
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.OnFormClosing);
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
this.toolStripContainer1.ContentPanel.ResumeLayout(false);
this.toolStripContainer1.TopToolStripPanel.ResumeLayout(false);
@@ -290,8 +368,8 @@
this.tabPage1.ResumeLayout(false);
this.tabPage2.ResumeLayout(false);
this.tabPage3.ResumeLayout(false);
this.toolStrip1.ResumeLayout(false);
this.toolStrip1.PerformLayout();
this.menuStrip1.ResumeLayout(false);
this.menuStrip1.PerformLayout();
this.ResumeLayout(false);
}
@@ -301,23 +379,33 @@
private System.Windows.Forms.ToolStripContainer toolStripContainer1;
private System.Windows.Forms.SplitContainer splitContainer1;
private System.Windows.Forms.ToolTip tt;
private System.Windows.Forms.ToolStrip toolStrip1;
private System.Windows.Forms.ToolStripButton toolStripButton1;
private System.Windows.Forms.TabControl tabControl1;
private System.Windows.Forms.TabPage tabPage1;
private System.Windows.Forms.FlowLayoutPanel tilePalette;
private System.Windows.Forms.TabPage tabPage2;
private System.Windows.Forms.ToolStripButton toolStripButton2;
private System.Windows.Forms.FlowLayoutPanel actorPalette;
private System.Windows.Forms.TabPage tabPage3;
private System.Windows.Forms.FlowLayoutPanel resourcePalette;
private System.Windows.Forms.ToolStripButton toolStripButton3;
private System.Windows.Forms.ToolStripButton toolStripButton5;
private System.Windows.Forms.ToolStripButton toolStripButton4;
private System.Windows.Forms.FolderBrowserDialog folderBrowser;
private System.Windows.Forms.ToolStripButton toolStripButton6;
private System.Windows.Forms.ToolStripButton toolStripButton7;
private Surface surface1;
private System.Windows.Forms.MenuStrip menuStrip1;
private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem newToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem openToolStripMenuItem;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
private System.Windows.Forms.ToolStripMenuItem saveToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem saveAsToolStripMenuItem;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
private System.Windows.Forms.ToolStripMenuItem exotToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem1;
private System.Windows.Forms.ToolStripMenuItem cCRedAlertMapToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem bitmapToolStripMenuItem;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator3;
private System.Windows.Forms.ToolStripMenuItem mapToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem propertiesToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem resizeToolStripMenuItem;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator4;
private System.Windows.Forms.ToolStripMenuItem spawnpointsToolStripMenuItem;
}
}

View File

@@ -32,22 +32,22 @@ 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);
surface1.AfterChange += MakeDirty;
}
void MakeDirty() { dirty = true; }
string loadedMapName;
string currentMod = "ra";
TileSet tileset;
bool dirty = false;
void LoadMap(string mapname)
{
@@ -57,17 +57,20 @@ 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));
PrepareMapResources(manifest, map);
// upgrade maps that have no player definitions. editor doesnt care,
// but this breaks the game pretty badly.
if (map.Players.Count == 0)
map.Players.Add("Neutral", new PlayerReference("Neutral",
Rules.Info["world"].Traits.WithInterface<CountryInfo>().First().Race, true, true));
PrepareMapResources(Game.modData.Manifest, map);
dirty = false;
}
void NewMap(Map map)
@@ -78,14 +81,11 @@ 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(Game.modData.Manifest, map);
PrepareMapResources(manifest, map);
MakeDirty();
}
void PrepareMapResources(Manifest manifest, Map map)
@@ -137,6 +137,7 @@ namespace OpenRA.Editor
try
{
var info = Rules.Info[a];
if( !info.Traits.Contains<RenderSimpleInfo>() ) continue;
var template = RenderActor(info, tileset, palette);
var ibox = new PictureBox
{
@@ -151,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);
}
@@ -328,6 +328,8 @@ namespace OpenRA.Editor
surface1.Map.PlayerCount = surface1.Map.Waypoints.Count;
surface1.Map.Package = new Folder(loadedMapName);
surface1.Map.Save(loadedMapName);
dirty = false;
}
}
@@ -337,7 +339,6 @@ namespace OpenRA.Editor
if (DialogResult.OK == folderBrowser.ShowDialog())
{
loadedMapName = folderBrowser.SelectedPath;
SaveClicked(sender, e);
}
@@ -396,5 +397,48 @@ namespace OpenRA.Editor
void SpawnPointsClicked(object sender, EventArgs e) { surface1.SetWaypoint(new WaypointTemplate()); }
void Form1_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Space) surface1.IsPanning = true; }
void Form1_KeyUp(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Space) surface1.IsPanning = false; }
void CloseClicked(object sender, EventArgs e)
{
Close();
}
void ImportLegacyMapClicked(object sender, EventArgs e)
{
using (var ofd = new OpenFileDialog { Filter = "Legacy maps (*.ini;*.mpr)|*.ini;*.mpr" })
if (DialogResult.OK == ofd.ShowDialog())
{
/* massive hack: we should be able to call NewMap() with the imported Map object,
* but something's not right internally in it, unless loaded via the real maploader */
var savePath = Path.Combine(Path.GetTempPath(), "OpenRA.Import");
Directory.CreateDirectory(savePath);
var map = LegacyMapImporter.Import(ofd.FileName);
map.Package = new Folder(savePath);
map.Players.Add("Neutral", new PlayerReference("Neutral",
Rules.Info["world"].Traits.WithInterface<CountryInfo>().First().Race, true, true));
map.Save(savePath);
LoadMap(savePath);
loadedMapName = null; /* editor needs to think this hasnt been saved */
Directory.Delete(savePath, true);
MakeDirty();
}
}
void OnFormClosing(object sender, FormClosingEventArgs e)
{
if (!dirty) return;
switch (MessageBox.Show("The map has been modified since it was last saved. Save changes now?",
"Unsaved Changes", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation))
{
case DialogResult.Yes: SaveClicked(null, EventArgs.Empty); break;
case DialogResult.No: break;
case DialogResult.Cancel: e.Cancel = true; break;
}
}
}
}

View File

@@ -117,113 +117,175 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>77, 17</value>
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>309, 17</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="toolStripButton3.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<data name="newToolStripMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIISURBVDhPpZP7S1NxGMbPPxKaXVUkMEq8IpKUCoY/hGgI
ymqkDYYXcCjDZOANURSjCNGFQUTsl4GXVMxKk62YU4fXQpaIlygHQxBRH8/zwvyaIAYe+HLgnPN8nue9
HA3nvDTq63oW/jm13XOwvPTB3DYFY5MH+bXfcN8ygfTSMSSXfESicQDxBqdYHwH29g9w2tnZ3UcguIvN
rR3417exuBJE5N1n/wfwLgXEOc38Bc6xNRHb+/y4nm49G0Bnit2zf9H6bkliE/jKuYxrd6oVgDWfjB+K
TWeKMyrGEVfowITvD9re/9ABVQrAhh0HHK+ZselMMaN/mvwtDb+aVqkA7HYIwIj3ysfluPTorJnP6Ezx
oHsD1s5ZXEktUwCOioB5f1CEPR9+wTG6iuiserTo8dkwng7HT/R+XUPF8xlcTjErAOdMcW6NW8STiwG8
7vej8oUPN/PsEv3t8Ao0TZP3T1u8uJRkUgAuSYHtO97oLxmXd5t9Ho8aPTK+GzntqNfrLm2fFoihwYOI
xGIF4KjoGBLzY1OrF9k6OOFxnwDC4wxIMX1G0pMhgVyMNyoA13PAtS7OrJk1PrC69LUdQWxuF6IybHrX
LRI7JrtZdoDAo1XmbjMyD+tjSXxGcXRmnYg5ttD9QuxDhN0uUgDOmbvNTpPOJaGAo2K36cyaGZvOFIfd
KlSA8/zRh9ABIDUG+1JpAAAAAElFTkSuQmCC
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHNSURBVDhPjZNdTxNREIb5LV54azSRxJ8gt95445XGa/8A
CU0MMVgIWNmgRSioGN0AapQaISAWrEaITQTkQyhL2WbbWtt1+7ll+7h7SDdBocskk3Mm58xzZt7JaWmx
bTdyk9/rPsz0fbYmz2JqfvSdHhJLPpxzTzMUid13F0D3k/h41V57iU+dI7/dfTqA8zKGBLUohjpurxER
7/24i/RkSnj/47csLsWOB27IZ1DmrlDQJrHIU8q8EbE63UrD7oVe09p243iIqEAPQGVOQJxKRJwfdgE9
gxO8X1wRkIWv/1Ri7A0QD5/HSrWT/HTNTgwIDapayAV0DTwXyQ0/IqwSvXU4hWQn6uwl4usPKCl9pGI+
F2DV6zwaX2BQjgjIiZPpHXrpJjU2di7lao2sXmLiwwYXL18/GeB/KP8HqJgH5Iwy++k/yDOrzQF3+p8d
AZi1A/RCleQvg81ElrHwt+aA232jLqBmWRjFKlq2wM/9HLGtFCOvPjcHdHQPCYBl1SmWTdK5IvFknu/b
GaIrKsEX880B7V1B6rZq5cqhaIqms7aT4cuqyuyygvR0xhvgiOao7Qjm9Byyyw7K80hj0wRGw94tOGPy
cs8fetoLfwGP5fd9L1vD4gAAAABJRU5ErkJggg==
</value>
</data>
<data name="toolStripButton5.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<data name="openToolStripMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIISURBVDhPpZP7S1NxGMbPPxKaXVUkMEq8IpKUCoY/hGgI
ymqkDYYXcCjDZOANURSjCNGFQUTsl4GXVMxKk62YU4fXQpaIlygHQxBRH8/zwvyaIAYe+HLgnPN8nue9
HA3nvDTq63oW/jm13XOwvPTB3DYFY5MH+bXfcN8ygfTSMSSXfESicQDxBqdYHwH29g9w2tnZ3UcguIvN
rR3417exuBJE5N1n/wfwLgXEOc38Bc6xNRHb+/y4nm49G0Bnit2zf9H6bkliE/jKuYxrd6oVgDWfjB+K
TWeKMyrGEVfowITvD9re/9ABVQrAhh0HHK+ZselMMaN/mvwtDb+aVqkA7HYIwIj3ysfluPTorJnP6Ezx
oHsD1s5ZXEktUwCOioB5f1CEPR9+wTG6iuiserTo8dkwng7HT/R+XUPF8xlcTjErAOdMcW6NW8STiwG8
7vej8oUPN/PsEv3t8Ao0TZP3T1u8uJRkUgAuSYHtO97oLxmXd5t9Ho8aPTK+GzntqNfrLm2fFoihwYOI
xGIF4KjoGBLzY1OrF9k6OOFxnwDC4wxIMX1G0pMhgVyMNyoA13PAtS7OrJk1PrC69LUdQWxuF6IybHrX
LRI7JrtZdoDAo1XmbjMyD+tjSXxGcXRmnYg5ttD9QuxDhN0uUgDOmbvNTpPOJaGAo2K36cyaGZvOFIfd
KlSA8/zRh9ABIDUG+1JpAAAAAElFTkSuQmCC
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsMAAALDAE/QCLIAAACeklE
QVQ4T6WTWUiUURiG/4suurJIjUIwskQsEANDqosQpUVNKbdCSRPKrQVDMddmXEedGdcpFXNGnUYdrSzF
sEIoFInQcqHE1CwoKsXUUMnt6Z+RLMtA8OK5OZz3Oe/5OEcAhPWwpnBaodZZmqfjWmY5GSo98XJNwq9D
1yQIilLhFaYIMIQuxZaZJ4uy6FS1T2BUIUKdNsdPhL9pqpEp/ne18PiiE94RCjzDco0C+rs1zIzXGvkx
pmNuRI2+XEG9NjV5NYlXuBLP0DzcQwoQaiuUTI9VrQgvfC5irjeSqrJMKovTURcmU5KbxE15HAWyGNH5
e/BCtSab6a+a5ZMXxTD9cdAbsir/CG6Xyvj+qcRY2xBurss28lCfRYNOxr3KdPTqFHQlUspV17mVl0Cx
Mg5VdqyxjVBZks7k+3wMtYdaJdRXKVkYksJw6kreiWuD8dAXBa8j6Krx5UqwR4WgVqUwMZAJH+Tcr1bQ
/ki+tOnPK7wKhA5v5tvcmHnsxNsKOy6ePTohCvYKpXkSvr2RMtyWSL1OzuKgBHpClwRdwWLwNPPtHsy0
HGGy6RCjdfuQhllzxsUs1zBMoUiZyGh3LHe02XQ+E5v0RYqC89AZwOLzk8w+Pc5U82HG6h34Um1LR9pm
/N2scLAxsTEKVFlx9LQkcVebBQNJ8FKs+8KH2VZ3pp44Md7gyIjejo+a3fTKTZGE7MJ+j0Xj8lPOz4ih
tjyT3gfhdOQIy7TLN9Ai20hj8iZqEsxRx1hw4+oO/F13LjraW7ouC3JSo8mRXCAjNoj4y36GyXLO14VT
xw7gfNCO/XbW2FpZYLndjK1bTNhmarLyIa3nKxuyPwG9D9E7Fbto+QAAAABJRU5ErkJggg==
</value>
</data>
<data name="toolStripButton4.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<data name="saveToolStripMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIISURBVDhPpZP7S1NxGMbPPxKaXVUkMEq8IpKUCoY/hGgI
ymqkDYYXcCjDZOANURSjCNGFQUTsl4GXVMxKk62YU4fXQpaIlygHQxBRH8/zwvyaIAYe+HLgnPN8nue9
HA3nvDTq63oW/jm13XOwvPTB3DYFY5MH+bXfcN8ygfTSMSSXfESicQDxBqdYHwH29g9w2tnZ3UcguIvN
rR3417exuBJE5N1n/wfwLgXEOc38Bc6xNRHb+/y4nm49G0Bnit2zf9H6bkliE/jKuYxrd6oVgDWfjB+K
TWeKMyrGEVfowITvD9re/9ABVQrAhh0HHK+ZselMMaN/mvwtDb+aVqkA7HYIwIj3ysfluPTorJnP6Ezx
oHsD1s5ZXEktUwCOioB5f1CEPR9+wTG6iuiserTo8dkwng7HT/R+XUPF8xlcTjErAOdMcW6NW8STiwG8
7vej8oUPN/PsEv3t8Ao0TZP3T1u8uJRkUgAuSYHtO97oLxmXd5t9Ho8aPTK+GzntqNfrLm2fFoihwYOI
xGIF4KjoGBLzY1OrF9k6OOFxnwDC4wxIMX1G0pMhgVyMNyoA13PAtS7OrJk1PrC69LUdQWxuF6IybHrX
LRI7JrtZdoDAo1XmbjMyD+tjSXxGcXRmnYg5ttD9QuxDhN0uUgDOmbvNTpPOJaGAo2K36cyaGZvOFIfd
KlSA8/zRh9ABIDUG+1JpAAAAAElFTkSuQmCC
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsMAAALDAE/QCLIAAACMklE
QVQ4T6WT3UuTcRTH9y90303QRbc1NYZrMRs86hzKsmFoIb09kqZpunxJUqxMxZYvkK1w6sTlahJOKvIF
TJEUMZaSlZkrWuJQU3QiGHz7nZ+wn0vrQh84Nw98Puc553wfBQDFXorDL18EQOXuXEGHaxlPHItoafbj
kXUW9XU/YLnnxd3yaZSWfEZR4Qfk5Y7japaHoVBwAYF9vUBd7Tq9/Oeztr4BfVIfTic95xUUuJ4tcUFl
xa//wv7FNRw3voJSKcN0yikEjrYFLigt+bmjgDoT/NW3hDCDG4UFMzhpbBUCW+McF1w3f9km2Aq/n/Lj
kORCTvYkEuJtQtDwwMcFmVfGQwR/w0MeH/ZrHZDlURgMViGorfnOBfKl0aBgJ7h35Bv2RTYj5cwbxMbW
C0FV5Qx6usFOCaSeH4bp7CDfNi2MZqbPps6bcD8STa8hSRYhuFU2hS43WAaAVvtvdvdVlN+Z50ulhdHM
GemeIByX0AmdrkIIbhRNwtkONNk2WHAC0Cdnh5TxnBnJl4t557h4Bsc4odWWCYE5b4LBQM39AO9MAgXL
WJhawtinOUxMz0POvY0T+g4Oq6Ps0GiKhYBiaX2I4GeTgOC0/Gp89C7AO7uMazctCNfaGdyC8GOPoY7M
F4KM9Hds7hU+80GdEweOSHjaNQB39xB6+kcw+HYM0QYTlJpGRDD4sKoBKlVOiCDx4oUBHk9KGIWE7kyn
om3Twmhm6kpF8NGITCGgn2K39QfkKu4LIHJHnwAAAABJRU5ErkJggg==
</value>
</data>
<data name="toolStripButton1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<data name="cCRedAlertMapToolStripMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIISURBVDhPpZP7S1NxGMbPPxKaXVUkMEq8IpKUCoY/hGgI
ymqkDYYXcCjDZOANURSjCNGFQUTsl4GXVMxKk62YU4fXQpaIlygHQxBRH8/zwvyaIAYe+HLgnPN8nue9
HA3nvDTq63oW/jm13XOwvPTB3DYFY5MH+bXfcN8ygfTSMSSXfESicQDxBqdYHwH29g9w2tnZ3UcguIvN
rR3417exuBJE5N1n/wfwLgXEOc38Bc6xNRHb+/y4nm49G0Bnit2zf9H6bkliE/jKuYxrd6oVgDWfjB+K
TWeKMyrGEVfowITvD9re/9ABVQrAhh0HHK+ZselMMaN/mvwtDb+aVqkA7HYIwIj3ysfluPTorJnP6Ezx
oHsD1s5ZXEktUwCOioB5f1CEPR9+wTG6iuiserTo8dkwng7HT/R+XUPF8xlcTjErAOdMcW6NW8STiwG8
7vej8oUPN/PsEv3t8Ao0TZP3T1u8uJRkUgAuSYHtO97oLxmXd5t9Ho8aPTK+GzntqNfrLm2fFoihwYOI
xGIF4KjoGBLzY1OrF9k6OOFxnwDC4wxIMX1G0pMhgVyMNyoA13PAtS7OrJk1PrC69LUdQWxuF6IybHrX
LRI7JrtZdoDAo1XmbjMyD+tjSXxGcXRmnYg5ttD9QuxDhN0uUgDOmbvNTpPOJaGAo2K36cyaGZvOFIfd
KlSA8/zRh9ABIDUG+1JpAAAAAElFTkSuQmCC
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAACH
DwAAjA8AAP1SAACBQAAAfXkAAOmLAAA85QAAGcxzPIV3AAAKOWlDQ1BQaG90b3Nob3AgSUNDIHByb2Zp
bGUAAEjHnZZ3VFTXFofPvXd6oc0wAlKG3rvAANJ7k15FYZgZYCgDDjM0sSGiAhFFRJoiSFDEgNFQJFZE
sRAUVLAHJAgoMRhFVCxvRtaLrqy89/Ly++Osb+2z97n77L3PWhcAkqcvl5cGSwGQyhPwgzyc6RGRUXTs
AIABHmCAKQBMVka6X7B7CBDJy82FniFyAl8EAfB6WLwCcNPQM4BOB/+fpFnpfIHomAARm7M5GSwRF4g4
JUuQLrbPipgalyxmGCVmvihBEcuJOWGRDT77LLKjmNmpPLaIxTmns1PZYu4V8bZMIUfEiK+ICzO5nCwR
3xKxRoowlSviN+LYVA4zAwAUSWwXcFiJIjYRMYkfEuQi4uUA4EgJX3HcVyzgZAvEl3JJS8/hcxMSBXQd
li7d1NqaQffkZKVwBALDACYrmcln013SUtOZvBwAFu/8WTLi2tJFRbY0tba0NDQzMv2qUP91829K3NtF
ehn4uWcQrf+L7a/80hoAYMyJarPziy2uCoDOLQDI3fti0zgAgKSobx3Xv7oPTTwviQJBuo2xcVZWlhGX
wzISF/QP/U+Hv6GvvmckPu6P8tBdOfFMYYqALq4bKy0lTcinZ6QzWRy64Z+H+B8H/nUeBkGceA6fwxNF
hImmjMtLELWbx+YKuGk8Opf3n5r4D8P+pMW5FonS+BFQY4yA1HUqQH7tBygKESDR+8Vd/6NvvvgwIH55
4SqTi3P/7zf9Z8Gl4iWDm/A5ziUohM4S8jMX98TPEqABAUgCKpAHykAd6ABDYAasgC1wBG7AG/iDEBAJ
VgMWSASpgA+yQB7YBApBMdgJ9oBqUAcaQTNoBcdBJzgFzoNL4Bq4AW6D+2AUTIBnYBa8BgsQBGEhMkSB
5CEVSBPSh8wgBmQPuUG+UBAUCcVCCRAPEkJ50GaoGCqDqqF6qBn6HjoJnYeuQIPQXWgMmoZ+h97BCEyC
qbASrAUbwwzYCfaBQ+BVcAK8Bs6FC+AdcCXcAB+FO+Dz8DX4NjwKP4PnEIAQERqiihgiDMQF8UeikHiE
j6xHipAKpAFpRbqRPuQmMorMIG9RGBQFRUcZomxRnqhQFAu1BrUeVYKqRh1GdaB6UTdRY6hZ1Ec0Ga2I
1kfboL3QEegEdBa6EF2BbkK3oy+ib6Mn0K8xGAwNo42xwnhiIjFJmLWYEsw+TBvmHGYQM46Zw2Kx8lh9
rB3WH8vECrCF2CrsUexZ7BB2AvsGR8Sp4Mxw7rgoHA+Xj6vAHcGdwQ3hJnELeCm8Jt4G749n43PwpfhG
fDf+On4Cv0CQJmgT7AghhCTCJkIloZVwkfCA8JJIJKoRrYmBRC5xI7GSeIx4mThGfEuSIemRXEjRJCFp
B+kQ6RzpLuklmUzWIjuSo8gC8g5yM/kC+RH5jQRFwkjCS4ItsUGiRqJDYkjiuSReUlPSSXK1ZK5kheQJ
yeuSM1J4KS0pFymm1HqpGqmTUiNSc9IUaVNpf+lU6RLpI9JXpKdksDJaMm4ybJkCmYMyF2TGKQhFneJC
YVE2UxopFykTVAxVm+pFTaIWU7+jDlBnZWVkl8mGyWbL1sielh2lITQtmhcthVZKO04bpr1borTEaQln
yfYlrUuGlszLLZVzlOPIFcm1yd2WeydPl3eTT5bfJd8p/1ABpaCnEKiQpbBf4aLCzFLqUtulrKVFS48v
vacIK+opBimuVTyo2K84p6Ss5KGUrlSldEFpRpmm7KicpFyufEZ5WoWiYq/CVSlXOavylC5Ld6Kn0Cvp
vfRZVUVVT1Whar3qgOqCmrZaqFq+WpvaQ3WCOkM9Xr1cvUd9VkNFw08jT6NF454mXpOhmai5V7NPc15L
Wytca6tWp9aUtpy2l3audov2Ax2yjoPOGp0GnVu6GF2GbrLuPt0berCehV6iXo3edX1Y31Kfq79Pf9AA
bWBtwDNoMBgxJBk6GWYathiOGdGMfI3yjTqNnhtrGEcZ7zLuM/5oYmGSYtJoct9UxtTbNN+02/R3Mz0z
llmN2S1zsrm7+QbzLvMXy/SXcZbtX3bHgmLhZ7HVosfig6WVJd+y1XLaSsMq1qrWaoRBZQQwShiXrdHW
ztYbrE9Zv7WxtBHYHLf5zdbQNtn2iO3Ucu3lnOWNy8ft1OyYdvV2o/Z0+1j7A/ajDqoOTIcGh8eO6o5s
xybHSSddpySno07PnU2c+c7tzvMuNi7rXM65Iq4erkWuA24ybqFu1W6P3NXcE9xb3Gc9LDzWepzzRHv6
eO7yHPFS8mJ5NXvNelt5r/Pu9SH5BPtU+zz21fPl+3b7wX7efrv9HqzQXMFb0ekP/L38d/s/DNAOWBPw
YyAmMCCwJvBJkGlQXlBfMCU4JvhI8OsQ55DSkPuhOqHC0J4wybDosOaw+XDX8LLw0QjjiHUR1yIVIrmR
XVHYqLCopqi5lW4r96yciLaILoweXqW9KnvVldUKq1NWn46RjGHGnIhFx4bHHol9z/RnNjDn4rziauNm
WS6svaxnbEd2OXuaY8cp40zG28WXxU8l2CXsTphOdEisSJzhunCruS+SPJPqkuaT/ZMPJX9KCU9pS8Wl
xqae5Mnwknm9acpp2WmD6frphemja2zW7Fkzy/fhN2VAGasyugRU0c9Uv1BHuEU4lmmfWZP5Jiss60S2
dDYvuz9HL2d7zmSue+63a1FrWWt78lTzNuWNrXNaV78eWh+3vmeD+oaCDRMbPTYe3kTYlLzpp3yT/LL8
V5vDN3cXKBVsLBjf4rGlpVCikF84stV2a9021DbutoHt5turtn8sYhddLTYprih+X8IqufqN6TeV33za
Eb9joNSydP9OzE7ezuFdDrsOl0mX5ZaN7/bb3VFOLy8qf7UnZs+VimUVdXsJe4V7Ryt9K7uqNKp2Vr2v
Tqy+XeNc01arWLu9dn4fe9/Qfsf9rXVKdcV17w5wD9yp96jvaNBqqDiIOZh58EljWGPft4xvm5sUmoqb
PhziHRo9HHS4t9mqufmI4pHSFrhF2DJ9NProje9cv+tqNWytb6O1FR8Dx4THnn4f+/3wcZ/jPScYJ1p/
0Pyhtp3SXtQBdeR0zHYmdo52RXYNnvQ+2dNt293+o9GPh06pnqo5LXu69AzhTMGZT2dzz86dSz83cz7h
/HhPTM/9CxEXbvUG9g5c9Ll4+ZL7pQt9Tn1nL9tdPnXF5srJq4yrndcsr3X0W/S3/2TxU/uA5UDHdavr
XTesb3QPLh88M+QwdP6m681Lt7xuXbu94vbgcOjwnZHokdE77DtTd1PuvriXeW/h/sYH6AdFD6UeVjxS
fNTws+7PbaOWo6fHXMf6Hwc/vj/OGn/2S8Yv7ycKnpCfVEyqTDZPmU2dmnafvvF05dOJZ+nPFmYKf5X+
tfa5zvMffnP8rX82YnbiBf/Fp99LXsq/PPRq2aueuYC5R69TXy/MF72Rf3P4LeNt37vwd5MLWe+x7ys/
6H7o/ujz8cGn1E+f/gUDmPP8usTo0wAAAAlwSFlzAAALDAAACwwBP0AiyAAAAN5JREFUOE+FUwEOAyEI
8+k+7X7GoFBWmZdtISYgtS3csrXsFtvzf2Mv8/8d4Hkec2SLM+IHzJsjdwVAcxQrtl8OsDgBxPMGgKJG
SQkwMlI2zSCS+cuL0YCceHEDAcBbM6gTQOhDDpnBxHqVOsmgdYvmfCxk7q+JpDZN4yQOJhOAOtmslGna
MYE3AN2HeBEmyvhYZ44mL5rRXkijgoAZAA3nCVBzbzM5vuE8x8xmbGIzqE1LkNwHOO2RZmZOjQYTjoWF
g/YYr+qn2QBQSl2ofL8oC6Wb2hIOM8WD+bXOex/mMAFDkP1eQgAAAABJRU5ErkJggg==
</value>
</data>
<data name="toolStripButton6.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<data name="bitmapToolStripMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIISURBVDhPpZP7S1NxGMbPPxKaXVUkMEq8IpKUCoY/hGgI
ymqkDYYXcCjDZOANURSjCNGFQUTsl4GXVMxKk62YU4fXQpaIlygHQxBRH8/zwvyaIAYe+HLgnPN8nue9
HA3nvDTq63oW/jm13XOwvPTB3DYFY5MH+bXfcN8ygfTSMSSXfESicQDxBqdYHwH29g9w2tnZ3UcguIvN
rR3417exuBJE5N1n/wfwLgXEOc38Bc6xNRHb+/y4nm49G0Bnit2zf9H6bkliE/jKuYxrd6oVgDWfjB+K
TWeKMyrGEVfowITvD9re/9ABVQrAhh0HHK+ZselMMaN/mvwtDb+aVqkA7HYIwIj3ysfluPTorJnP6Ezx
oHsD1s5ZXEktUwCOioB5f1CEPR9+wTG6iuiserTo8dkwng7HT/R+XUPF8xlcTjErAOdMcW6NW8STiwG8
7vej8oUPN/PsEv3t8Ao0TZP3T1u8uJRkUgAuSYHtO97oLxmXd5t9Ho8aPTK+GzntqNfrLm2fFoihwYOI
xGIF4KjoGBLzY1OrF9k6OOFxnwDC4wxIMX1G0pMhgVyMNyoA13PAtS7OrJk1PrC69LUdQWxuF6IybHrX
LRI7JrtZdoDAo1XmbjMyD+tjSXxGcXRmnYg5ttD9QuxDhN0uUgDOmbvNTpPOJaGAo2K36cyaGZvOFIfd
KlSA8/zRh9ABIDUG+1JpAAAAAElFTkSuQmCC
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsMAAALDAE/QCLIAAACR0lE
QVQ4T6WTXUiTARSGdxkRRBdB0IUREV1EKRFEYYuQAklL8sKQfphUoJSUWJbLxAVZWqyBio5WajorM1PL
xExUlLQfbU5n0lygRerMP/qxOZ92vpjfCKLAi3P5Puc957xHA2gWUoo49147gWUqbSPrdjOXChrQm56S
lFVFvKEcXaqV2OQiohMt7Esw+6Ro5gGeWfhbff0BY9NzDI3N0f/JS6fLy7rd6f8HcNsdOI6EYD8cjK3V
roiLawdYu+vivwHSWcQM1THz/DQv96+ixeHlVvU71oQFAGTmP+37bdsPBf8WZ4fRFhVEXdcsBQ9srN6Z
pjqQhQUCAmcW29JZxA8fvaWyY5YcawdBOy6oANm2H9A3MMypDLNS7b1flJnFtnQWcVmrB2NhMytD9SpA
TiUA5+CYInQOjWPrH2V7RBzmijdo9x5VKr+yj6ImD9nmelZsS1UBcme/+OPIJIOfv/Gi201ZXQ/hMfFU
Peuktqnbd3QN+U/cXM6tYfmW8ypAQiKdh91TTE7PYHNOYKl6z916F3m+eSNjT1B4vxHjzWoFYrhRwbLN
51SAJOynx8vo5Ay9rimKHzu5XtJDpuU1WZY2Eg1WgrXRJOpNxKdcUyBLN6WoAInnyPh3XvW6KW/4MC9O
z2ki+Wo1CWklROkMCiT8wEm0kXEsCTmrAiTbDtcEpTVdZOS1oDfWk5xZ6RPeQZeUR8zxK0Qe1BO65xjr
t0YotXhjAEAeQ7It8ZSESUjkznIq2bYsTGYW29JZxIs2nFEdLOSdfwFwpvLxRKIY2AAAAABJRU5ErkJg
gg==
</value>
</data>
<data name="toolStripButton7.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<data name="propertiesToolStripMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIISURBVDhPpZP7S1NxGMbPPxKaXVUkMEq8IpKUCoY/hGgI
ymqkDYYXcCjDZOANURSjCNGFQUTsl4GXVMxKk62YU4fXQpaIlygHQxBRH8/zwvyaIAYe+HLgnPN8nue9
HA3nvDTq63oW/jm13XOwvPTB3DYFY5MH+bXfcN8ygfTSMSSXfESicQDxBqdYHwH29g9w2tnZ3UcguIvN
rR3417exuBJE5N1n/wfwLgXEOc38Bc6xNRHb+/y4nm49G0Bnit2zf9H6bkliE/jKuYxrd6oVgDWfjB+K
TWeKMyrGEVfowITvD9re/9ABVQrAhh0HHK+ZselMMaN/mvwtDb+aVqkA7HYIwIj3ysfluPTorJnP6Ezx
oHsD1s5ZXEktUwCOioB5f1CEPR9+wTG6iuiserTo8dkwng7HT/R+XUPF8xlcTjErAOdMcW6NW8STiwG8
7vej8oUPN/PsEv3t8Ao0TZP3T1u8uJRkUgAuSYHtO97oLxmXd5t9Ho8aPTK+GzntqNfrLm2fFoihwYOI
xGIF4KjoGBLzY1OrF9k6OOFxnwDC4wxIMX1G0pMhgVyMNyoA13PAtS7OrJk1PrC69LUdQWxuF6IybHrX
LRI7JrtZdoDAo1XmbjMyD+tjSXxGcXRmnYg5ttD9QuxDhN0uUgDOmbvNTpPOJaGAo2K36cyaGZvOFIfd
KlSA8/zRh9ABIDUG+1JpAAAAAElFTkSuQmCC
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsMAAALDAE/QCLIAAACCklE
QVQ4T6WT30tTUQDHz39QD0UQSpAPIkgPyR60AiUiyoGBL4qZjOyt0tZP9tBDk1AsXIhj93ILdd27DPPH
3VwrUrdK2ioJHAgVOdoPaQsarBg43L6ec+akuRsEPnw5D4fP5/vlXg4BQHaSf8LjjwdqaTA2cg+y1Ith
qxmi5Tas/SYWk+QRoPgmtNsLcPq7GYX8+XoTqaUL+KR2cAmDxeBgqeBv2NhWzwUFOBk4g5/zRxB3V8Es
38AtbzuIw/0RLMrMeyhTs2CzGdRtaOTn1dajvLkAR9VKLNvLcHmoExddzSDKzAfkcjkkEtEiOLxoQpdB
z+Er7ScQUg/DL5TD2bsfl5oPoKWnCZ3jehDZGeAC1hwL3uXNs55Jep7Gis/AJWxyTeVe1FXvhq5qH6oP
7nlw/NoxGByNIKPT77CezSEeD6Pn+jlEIiGk6X8N+F9zyfLYIZzUVUCwT0J36jy9yX83KjDq7zSADE8t
cGB7kpksFt76YDzbgMWVFLzBX0WCTcku8mjiTSm8lkUslcHnH2kK/+aw6k+UCJiESE+9XCApz2Gzu5Gk
sEVy4r6got82vRV5blVbIDyZ21rAZrPmL7w5P5s1KxQWXRFtgU15lV8ge2AbdXPY8pAuEIsXWJ6FtAVW
+SWSmXWssuZ4vnl+s5nNFl1hMLjP8U1bMGR/wS/+N9tf7o6eMpNtADko6xybtEXLAAAAAElFTkSuQmCC
</value>
</data>
<data name="toolStripButton2.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<data name="resizeToolStripMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIISURBVDhPpZP7S1NxGMbPPxKaXVUkMEq8IpKUCoY/hGgI
ymqkDYYXcCjDZOANURSjCNGFQUTsl4GXVMxKk62YU4fXQpaIlygHQxBRH8/zwvyaIAYe+HLgnPN8nue9
HA3nvDTq63oW/jm13XOwvPTB3DYFY5MH+bXfcN8ygfTSMSSXfESicQDxBqdYHwH29g9w2tnZ3UcguIvN
rR3417exuBJE5N1n/wfwLgXEOc38Bc6xNRHb+/y4nm49G0Bnit2zf9H6bkliE/jKuYxrd6oVgDWfjB+K
TWeKMyrGEVfowITvD9re/9ABVQrAhh0HHK+ZselMMaN/mvwtDb+aVqkA7HYIwIj3ysfluPTorJnP6Ezx
oHsD1s5ZXEktUwCOioB5f1CEPR9+wTG6iuiserTo8dkwng7HT/R+XUPF8xlcTjErAOdMcW6NW8STiwG8
7vej8oUPN/PsEv3t8Ao0TZP3T1u8uJRkUgAuSYHtO97oLxmXd5t9Ho8aPTK+GzntqNfrLm2fFoihwYOI
xGIF4KjoGBLzY1OrF9k6OOFxnwDC4wxIMX1G0pMhgVyMNyoA13PAtS7OrJk1PrC69LUdQWxuF6IybHrX
LRI7JrtZdoDAo1XmbjMyD+tjSXxGcXRmnYg5ttD9QuxDhN0uUgDOmbvNTpPOJaGAo2K36cyaGZvOFIfd
KlSA8/zRh9ABIDUG+1JpAAAAAElFTkSuQmCC
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsMAAALDAE/QCLIAAADLklE
QVQ4T3WS60/ScRjFf39CL3rR1ovurduWbbU2t7ZqdlkXt7Lrym6O1VoXcdYqQ54wKlGxABE1CzWF0nIm
lZcoTbyUNJFIzDJSJK+E0qy8wInvz+i2erazfd+cz3m+zw4nTS+FLOeZNim3Vp6UU5sqvf5MSZqnyjhV
pfKUvEx54kqp8rCkWHVQVKSJPHtbs+tUgSYiOicVAMfEqfR1dVFCGSqrX94cHQeN/aXhbyD3Zz91uf3U
9tFHTQ4fzVtH9BOQVdKcW2F6/eKkWA1zcxu6vUAQMmCzk/3AErLtDyFrrY035z16T3PXin8B5LfqZE6P
HyZzKw5FX+Yh4XtjwZKZGV3lNPIkhhojplON3Uc3S9/Q7LDfADKtKYklMsgTkwV7joixKHQr2Nq2fSET
5uQwqtsyjcot45R510ozV8f/2iAhsyolCNh9WIyFoRGYHbIGfYN+SVd7Nznzk8ll0FLzKxdZO3xkaOjG
GYUxcIIfRxSlPU4N/rn1fS8JJVlU1WCT3npggVLfAIWuHjJtNRIyjIhTPEKM7D6OSop4RcXpwZ2Wl11l
gHanmze/c36ioc8jyRlFjSzljxl2daI9Pwuv1Cnw9PRjZeQ1cCcSS68Fza6+IXL2fCG7w0ssmc3o2IR8
gbc1PQU9NUp48sNRcUyAkPBL4AIlUbLk3gEvS6YXrW6p0dx9UZ5n4gHjzBmYAAPPYwRwazeiMXYWqjcs
wLSVInAHRYWq0TEf9Q+NUEsg+fr9t2nF1U71leyqn+u3vPuIl2298HZ2wnxkO5q2LYOtxoKpK86D23tW
r+7zfCVzywAVGT/QVV2LvNDo0Eo0j3mAtbUTiRnFOJ94Axp9LfIKK5Ctq0RDmw9TQuPAsW7bHYNUYLCQ
JL2GRAqjtMDYwV+8wzUAWeY9eIfHYW0fxNodxyGWZeOSPBvzl27C5OXnwG0T5irmr79wgdWTNYyVJFL8
kAeo8wyBA/rx2jGErBIH0nXPsWmPEHcM9YgURGPS0jMTZfiXGKDfM4Y0fVOgA/UgRQWECTpExaqxUyDF
wuUbJjb4H0AQfxtrDqiweLMUc8LEmLEqnv9zUMzM9B1/cM83lepxTAAAAABJRU5ErkJggg==
</value>
</data>
<data name="spawnpointsToolStripMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsMAAALDAE/QCLIAAABrUlE
QVQ4T6WTWyhDcRzHl8g1D/IkxZ4mjcgDB0U5ubbVbJLkkubJSt7w6M1eENHUENrktmzJ3HKEiSh7US5l
QiK5JUXpy/9f/9O2zhY59at//c/n87uc35EBkP0nAsL6Bi33E9CpeBINJElHthIkvBNKChh8YVPi1Cpn
Eo7ANSnJwQUMfnLEobVejZbaCnis8VRiyFSgXJ4QWOAPO5fXYJ2xo1HD48QcRSV5CfHSAn94fV0gL9Jn
eVWAtjgXRwPhrJ18Ngc6g2AwkzhXBJTmZ+GgN8xHwgRgPXtnZvDH5xde3j6x6BTAcxk47JFRCUnOBAWF
2WkQhC2xbH/49uEd59evGLfYoJAnEkGjKCCHyjqDDzw72YOpUSMmTF0w93VS2H32iLmlXSSlcuIgxT1Q
VTf7CAj87IjFgy2Gfk4Cu9x3sNhd0oIyXZMoID2TzPez0biyREKvK6bw6t4NxuYDCEo0ZFsBNjBStmci
Asb2Kmj4HAovbF5ieHpHugJeXUfhja19rGzsYai7DcemULj7Q1DEpWNkzgXTDzxo3ZYWFKlq6cVvw2eR
vP+uv56/AehVvkSccelEAAAAAElFTkSuQmCC
</value>
</data>
<metadata name="tt.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">

View File

@@ -0,0 +1,469 @@
#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.Text;
using OpenRA;
using OpenRA.FileFormats;
using OpenRA.Traits;
using System.Drawing;
namespace OpenRA.Editor
{
public class LegacyMapImporter
{
// Mapping from ra overlay index to type string
static string[] raOverlayNames =
{
"sbag", "cycl", "brik", "fenc", "wood",
"gold01", "gold02", "gold03", "gold04",
"gem01", "gem02", "gem03", "gem04",
"v12", "v13", "v14", "v15", "v16", "v17", "v18",
"fpls", "wcrate", "scrate", "barb", "sbag",
};
static Dictionary<string, Pair<byte, byte>> overlayResourceMapping = new Dictionary<string, Pair<byte, byte>>()
{
// RA Gems, Gold
{ "gold01", new Pair<byte,byte>(1,0) },
{ "gold02", new Pair<byte,byte>(1,1) },
{ "gold03", new Pair<byte,byte>(1,2) },
{ "gold04", new Pair<byte,byte>(1,3) },
{ "gem01", new Pair<byte,byte>(2,0) },
{ "gem02", new Pair<byte,byte>(2,1) },
{ "gem03", new Pair<byte,byte>(2,2) },
{ "gem04", new Pair<byte,byte>(2,3) },
// cnc tiberium
{ "ti1", new Pair<byte,byte>(1,0) },
{ "ti2", new Pair<byte,byte>(1,1) },
{ "ti3", new Pair<byte,byte>(1,2) },
{ "ti4", new Pair<byte,byte>(1,3) },
{ "ti5", new Pair<byte,byte>(1,4) },
{ "ti6", new Pair<byte,byte>(1,5) },
{ "ti7", new Pair<byte,byte>(1,6) },
{ "ti8", new Pair<byte,byte>(1,7) },
{ "ti9", new Pair<byte,byte>(1,8) },
{ "ti10", new Pair<byte,byte>(1,9) },
{ "ti11", new Pair<byte,byte>(1,10) },
{ "ti12", new Pair<byte,byte>(1,11) },
};
static Dictionary<string, string> overlayActorMapping = new Dictionary<string, string>() {
// Fences
{"sbag","sbag"},
{"cycl","cycl"},
{"brik","brik"},
{"fenc","fenc"},
{"wood","wood"},
// Fields
{"v12","v12"},
{"v13","v13"},
{"v14","v14"},
{"v15","v15"},
{"v16","v16"},
{"v17","v17"},
{"v18","v18"},
// Crates
// {"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);
}
public static Map Import(string filename)
{
var converter = new LegacyMapImporter(filename);
return converter.Map;
}
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));
var basic = file.GetSection("Basic");
var map = file.GetSection("Map");
var legacyMapFormat = (IniMapFormat)int.Parse(basic.GetValue("NewINIFormat", "0"));
var XOffset = int.Parse(map.GetValue("X", "0"));
var YOffset = int.Parse(map.GetValue("Y", "0"));
var Width = int.Parse(map.GetValue("Width", "0"));
var Height = int.Parse(map.GetValue("Height", "0"));
MapSize = (legacyMapFormat == IniMapFormat.RedAlert) ? 128 : 64;
Map.Title = basic.GetValue("Name", "(null)");
Map.Author = "Westwood Studios";
Map.Tileset = Truncate(map.GetValue("Theater", "TEMPERAT"), 8);
Map.MapSize.X = MapSize;
Map.MapSize.Y = MapSize;
Map.TopLeft = new int2(XOffset, YOffset);
Map.BottomRight = new int2(XOffset + Width, YOffset + Height);
Map.Selectable = true;
if (legacyMapFormat == IniMapFormat.RedAlert)
{
UnpackRATileData(ReadPackedSection(file.GetSection("MapPack")));
UnpackRAOverlayData(ReadPackedSection(file.GetSection("OverlayPack")));
ReadRATrees(file);
}
else // CNC
{
UnpackCncTileData(FileSystem.Open(iniFile.Substring(0, iniFile.Length - 4) + ".bin"));
ReadCncOverlay(file);
ReadCncTrees(file);
}
LoadActors(file, "STRUCTURES");
LoadActors(file, "UNITS");
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)))
.ToArray();
Map.PlayerCount = wp.Count();
foreach (var kv in wp)
Map.Waypoints.Add("spawn" + kv.First, kv.Second);
}
static int2 LocationFromMapOffset(int offset, int mapSize)
{
return new int2(offset % mapSize, offset / mapSize);
}
static MemoryStream ReadPackedSection(IniSection mapPackSection)
{
StringBuilder sb = new StringBuilder();
for (int i = 1; ; i++)
{
string line = mapPackSection.GetValue(i.ToString(), null);
if (line == null)
break;
sb.Append(line.Trim());
}
byte[] data = Convert.FromBase64String(sb.ToString());
List<byte[]> chunks = new List<byte[]>();
BinaryReader reader = new BinaryReader(new MemoryStream(data));
try
{
while (true)
{
uint length = reader.ReadUInt32() & 0xdfffffff;
byte[] dest = new byte[8192];
byte[] src = reader.ReadBytes((int)length);
/*int actualLength =*/
Format80.DecodeInto(src, dest);
chunks.Add(dest);
}
}
catch (EndOfStreamException) { }
MemoryStream ms = new MemoryStream();
foreach (byte[] chunk in chunks)
ms.Write(chunk, 0, chunk.Length);
ms.Position = 0;
return ms;
}
static byte ReadByte(Stream s)
{
int ret = s.ReadByte();
if (ret == -1)
throw new NotImplementedException();
return (byte)ret;
}
static ushort ReadWord(Stream s)
{
ushort ret = ReadByte(s);
ret |= (ushort)(ReadByte(s) << 8);
return ret;
}
void UnpackRATileData(MemoryStream ms)
{
Map.MapTiles = new TileReference<ushort, byte>[MapSize, MapSize];
for (int i = 0; i < MapSize; i++)
for (int j = 0; j < MapSize; j++)
Map.MapTiles[i, j] = new TileReference<ushort, byte>();
for (int j = 0; j < MapSize; j++)
for (int i = 0; i < MapSize; i++)
Map.MapTiles[i, j].type = ReadWord(ms);
for (int j = 0; j < MapSize; j++)
for (int i = 0; i < MapSize; i++)
{
Map.MapTiles[i, j].index = ReadByte(ms);
if (Map.MapTiles[i, j].type == 0xff || Map.MapTiles[i, j].type == 0xffff)
Map.MapTiles[i, j].index = byte.MaxValue;
}
}
void UnpackRAOverlayData(MemoryStream ms)
{
Map.MapResources = new TileReference<byte, byte>[MapSize, MapSize];
for (int j = 0; j < MapSize; j++)
for (int i = 0; i < MapSize; i++)
{
byte o = ReadByte(ms);
var res = Pair.New((byte)0, (byte)0);
if (o != 255 && overlayResourceMapping.ContainsKey(raOverlayNames[o]))
res = overlayResourceMapping[raOverlayNames[o]];
Map.MapResources[i, j] = new TileReference<byte, byte>(res.First, res.Second);
if (o != 255 && overlayActorMapping.ContainsKey(raOverlayNames[o]))
Map.Actors.Add("Actor" + ActorCount++,
new ActorReference(overlayActorMapping[raOverlayNames[o]])
{
new LocationInit( new int2(i, j) ),
new OwnerInit( "Neutral" )
});
}
}
void ReadRATrees(IniFile file)
{
IniSection terrain = file.GetSection("TERRAIN", true);
if (terrain == null)
return;
foreach (KeyValuePair<string, string> kv in terrain)
{
var loc = int.Parse(kv.Key);
Map.Actors.Add("Actor" + ActorCount++,
new ActorReference(kv.Value.ToLowerInvariant())
{
new LocationInit(new int2(loc % MapSize, loc / MapSize)),
new OwnerInit("Neutral")
});
}
}
void UnpackCncTileData(Stream ms)
{
Map.MapTiles = new TileReference<ushort, byte>[MapSize, MapSize];
for (int i = 0; i < MapSize; i++)
for (int j = 0; j < MapSize; j++)
Map.MapTiles[i, j] = new TileReference<ushort, byte>();
for (int j = 0; j < MapSize; j++)
for (int i = 0; i < MapSize; i++)
{
Map.MapTiles[i, j].type = ReadByte(ms);
Map.MapTiles[i, j].index = ReadByte(ms);
if (Map.MapTiles[i, j].type == 0xff)
Map.MapTiles[i, j].index = byte.MaxValue;
}
}
void ReadCncOverlay(IniFile file)
{
IniSection overlay = file.GetSection("OVERLAY", true);
if (overlay == null)
return;
Map.MapResources = new TileReference<byte, byte>[MapSize, MapSize];
foreach (KeyValuePair<string, string> kv in overlay)
{
var loc = int.Parse(kv.Key);
int2 cell = new int2(loc % MapSize, loc / MapSize);
var res = Pair.New((byte)0, (byte)0);
if (overlayResourceMapping.ContainsKey(kv.Value.ToLower()))
res = overlayResourceMapping[kv.Value.ToLower()];
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++,
new ActorReference(overlayActorMapping[kv.Value.ToLower()])
{
new LocationInit(cell),
new OwnerInit("Neutral")
});
}
}
void ReadCncTrees(IniFile file)
{
IniSection terrain = file.GetSection("TERRAIN", true);
if (terrain == null)
return;
foreach (KeyValuePair<string, string> kv in terrain)
{
var loc = int.Parse(kv.Key);
Map.Actors.Add("Actor" + ActorCount++,
new ActorReference(kv.Value.Split(',')[0].ToLowerInvariant())
{
new LocationInit(new int2(loc % MapSize, loc / MapSize)),
new OwnerInit("Neutral")
});
}
}
void LoadActors(IniFile file, string section)
{
foreach (var s in file.GetSection(section, true))
{
//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";
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);
}
}
void LoadSmudges(IniFile file, string section)
{
foreach (var s in file.GetSection(section, true))
{
//loc=type,loc,depth
var parts = s.Value.Split(',');
var loc = int.Parse(parts[1]);
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)
{
return s.Length <= maxLength ? s : s.Substring(0, maxLength);
}
}
}

22
OpenRA.Editor/NewMapDialog.Designer.cs generated Normal file → Executable file
View 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
View 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);
}
}
}

View File

@@ -23,6 +23,7 @@
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -31,6 +32,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<UseVSHostingProcess>false</UseVSHostingProcess>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
@@ -44,7 +46,6 @@
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
@@ -57,6 +58,7 @@
<Compile Include="Form1.Designer.cs">
<DependentUpon>Form1.cs</DependentUpon>
</Compile>
<Compile Include="LegacyMapImporter.cs" />
<Compile Include="NewMapDialog.cs">
<SubType>Form</SubType>
</Compile>
@@ -121,7 +123,7 @@
<Name>OpenRA.Game</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@@ -22,6 +22,7 @@ namespace OpenRA.Editor
Application.CurrentCulture = CultureInfo.InvariantCulture;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1(args));
}
}

190
OpenRA.Editor/Surface.cs Normal file → Executable file
View File

@@ -8,13 +8,13 @@
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Windows.Forms;
using OpenRA.FileFormats;
using OpenRA.Thirdparty;
namespace OpenRA.Editor
{
@@ -25,12 +25,15 @@ namespace OpenRA.Editor
public Palette Palette { get; private set; }
int2 Offset;
float Zoom = 1.0f;
BrushTemplate Brush;
ActorTemplate Actor;
ResourceTemplate Resource;
WaypointTemplate Waypoint;
public bool IsPanning;
public event Action AfterChange = () => { };
Dictionary<string, ActorTemplate> ActorTemplates = new Dictionary<string, ActorTemplate>();
Dictionary<int, ResourceTemplate> ResourceTemplates = new Dictionary<int, ResourceTemplate>();
@@ -79,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);
@@ -95,7 +125,7 @@ namespace OpenRA.Editor
Erase();
if (e.Button == MouseButtons.Left)
Draw();
Draw();
Invalidate();
}
@@ -129,6 +159,8 @@ namespace OpenRA.Editor
/* todo: optimize */
foreach (var ch in Chunks.Values) ch.Dispose();
Chunks.Clear();
AfterChange();
}
int2 FindEdge(int2 p, int2 d, TileReference<ushort, byte> replace)
@@ -178,6 +210,8 @@ namespace OpenRA.Editor
}
}
}
AfterChange();
}
int wpid;
@@ -197,22 +231,33 @@ namespace OpenRA.Editor
if (k.Key != null) Map.Waypoints.Remove(k.Key);
Map.Waypoints.Add(NextWpid(), GetBrushLocation());
AfterChange();
}
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();
@@ -220,8 +265,10 @@ 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();
}
void Draw()
@@ -230,6 +277,8 @@ namespace OpenRA.Editor
if (Actor != null) DrawWithActor();
if (Resource != null) DrawWithResource();
if (Waypoint != null) DrawWithWaypoint();
AfterChange();
}
int id;
@@ -244,15 +293,21 @@ namespace OpenRA.Editor
void DrawWithActor()
{
if (Map.Actors.Any(a => a.Value.Location == GetBrushLocation()))
if (Map.Actors.Any(a => a.Value.Location() == GetBrushLocation()))
return;
var owner = "Neutral";
var id = NextActorName();
Map.Actors[id] = new ActorReference(id,Actor.Info.Name.ToLowerInvariant(), GetBrushLocation(), owner);
Map.Actors[id] = new ActorReference(Actor.Info.Name.ToLowerInvariant())
{
new LocationInit( GetBrushLocation() ),
new OwnerInit( owner)
};
AfterChange();
}
Random r = new Random();
System.Random r = new System.Random();
void DrawWithResource()
{
Map.MapResources[GetBrushLocation().X, GetBrushLocation().Y]
@@ -269,6 +324,8 @@ namespace OpenRA.Editor
Chunks[ch].Dispose();
Chunks.Remove(ch);
}
AfterChange();
}
protected override void OnMouseDown(MouseEventArgs e)
@@ -288,10 +345,11 @@ namespace OpenRA.Editor
Bitmap RenderChunk(int u, int v)
{
var bitmap = new Bitmap(ChunkSize * 24, ChunkSize * 24);
bitmap.SetPixel(0, 0, Color.Green);
var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
unsafe
@@ -308,7 +366,7 @@ namespace OpenRA.Editor
var rawImage = tile.TileBitmapBytes[index];
for (var x = 0; x < 24; x++)
for (var y = 0; y < 24; y++)
p[ (j * 24 + y) * stride + i * 24 + x ] = Palette.GetColor(rawImage[x + 24 * y]).ToArgb();
p[(j * 24 + y) * stride + i * 24 + x] = Palette.GetColor(rawImage[x + 24 * y]).ToArgb();
if (Map.MapResources[u * ChunkSize + i, v * ChunkSize + j].type != 0)
{
@@ -338,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)
@@ -372,44 +458,64 @@ 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]);
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)
{
var x = Map.Actors.FirstOrDefault(a => a.Value.Location == GetBrushLocation());
var x = Map.Actors.FirstOrDefault(a => a.Value.Location() == GetBrushLocation());
if (x.Key != null)
DrawActorBorder(e.Graphics, x.Value.Location, ActorTemplates[x.Value.Type]);
DrawActorBorder(e.Graphics, x.Value.Location(), ActorTemplates[x.Value.Type]);
}
}
}
static class ActorReferenceExts
{
public static int2 Location(this ActorReference ar)
{
return ar.InitDict.Get<LocationInit>().value;
}
}
}

View File

@@ -36,5 +36,10 @@ namespace OpenRA.FileFormats
}
public IEnumerable<uint> AllFileHashes() { return hashes; }
public bool Exists(string filename)
{
return hashes.Contains(PackageEntry.HashFilename(filename));
}
}
}

View File

@@ -22,19 +22,40 @@ 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 ) );
};
public static void Load(object self, MiniYaml my) { Load(self, my.Nodes); }
public static void Load(object self, Dictionary<string, MiniYaml> my)
public static void Load( object self, MiniYaml my )
{
foreach (var x in my)
if (!x.Key.StartsWith("-"))
LoadField(self, x.Key, x.Value.Value);
var loadDict = typeLoadInfo[ self.GetType() ];
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()
@@ -43,26 +64,19 @@ 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 )
{
var field = self.GetType().GetField( key.Trim() );
if( field == null )
UnknownFieldAction(key.Trim(), self.GetType());
UnknownFieldAction( key.Trim(), self.GetType() );
else if( field.HasAttribute<FieldFromYamlKeyAttribute>() )
return;
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();
@@ -73,7 +87,7 @@ namespace OpenRA.FileFormats
return res;
return InvalidValueAction(x,fieldType, field);
}
else if( fieldType == typeof( ushort ) )
{
ushort res;
@@ -92,7 +106,7 @@ namespace OpenRA.FileFormats
else if (fieldType == typeof(string))
return x;
else if (fieldType == typeof(Color))
{
var parts = x.Split(',');
@@ -102,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);
@@ -130,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;
}
@@ -144,18 +170,75 @@ 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)
{
return new MiniYaml(null, o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance)
.ToDictionary(
f => f.Name,
f => new MiniYaml(FormatValue(o, f))));
var nodes = new List<MiniYamlNode>();
string root = null;
foreach( var f in o.GetType().GetFields( BindingFlags.Public | BindingFlags.Instance ) )
{
if( f.HasAttribute<FieldFromYamlKeyAttribute>() )
root = FormatValue( o, f );
else
nodes.Add( new MiniYamlNode( f.Name, FormatValue( o, f ) ) );
}
return new MiniYaml( root, nodes );
}
public static MiniYaml SaveDifferences(object o, object from)
{
if (o.GetType() != from.GetType())
@@ -163,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)
@@ -181,10 +264,12 @@ 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();
}
}
public class FieldFromYamlKeyAttribute : Attribute { }
}

View File

@@ -76,6 +76,32 @@ namespace OpenRA.FileFormats
return (short)current;
}
public static byte[] LoadSound(byte[] raw, ref int index)
{
var br = new BinaryReader(new MemoryStream(raw));
var dataSize = raw.Length;
var outputSize = raw.Length * 4;
var output = new byte[outputSize];
var offset = 0;
var currentSample = 0;
while (dataSize-- > 0)
{
var b = br.ReadByte();
var t = DecodeSample(b, ref index, ref currentSample);
output[offset++] = (byte)t;
output[offset++] = (byte)(t >> 8);
t = DecodeSample((byte)(b >> 4), ref index, ref currentSample);
output[offset++] = (byte)t;
output[offset++] = (byte)(t >> 8);
}
return output;
}
public static byte[] LoadSound(Stream s)
{
var br = new BinaryReader(s);

View File

@@ -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;
}

View File

@@ -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));
}
}
}

View File

@@ -32,6 +32,7 @@ namespace OpenRA.FileFormats.Graphics
IVertexBuffer<Vertex> CreateVertexBuffer( int length );
IIndexBuffer CreateIndexBuffer( int length );
ITexture CreateTexture( Bitmap bitmap );
ITexture CreateTexture();
IShader CreateShader( Stream stream );
Size WindowSize { get; }
@@ -70,14 +71,9 @@ namespace OpenRA.FileFormats.Graphics
public interface ITexture
{
void SetData( Bitmap bitmap );
}
public interface IFont
{
void DrawText( string text, int2 pos, Color c );
int2 Measure( string text );
void SetData(Bitmap bitmap);
void SetData(uint[,] colors);
void SetData(byte[] colors, int width, int height);
}
public enum PrimitiveType

View File

@@ -0,0 +1,294 @@
#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.IO;
namespace OpenRA.FileFormats
{
public class VqaReader
{
public readonly ushort Frames;
public readonly byte Framerate;
public readonly ushort Width;
public readonly ushort Height;
Stream stream;
int currentFrame;
ushort numColors;
ushort blockWidth;
ushort blockHeight;
byte cbParts;
int2 blocks;
UInt32[] offsets;
uint[] palette;
// Stores a list of subpixels, referenced by the VPTZ chunk
byte[] cbf;
byte[] cbp;
int cbChunk = 0;
int cbOffset = 0;
// Top half contains block info, bottom half contains references to cbf array
byte[] origData;
// Final frame output
uint[,] frameData;
byte[] audioData; // audio for this frame: 22050Hz 16bit mono pcm, uncompressed.
public byte[] AudioData { get { return audioData; } }
public int CurrentFrame { get { return currentFrame; } }
public VqaReader( Stream stream )
{
this.stream = stream;
BinaryReader reader = new BinaryReader( stream );
// Decode FORM chunk
if (new String(reader.ReadChars(4)) != "FORM")
throw new InvalidDataException("Invalid vqa (invalid FORM section)");
/*var length = */ reader.ReadUInt32();
if (new String(reader.ReadChars(8)) != "WVQAVQHD")
throw new InvalidDataException("Invalid vqa (not WVQAVQHD)");
/* var length = */reader.ReadUInt32();
/*var version = */reader.ReadUInt16();
/*var flags = */reader.ReadUInt16();
Frames = reader.ReadUInt16();
Width = reader.ReadUInt16();
Height = reader.ReadUInt16();
blockWidth = reader.ReadByte();
blockHeight = reader.ReadByte();
Framerate = reader.ReadByte();
cbParts = reader.ReadByte();
blocks = new int2(Width / blockWidth, Height / blockHeight);
numColors = reader.ReadUInt16();
/*var maxBlocks = */reader.ReadUInt16();
/*var unknown1 = */reader.ReadUInt16();
/*var unknown2 = */reader.ReadUInt32();
// Audio
/*var freq = */reader.ReadUInt16();
/*var channels = */reader.ReadByte();
/*var bits = */reader.ReadByte();
/*var unknown3 = */reader.ReadChars(14);
var frameSize = NextPowerOf2(Math.Max(Width,Height));
cbf = new byte[Width*Height];
cbp = new byte[Width*Height];
palette = new uint[numColors];
origData = new byte[2*blocks.X*blocks.Y];
frameData = new uint[frameSize,frameSize];
var type = new String(reader.ReadChars(4));
if (type != "FINF")
{
reader.ReadBytes(27);
type = new String(reader.ReadChars(4));
}
/*var length = */reader.ReadUInt16();
/*var unknown4 = */reader.ReadUInt16();
// Frame offsets
offsets = new UInt32[Frames];
for (int i = 0; i < Frames; i++)
{
offsets[i] = reader.ReadUInt32();
if (offsets[i] > 0x40000000) offsets[i] -= 0x40000000;
offsets[i] <<= 1;
}
CollectAudioData();
Reset();
}
public void Reset()
{
currentFrame = cbOffset = cbChunk = 0;
LoadFrame();
}
void CollectAudioData()
{
var ms = new MemoryStream();
var adpcmIndex = 0;
bool compressed = false;
for (var i = 0; i < Frames; i++)
{
stream.Seek(offsets[i], SeekOrigin.Begin);
BinaryReader reader = new BinaryReader(stream);
var end = (i < Frames - 1) ? offsets[i + 1] : stream.Length;
while (reader.BaseStream.Position < end)
{
var type = new String(reader.ReadChars(4));
var length = int2.Swap(reader.ReadUInt32());
switch (type)
{
case "SND0":
case "SND2":
var rawAudio = reader.ReadBytes((int)length);
ms.Write(rawAudio);
compressed = (type == "SND2");
break;
default:
reader.ReadBytes((int)length);
break;
}
if (reader.PeekChar() == 0) reader.ReadByte();
}
}
audioData = (compressed) ? AudLoader.LoadSound(ms.ToArray(), ref adpcmIndex) : ms.ToArray();
}
public void AdvanceFrame()
{
currentFrame++;
LoadFrame();
}
void LoadFrame()
{
if (currentFrame >= Frames)
return;
// Seek to the start of the frame
stream.Seek(offsets[currentFrame], SeekOrigin.Begin);
BinaryReader reader = new BinaryReader(stream);
var end = (currentFrame < Frames - 1) ? offsets[currentFrame+1] : stream.Length;
while(reader.BaseStream.Position < end)
{
var type = new String(reader.ReadChars(4));
var length = int2.Swap(reader.ReadUInt32());
switch(type)
{
case "VQFR":
DecodeVQFR(reader);
break;
default:
// Don't parse sound here.
reader.ReadBytes((int)length);
break;
}
// Chunks are aligned on even bytes; advance by a byte if the next one is null
if (reader.PeekChar() == 0) reader.ReadByte();
}
}
// VQA Frame
public void DecodeVQFR(BinaryReader reader)
{
while(true)
{
// Chunks are aligned on even bytes; may be padded with a single null
if (reader.PeekChar() == 0) reader.ReadByte();
var type = new String(reader.ReadChars(4));
int subchunkLength = (int)int2.Swap(reader.ReadUInt32());
switch(type)
{
// Full frame-modifier
case "CBFZ":
Format80.DecodeInto( reader.ReadBytes(subchunkLength), cbf );
break;
case "CBF0":
cbf = reader.ReadBytes(subchunkLength);
break;
// frame-modifier chunk
case "CBP0":
case "CBPZ":
// Partial buffer is full; dump and recreate
if (cbChunk == cbParts)
{
if (type == "CBP0")
cbf = (byte[])cbp.Clone();
else
Format80.DecodeInto( cbp, cbf );
cbOffset = cbChunk = 0;
}
var bytes = reader.ReadBytes(subchunkLength);
bytes.CopyTo(cbp,cbOffset);
cbOffset += subchunkLength;
cbChunk++;
break;
// Palette
case "CPL0":
for (int i = 0; i < numColors; i++)
{
byte r = (byte)(reader.ReadByte() << 2);
byte g = (byte)(reader.ReadByte() << 2);
byte b = (byte)(reader.ReadByte() << 2);
palette[i] = (uint)((255 << 24) | (r << 16) | (g << 8) | b);
}
break;
// Frame data
case "VPTZ":
Format80.DecodeInto( reader.ReadBytes(subchunkLength), origData );
// This is the last subchunk
return;
default:
throw new InvalidDataException("Unknown sub-chunk {0}".F(type));
}
}
}
int cachedFrame = -1;
public uint[,] FrameData { get
{
if (cachedFrame != currentFrame)
{
cachedFrame = currentFrame;
for (var y = 0; y < blocks.Y; y++)
for (var x = 0; x < blocks.X; x++)
{
var px = origData[x + y*blocks.X];
var mod = origData[x + (y + blocks.Y)*blocks.X];
for (var j = 0; j < blockHeight; j++)
for (var i = 0; i < blockWidth; i++)
{
var cbfi = (mod*256 + px)*8 + j*blockWidth + i;
byte color = (mod == 0x0f) ? px : cbf[cbfi];
frameData[y*blockHeight + j, x*blockWidth + i] = palette[color];
}
}
}
return frameData;
}}
int NextPowerOf2(int v)
{
--v;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
++v;
return v;
}
}
}

View 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();
}
}
}

View File

@@ -1,34 +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
namespace OpenRA.FileFormats
{
public class ActorReference
{
public readonly string Id;
public readonly string Type;
public readonly int2 Location;
public readonly string Owner;
public ActorReference(MiniYaml my)
{
FieldLoader.Load(this, my);
}
// Legacy construtor for old format maps
public ActorReference(string id, string type, int2 location, string owner )
{
Id = id;
Type = type;
Location = location;
Owner = owner;
}
}
}

View File

@@ -17,44 +17,45 @@ namespace OpenRA.FileFormats
{
public class MapStub
{
public IFolder Package;
public readonly IFolder Package;
// Yaml map data
public string Uid;
public bool Selectable;
public readonly string Uid;
[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; } }
public Map Map { get { return new Map(Package); }}
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;
}
}
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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,13 +72,10 @@
<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" />
<Compile Include="TypeDictionary.cs" />
<Compile Include="Map\ActorReference.cs" />
<Compile Include="Map\Map.cs" />
<Compile Include="Map\TileReference.cs" />
<Compile Include="Map\Terrain.cs" />
<Compile Include="Primitives\Cache.cs" />
@@ -101,6 +99,7 @@
<Compile Include="Map\SmudgeReference.cs" />
<Compile Include="Map\PlayerReference.cs" />
<Compile Include="CompressedPackage.cs" />
<Compile Include="Graphics\VqaReader.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View File

@@ -18,6 +18,7 @@ namespace OpenRA.FileFormats
public interface IFolder
{
Stream GetContent(string filename);
bool Exists(string filename);
IEnumerable<uint> AllFileHashes();
}
@@ -68,7 +69,7 @@ namespace OpenRA.FileFormats
BinaryReader reader2 = new BinaryReader(ms);
ushort numFiles = reader2.ReadUInt16();
uint datasize = reader2.ReadUInt32();
reader2.ReadUInt32(); /*datasize*/
s.Position = headerStart;
reader = new BinaryReader(s);
@@ -143,6 +144,11 @@ namespace OpenRA.FileFormats
{
return index.Keys;
}
public bool Exists(string filename)
{
return index.ContainsKey(PackageEntry.HashFilename(filename));
}
}
[Flags]

View File

@@ -11,20 +11,38 @@
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System;
namespace OpenRA.FileFormats
{
public class Palette
{
List<Color> colors = new List<Color>();
uint[] colors;
public Color GetColor(int index)
{
return colors[index];
return Color.FromArgb((int)colors[index]);
}
public void SetColor(int index, Color color)
{
colors[index] = (uint)color.ToArgb();
}
public void SetColor(int index, uint color)
{
colors[index] = (uint)color;
}
public uint[] Values
{
get { return colors; }
}
public Palette(Stream s, bool remapTransparent)
{
colors = new uint[256];
using (BinaryReader reader = new BinaryReader(s))
{
for (int i = 0; i < 256; i++)
@@ -32,24 +50,28 @@ namespace OpenRA.FileFormats
byte r = (byte)(reader.ReadByte() << 2);
byte g = (byte)(reader.ReadByte() << 2);
byte b = (byte)(reader.ReadByte() << 2);
colors.Add(Color.FromArgb(r, g, b));
colors[i] = (uint)((255 << 24) | (r << 16) | (g << 8) | b);
}
}
colors[0] = Color.FromArgb(0, 0, 0, 0);
colors[0] = 0;
if (remapTransparent)
{
colors[3] = Color.FromArgb(178, 0, 0, 0);
colors[4] = Color.FromArgb(140, 0, 0, 0);
colors[3] = 178u << 24;
colors[4] = 140u << 24;
}
}
public Palette(Palette p, IPaletteRemap r)
{
for (int i = 0; i < 256; i++)
colors.Add(r.GetRemappedColor(p.GetColor(i), i));
colors = new uint[256];
for(int i = 0; i < 256; i++)
colors[i] = (uint)r.GetRemappedColor(Color.FromArgb((int)p.colors[i]),i).ToArgb();
}
public Palette(Palette p)
{
colors = (uint[])p.colors.Clone();
}
}

View File

@@ -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

View File

@@ -53,5 +53,11 @@ namespace OpenRA
public float2 ToFloat2() { return new float2(X, Y); }
public override string ToString() { return string.Format("{0},{1}", X, Y); }
// Change endianness of a uint32
public static uint Swap(uint orig)
{
return (uint)((orig & 0xff000000) >> 24) | ((orig & 0x00ff0000) >> 8) | ((orig & 0x0000ff00) << 8) | ((orig & 0x000000ff) << 24);
}
}
}

View File

@@ -1,88 +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 class Manifest
{
public readonly string[]
Folders, Packages, Rules,
Sequences, Chrome, Assemblies, ChromeLayout,
Weapons, Voices, Music, 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");
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[] { };
}
}
}

View File

@@ -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,35 +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");
using (var requestStream = request.GetRequestStream())
requestStream.Write(buffer, 0, buffer.Length);
//var response = (HttpWebResponse)request.GetResponse();
}
}
}
}

View File

@@ -9,12 +9,13 @@
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace OpenRA.FileFormats
{
public class TypeDictionary
public class TypeDictionary : IEnumerable
{
Dictionary<Type, object> dataSingular = new Dictionary<Type, object>();
Dictionary<Type, List<object>> dataMultiple = new Dictionary<Type, List<object>>();
@@ -85,13 +86,13 @@ namespace OpenRA.FileFormats
return new T[ 0 ];
}
public IEnumerator<object> GetEnumerator()
public IEnumerator GetEnumerator()
{
return WithInterface<object>().GetEnumerator();
}
}
static class TypeExts
public static class TypeExts
{
public static IEnumerable<Type> BaseTypes( this Type t )
{

View File

@@ -21,29 +21,26 @@ namespace OpenRA
{
public class Actor
{
[Sync]
public readonly TypeDictionary traits = new TypeDictionary();
public readonly ActorInfo Info;
public readonly World World;
public readonly uint ActorID;
public int2 Location { get { return traits.Get<IOccupySpace>().TopLeft; } }
public int2 Location { get { return Trait<IOccupySpace>().TopLeft; } }
[Sync]
public Player Owner;
[Sync]
public int Health;
IActivity currentActivity;
public Group Group;
public Actor(World world, string name, int2 location, Player owner)
public Group Group;
internal Actor(World world, string name, TypeDictionary initDict )
{
World = world;
ActorID = world.NextAID();
Owner = owner;
var init = new ActorInitializer( this, initDict );
var init = new ActorInitializer( this, location );
World = world;
ActorID = world.NextAID();
if( initDict.Contains<OwnerInit>() )
Owner = init.Get<OwnerInit,Player>();
if (name != null)
{
@@ -51,13 +48,11 @@ namespace OpenRA
throw new NotImplementedException("No rules definition for unit {0}".F(name.ToLowerInvariant()));
Info = Rules.Info[name.ToLowerInvariant()];
Health = this.GetMaxHP();
foreach (var trait in Info.TraitsInConstructOrder())
traits.Add(trait.Create(init));
AddTrait(trait.Create(init));
}
if( CenterLocation == float2.Zero && traits.Contains<IOccupySpace>() )
if( CenterLocation == float2.Zero && HasTrait<IOccupySpace>() )
CenterLocation = Traits.Util.CenterOfCell(Location);
Size = Lazy.New(() =>
@@ -67,14 +62,14 @@ namespace OpenRA
return new float2(si.Bounds[0], si.Bounds[1]);
// auto size from render
var firstSprite = traits.WithInterface<IRender>().SelectMany(x => x.Render(this)).FirstOrDefault();
var firstSprite = TraitsImplementing<IRender>().SelectMany(x => x.Render(this)).FirstOrDefault();
if (firstSprite.Sprite == null) return float2.Zero;
return firstSprite.Sprite.size;
});
}
public void Tick()
{
{
var wasIdle = currentActivity is Idle;
while (currentActivity != null)
{
@@ -86,7 +81,7 @@ namespace OpenRA
if (currentActivity is Idle)
{
if (!wasIdle)
foreach (var ni in traits.WithInterface<INotifyIdle>())
foreach (var ni in TraitsImplementing<INotifyIdle>())
ni.Idle(this);
break;
@@ -105,8 +100,8 @@ namespace OpenRA
public IEnumerable<Renderable> Render()
{
var mods = traits.WithInterface<IRenderModifier>();
var sprites = traits.WithInterface<IRender>().SelectMany(x => x.Render(this));
var mods = TraitsImplementing<IRenderModifier>();
var sprites = TraitsImplementing<IRender>().SelectMany(x => x.Render(this));
return mods.Aggregate(sprites, (m, p) => p.ModifyRender(this, m));
}
@@ -116,14 +111,17 @@ 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>())
.OrderByDescending(a => a.Info.Traits.Get<SelectableInfo>().Priority)
.Where(a => a.Info.Traits.Contains<TargetableInfo>())
.OrderByDescending(a => a.Info.Traits.Contains<SelectableInfo>() ? a.Info.Traits.Get<SelectableInfo>().Priority : int.MinValue)
.FirstOrDefault();
return traits.WithInterface<IIssueOrder>()
return TraitsImplementing<IIssueOrder>()
.Select( x => x.IssueOrder( this, xy, mi, underCursor ) )
.FirstOrDefault( x => x != null );
}
@@ -140,73 +138,14 @@ namespace OpenRA
if (useAltitude)
{
var unit = traits.GetOrDefault<Unit>();
if (unit != null) loc -= new float2(0, unit.Altitude);
var move = TraitOrDefault<IMove>();
if (move != null) loc -= new float2(0, move.Altitude);
}
return new RectangleF(loc.X, loc.Y, size.X, size.Y);
}
public bool IsDead { get { return Health <= 0; } }
public bool IsInWorld { get; set; }
public bool RemoveOnDeath = true;
public DamageState GetDamageState()
{
if (Health <= 0)
return DamageState.Dead;
if (Health < this.GetMaxHP() * World.Defaults.ConditionYellow)
return DamageState.Half;
return DamageState.Normal;
}
public void InflictDamage(Actor attacker, int damage, WarheadInfo warhead)
{
if (IsDead) return; /* overkill! don't count extra hits as more kills! */
var oldState = GetDamageState();
/* apply the damage modifiers, if we have any. */
var modifier = (float)traits.WithInterface<IDamageModifier>()
.Select(t => t.GetDamageModifier(warhead)).Product();
damage = (int)(damage * modifier);
Health -= damage;
if (Health <= 0)
{
Health = 0;
attacker.Owner.Kills++;
Owner.Deaths++;
if (RemoveOnDeath)
World.AddFrameEndTask(w => w.Remove(this));
Log.Write("debug", "{0} #{1} killed by {2} #{3}", Info.Name, ActorID, attacker.Info.Name, attacker.ActorID);
}
var maxHP = this.GetMaxHP();
if (Health > maxHP) Health = maxHP;
// Log.Write("debug", "InflictDamage: {0} #{1} -> {2} #{3} raw={4} adj={5} hp={6} mod={7}",
// attacker.Info.Name, attacker.ActorID, Info.Name, ActorID, rawDamage, damage, Health, modifier);
var newState = GetDamageState();
foreach (var nd in traits.WithInterface<INotifyDamage>())
nd.Damaged(this, new AttackInfo
{
Attacker = attacker,
Damage = damage,
DamageState = newState,
DamageStateChanged = newState != oldState,
Warhead = warhead
});
}
public bool IsInWorld { get; internal set; }
public void QueueActivity( IActivity nextActivity )
{
@@ -245,18 +184,47 @@ namespace OpenRA
var o = obj as Actor;
return ( o != null && o.ActorID == ActorID );
}
}
public class ActorInitializer
{
public readonly Actor self;
public World world { get { return self.World; } }
public readonly int2 location;
public ActorInitializer( Actor actor, int2 location )
public override string ToString()
{
this.self = actor;
this.location = location;
return "{0} {1}{2}".F( Info.Name, ActorID, IsInWorld ? "" : " (not in world)" );
}
public T Trait<T>()
{
return World.traitDict.Get<T>( this );
}
}
public T TraitOrDefault<T>()
{
return World.traitDict.GetOrDefault<T>( this );
}
public IEnumerable<T> TraitsImplementing<T>()
{
return World.traitDict.WithInterface<T>( this );
}
public bool HasTrait<T>()
{
return World.traitDict.Contains<T>( this );
}
public void AddTrait( object trait )
{
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;
} );
}
}
}

134
OpenRA.Game/ActorInitializer.cs Executable file
View File

@@ -0,0 +1,134 @@
#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.Linq;
using OpenRA.FileFormats;
namespace OpenRA
{
public class ActorInitializer
{
public readonly Actor self;
public World world { get { return self.World; } }
internal TypeDictionary dict;
public ActorInitializer( Actor actor, TypeDictionary dict )
{
this.self = actor;
this.dict = dict;
}
public T Get<T>()
where T : IActorInit
{
return dict.Get<T>();
}
public U Get<T,U>()
where T : IActorInit<U>
{
return dict.Get<T>().Value( world );
}
public bool Contains<T>()
where T : IActorInit
{
return dict.Contains<T>();
}
}
public interface IActorInit {}
public interface IActorInit<T> : IActorInit
{
T Value( World world );
}
public class FacingInit : IActorInit<int>
{
[FieldFromYamlKey]
public readonly int value = 128;
public FacingInit() { }
public FacingInit( int init )
{
value = init;
}
public int Value( World world )
{
return value;
}
}
public class AltitudeInit : IActorInit<int>
{
[FieldFromYamlKey]
public readonly int value = 0;
public AltitudeInit() { }
public AltitudeInit( int init )
{
value = init;
}
public int Value( World world )
{
return value;
}
}
public class LocationInit : IActorInit<int2>
{
[FieldFromYamlKey]
public readonly int2 value = int2.Zero;
public LocationInit() { }
public LocationInit( int2 init )
{
value = init;
}
public int2 Value( World world )
{
return value;
}
}
public class OwnerInit : IActorInit<Player>
{
[FieldFromYamlKey]
public readonly string PlayerName = "Neutral";
Player player;
public OwnerInit() { }
public OwnerInit( string playerName )
{
this.PlayerName = playerName;
}
public OwnerInit( Player player )
{
this.player = player;
this.PlayerName = player.InternalName;
}
public Player Value( World world )
{
if( player != null )
return player;
return world.players.Values.First( x => x.InternalName == PlayerName );
}
}
}

57
OpenRA.Game/ActorReference.cs Executable file
View File

@@ -0,0 +1,57 @@
#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;
using System.Collections.Generic;
using System.IO;
namespace OpenRA.FileFormats
{
public class ActorReference : IEnumerable
{
public readonly string Type;
public readonly TypeDictionary InitDict;
public ActorReference( string type ) : this( type, new Dictionary<string, MiniYaml>() ) { }
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 )
InitDict.Add( LoadInit( i.Key, i.Value ) );
}
static IActorInit LoadInit(string traitName, MiniYaml my)
{
var info = Game.CreateObject<IActorInit>(traitName + "Init");
FieldLoader.Load(info, my);
return info;
}
public MiniYaml Save()
{
var ret = new MiniYaml( Type );
foreach( var init in InitDict )
{
var initName = init.GetType().Name;
ret.Nodes.Add( new MiniYamlNode( initName.Substring( 0, initName.Length - 4 ), FieldSaver.Save( init ) ) );
}
return ret;
}
// for initialization syntax
public void Add( object o ) { InitDict.Add( o ); }
public IEnumerator GetEnumerator() { return InitDict.GetEnumerator(); }
}
}

View File

@@ -1,91 +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.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Widgets;
namespace OpenRA
{
class Chrome : IHandleInput
{
public readonly Renderer renderer;
public readonly LineRenderer lineRenderer;
SpriteRenderer rgbaRenderer { get { return renderer.RgbaSpriteRenderer; } }
SpriteRenderer shpRenderer { get { return renderer.WorldSpriteRenderer; } }
public Chrome(Renderer r, Manifest m)
{
this.renderer = r;
lineRenderer = new LineRenderer(renderer);
var widgetYaml = m.ChromeLayout.Select(a => MiniYaml.FromFile(a)).Aggregate(MiniYaml.Merge);
if (rootWidget == null)
{
rootWidget = WidgetLoader.LoadWidget( widgetYaml.FirstOrDefault() );
rootWidget.Initialize();
rootWidget.InitDelegates();
}
}
public static Widget rootWidget = null;
public void Tick(World world)
{
rootWidget.Tick(world);
if (!world.GameHasStarted) return;
if (world.LocalPlayer == null) return;
++ticksSinceLastMove;
}
public void Draw(World world) { rootWidget.Draw(world); shpRenderer.Flush(); rgbaRenderer.Flush(); lineRenderer.Flush(); }
public int ticksSinceLastMove = 0;
public int2 lastMousePos;
public bool HandleInput(World world, MouseInput mi)
{
if (Widget.SelectedWidget != null && Widget.SelectedWidget.HandleMouseInputOuter(mi))
return true;
if (rootWidget.HandleMouseInputOuter(mi))
return true;
if (mi.Event == MouseInputEvent.Move)
{
lastMousePos = mi.Location;
ticksSinceLastMove = 0;
}
return false;
}
public bool HandleKeyPress(KeyInput e)
{
if (Widget.SelectedWidget != null)
return Widget.SelectedWidget.HandleKeyPressOuter(e);
if (rootWidget.HandleKeyPressOuter(e))
return true;
return false;
}
public bool HitTest(int2 mousePos)
{
if (Widget.SelectedWidget != null)
return true;
return rootWidget.HitTest(mousePos);
}
}
}

View File

@@ -1,156 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see LICENSE.
*/
#endregion
using System;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Orders;
using OpenRA.Traits;
namespace OpenRA
{
public class Controller : IHandleInput
{
public IOrderGenerator orderGenerator = new UnitOrderGenerator();
public Selection selection = new Selection();
public void CancelInputMode() { orderGenerator = new UnitOrderGenerator(); }
public bool ToggleInputMode<T>() where T : IOrderGenerator, new()
{
if (orderGenerator is T)
{
CancelInputMode();
return false;
}
else
{
orderGenerator = new T();
return true;
}
}
void ApplyOrders(World world, float2 xy, MouseInput mi)
{
if (orderGenerator == null) return;
var orders = orderGenerator.Order(world, xy.ToInt2(), mi).ToArray();
Game.orderManager.IssueOrders( orders );
var voicedActor = orders.Select(o => o.Subject)
.FirstOrDefault(a => a.Owner == world.LocalPlayer && a.HasVoice());
var isMove = orders.Any(o => o.OrderString == "Move");
var isAttack = orders.Any( o => o.OrderString == "Attack" );
if (voicedActor != null)
{
if(voicedActor.traits.GetOrDefault<IMove>().CanEnterCell(xy.ToInt2()))
Sound.PlayVoice(isAttack ? "Attack" : "Move", voicedActor);
if (isMove)
world.Add(new Effects.MoveFlash(world, Game.CellSize * xy));
}
}
float2 dragStart, dragEnd;
public bool HandleInput(World world, MouseInput mi)
{
var xy = Game.viewport.ViewToWorld(mi);
if (mi.Button == MouseButton.Left && mi.Event == MouseInputEvent.Down)
{
dragStart = dragEnd = xy;
ApplyOrders(world, xy, mi);
}
if (mi.Button == MouseButton.Left && mi.Event == MouseInputEvent.Move)
dragEnd = xy;
if (mi.Button == MouseButton.Left && mi.Event == MouseInputEvent.Up)
{
if (orderGenerator is UnitOrderGenerator)
{
var newSelection = world.SelectActorsInBox(Game.CellSize * dragStart, Game.CellSize * xy);
selection.Combine(world, newSelection, mi.Modifiers.HasModifier(Modifiers.Shift), dragStart == xy);
}
dragStart = dragEnd = xy;
}
if (mi.Button == MouseButton.None && mi.Event == MouseInputEvent.Move)
dragStart = dragEnd = xy;
if (mi.Button == MouseButton.Right && mi.Event == MouseInputEvent.Down)
ApplyOrders(world, xy, mi);
return true;
}
public Pair<float2, float2>? SelectionBox
{
get
{
if (dragStart == dragEnd) return null;
return Pair.New(Game.CellSize * dragStart, Game.CellSize * dragEnd);
}
}
public float2 MousePosition { get { return dragEnd; } }
Modifiers modifiers;
public string ChooseCursor( World world )
{
int sync = world.SyncHash();
try
{
if (!world.GameHasStarted)
return "default";
var mi = new MouseInput
{
Location = ( Game.CellSize * MousePosition - Game.viewport.Location ).ToInt2(),
Button = MouseButton.Right,
Modifiers = modifiers
};
return orderGenerator.GetCursor( world, MousePosition.ToInt2(), mi );
}
finally
{
if( sync != world.SyncHash() )
throw new InvalidOperationException( "Desync in Controller.ChooseCursor" );
}
}
public void SetModifiers(Modifiers mods) { modifiers = mods; }
public Modifiers GetModifiers() { return modifiers; }
public void GotoNextBase()
{
var bases = Game.world.Queries.OwnedBy[Game.world.LocalPlayer].WithTrait<BaseBuilding>().ToArray();
if (!bases.Any()) return;
var next = bases
.Select( b => b.Actor )
.SkipWhile(b => Game.controller.selection.Actors.Contains(b))
.Skip(1)
.FirstOrDefault();
if (next == null)
next = bases.Select(b => b.Actor).First();
Game.controller.selection.Combine(Game.world, new Actor[] { next }, false, true);
Game.viewport.Center(Game.controller.selection.Actors);
}
}
}

View File

@@ -17,10 +17,12 @@ namespace OpenRA
CursorSequence sequence;
public Cursor(string cursor)
{
sequence = SequenceProvider.GetCursorSequence(cursor);
sequence = CursorProvider.GetCursorSequence(cursor);
}
public void Draw(int frame, float2 pos)
{
Game.Renderer.SpriteRenderer.DrawSprite(sequence.GetSprite(frame), pos - sequence.Hotspot, sequence.Palette);
}
public Sprite GetSprite(int frame) { return sequence.GetSprite(frame); }
public int2 GetHotspot() { return sequence.Hotspot; }
}
}

View File

@@ -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");

View File

@@ -14,11 +14,20 @@ using OpenRA.Traits;
namespace OpenRA.Effects
{
class MoveFlash : IEffect
public class MoveFlash : IEffect
{
Animation anim = new Animation("moveflsh");
float2 pos;
public MoveFlash( World world, int2 cell )
{
this.pos = Game.CellSize * (cell + new float2(0.5f, 0.5f));
anim.PlayThen( "idle",
() => world.AddFrameEndTask(
w => w.Remove( this ) ) );
}
public MoveFlash( World world, float2 pos )
{
this.pos = pos;

View File

@@ -23,12 +23,12 @@ namespace OpenRA.Effects
public RepairIndicator(Actor a)
{
this.a = a; anim.PlayRepeating("repair");
framesLeft = (int)(a.World.Defaults.RepairRate * 25 * 60 / 2);
framesLeft = (int)(a.Info.Traits.Get<RepairableBuildingInfo>().RepairRate * 25 * 60 / 2);
}
public void Tick( World world )
{
if (--framesLeft == 0 || a.IsDead)
if (--framesLeft == 0 || a.IsDead())
world.AddFrameEndTask(w => w.Remove(this));
}

View File

@@ -34,13 +34,6 @@ namespace OpenRA
return xs.Aggregate(1f, (a, x) => a * x);
}
public static int GetMaxHP(this Actor self)
{
var oai = self.Info.Traits.GetOrDefault<OwnedActorInfo>();
if (oai == null) return 0;
return oai.HP;
}
public static V GetOrAdd<K, V>( this Dictionary<K, V> d, K k )
where V : new()
{

View File

@@ -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,125 +31,27 @@ namespace OpenRA
{
public static readonly int CellSize = 24;
public static ModData modData;
public static World world;
internal static Viewport viewport;
public static Controller controller;
internal static Chrome chrome;
internal static UserSettings Settings;
public static Viewport viewport;
public static Settings Settings;
internal static OrderManager orderManager;
public static bool skipMakeAnims = true;
public static XRandom CosmeticRandom = new XRandom(); // not synced
internal static Renderer renderer;
static int2 clientSize;
static string mapName;
public static Renderer Renderer;
public static Session LobbyInfo = new Session();
static bool packageChangePending;
static bool mapChangePending;
static Pair<Assembly, string>[] ModAssemblies;
static internal bool scrollUp = false;
static internal bool scrollDown = false;
static internal bool scrollLeft = false;
static internal bool scrollRight = false;
static void LoadModPackages(Manifest manifest)
static void LoadMap(string uid)
{
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 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;
}
throw new InvalidOperationException("Cannot locate type: {0}".F(classname));
}
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");
var manifest = new Manifest(LobbyInfo.GlobalSettings.Mods);
Timer.Time("manifest: {0}");
LoadModAssemblies(manifest);
SheetBuilder.Initialize(renderer);
LoadModPackages(manifest);
Timer.Time("load assemblies, packages: {0}");
packageChangePending = false;
}
static void LoadMap(string mapName)
{
Timer.Time("----LoadMap");
SheetBuilder.Initialize(renderer);
var 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}");
chrome = new Chrome(renderer, manifest);
Timer.Time("chrome: {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);
@@ -169,6 +67,9 @@ namespace OpenRA
CurrentHost = host;
CurrentPort = port;
lastConnectionState = ConnectionState.PreConnecting;
ConnectionStateChanged();
orderManager = new OrderManager(new NetworkConnection(host, port), ChooseReplayFilename());
}
@@ -179,6 +80,9 @@ namespace OpenRA
static void JoinLocal()
{
lastConnectionState = ConnectionState.PreConnecting;
ConnectionStateChanged();
if (orderManager != null) orderManager.Dispose();
orderManager = new OrderManager(new EchoConnection());
}
@@ -193,95 +97,27 @@ 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 == null)
{
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;
chrome.Tick(world);
lastTime += Settings.Game.Timestep;
Widget.DoTick(world);
orderManager.TickImmediate(world);
@@ -293,9 +129,8 @@ namespace OpenRA
if (isNetTick) orderManager.Tick(world);
controller.orderGenerator.Tick(world);
controller.selection.Tick(world);
world.OrderGenerator.Tick(world);
world.Selection.Tick(world);
world.Tick();
PerfHistory.Tick();
@@ -306,15 +141,6 @@ namespace OpenRA
}
}
if (scrollUp == true)
viewport.Scroll(new float2(0, -10));
if (scrollRight == true)
viewport.Scroll(new float2(10, 0));
if (scrollDown == true)
viewport.Scroll(new float2(0, 10));
if (scrollLeft == true)
viewport.Scroll(new float2(-10, 0));
using (new PerfSample("render"))
{
++RenderFrame;
@@ -334,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);
@@ -372,99 +175,48 @@ 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.traits.WithInterface<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.traits.WithInterface<IGameStarted>())
gs.GameStarted(world);
viewport.GoToStartLocation(world.LocalPlayer);
orderManager.StartGame();
OnGameStart();
viewport.RefreshPalette();
AfterGameStart();
}
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);
}
static int2 lastPos;
public static void DispatchMouseInput(MouseInputEvent ev, MouseEventArgs e, Modifiers modifierKeys)
{
if (world == null)
return;
int sync = world.SyncHash();
var initialWorld = world;
if (ev == MouseInputEvent.Down)
lastPos = new int2(e.Location);
if (ev == MouseInputEvent.Move &&
(e.Button == MouseButtons.Middle ||
e.Button == (MouseButtons.Left | MouseButtons.Right)))
var mi = new MouseInput
{
var p = new int2(e.Location);
viewport.Scroll(lastPos - p);
lastPos = p;
}
viewport.DispatchMouseInput(world,
new MouseInput
{
Button = (MouseButton)(int)e.Button,
Event = ev,
Location = new int2(e.Location),
Modifiers = modifierKeys,
});
Button = (MouseButton)(int)e.Button,
Event = ev,
Location = new int2(e.Location),
Modifiers = modifierKeys,
};
Widget.HandleInput(world, mi);
if (sync != world.SyncHash() && world == initialWorld)
throw new InvalidOperationException("Desync in DispatchMouseInput");
}
internal static bool IsHost
public static bool IsHost
{
get { return orderManager.Connection.LocalClientId == 0; }
}
@@ -474,129 +226,57 @@ namespace OpenRA
get { return LobbyInfo.Clients.FirstOrDefault(c => c.Index == orderManager.Connection.LocalClientId); }
}
public static void HandleKeyDown(KeyInput e)
public static void HandleKeyEvent(KeyInput e)
{
int sync = world.SyncHash();
if (chrome.HandleKeyPress(e))
if (world == null)
return;
switch (e.KeyName)
{
case "up": scrollUp = true; break;
case "down": scrollDown = true; break;
case "left": scrollLeft = true; break;
case "right": scrollRight = true; break;
}
if (e.KeyName.Length == 1 && char.IsDigit(e.KeyName[0]))
Game.controller.selection.DoControlGroup(world, e.KeyName[0] - '0', e.Modifiers);
if (e.KeyChar == 08)
Game.controller.GotoNextBase();
int sync = world.SyncHash();
if (Widget.HandleKeyPress(e))
return;
if (sync != Game.world.SyncHash())
throw new InvalidOperationException("Desync in OnKeyPress");
}
public static void HandleKeyUp(KeyInput e)
{
switch (e.KeyName)
{
case "up": scrollUp = false; break;
case "down": scrollDown = false; break;
case "left": scrollLeft = false; break;
case "right": scrollRight = false; break;
}
}
static Modifiers modifiers;
public static Modifiers GetModifierKeys() { return modifiers; }
public static void HandleModifierKeys(Modifiers mods) { modifiers = mods; }
public static void HandleArrowKeyScroll(String k, Boolean pressed)
{
if (k == "up")
{
scrollUp = pressed;
}
if (k == "left")
{
scrollLeft = pressed;
}
if (k == "down")
{
scrollDown = pressed;
}
if (k == "right")
{
scrollRight = pressed;
}
}
public static void HandleModifierKeys(Modifiers mods)
{
controller.SetModifiers(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;
// Load the default mod to access required files
LoadModPackages(new Manifest(LobbyInfo.GlobalSettings.Mods));
Renderer.SheetSize = Settings.SheetSize;
var resolution = GetResolution(settings, Game.Settings.WindowMode);
renderer = new Renderer(resolution, Game.Settings.WindowMode);
resolution = renderer.Resolution;
controller = new Controller();
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(new Manifest(LobbyInfo.GlobalSettings.Mods).ShellmapUid);
PerfHistory.items["cursor"].hasNormalTick = false;
JoinLocal();
StartGame(modData.Manifest.ShellmapUid);
ResetTimer();
}
@@ -610,77 +290,49 @@ 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);
}
public static void Disconnect()
var shellmap = new Manifest(LobbyInfo.GlobalSettings.Mods).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);
Chrome.rootWidget.CloseWindow();
Chrome.rootWidget.OpenWindow("MAINMENU_BG");
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;
var 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 );
}
}

View File

@@ -14,32 +14,27 @@ using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Traits;
namespace OpenRA.GameRules
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;

View File

@@ -13,38 +13,16 @@ using OpenRA.FileFormats;
namespace OpenRA.GameRules
{
public class MusicInfo
{
public readonly MusicPool Pool;
public readonly string[] Music = { };
{
[FieldLoader.Load] public readonly string Filename = null;
[FieldLoader.Load] public readonly string Title = null;
[FieldLoader.Load] public readonly int Length = 0; // seconds
public MusicInfo( MiniYaml y )
public MusicInfo( string key, MiniYaml value )
{
FieldLoader.Load(this, y);
Pool = new MusicPool(Music);
FieldLoader.Load(this, value);
if (Filename == null)
Filename = key+".aud";
}
}
public class MusicPool
{
readonly string[] clips;
int playing = 0;
public MusicPool(params string[] clips)
{
this.clips = clips;
}
public string GetNext()
{
playing = (playing + 1) % clips.Length;
return clips[playing];
}
public string GetPrev()
{
playing = (playing + clips.Length - 1) % clips.Length;
return clips[playing];
}
public string GetCurrent(){ return clips[playing];}
}
}

View File

@@ -24,14 +24,16 @@ namespace OpenRA
public static Dictionary<string, WeaponInfo> Weapons;
public static Dictionary<string, VoiceInfo> Voices;
public static Dictionary<string, MusicInfo> Music;
public static Dictionary<string, string> Movies;
public static Dictionary<string, TileSet> TileSets;
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.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)
@@ -43,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
View 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);
}
}
}

View File

@@ -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 ];
}
}
}

View File

@@ -1,108 +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
{
// Debug settings
public bool UnitDebug = false;
public bool PathDebug = false;
public bool PerfDebug = false;
public bool IndexDebug = false;
public bool RecordSyncReports = true;
public bool ShowGameTimer = true;
// 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 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/";
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);
}
}
}

View File

@@ -9,33 +9,40 @@
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
using System;
namespace OpenRA.GameRules
{
public class VoiceInfo
{
public readonly string[] SovietVariants = { ".aud" };
public readonly string[] AlliedVariants = { ".aud" };
public readonly string[] Select = { };
public readonly string[] Move = { };
public readonly string[] Attack = null;
public readonly string[] Die = { };
public readonly Dictionary<string,string[]> Variants;
public readonly Dictionary<string,string[]> Voices;
public readonly string DefaultVariant = ".aud" ;
[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.Load(this, y);
Pools = Lazy.New(() =>
new Dictionary<string, VoicePool>
{
{ "Select", new VoicePool(Select) },
{ "Move", new VoicePool(Move) },
{ "Attack", new VoicePool( Attack ?? Move ) },
{ "Die", new VoicePool(Die) },
});
FieldLoader.Load( this, y );
Variants = Load(y, "Variants");
Voices = Load(y, "Voices");
if (!Voices.ContainsKey("Attack"))
Voices.Add("Attack", Voices["Move"]);
Pools = Lazy.New(() => Voices.ToDictionary( a => a.Key, a => new VoicePool(a.Value) ));
}
}

View File

@@ -11,36 +11,55 @@
using System.Collections.Generic;
using OpenRA.Effects;
using OpenRA.FileFormats;
using OpenRA.Traits;
using System.Linq;
namespace OpenRA.GameRules
{
public class WarheadInfo
{
public readonly int Spread = 1; // distance (in pixels) from the explosion center at which damage is 1/2.
public readonly float[] Verses = { 1, 1, 1, 1, 1 }; // damage vs each armortype
public readonly bool Ore = false; // can this damage ore?
public readonly string Explosion = null; // explosion effect to use
public readonly string WaterExplosion = null; // explosion effect on hitting water (usually a splash)
public readonly string SmudgeType = null; // type of smudge to apply
public readonly int[] Size = { 0, 0 }; // size of the explosion. provide 2 values for a ring effect (outer/inner)
public readonly int InfDeath = 0; // infantry death animation to use
public readonly string ImpactSound = null; // sound to play on impact
public readonly string WaterImpactSound = null; // sound to play on impact with water
public readonly int Damage = 0; // how much (raw) damage to deal
public readonly int Delay = 0; // delay in ticks before dealing the damage. 0=instant (old model)
public readonly DamageModel DamageModel = DamageModel.Normal; // which damage model to use
[FieldLoader.Load] public readonly int Spread = 1; // distance (in pixels) from the explosion center at which damage is 1/2.
[FieldLoader.LoadUsing( "LoadVersus" )]
public readonly Dictionary<string, float> Versus; // damage vs each armortype
[FieldLoader.Load] public readonly bool Ore = false; // can this damage ore?
[FieldLoader.Load] public readonly string Explosion = null; // explosion effect to use
[FieldLoader.Load] public readonly string WaterExplosion = null; // explosion effect on hitting water (usually a splash)
[FieldLoader.Load] public readonly string SmudgeType = null; // type of smudge to apply
[FieldLoader.Load] public readonly int[] Size = { 0, 0 }; // size of the explosion. provide 2 values for a ring effect (outer/inner)
[FieldLoader.Load] public readonly int InfDeath = 0; // infantry death animation to use
[FieldLoader.Load] public readonly string ImpactSound = null; // sound to play on impact
[FieldLoader.Load] public readonly string WaterImpactSound = null; // sound to play on impact with water
[FieldLoader.Load] public readonly int Damage = 0; // how much (raw) damage to deal
[FieldLoader.Load] public readonly int Delay = 0; // delay in ticks before dealing the damage. 0=instant (old model)
[FieldLoader.Load] public readonly DamageModel DamageModel = DamageModel.Normal; // which damage model to use
public float EffectivenessAgainst(ArmorType at) { return Verses[(int)at]; }
}
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;
float versus;
return Versus.TryGetValue(armor.Type, out versus) ? versus : 1;
}
public enum ArmorType
{
none = 0,
wood = 1,
light = 2,
heavy = 3,
concrete = 4,
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 DamageModel
{
@@ -55,58 +74,51 @@ namespace OpenRA.GameRules
public int2 src;
public int srcAltitude;
public int facing;
public Actor target;
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;
}
}
}

View File

@@ -16,7 +16,7 @@ using OpenRA.FileFormats;
namespace OpenRA.Graphics
{
static class ChromeProvider
public static class ChromeProvider
{
static Dictionary<string, Dictionary<string, MappedImage>> collections;
static Dictionary<string, Sheet> cachedSheets;
@@ -52,7 +52,7 @@ namespace OpenRA.Graphics
collections.Add(elementName, images);
}
public static Sprite GetImage(Renderer renderer, string collection, string image)
public static Sprite GetImage(string collection, string image)
{
// Cached sprite
if (cachedSprites.ContainsKey(collection) && cachedSprites[collection].ContainsKey(image))
@@ -72,14 +72,14 @@ namespace OpenRA.Graphics
sheet = cachedSheets[mi.Src];
else
{
sheet = new Sheet(renderer, mi.Src);
sheet = new Sheet(mi.Src);
cachedSheets.Add(mi.Src, sheet);
}
// Cache the sprite
if (!cachedSprites.ContainsKey(collection))
cachedSprites.Add(collection, new Dictionary<string, Sprite>());
cachedSprites[collection].Add(image, mi.GetImage(renderer, sheet));
cachedSprites[collection].Add(image, mi.GetImage(sheet));
return cachedSprites[collection][image];
}

View 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));
}
}
}
}

View File

@@ -15,21 +15,23 @@ namespace OpenRA.Graphics
public class CursorSequence
{
readonly int start, length;
readonly string palette;
public int Start { get { return start; } }
public int End { get { return start + length; } }
public int Length { get { return length; } }
public string Palette { get { return palette; } }
public readonly int2 Hotspot;
Sprite[] sprites;
public CursorSequence(string cursorSrc, XmlElement e)
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;
if (e.GetAttribute("length") == "*" || e.GetAttribute("end") == "*")
length = sprites.Length - start;
else if (e.HasAttribute("length"))

View File

@@ -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]; }
}
}

View File

@@ -10,78 +10,70 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.FileFormats.Graphics;
using OpenRA.Traits;
namespace OpenRA.Graphics
{
class HardwarePalette : Sheet
class HardwarePalette
{
public const int MaxPalettes = 64;
int allocated = 0;
// We need to store the Palettes themselves for the remap palettes to work
// We should probably try to fix this somehow
static Dictionary<string, Palette> palettes;
static Dictionary<string, int> indices;
public HardwarePalette(Renderer renderer, Map map)
: base(renderer,new Size(256, MaxPalettes))
ITexture texture;
Dictionary<string, Palette> palettes;
Dictionary<string, int> indices;
public HardwarePalette(Map map)
{
palettes = new Dictionary<string, Palette>();
indices = new Dictionary<string, int>();
texture = Game.Renderer.Device.CreateTexture();
}
public Palette GetPalette(string name)
{
try { return palettes[name]; }
catch (KeyNotFoundException)
{
throw new InvalidOperationException(
"Palette `{0}` does not exist".F(name));
}
Palette ret;
if (!palettes.TryGetValue(name,out ret))
throw new InvalidOperationException("Palette `{0}` does not exist".F(name));
return ret;
}
public int GetPaletteIndex(string name)
{
try { return indices[name]; }
catch (KeyNotFoundException)
{
throw new InvalidOperationException(
"Palette `{0}` does not exist".F(name));
}
int ret;
if (!indices.TryGetValue(name,out ret))
throw new InvalidOperationException("Palette `{0}` does not exist".F(name));
return ret;
}
public int AddPalette(string name, Palette p)
public void AddPalette(string name, Palette p)
{
palettes.Add(name, p);
indices.Add(name, allocated);
for (int i = 0; i < 256; i++)
{
this[new Point(i, allocated)] = p.GetColor(i);
}
return allocated++;
}
public void UpdatePalette(string name, Palette p)
{
palettes[name] = p;
var j = indices[name];
for (int i = 0; i < 256; i++)
{
this[new Point(i, j)] = p.GetColor(i);
}
indices.Add(name, allocated++);
}
public void Update(IEnumerable<IPaletteModifier> paletteMods)
{
var b = new Bitmap(Bitmap);
var copy = palettes.ToDictionary(p => p.Key, p => new Palette(p.Value));
foreach (var mod in paletteMods)
mod.AdjustPalette(b);
Texture.SetData(b);
Game.renderer.PaletteTexture = Texture;
mod.AdjustPalette(copy);
var data = new uint[MaxPalettes,256];
foreach (var pal in copy)
{
var j = indices[pal.Key];
var c = pal.Value.Values;
for (var i = 0; i < 256; i++)
data[j,i] = c[i];
}
// Doesn't work
texture.SetData(data);
Game.Renderer.PaletteTexture = texture;
}
}
}

View File

@@ -13,7 +13,7 @@ using OpenRA.FileFormats.Graphics;
namespace OpenRA.Graphics
{
class LineRenderer
public class LineRenderer
{
Renderer renderer;
IVertexBuffer<Vertex> vertexBuffer;

View File

@@ -33,7 +33,7 @@ namespace OpenRA.Graphics
int.Parse(e.GetAttribute("height")));
}
public Sprite GetImage(Renderer r, Sheet s)
public Sprite GetImage(Sheet s)
{
return new Sprite(s, rect, TextureChannel.Alpha);
}

View File

@@ -17,168 +17,41 @@ using OpenRA.Traits;
namespace OpenRA.Graphics
{
class Minimap
{
readonly World world;
Sheet sheet;
SpriteRenderer rgbaRenderer;
Sprite sprite;
Bitmap terrain, customLayer;
Rectangle bounds;
const int alpha = 230;
public Minimap(World world, Renderer r)
public class Minimap
{
public static Bitmap TerrainBitmap(Map map)
{
this.world = world;
sheet = new Sheet(r, new Size(world.Map.MapSize.X, world.Map.MapSize.Y));
rgbaRenderer = r.RgbaSpriteRenderer;
var size = Math.Max(world.Map.Width, world.Map.Height);
var dw = (size - world.Map.Width) / 2;
var dh = (size - world.Map.Height) / 2;
bounds = new Rectangle(world.Map.TopLeft.X - dw, world.Map.TopLeft.Y - dh, size, size);
sprite = new Sprite(sheet, bounds, TextureChannel.Alpha);
shroudColor = Color.FromArgb(alpha, Color.Black);
}
public static Rectangle MakeMinimapBounds(Map m)
{
var size = Math.Max(m.Width, m.Height);
var dw = (size - m.Width) / 2;
var dh = (size - m.Height) / 2;
return new Rectangle(m.TopLeft.X - dw, m.TopLeft.Y - dh, size, size);
}
static Color shroudColor;
public void InvalidateCustom() { customLayer = null; }
public static Bitmap RenderTerrainBitmap(Map map, TileSet tileset)
{
var terrain = new Bitmap(map.MapSize.X, map.MapSize.Y);
var tileset = Rules.TileSets[map.Tileset];
var size = Util.NextPowerOf2(Math.Max(map.Width, map.Height));
Bitmap terrain = new Bitmap(size, size);
var bitmapData = terrain.LockBits(new Rectangle(0, 0, terrain.Width, terrain.Height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
unsafe
{
int* c = (int*)bitmapData.Scan0;
for (var x = 0; x < map.MapSize.X; x++)
for (var y = 0; y < map.MapSize.Y; y++)
for (var x = 0; x < map.Width; x++)
for (var y = 0; y < map.Height; y++)
{
var type = tileset.GetTerrainType(map.MapTiles[x, y]);
*(c + (y * bitmapData.Stride >> 2) + x) = map.IsInMap(x, y)
? Color.FromArgb(alpha, tileset.Terrain[type].Color).ToArgb()
: shroudColor.ToArgb();
var mapX = x + map.TopLeft.X;
var mapY = y + map.TopLeft.Y;
var type = tileset.GetTerrainType(map.MapTiles[mapX, mapY]);
*(c + (y * bitmapData.Stride >> 2) + x) = tileset.Terrain[type].Color.ToArgb();
}
}
terrain.UnlockBits(bitmapData);
return terrain;
}
public void Update()
{
if (terrain == null)
terrain = RenderTerrainBitmap(world.Map, world.TileSet);
// Custom terrain layer
if (customLayer == null)
{
customLayer = new Bitmap(terrain);
for (var x = world.Map.TopLeft.X; x < world.Map.BottomRight.X; x++)
for (var y = world.Map.TopLeft.Y; y < world.Map.BottomRight.Y; y++)
{
var customTerrain = world.WorldActor.traits.WithInterface<ITerrainTypeModifier>()
.Select( t => t.GetTerrainType(new int2(x,y)) )
.FirstOrDefault( t => t != null );
if (customTerrain == null) continue;
customLayer.SetPixel(x, y, Color.FromArgb(alpha, world.TileSet.Terrain[customTerrain].Color));
}
}
if (!world.GameHasStarted || !world.Queries.OwnedBy[world.LocalPlayer].WithTrait<ProvidesRadar>().Any())
return;
var bitmap = new Bitmap(customLayer);
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
unsafe
{
int* c = (int*)bitmapData.Scan0;
foreach (var a in world.Queries.WithTrait<Unit>().Where(a => a.Actor.Owner != null && a.Actor.IsVisible()))
*(c + (a.Actor.Location.Y * bitmapData.Stride >> 2) + a.Actor.Location.X) =
Color.FromArgb(alpha, a.Actor.Owner.Color).ToArgb();
for (var x = world.Map.TopLeft.X; x < world.Map.BottomRight.X; x++)
for (var y = world.Map.TopLeft.Y; y < world.Map.BottomRight.Y; y++)
{
if (!world.LocalPlayer.Shroud.DisplayOnRadar(x, y))
{
*(c + (y * bitmapData.Stride >> 2) + x) = shroudColor.ToArgb();
continue;
}
var b = world.WorldActor.traits.Get<BuildingInfluence>().GetBuildingAt(new int2(x, y));
if (b != null)
*(c + (y * bitmapData.Stride >> 2) + x) = Color.FromArgb(alpha, b.Owner.Color).ToArgb();
}
}
bitmap.UnlockBits(bitmapData);
sheet.Texture.SetData(bitmap);
}
public void Draw(RectangleF rect)
{
rgbaRenderer.DrawSprite(sprite,
new float2(rect.X, rect.Y), "chrome", new float2(rect.Width, rect.Height));
rgbaRenderer.Flush();
}
int2 CellToMinimapPixel(RectangleF viewRect, int2 p)
{
var fx = (float)(p.X - bounds.X) / bounds.Width;
var fy = (float)(p.Y - bounds.Y) / bounds.Height;
return new int2(
(int)(viewRect.Width * fx + viewRect.Left),
(int)(viewRect.Height * fy + viewRect.Top));
}
public int2 MinimapPixelToCell(RectangleF viewRect, int2 p)
{
var fx = (float)(p.X - viewRect.Left) / viewRect.Width;
var fy = (float)(p.Y - viewRect.Top) / viewRect.Height;
return new int2(
(int)(bounds.Width * fx + bounds.Left),
(int)(bounds.Height * fy + bounds.Top));
}
static int NextPowerOf2(int v)
// Add the static resources defined in the map; if the map lives
// in a world use AddCustomTerrain instead
public static Bitmap AddStaticResources(Map map, Bitmap terrainBitmap)
{
--v;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
++v;
return v;
}
public static Bitmap RenderMapPreview(MapStub stub)
{
Map map = stub.Map;
Bitmap terrain = new Bitmap(terrainBitmap);
var tileset = Rules.TileSets[map.Tileset];
var size = NextPowerOf2(Math.Max(map.Width, map.Height));
Bitmap terrain = new Bitmap(size, size);
var bitmapData = terrain.LockBits(new Rectangle(0, 0, terrain.Width, terrain.Height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
@@ -190,22 +63,114 @@ namespace OpenRA.Graphics
for (var y = 0; y < map.Height; y++)
{
var mapX = x + map.TopLeft.X;
var mapY = y + map.TopLeft.Y;
var type = tileset.GetTerrainType(map.MapTiles[mapX, mapY]);
string res = null;
if (map.MapResources[mapX, mapY].type != 0)
res = Rules.Info["world"].Traits.WithInterface<ResourceTypeInfo>()
var mapY = y + map.TopLeft.Y;
if (map.MapResources[mapX, mapY].type == 0)
continue;
var res = Rules.Info["world"].Traits.WithInterface<ResourceTypeInfo>()
.Where(t => t.ResourceType == map.MapResources[mapX, mapY].type)
.Select(t => t.TerrainType).FirstOrDefault();
if (res != null)
type = res;
if (res == null)
continue;
*(c + (y * bitmapData.Stride >> 2) + x) = tileset.Terrain[type].Color.ToArgb();
*(c + (y * bitmapData.Stride >> 2) + x) = tileset.Terrain[res].Color.ToArgb();
}
}
terrain.UnlockBits(bitmapData);
return terrain;
}
public static Bitmap CustomTerrainBitmap(World world)
{
var map = world.Map;
var size = Util.NextPowerOf2(Math.Max(map.Width, map.Height));
Bitmap bitmap = new Bitmap(size, size);
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
unsafe
{
int* c = (int*)bitmapData.Scan0;
for (var x = 0; x < map.Width; x++)
for (var y = 0; y < map.Height; y++)
{
var mapX = x + map.TopLeft.X;
var mapY = y + map.TopLeft.Y;
var custom = map.CustomTerrain[mapX,mapY];
if (custom == null)
continue;
*(c + (y * bitmapData.Stride >> 2) + x) = world.TileSet.Terrain[custom].Color.ToArgb();
}
}
bitmap.UnlockBits(bitmapData);
return bitmap;
}
public static Bitmap ActorsBitmap(World world)
{
var map = world.Map;
var size = Util.NextPowerOf2(Math.Max(map.Width, map.Height));
Bitmap bitmap = new Bitmap(size, size);
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
unsafe
{
int* c = (int*)bitmapData.Scan0;
foreach (var t in world.Queries.WithTraitMultiple<IRadarSignature>())
{
if (!t.Actor.IsVisible(world.LocalPlayer))
continue;
var color = t.Trait.RadarSignatureColor(t.Actor);
foreach (var cell in t.Trait.RadarSignatureCells(t.Actor))
if (world.Map.IsInMap(cell))
*(c + ((cell.Y - world.Map.TopLeft.Y) * bitmapData.Stride >> 2) + cell.X - world.Map.TopLeft.X) = color.ToArgb();
}
}
bitmap.UnlockBits(bitmapData);
return bitmap;
}
public static Bitmap ShroudBitmap(World world)
{
var map = world.Map;
var size = Util.NextPowerOf2(Math.Max(map.Width, map.Height));
Bitmap bitmap = new Bitmap(size, size);
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
var shroud = Color.Black.ToArgb();
var fog = Color.FromArgb(128, Color.Black).ToArgb();
unsafe
{
int* c = (int*)bitmapData.Scan0;
for (var x = 0; x < map.Width; x++)
for (var y = 0; y < map.Height; y++)
{
var mapX = x + map.TopLeft.X;
var mapY = y + map.TopLeft.Y;
if (!world.LocalPlayer.Shroud.IsExplored(mapX, mapY))
*(c + (y * bitmapData.Stride >> 2) + x) = shroud;
else if (!world.LocalPlayer.Shroud.IsVisible(mapX,mapY))
*(c + (y * bitmapData.Stride >> 2) + x) = fog;
}
}
bitmap.UnlockBits(bitmapData);
return bitmap;
}
public static Bitmap RenderMapPreview(Map map)
{
Bitmap terrain = TerrainBitmap(map);
return AddStaticResources(map, terrain);
}
}
}

View File

@@ -15,15 +15,14 @@ using System.Reflection;
using OpenRA.FileFormats;
using OpenRA.FileFormats.Graphics;
using OpenRA.Support;
using System.Windows.Forms;
namespace OpenRA.Graphics
{
internal class Renderer
public class Renderer
{
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; }
@@ -32,17 +31,14 @@ namespace OpenRA.Graphics
public SpriteRenderer SpriteRenderer { get; private set; }
public SpriteRenderer RgbaSpriteRenderer { get; private set; }
public SpriteRenderer WorldSpriteRenderer { get; private set; }
public LineRenderer LineRenderer { get; private set; }
public ITexture PaletteTexture;
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"));
@@ -51,29 +47,23 @@ namespace OpenRA.Graphics
SpriteRenderer = new SpriteRenderer( this, SpriteShader );
RgbaSpriteRenderer = new SpriteRenderer( this, RgbaSpriteShader );
WorldSpriteRenderer = new SpriteRenderer( this, WorldSpriteShader );
LineRenderer = new LineRenderer(this);
RegularFont = new SpriteFont(this, "FreeSans.ttf", 14);
BoldFont = new SpriteFont(this, "FreeSansBold.ttf", 14);
TitleFont = new SpriteFont(this, "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();
RegularFont = new SpriteFont("FreeSans.ttf", 14);
BoldFont = new SpriteFont("FreeSansBold.ttf", 14);
TitleFont = new SpriteFont("titles.ttf", 48);
}
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 );
@@ -118,5 +108,51 @@ namespace OpenRA.Graphics
PerfHistory.Increment("batches", 1);
}
public void Flush()
{
WorldSpriteRenderer.Flush();
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();
}
}
}

View File

@@ -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 );

View File

@@ -20,50 +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");
foreach (XmlElement eSequence in eCursor.SelectNodes("./sequence"))
cursors.Add(eSequence.GetAttribute("name"), new CursorSequence(cursorSrc, 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]; }
@@ -78,15 +67,5 @@ namespace OpenRA.Graphics
{
return units[unit].ContainsKey(seq);
}
public static CursorSequence GetCursorSequence(string cursor)
{
try { return cursors[cursor]; }
catch (KeyNotFoundException)
{
throw new InvalidOperationException(
"Cursor does not have a sequence `{0}`".F(cursor));
}
}
}
}

View File

@@ -16,21 +16,21 @@ namespace OpenRA.Graphics
{
public class Sheet
{
readonly Renderer renderer;
protected readonly Bitmap bitmap;
Bitmap bitmap;
ITexture texture;
bool dirty;
byte[] data;
public readonly Size Size;
internal Sheet(Renderer renderer, Size size)
public Sheet(Size size)
{
this.renderer = renderer;
this.bitmap = new Bitmap(size.Width, size.Height);
Size = size;
}
internal Sheet(Renderer renderer, string filename)
public Sheet(string filename)
{
this.renderer = renderer;
this.bitmap = (Bitmap)Image.FromStream(FileSystem.Open(filename));
bitmap = (Bitmap)Image.FromStream(FileSystem.Open(filename));
Size = bitmap.Size;
}
public ITexture Texture
@@ -38,28 +38,30 @@ namespace OpenRA.Graphics
get
{
if (texture == null)
texture = 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; }
}
}

View File

@@ -14,15 +14,8 @@ namespace OpenRA.Graphics
{
public class SheetBuilder
{
public static SheetBuilder SharedInstance;
internal static void Initialize(Renderer r)
internal SheetBuilder(TextureChannel ch)
{
SharedInstance = new SheetBuilder(r, TextureChannel.Red);
}
internal SheetBuilder(Renderer r, TextureChannel ch)
{
renderer = r;
current = null;
rowHeight = 0;
channel = null;
@@ -45,9 +38,8 @@ namespace OpenRA.Graphics
return Add(data, size);
}
Sheet NewSheet() { return new Sheet( renderer, new Size( Renderer.SheetSize, Renderer.SheetSize ) ); }
Renderer renderer;
Sheet NewSheet() { return new Sheet(new Size( Renderer.SheetSize, Renderer.SheetSize ) ); }
Sheet current = null;
int rowHeight = 0;
Point p;

View File

@@ -22,7 +22,7 @@ namespace OpenRA.Graphics
readonly float2[] uvhax;
internal Sprite(Sheet sheet, Rectangle bounds, TextureChannel channel)
public Sprite(Sheet sheet, Rectangle bounds, TextureChannel channel)
{
this.bounds = bounds;
this.sheet = sheet;

View File

@@ -17,14 +17,12 @@ using Tao.FreeType;
namespace OpenRA.Graphics
{
class SpriteFont
public class SpriteFont
{
Renderer renderer;
int size;
public SpriteFont(Renderer r, string name, int size)
public SpriteFont(string name, int size)
{
this.renderer = r;
this.size = size;
if (0 != FT.FT_New_Face(library, name, 0, out face))
@@ -34,7 +32,7 @@ namespace OpenRA.Graphics
glyphs = new Cache<Pair<char, Color>, GlyphInfo>(CreateGlyph);
// setup a 1-channel SheetBuilder for our private use
if (builder == null) builder = new SheetBuilder(r, TextureChannel.Alpha);
if (builder == null) builder = new SheetBuilder(TextureChannel.Alpha);
PrecacheColor(Color.White);
PrecacheColor(Color.Red);
@@ -63,15 +61,12 @@ namespace OpenRA.Graphics
}
var g = glyphs[Pair.New(s, c)];
renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite,
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)
@@ -102,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;
}

View File

@@ -12,7 +12,7 @@ using OpenRA.FileFormats.Graphics;
namespace OpenRA.Graphics
{
class SpriteRenderer
public class SpriteRenderer
{
IVertexBuffer<Vertex> vertexBuffer;
IIndexBuffer indexBuffer;
@@ -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);
}
}
}

View File

@@ -13,11 +13,10 @@ using OpenRA.FileFormats;
namespace OpenRA.Graphics
{
static class SpriteSheetBuilder
public static class SpriteSheetBuilder
{
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]; }

View File

@@ -23,19 +23,17 @@ namespace OpenRA.Graphics
Sheet terrainSheet;
World world;
Renderer renderer;
Map map;
public TerrainRenderer(World world, Renderer renderer, WorldRenderer wr)
public TerrainRenderer(World world, WorldRenderer wr)
{
this.world = world;
this.renderer = renderer;
this.map = world.Map;
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];
@@ -58,10 +56,10 @@ namespace OpenRA.Graphics
throw new InvalidOperationException("Terrain sprites span multiple sheets");
}
vertexBuffer = renderer.Device.CreateVertexBuffer( vertices.Length );
vertexBuffer = Game.Renderer.Device.CreateVertexBuffer( vertices.Length );
vertexBuffer.SetData( vertices );
indexBuffer = renderer.Device.CreateIndexBuffer( indices.Length );
indexBuffer = Game.Renderer.Device.CreateIndexBuffer( indices.Length );
indexBuffer.SetData( indices );
}
@@ -93,14 +91,14 @@ namespace OpenRA.Graphics
if( lastRow < firstRow ) lastRow = firstRow;
renderer.SpriteShader.SetValue( "DiffuseTexture", terrainSheet.Texture );
renderer.SpriteShader.Render(() =>
renderer.DrawBatch(vertexBuffer, indexBuffer,
Game.Renderer.SpriteShader.SetValue( "DiffuseTexture", terrainSheet.Texture );
Game.Renderer.SpriteShader.Render(() =>
Game.Renderer.DrawBatch(vertexBuffer, indexBuffer,
new Range<int>(verticesPerRow * firstRow, verticesPerRow * lastRow),
new Range<int>(indicesPerRow * firstRow, indicesPerRow * lastRow),
PrimitiveType.TriangleList, renderer.SpriteShader));
PrimitiveType.TriangleList, Game.Renderer.SpriteShader));
foreach (var r in world.WorldActor.traits.WithInterface<IRenderOverlay>())
foreach (var r in world.WorldActor.TraitsImplementing<IRenderOverlay>())
r.Render();
}
}

View File

@@ -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;
}
}
@@ -112,10 +93,30 @@ namespace OpenRA.Graphics
LerpChannel(t, a.G, b.G),
LerpChannel(t, a.B, b.B));
}
public static int LerpARGBColor(float t, int c1, int c2)
{
int a = LerpChannel(t, (c1 >> 24) & 255, (c2 >> 24) & 255);
int r = LerpChannel(t, (c1 >> 16) & 255, (c2 >> 16) & 255);
int g = LerpChannel(t, (c1 >> 8) & 255, (c2 >> 8) & 255);
int b = LerpChannel(t, c1 & 255, c2 & 255);
return (a << 24) | (r << 16) | (g << 8) | b;
}
public static int LerpChannel(float t, int a, int b)
{
return (int)((1 - t) * a + t * b);
}
public static int NextPowerOf2(int v)
{
--v;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
++v;
return v;
}
}
}

View File

@@ -13,105 +13,131 @@ using System.Drawing;
using System.Linq;
using OpenRA.Support;
using OpenRA.Traits;
using OpenRA.Widgets;
namespace OpenRA.Graphics
{
interface IHandleInput
{
bool HandleInput(World world, MouseInput mi);
}
class Viewport
public class Viewport
{
readonly float2 screenSize;
float2 scrollPosition;
readonly Renderer renderer;
readonly int2 mapStart;
readonly int2 mapEnd;
public float2 Location { get { return scrollPosition; } }
public int Width { get { return (int)screenSize.X; } }
public int Height { get { return (int)screenSize.Y; } }
SpriteRenderer cursorRenderer;
int2 mousePos;
float cursorFrame = 0f;
public static int TicksSinceLastMove = 0;
public static int2 LastMousePos;
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);
public IEnumerable<IHandleInput> regions { get { return new IHandleInput[] { Game.chrome, Game.controller }; } }
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;
cursorRenderer = renderer.SpriteRenderer;
this.mapStart = mapStart;
this.mapEnd = mapEnd;
this.scrollPosition = Game.CellSize* mapStart;
}
public void DrawRegions( World world )
{
Timer.Time( "DrawRegions start" );
world.WorldRenderer.palette.Update(
world.WorldActor.traits.WithInterface<IPaletteModifier>());
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}" );
Game.chrome.Draw(world);
Timer.Time( "widgets: {0}" );
Widget.DoDraw(world);
var cursorName = Game.chrome.HitTest(mousePos) ? "default" : Game.controller.ChooseCursor( world );
var cursorName = Widget.RootWidget.GetCursorOuter(Viewport.LastMousePos) ?? "default";
var c = new Cursor(cursorName);
cursorRenderer.DrawSprite(c.GetSprite((int)cursorFrame), mousePos + Location - c.GetHotspot(), "cursor");
Timer.Time( "cursors: {0}" );
c.Draw((int)cursorFrame, Viewport.LastMousePos + Location);
renderer.RgbaSpriteRenderer.Flush();
renderer.SpriteRenderer.Flush();
renderer.WorldSpriteRenderer.Flush();
renderer.EndFrame();
Timer.Time( "endFrame: {0}" );
}
public void RefreshPalette()
{
Game.world.WorldRenderer.palette.Update(
Game.world.WorldActor.TraitsImplementing<IPaletteModifier>());
}
public void Tick()
{
cursorFrame += 0.5f;
RefreshPalette();
}
IHandleInput dragRegion = null;
public void DispatchMouseInput(World world, MouseInput mi)
public float2 ViewToWorld(int2 loc)
{
if (mi.Event == MouseInputEvent.Move)
mousePos = mi.Location;
if (dragRegion != null) {
dragRegion.HandleInput( world, mi );
if (mi.Event == MouseInputEvent.Up) dragRegion = null;
return;
}
dragRegion = regions.FirstOrDefault(r => r.HandleInput(world, mi));
if (mi.Event != MouseInputEvent.Down)
dragRegion = null;
return (1f / Game.CellSize) * (loc.ToFloat2() + Location);
}
public float2 ViewToWorld(MouseInput mi)
{
return (1 / 24.0f) * (new float2(mi.Location.X, mi.Location.Y) + Location);
return ViewToWorld(mi.Location);
}
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)
@@ -122,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()
@@ -138,4 +159,4 @@ namespace OpenRA.Graphics
return localPlayer.Shroud.Bounds;
}
}
}
}

View File

@@ -21,57 +21,22 @@ namespace OpenRA.Graphics
{
readonly World world;
internal readonly TerrainRenderer terrainRenderer;
internal readonly SpriteRenderer spriteRenderer;
internal readonly LineRenderer lineRenderer;
internal readonly UiOverlay uiOverlay;
internal readonly Renderer renderer;
internal readonly HardwarePalette palette;
internal WorldRenderer(World world, Renderer renderer)
internal WorldRenderer(World world)
{
this.world = world;
this.renderer = renderer;
terrainRenderer = new TerrainRenderer(world, renderer, this);
spriteRenderer = renderer.SpriteRenderer;
lineRenderer = new LineRenderer(renderer);
uiOverlay = new UiOverlay(spriteRenderer);
palette = new HardwarePalette(renderer, world.Map);
terrainRenderer = new TerrainRenderer(world, this);
uiOverlay = new UiOverlay();
palette = new HardwarePalette(world.Map);
}
public void DrawLine(float2 start, float2 end, Color startColor, Color endColor)
{
lineRenderer.DrawLine(start,end,startColor,endColor);
}
public int GetPaletteIndex(string name) { return palette.GetPaletteIndex(name); }
public Palette GetPalette(string name) { return palette.GetPalette(name); }
public void AddPalette(string name, Palette pal) { palette.AddPalette(name, pal); }
public int GetPaletteIndex(string name)
{
return palette.GetPaletteIndex(name);
}
public Palette GetPalette(string name)
{
return palette.GetPalette(name);
}
public void AddPalette(string name, Palette pal)
{
System.Console.WriteLine("Registering Palette "+name);
palette.AddPalette(name, pal);
}
public void UpdatePalette(string name, Palette pal)
{
palette.UpdatePalette(name, pal);
}
void DrawSpriteList(IEnumerable<Renderable> images)
{
foreach (var image in images)
spriteRenderer.DrawSprite(image.Sprite, image.Pos, image.Palette);
}
class SpriteComparer : IComparer<Renderable>
{
public int Compare(Renderable x, Renderable y)
@@ -129,32 +94,32 @@ namespace OpenRA.Graphics
public void Draw()
{
var bounds = GetBoundsRect();
renderer.Device.EnableScissor(bounds.Left, bounds.Top, bounds.Width, bounds.Height);
Game.Renderer.Device.EnableScissor(bounds.Left, bounds.Top, bounds.Width, bounds.Height);
terrainRenderer.Draw(Game.viewport);
DrawSpriteList(worldSprites);
uiOverlay.Draw(world);
spriteRenderer.Flush();
DrawBandBox();
if (world.OrderGenerator != null)
world.OrderGenerator.RenderBeforeWorld(world);
if (Game.controller.orderGenerator != null)
Game.controller.orderGenerator.Render(world);
Game.Renderer.SpriteRenderer.Flush();
Game.Renderer.LineRenderer.Flush();
foreach (var image in worldSprites)
Game.Renderer.SpriteRenderer.DrawSprite(image.Sprite, image.Pos, image.Palette);
uiOverlay.Draw(world);
Game.Renderer.SpriteRenderer.Flush();
if (world.OrderGenerator != null)
world.OrderGenerator.RenderAfterWorld(world);
if (world.LocalPlayer != null)
world.LocalPlayer.Shroud.Draw(spriteRenderer);
world.LocalPlayer.Shroud.Draw();
spriteRenderer.Flush();
Game.Renderer.SpriteRenderer.Flush();
renderer.Device.DisableScissor();
Game.Renderer.Device.DisableScissor();
if (Game.Settings.IndexDebug)
{
bounds.Offset((int)Game.viewport.Location.X, (int)Game.viewport.Location.Y);
DrawBins(bounds);
}
lineRenderer.Flush();
Game.Renderer.LineRenderer.Flush();
}
void DrawBox(RectangleF r, Color color)
@@ -162,10 +127,10 @@ namespace OpenRA.Graphics
var a = new float2(r.Left, r.Top);
var b = new float2(r.Right - a.X, 0);
var c = new float2(0, r.Bottom - a.Y);
lineRenderer.DrawLine(a, a + b, color, color);
lineRenderer.DrawLine(a + b, a + b + c, color, color);
lineRenderer.DrawLine(a + b + c, a + c, color, color);
lineRenderer.DrawLine(a, a + c, color, color);
Game.Renderer.LineRenderer.DrawLine(a, a + b, color, color);
Game.Renderer.LineRenderer.DrawLine(a + b, a + b + c, color, color);
Game.Renderer.LineRenderer.DrawLine(a + b + c, a + c, color, color);
Game.Renderer.LineRenderer.DrawLine(a, a + c, color, color);
}
void DrawBins(RectangleF bounds)
@@ -177,30 +142,12 @@ namespace OpenRA.Graphics
for (var j = 0; j < world.Map.MapSize.Y;
j += world.WorldActor.Info.Traits.Get<SpatialBinsInfo>().BinSize)
{
lineRenderer.DrawLine(new float2(0, j * 24), new float2(world.Map.MapSize.X * 24, j * 24), Color.Black, Color.Black);
lineRenderer.DrawLine(new float2(j * 24, 0), new float2(j * 24, world.Map.MapSize.Y * 24), Color.Black, Color.Black);
Game.Renderer.LineRenderer.DrawLine(new float2(0, j * 24), new float2(world.Map.MapSize.X * 24, j * 24), Color.Black, Color.Black);
Game.Renderer.LineRenderer.DrawLine(new float2(j * 24, 0), new float2(j * 24, world.Map.MapSize.Y * 24), Color.Black, Color.Black);
}
}
void DrawBandBox()
{
var selbox = Game.controller.SelectionBox;
if (selbox == null) return;
var a = selbox.Value.First;
var b = new float2(selbox.Value.Second.X - a.X, 0);
var c = new float2(0, selbox.Value.Second.Y - a.Y);
lineRenderer.DrawLine(a, a + b, Color.White, Color.White);
lineRenderer.DrawLine(a + b, a + b + c, Color.White, Color.White);
lineRenderer.DrawLine(a + b + c, a + c, Color.White, Color.White);
lineRenderer.DrawLine(a, a + c, Color.White, Color.White);
foreach (var u in world.SelectActorsInBox(selbox.Value.First, selbox.Value.Second))
DrawSelectionBox(u, Color.Yellow, false);
}
public void DrawSelectionBox(Actor selectedUnit, Color c, bool drawHealthBar)
public void DrawSelectionBox(Actor selectedUnit, Color c)
{
var bounds = selectedUnit.GetBounds(true);
@@ -209,145 +156,16 @@ namespace OpenRA.Graphics
var xY = new float2(bounds.Left, bounds.Bottom);
var XY = new float2(bounds.Right, bounds.Bottom);
lineRenderer.DrawLine(xy, xy + new float2(4, 0), c, c);
lineRenderer.DrawLine(xy, xy + new float2(0, 4), c, c);
lineRenderer.DrawLine(Xy, Xy + new float2(-4, 0), c, c);
lineRenderer.DrawLine(Xy, Xy + new float2(0, 4), c, c);
Game.Renderer.LineRenderer.DrawLine(xy, xy + new float2(4, 0), c, c);
Game.Renderer.LineRenderer.DrawLine(xy, xy + new float2(0, 4), c, c);
Game.Renderer.LineRenderer.DrawLine(Xy, Xy + new float2(-4, 0), c, c);
Game.Renderer.LineRenderer.DrawLine(Xy, Xy + new float2(0, 4), c, c);
lineRenderer.DrawLine(xY, xY + new float2(4, 0), c, c);
lineRenderer.DrawLine(xY, xY + new float2(0, -4), c, c);
lineRenderer.DrawLine(XY, XY + new float2(-4, 0), c, c);
lineRenderer.DrawLine(XY, XY + new float2(0, -4), c, c);
if (drawHealthBar)
{
DrawHealthBar(selectedUnit, xy, Xy);
DrawControlGroup(selectedUnit, xy);
// Only display pips and tags to the owner
if (selectedUnit.Owner == world.LocalPlayer)
{
DrawPips(selectedUnit, xY);
DrawTags(selectedUnit, new float2(.5f * (bounds.Left + bounds.Right ), xy.Y));
Game.Renderer.LineRenderer.DrawLine(xY, xY + new float2(4, 0), c, c);
Game.Renderer.LineRenderer.DrawLine(xY, xY + new float2(0, -4), c, c);
Game.Renderer.LineRenderer.DrawLine(XY, XY + new float2(-4, 0), c, c);
Game.Renderer.LineRenderer.DrawLine(XY, XY + new float2(0, -4), c, c);
}
}
if (Game.Settings.PathDebug)
DrawUnitPath(selectedUnit);
}
void DrawUnitPath(Actor selectedUnit)
{
var mobile = selectedUnit.traits.WithInterface<IMove>().FirstOrDefault();
if (mobile != null)
{
var path = mobile.GetCurrentPath(selectedUnit);
var start = selectedUnit.CenterLocation;
var c = Color.Green;
foreach (var step in path)
{
lineRenderer.DrawLine(step + new float2(-1, -1), step + new float2(-1, 1), c, c);
lineRenderer.DrawLine(step + new float2(-1, 1), step + new float2(1, 1), c, c);
lineRenderer.DrawLine(step + new float2(1, 1), step + new float2(1, -1), c, c);
lineRenderer.DrawLine(step + new float2(1, -1), step + new float2(-1, -1), c, c);
lineRenderer.DrawLine(start, step, c, c);
start = step;
}
}
}
void DrawHealthBar(Actor selectedUnit, float2 xy, float2 Xy)
{
var c = Color.Gray;
lineRenderer.DrawLine(xy + new float2(0, -2), xy + new float2(0, -4), c, c);
lineRenderer.DrawLine(Xy + new float2(0, -2), Xy + new float2(0, -4), c, c);
var healthAmount = (float)selectedUnit.Health / selectedUnit.Info.Traits.Get<OwnedActorInfo>().HP;
var healthColor = (healthAmount < selectedUnit.World.Defaults.ConditionRed) ? Color.Red
: (healthAmount < selectedUnit.World.Defaults.ConditionYellow) ? Color.Yellow
: Color.LimeGreen;
var healthColor2 = Color.FromArgb(
255,
healthColor.R / 2,
healthColor.G / 2,
healthColor.B / 2);
var z = float2.Lerp(xy, Xy, healthAmount);
lineRenderer.DrawLine(z + new float2(0, -4), Xy + new float2(0, -4), c, c);
lineRenderer.DrawLine(z + new float2(0, -2), Xy + new float2(0, -2), c, c);
lineRenderer.DrawLine(xy + new float2(0, -3), z + new float2(0, -3), healthColor, healthColor);
lineRenderer.DrawLine(xy + new float2(0, -2), z + new float2(0, -2), healthColor2, healthColor2);
lineRenderer.DrawLine(xy + new float2(0, -4), z + new float2(0, -4), healthColor2, healthColor2);
}
// depends on the order of pips in TraitsInterfaces.cs!
static readonly string[] pipStrings = { "pip-empty", "pip-green", "pip-yellow", "pip-red", "pip-gray" };
static readonly string[] tagStrings = { "", "tag-fake", "tag-primary" };
void DrawControlGroup(Actor selectedUnit, float2 basePosition)
{
var group = Game.controller.selection.GetControlGroupForActor(selectedUnit);
if (group == null) return;
var pipImages = new Animation("pips");
pipImages.PlayFetchIndex("groups", () => (int)group);
pipImages.Tick();
spriteRenderer.DrawSprite(pipImages.Image, basePosition + new float2(-8, 1), "chrome");
}
void DrawPips(Actor selectedUnit, float2 basePosition)
{
// If a mod wants to implement a unit with multiple pip sources, then they are placed on multiple rows
var pipxyBase = basePosition + new float2(-12, -7); // Correct for the offset in the shp file
var pipxyOffset = new float2(0, 0); // Correct for offset due to multiple columns/rows
foreach (var pips in selectedUnit.traits.WithInterface<IPips>())
{
foreach (var pip in pips.GetPips(selectedUnit))
{
if (pipxyOffset.X+5 > selectedUnit.GetBounds(false).Width)
{
pipxyOffset.X = 0;
pipxyOffset.Y -= 4;
}
var pipImages = new Animation("pips");
pipImages.PlayRepeating(pipStrings[(int)pip]);
spriteRenderer.DrawSprite(pipImages.Image, pipxyBase + pipxyOffset, "chrome");
pipxyOffset += new float2(4, 0);
}
// Increment row
pipxyOffset.X = 0;
pipxyOffset.Y -= 5;
}
}
void DrawTags(Actor selectedUnit, float2 basePosition)
{
// If a mod wants to implement a unit with multiple tags, then they are placed on multiple rows
var tagxyBase = basePosition + new float2(-16, 2); // Correct for the offset in the shp file
var tagxyOffset = new float2(0, 0); // Correct for offset due to multiple rows
foreach (var tags in selectedUnit.traits.WithInterface<ITags>())
{
foreach (var tag in tags.GetTags())
{
if (tag == TagType.None)
continue;
var tagImages = new Animation("pips");
tagImages.PlayRepeating(tagStrings[(int)tag]);
spriteRenderer.DrawSprite(tagImages.Image, tagxyBase + tagxyOffset, "chrome");
// Increment row
tagxyOffset.Y += 8;
}
}
}
public void DrawLocus(Color c, int2[] cells)
{
@@ -355,16 +173,16 @@ namespace OpenRA.Graphics
foreach (var t in dict.Keys)
{
if (!dict.ContainsKey(t + new int2(-1, 0)))
lineRenderer.DrawLine(Game.CellSize * t, Game.CellSize * (t + new int2(0, 1)),
Game.Renderer.LineRenderer.DrawLine(Game.CellSize * t, Game.CellSize * (t + new int2(0, 1)),
c, c);
if (!dict.ContainsKey(t + new int2(1, 0)))
lineRenderer.DrawLine(Game.CellSize * (t + new int2(1, 0)), Game.CellSize * (t + new int2(1, 1)),
Game.Renderer.LineRenderer.DrawLine(Game.CellSize * (t + new int2(1, 0)), Game.CellSize * (t + new int2(1, 1)),
c, c);
if (!dict.ContainsKey(t + new int2(0, -1)))
lineRenderer.DrawLine(Game.CellSize * t, Game.CellSize * (t + new int2(1, 0)),
Game.Renderer.LineRenderer.DrawLine(Game.CellSize * t, Game.CellSize * (t + new int2(1, 0)),
c, c);
if (!dict.ContainsKey(t + new int2(0, 1)))
lineRenderer.DrawLine(Game.CellSize * (t + new int2(0, 1)), Game.CellSize * (t + new int2(1, 1)),
Game.Renderer.LineRenderer.DrawLine(Game.CellSize * (t + new int2(0, 1)), Game.CellSize * (t + new int2(1, 1)),
c, c);
}
}
@@ -374,8 +192,8 @@ namespace OpenRA.Graphics
var prev = location + Game.CellSize * range * float2.FromAngle(0);
for (var i = 1; i <= 32; i++)
{
var pos = location + Game.CellSize * range * float2.FromAngle((float)(Math.PI * i) / 8);
lineRenderer.DrawLine(prev, pos, c, c);
var pos = location + Game.CellSize * range * float2.FromAngle((float)(Math.PI * i) / 16);
Game.Renderer.LineRenderer.DrawLine(prev, pos, c, c);
prev = pos;
}
}

View File

@@ -41,8 +41,10 @@ namespace OpenRA
Ctrl = (int)Keys.Control,
}
public enum KeyInputEvent { Down, Up };
public struct KeyInput
{
public KeyInputEvent Event;
public char KeyChar;
public string KeyName;
public Modifiers Modifiers;

203
OpenRA.FileFormats/Map/Map.cs → OpenRA.Game/Map.cs Normal file → Executable file
View File

@@ -15,8 +15,9 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using OpenRA.FileFormats;
namespace OpenRA.FileFormats
namespace OpenRA
{
public class Map
{
@@ -24,13 +25,13 @@ namespace OpenRA.FileFormats
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>();
@@ -38,22 +39,18 @@ namespace OpenRA.FileFormats
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;
public string [,] CustomTerrain;
// Temporary compat hacks
public int XOffset { get { return TopLeft.X; } }
@@ -64,10 +61,6 @@ namespace OpenRA.FileFormats
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);
@@ -87,58 +80,107 @@ namespace OpenRA.FileFormats
Author = "Your name here";
}
class Format2ActorReference
{
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])));
}
// Players
if (MapFormat == 1)
{
Players.Add("Neutral", new PlayerReference("Neutral", "allies", true, true));
}
else
{
foreach (var kv in yaml["Players"].Nodes)
{
var player = new PlayerReference(kv.Value);
Players.Add(player.Name, player);
}
}
// Actors
if (MapFormat == 1)
{
int actors = 0;
foreach (var kv in yaml["Actors"].Nodes)
{
string[] vals = kv.Value.Value.Split(' ');
string[] loc = vals[2].Split(',');
var a = new ActorReference("Actor"+actors++, vals[0], new int2(int.Parse(loc[0]), int.Parse(loc[1])), "Neutral");
Actors.Add(a.Id, a);
}
}
else
{
foreach (var kv in yaml["Actors"].Nodes)
{
var a = new ActorReference(kv.Value);
Actors.Add(a.Id, a);
}
}
// Players & Actors -- this has changed several times.
// - Be backwards compatible wherever possible.
// - Loading a map then saving it out upgrades to latest.
// Minimum criteria for dropping a format:
// - There are no maps of this format left in tree
switch (MapFormat)
{
case 1:
{
Players.Add("Neutral", new PlayerReference("Neutral", "allies", true, true));
int actors = 0;
foreach (var kv in yaml.NodesDict["Actors"].NodesDict)
{
string[] vals = kv.Value.Value.Split(' ');
string[] loc = vals[2].Split(',');
Actors.Add("Actor" + actors++, new ActorReference(vals[0])
{
new LocationInit( new int2( int.Parse( loc[ 0 ] ), int.Parse( loc[ 1 ] ) ) ),
new OwnerInit( "Neutral" ),
});
}
} break;
case 2:
{
foreach (var kv in yaml.NodesDict["Players"].NodesDict)
{
var player = new PlayerReference(kv.Value);
Players.Add(player.Name, player);
}
foreach (var kv in yaml.NodesDict["Actors"].NodesDict)
{
var oldActorReference = FieldLoader.Load<Format2ActorReference>(kv.Value);
Actors.Add(oldActorReference.Id, new ActorReference(oldActorReference.Type)
{
new LocationInit( oldActorReference.Location ),
new OwnerInit( oldActorReference.Owner )
});
}
} break;
case 3:
{
foreach (var kv in yaml.NodesDict["Players"].NodesDict)
{
var player = new PlayerReference(kv.Value);
Players.Add(player.Name, player);
}
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(',');
@@ -146,37 +188,38 @@ namespace OpenRA.FileFormats
}
// Rules
Rules = yaml["Rules"].Nodes;
Rules = yaml.NodesDict["Rules"].Nodes;
CustomTerrain = new string[MapSize.X, MapSize.Y];
LoadUid();
LoadBinaryData();
}
public void Save(string filepath)
{
MapFormat = 2;
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(
a => "ActorReference@{0}".F(a.Key),
a => FieldSaver.Save(a.Value))));
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( "Actors", null,
Actors.Select( x => new MiniYamlNode(
x.Key,
x.Value.Save() ) ).ToList() ) );
root.Add( new MiniYamlNode( "Waypoints", MiniYaml.FromDictionary<string, int2>( Waypoints ) ) );
root.Add( new MiniYamlNode( "Smudges", MiniYaml.FromList<SmudgeReference>( Smudges ) ) );
root.Add( new MiniYamlNode( "Rules", null, Rules ) );
SaveBinaryData(Path.Combine(filepath, "map.bin"));
root.WriteToFile(Path.Combine(filepath, "map.yaml"));
@@ -272,8 +315,8 @@ namespace OpenRA.FileFormats
{
// UID is calculated by taking an SHA1 of the yaml and binary data
// Read the relevant data into a buffer
var data = Exts.ReadAllBytes(Package.GetContent("map.yaml"))
.Concat(Exts.ReadAllBytes(Package.GetContent("map.bin"))).ToArray();
var data = Package.GetContent("map.yaml").ReadAllBytes()
.Concat(Package.GetContent("map.bin").ReadAllBytes()).ToArray();
// Take the SHA1
using (var csp = SHA1.Create())

77
OpenRA.Game/ModData.cs Executable file
View 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(); }
}

View File

@@ -14,6 +14,7 @@ using System.IO;
using System.Net.Sockets;
using System.Threading;
using OpenRA.Server;
using OpenRA.Support;
namespace OpenRA.Network
{
@@ -74,15 +75,16 @@ namespace OpenRA.Network
}
}
class NetworkConnection : EchoConnection
class NetworkConnection : EchoConnection, IDisposable
{
TcpClient socket;
int clientId;
ConnectionState connectionState = ConnectionState.Connecting;
Thread t;
public NetworkConnection( string host, int port )
{
new Thread( _ =>
t = new Thread( _ =>
{
try
{
@@ -114,8 +116,11 @@ namespace OpenRA.Network
{
connectionState = ConnectionState.NotConnected;
}
catch ( IOException ) { socket.Close(); }
catch (ThreadAbortException ) { socket.Close(); }
}
) { IsBackground = true }.Start();
) { IsBackground = true };
t.Start();
}
public override int LocalClientId { get { return clientId; } }
@@ -135,7 +140,25 @@ 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;
disposed = true;
GC.SuppressFinalize( this );
t.Abort();
if (socket != null)
socket.Client.Close();
using( new PerfSample( "Thread.Join" ))
t.Join();
}
~NetworkConnection() { Dispose(); }
}
class ReplayConnection : IConnection

View File

@@ -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);
}
}
}

View File

@@ -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 );

View 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;
}
}
}

View 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);
}
}
}

View File

@@ -16,37 +16,64 @@ 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)
Game.AddChatLine(client.Color1,
client.Name, order.TargetString);
{
var player = FindPlayerByClientId(clientId);
if (player != null && player.WinState == WinState.Lost)
Game.AddChatLine(client.Color1, client.Name + " (Dead)", order.TargetString);
else
Game.AddChatLine(client.Color1, client.Name, order.TargetString);
}
break;
}
case "TeamChat":
{
var client = Game.LobbyInfo.Clients.FirstOrDefault(c => c.Index == clientId);
var client = FindClientById(clientId);
if (client != null)
{
var player = Game.world.players.Values.FirstOrDefault(p => p.Index == client.Index);
var isAlly = (world.GameHasStarted) ?
player != null && Game.world.LocalPlayer != null && player.Stances[Game.world.LocalPlayer] == Stance.Ally :
var player = FindPlayerByClientId(clientId);
var display = (world.GameHasStarted) ?
player != null && (world.LocalPlayer != null && player.Stances[world.LocalPlayer] == Stance.Ally
|| player.WinState == WinState.Lost) :
client == Game.LocalClient || (client.Team == Game.LocalClient.Team && client.Team != 0);
if (isAlly)
Game.AddChatLine(client.Color1, client.Name + " (Team)", order.TargetString);
if (display)
{
var suffix = (player != null && player.WinState == WinState.Lost) ? " (Dead)" : " (Team)";
Game.AddChatLine(client.Color1, client.Name + suffix, order.TargetString);
}
}
break;
}
case "StartGame":
{
Game.AddChatLine(Color.White, "Server", "The game has started.");
Game.StartGame();
Game.StartGame(Game.LobbyInfo.GlobalSettings.Map);
break;
}
case "SyncInfo":
@@ -57,15 +84,20 @@ namespace OpenRA.Network
case "SetStance":
{
var targetPlayer = order.Player.World.players[order.TargetLocation.X];
var oldStance = order.Player.Stances[targetPlayer];
order.Player.Stances[targetPlayer] = (Stance)order.TargetLocation.Y;
if (targetPlayer == world.LocalPlayer)
world.WorldActor.Trait<Shroud>().UpdatePlayerStance(world, order.Player, oldStance, order.Player.Stances[targetPlayer]);
Game.Debug("{0} has set diplomatic stance vs {1} to {2}".F(
order.Player.PlayerName, targetPlayer.PlayerName, (Stance)order.TargetLocation.Y));
order.Player.PlayerName, targetPlayer.PlayerName, order.Player.Stances[targetPlayer]));
break;
}
default:
{
if( !order.IsImmediate )
foreach (var t in order.Subject.traits.WithInterface<IResolveOrder>())
foreach (var t in order.Subject.TraitsImplementing<IResolveOrder>())
t.ResolveOrder(order.Subject, order);
break;
}

48
OpenRA.Game/ObjectCreator.cs Executable file
View 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);
}
}
}

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -75,31 +75,24 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Chrome.cs" />
<Compile Include="Effects\RepairIndicator.cs" />
<Compile Include="GameRules\WeaponInfo.cs" />
<Compile Include="Group.cs" />
<Compile Include="Orders\GenericSelectTarget.cs" />
<Compile Include="Server\ProtocolVersion.cs" />
<Compile Include="Traits\BaseBuilding.cs" />
<Compile Include="Traits\DetectCloaked.cs" />
<Compile Include="Traits\LintAttributes.cs" />
<Compile Include="Traits\Modifiers\FrozenUnderFog.cs" />
<Compile Include="Traits\Player\PlayerResources.cs" />
<Compile Include="Traits\Player\TechTreeCache.cs" />
<Compile Include="Traits\Modifiers\HiddenUnderFog.cs" />
<Compile Include="Traits\World\Shroud.cs" />
<Compile Include="Widgets\ChatEntryWidget.cs" />
<Compile Include="Widgets\Delegates\ConnectionDialogsDelegate.cs" />
<Compile Include="Widgets\Delegates\CreateServerMenuDelegate.cs" />
<Compile Include="Widgets\Delegates\DiplomacyDelegate.cs" />
<Compile Include="Widgets\Delegates\MainMenuButtonsDelegate.cs" />
<Compile Include="Widgets\RadarBinWidget.cs" />
<Compile Include="Widgets\Delegates\ServerBrowserDelegate.cs" />
<Compile Include="Widgets\Delegates\SettingsMenuDelegate.cs" />
<Compile Include="Widgets\MoneyBinWidget.cs" />
<Compile Include="Widgets\MapPreviewWidget.cs" />
<Compile Include="Widgets\PostGameWidget.cs" />
<Compile Include="Widgets\WidgetUtils.cs" />
<Compile Include="Effects\DelayedAction.cs" />
<Compile Include="Effects\FlashTarget.cs" />
@@ -107,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" />
@@ -130,10 +122,8 @@
<Compile Include="Sync.cs" />
<Compile Include="Traits\CustomSellValue.cs" />
<Compile Include="Traits\World\SpatialBins.cs" />
<Compile Include="Traits\World\ChoosePaletteOnSelect.cs" />
<Compile Include="Traits\World\Country.cs" />
<Compile Include="Actor.cs" />
<Compile Include="Controller.cs" />
<Compile Include="Cursor.cs" />
<Compile Include="GameRules\Footprint.cs" />
<Compile Include="GameRules\Rules.cs" />
@@ -161,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" />
@@ -171,7 +160,6 @@
<Compile Include="Traits\Buildable.cs" />
<Compile Include="Traits\Building.cs" />
<Compile Include="Traits\World\BuildingInfluence.cs" />
<Compile Include="Traits\LimitedAmmo.cs" />
<Compile Include="Traits\Player\PlaceBuilding.cs" />
<Compile Include="Traits\World\PlayerColorPalette.cs" />
<Compile Include="Traits\World\ResourceLayer.cs" />
@@ -184,10 +172,8 @@
<Compile Include="Traits\Production.cs" />
<Compile Include="Traits\RallyPoint.cs" />
<Compile Include="Traits\Render\RenderSimple.cs" />
<Compile Include="Traits\Cloak.cs" />
<Compile Include="Traits\TraitsInterfaces.cs" />
<Compile Include="Traits\Turreted.cs" />
<Compile Include="Traits\Unit.cs" />
<Compile Include="Traits\World\UnitInfluence.cs" />
<Compile Include="Network\UnitOrders.cs" />
<Compile Include="Traits\Util.cs" />
@@ -207,23 +193,16 @@
<Compile Include="Widgets\BackgroundWidget.cs" />
<Compile Include="Widgets\LabelWidget.cs" />
<Compile Include="Widgets\CheckboxWidget.cs" />
<Compile Include="Traits\World\GlobalDefaults.cs" />
<Compile Include="Traits\World\BibLayer.cs" />
<Compile Include="Traits\World\SmudgeLayer.cs" />
<Compile Include="Widgets\Delegates\IngameChromeDelegate.cs" />
<Compile Include="Widgets\SpecialPowerBinWidget.cs" />
<Compile Include="Widgets\Delegates\MusicPlayerDelegate.cs" />
<Compile Include="Widgets\PerfGraphWidget.cs" />
<Compile Include="Widgets\Delegates\PerfDebugDelegate.cs" />
<Compile Include="Widgets\BuildPaletteWidget.cs" />
<Compile Include="Widgets\Delegates\LobbyDelegate.cs" />
<Compile Include="Widgets\ColorBlockWidget.cs" />
<Compile Include="GameRules\MusicInfo.cs" />
<Compile Include="Widgets\PowerBinWidget.cs" />
<Compile Include="Widgets\ImageWidget.cs" />
<Compile Include="Traits\SharesCell.cs" />
<Compile Include="Traits\World\AircraftInfluence.cs" />
<Compile Include="Traits\World\HazardLayer.cs" />
<Compile Include="Widgets\TextFieldWidget.cs" />
<Compile Include="Widgets\ChatDisplayWidget.cs" />
<Compile Include="Widgets\Delegates\MapChooserDelegate.cs" />
@@ -231,12 +210,39 @@
<Compile Include="Widgets\SliderWidget.cs" />
<Compile Include="Widgets\TimerWidget.cs" />
<Compile Include="Widgets\ShpImageWidget.cs" />
<Compile Include="Traits\DrawLineToTarget.cs" />
<Compile Include="Widgets\WorldInteractionControllerWidget.cs" />
<Compile Include="Widgets\ViewportScrollControllerWidget.cs" />
<Compile Include="Traits\Player\DeveloperMode.cs" />
<Compile Include="Traits\RevealsShroud.cs" />
<Compile Include="Traits\Targetable.cs" />
<Compile Include="Traits\Health.cs" />
<Compile Include="Traits\RepairableBuilding.cs" />
<Compile Include="Traits\Activities\Drag.cs" />
<Compile Include="Widgets\VqaPlayerWidget.cs" />
<Compile Include="Widgets\Delegates\VideoPlayerDelegate.cs" />
<Compile Include="Traits\MPStartLocations.cs" />
<Compile Include="GameRules\Settings.cs" />
<Compile Include="Support\Arguments.cs" />
<Compile Include="Traits\ActorStance.cs" />
<Compile Include="Traits\Armor.cs" />
<Compile Include="Graphics\CursorProvider.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
<Project>{BDAEAB25-991E-46A7-AF1E-4F0E03358DAA}</Project>
<Name>OpenRA.FileFormats</Name>
</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>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Framework.2.0">

View File

@@ -29,7 +29,7 @@ namespace OpenRA.Orders
public IEnumerable<Order> Order(World world, int2 xy, MouseInput mi)
{
if (mi.Button == MouseButton.Right)
Game.controller.CancelInputMode();
world.CancelInputMode();
return OrderInner(world, xy, mi);
}
@@ -40,7 +40,8 @@ namespace OpenRA.Orders
}
public virtual void Tick(World world) { }
public void Render(World world) { }
public void RenderAfterWorld(World world) { }
public void RenderBeforeWorld(World world) { }
public string GetCursor(World world, int2 xy, MouseInput mi) { return world.Map.IsInMap(xy) ? cursor : "generic-blocked"; }
}
@@ -59,7 +60,7 @@ namespace OpenRA.Orders
.Any();
if (!hasStructure)
Game.controller.CancelInputMode();
world.CancelInputMode();
}
}

View File

@@ -14,9 +14,10 @@ namespace OpenRA
{
public interface IOrderGenerator
{
IEnumerable<Order> Order( World world, int2 xy, MouseInput mi );
void Tick( World world );
void Render( World world );
string GetCursor( World world, int2 xy, MouseInput mi );
IEnumerable<Order> Order(World world, int2 xy, MouseInput mi);
void Tick(World world);
void RenderBeforeWorld(World world);
void RenderAfterWorld(World world);
string GetCursor(World world, int2 xy, MouseInput mi);
}
}

View File

@@ -9,12 +9,13 @@
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.GameRules;
using OpenRA.Traits;
namespace OpenRA.Orders
{
class PlaceBuildingOrderGenerator : IOrderGenerator
public class PlaceBuildingOrderGenerator : IOrderGenerator
{
readonly Actor Producer;
readonly string Building;
@@ -29,7 +30,7 @@ namespace OpenRA.Orders
public IEnumerable<Order> Order(World world, int2 xy, MouseInput mi)
{
if (mi.Button == MouseButton.Right)
Game.controller.CancelInputMode();
world.CancelInputMode();
return InnerOrder(world, xy, mi);
}
@@ -56,14 +57,22 @@ namespace OpenRA.Orders
public void Tick( World world )
{
var producing = Producer.traits.Get<Traits.ProductionQueue>().CurrentItem( Rules.Info[ Building ].Category );
if (producing == null || producing.Item != Building || producing.RemainingTime != 0)
Game.controller.CancelInputMode();
// Find the queue with the target actor
var queue = Producer.TraitsImplementing<ProductionQueue>()
.Where(p => p.CurrentItem() != null &&
p.CurrentItem().Item == Building &&
p.CurrentItem().RemainingTime == 0)
.FirstOrDefault();
if (queue == null)
world.CancelInputMode();
}
public void Render( World world )
public void RenderAfterWorld( World world ) {}
public void RenderBeforeWorld(World world)
{
world.WorldRenderer.uiOverlay.DrawBuildingGrid( world, Building, BuildingInfo );
world.WorldRenderer.uiOverlay.DrawBuildingGrid(world, Building, BuildingInfo);
}
public string GetCursor(World world, int2 xy, MouseInput mi) { return "default"; }

View File

@@ -1,78 +1,71 @@
#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
#region Copyright & License Information
/*
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see LICENSE.
*/
#endregion
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Orders
{
class UnitOrderGenerator : IOrderGenerator
{
public IEnumerable<Order> Order( World world, int2 xy, MouseInput mi )
{
var orders = world.Selection.Actors
.Select(a => a.Order(xy, mi))
.Where(o => o != null)
.ToArray();
var actorsInvolved = orders.Select(o => o.Subject).Distinct();
if (actorsInvolved.Any())
yield return new Order("CreateGroup", actorsInvolved.First().Owner.PlayerActor,
string.Join(",", actorsInvolved.Select(a => a.ActorID.ToString()).ToArray()));
foreach (var o in orders)
yield return o;
}
public void Tick( World world ) {}
public void RenderBeforeWorld(World world)
{
foreach (var a in world.Selection.Actors)
if (!a.Destroyed)
foreach (var t in a.TraitsImplementing<IPreRenderSelection>())
t.RenderBeforeWorld(a);
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Orders
{
class UnitOrderGenerator : IOrderGenerator
{
public IEnumerable<Order> Order( World world, int2 xy, MouseInput mi )
{
var orders = Game.controller.selection.Actors
.Select(a => a.Order(xy, mi))
.Where(o => o != null)
.ToArray();
var actorsInvolved = orders.Select(o => o.Subject).Distinct();
if (actorsInvolved.Any())
yield return new Order("CreateGroup", actorsInvolved.First().Owner.PlayerActor,
string.Join(",", actorsInvolved.Select(a => a.ActorID.ToString()).ToArray()));
foreach (var o in orders)
yield return o;
}
public void Tick( World world ) {}
public void Render( World world )
{
foreach (var a in Game.controller.selection.Actors)
{
world.WorldRenderer.DrawSelectionBox(a, Color.White, true);
if (a.Owner == world.LocalPlayer)
{
//if (a.traits.Contains<RenderRangeCircle>())
// world.WorldRenderer.DrawRangeCircle(Color.FromArgb(128, Color.Yellow),
// a.CenterLocation, (int)a.GetPrimaryWeapon().Range);
if (a.traits.Contains<DetectCloaked>())
world.WorldRenderer.DrawRangeCircle(Color.FromArgb(128, Color.LimeGreen),
a.CenterLocation, a.Info.Traits.Get<DetectCloakedInfo>().Range);
}
}
}
public string GetCursor( World world, int2 xy, MouseInput mi )
{
return ChooseCursor(world, mi);
}
string ChooseCursor(World world, MouseInput mi)
{
//using (new PerfSample("cursor"))
{
var p = Game.controller.MousePosition;
var c = Order(world, p.ToInt2(), mi)
.Select(o => o.Subject.traits.WithInterface<IProvideCursor>()
.Select(pc => pc.CursorForOrderString(o.OrderString, o.Subject, o.TargetLocation)).FirstOrDefault(a => a != null))
.FirstOrDefault(a => a != null);
return c ??
(world.SelectActorsInBox(Game.CellSize * p,
Game.CellSize * p).Any()
? "select" : "default");
}
}
}
}
Game.Renderer.Flush();
}
public void RenderAfterWorld( World world )
{
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 )
{
var c = Order(world, xy, mi)
.Select(o => o.Subject.TraitsImplementing<IOrderCursor>()
.Select(pc => pc.CursorForOrder(o.Subject, o)).FirstOrDefault(a => a != null))
.FirstOrDefault(a => a != null);
return c ??
(world.FindUnitsAtMouse(mi.Location)
.Any(a => a.Info.Traits.Contains<SelectableInfo>())
? "select" : "default");
}
}
}

View File

@@ -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.traits.Get<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();
@@ -86,7 +88,7 @@ namespace OpenRA
return q =>
p != q &&
((p - q).LengthSquared < dist * dist) &&
(world.WorldActor.traits.Get<UnitInfluence>().GetUnitsAt(q).Any(a => a.Group != self.Group));
(world.WorldActor.Trait<UnitInfluence>().GetUnitsAt(q).Any(a => a.Group != self.Group));
}
public List<int2> FindPath( PathSearch search )

View File

@@ -24,14 +24,19 @@ namespace OpenRA
Func<int2, bool> customBlock;
public bool checkForBlocked;
public Actor ignoreBuilding;
Actor self;
public bool inReverse;
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();
this.mobileInfo = mobileInfo;
queue = new PriorityQueue<PathDistance>();
}
@@ -59,21 +64,32 @@ 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 )
{
var p = queue.Pop();
cellInfo[ p.Location.X, p.Location.Y ].Seen = true;
while (cellInfo[p.Location.X, p.Location.Y].Seen)
if (queue.Empty)
return p.Location;
else
p = queue.Pop();
cellInfo[p.Location.X, p.Location.Y].Seen = true;
var mobile = self.traits.Get<Mobile>();
var thisCost = mobile.MovementCostForCell(self, p.Location);
var thisCost = Mobile.MovementCostForCell(mobileInfo, world, p.Location);
if (thisCost == float.PositiveInfinity)
return p.Location;
@@ -86,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 (checkForBlocked && !mobile.CanEnterCell(newHere, ignoreBuilding, checkForBlocked))
if (!Mobile.CanEnterCell(mobileInfo, world, uim, bim, newHere, ignoreBuilding, checkForBlocked))
continue;
if (customBlock != null && customBlock(newHere))
@@ -147,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;
}

View File

@@ -8,21 +8,24 @@
*/
#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
{
public enum PowerState { Normal, Low, Critical };
public enum WinState { Won, Lost, Undefined };
public class Player
{
public Actor PlayerActor;
public int Kills;
public int Deaths;
public int Deaths;
public WinState WinState = WinState.Undefined;
public readonly string Palette;
public readonly Color Color;
@@ -32,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; }
@@ -42,37 +47,44 @@ namespace OpenRA
World = world;
Shroud = new ShroudRenderer(this, world.Map);
PlayerActor = world.CreateActor("Player", new int2(int.MaxValue, int.MaxValue), this);
PlayerActor = world.CreateActor("Player", new TypeDictionary{ new OwnerInit( this ) });
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 int2(int.MaxValue, int.MaxValue), this);
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);
}
@@ -87,8 +99,6 @@ namespace OpenRA
public void GiveAdvice(string advice)
{
// todo: store the condition or something.
// repeat after World.Defaults.SpeakDelay, as long as the condition holds.
Sound.PlayToPlayer(this, advice);
}

View File

@@ -18,6 +18,17 @@ namespace OpenRA
public class Selection
{
List<Actor> actors = new List<Actor>();
public void Add(World w, Actor a)
{
actors.Add(a);
foreach (var ns in w.WorldActor.TraitsImplementing<INotifySelection>())
ns.SelectionChanged();
}
public bool Contains(Actor a)
{
return actors.AsEnumerable().Contains(a);
}
public void Combine(World world, IEnumerable<Actor> newSelection, bool isCombine, bool isClick)
{
@@ -32,9 +43,10 @@ namespace OpenRA
actors = (isCombine ? oldSelection.Union(newSelection) : newSelection).ToList();
var voicedUnit = actors.FirstOrDefault(a => a.Owner == world.LocalPlayer && a.HasVoice());
Sound.PlayVoice("Select", voicedUnit);
if (voicedUnit != null)
Sound.PlayVoice("Select", voicedUnit, voicedUnit.Owner.Country.Race);
foreach (var ns in world.WorldActor.traits.WithInterface<INotifySelection>())
foreach (var ns in world.WorldActor.TraitsImplementing<INotifySelection>())
ns.SelectionChanged();
}

View File

@@ -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;
}
}

View File

@@ -11,27 +11,27 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
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
{
static class Server
{
static List<Connection> conns = new List<Connection>();
static TcpListener listener;
static TcpListener listener = null;
static Dictionary<int, List<Connection>> inFlightFrames
= new Dictionary<int, List<Connection>>();
static Session lobbyInfo;
static bool GameStarted = false;
static string[] initialMods;
static string Name;
static WebClient wc = new WebClient();
static int ExternalPort;
static int randomSeed;
@@ -44,28 +44,35 @@ 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)
public static void ServerMain(ModData modData, Settings settings, string map)
{
Log.AddChannel("server", "server.log");
isInitialPing = true;
Server.masterServerUrl = masterServerUrl;
isInternetServer = internetServer;
listener = new TcpListener(IPAddress.Any, port);
initialMods = mods;
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 = settings.Server.AllowCheats;
Console.WriteLine("Initial mods: ");
LoadMap(); // populates the Session's slots, too.
Log.Write("server", "Initial mods: ");
foreach( var m in lobbyInfo.GlobalSettings.Mods )
Console.WriteLine("- {0}", m);
Log.Write("server","- {0}", m);
Console.WriteLine("Initial map: {0}",lobbyInfo.GlobalSettings.Map);
Log.Write("server", "Initial map: {0}",lobbyInfo.GlobalSettings.Map);
try
{
@@ -84,7 +91,7 @@ namespace OpenRA.Server
checkRead.Add( listener.Server );
foreach( var c in conns ) checkRead.Add( c.socket );
Socket.Select( checkRead, null, null, MasterPingInterval * 1000000 );
Socket.Select( checkRead, null, null, MasterPingInterval * 10000 );
foreach( Socket s in checkRead )
if( s == listener.Server ) AcceptConnection();
@@ -92,10 +99,60 @@ namespace OpenRA.Server
if (Environment.TickCount - lastPing > MasterPingInterval * 1000)
PingMasterServer();
else
lock (masterServerMessages)
while (masterServerMessages.Count > 0)
SendChat(null, masterServerMessages.Dequeue());
if (conns.Count() == 0)
{
listener.Stop();
GameStarted = false;
break;
}
}
} ) { 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++)
@@ -105,10 +162,9 @@ namespace OpenRA.Server
throw new InvalidOperationException("Already got 8 players");
}
static int ChooseFreePalette()
static int ChooseFreeSlot()
{
// TODO: Query the list of palettes from somewhere, and pick one
return 0;
return lobbyInfo.Slots.First(s => !s.Closed && s.Bot == null).Index;
}
static void AcceptConnection()
@@ -118,7 +174,7 @@ namespace OpenRA.Server
{
if (GameStarted)
{
Console.WriteLine("Rejected connection from {0}; game is already started.",
Log.Write("server", "Rejected connection from {0}; game is already started.",
newConn.socket.RemoteEndPoint);
newConn.socket.Close();
return;
@@ -133,20 +189,22 @@ namespace OpenRA.Server
newConn.socket.Send(BitConverter.GetBytes(newConn.PlayerIndex));
conns.Add(newConn);
var defaults = new GameRules.PlayerSettings();
lobbyInfo.Clients.Add(
new Session.Client()
{
Index = newConn.PlayerIndex,
Color1 = System.Drawing.Color.FromArgb(246,214,121),
Color2 = System.Drawing.Color.FromArgb(40,32,8),
Name = "Player {0}".F(1 + newConn.PlayerIndex),
Color1 = defaults.Color1,
Color2 = defaults.Color2,
Name = defaults.Name,
Country = "random",
State = Session.ClientState.NotReady,
SpawnPoint = 0,
Team = 0,
Slot = ChooseFreeSlot(),
});
Console.WriteLine("Client {0}: Accepted connection from {1}",
Log.Write("server", "Client {0}: Accepted connection from {1}",
newConn.PlayerIndex, newConn.socket.RemoteEndPoint);
SendChat(newConn, "has joined the game.");
@@ -231,32 +289,34 @@ namespace OpenRA.Server
else if (client.State == Session.ClientState.Ready)
client.State = Session.ClientState.NotReady;
Console.WriteLine("Player @{0} is {1}",
Log.Write("server", "Player @{0} is {1}",
conn.socket.RemoteEndPoint, client.State);
SyncLobbyInfo();
// start the game if everyone is ready.
if (conns.Count > 0 && conns.All(c => GetClient(c).State == Session.ClientState.Ready))
{
Console.WriteLine("All players are ready. Starting the game!");
GameStarted = true;
foreach( var c in conns )
foreach( var d in conns )
DispatchOrdersToClient( c, d.PlayerIndex, 0x7FFFFFFF, new byte[] { 0xBF } );
InterpretCommand(conn, "startgame");
return true;
}},
{ "startgame",
s =>
{
GameStarted = true;
foreach( var c in conns )
foreach( var d in conns )
DispatchOrdersToClient( c, d.PlayerIndex, 0x7FFFFFFF, new byte[] { 0xBF } );
DispatchOrders(null, 0,
new ServerOrder("StartGame", "").Serialize());
PingMasterServer();
}
DispatchOrders(null, 0,
new ServerOrder("StartGame", "").Serialize());
PingMasterServer();
return true;
}},
{ "name",
s =>
{
Console.WriteLine("Player@{0} is now known as {1}", conn.socket.RemoteEndPoint, s);
Log.Write("server", "Player@{0} is now known as {1}", conn.socket.RemoteEndPoint, s);
GetClient(conn).Name = s;
SyncLobbyInfo();
return true;
@@ -265,9 +325,9 @@ namespace OpenRA.Server
s =>
{
int lag;
if (!int.TryParse(s, out lag)) { Console.WriteLine("Invalid order lag: {0}", s); return false; }
if (!int.TryParse(s, out lag)) { Log.Write("server", "Invalid order lag: {0}", s); return false; }
Console.WriteLine("Order lag is now {0} frames.", lag);
Log.Write("server", "Order lag is now {0} frames.", lag);
lobbyInfo.GlobalSettings.OrderLatency = lag;
SyncLobbyInfo();
@@ -284,7 +344,7 @@ namespace OpenRA.Server
s =>
{
int team;
if (!int.TryParse(s, out team)) { Console.WriteLine("Invalid team: {0}", s ); return false; }
if (!int.TryParse(s, out team)) { Log.Write("server", "Invalid team: {0}", s ); return false; }
GetClient(conn).Team = team;
SyncLobbyInfo();
@@ -296,7 +356,7 @@ namespace OpenRA.Server
int spawnPoint;
if (!int.TryParse(s, out spawnPoint) || spawnPoint < 0 || spawnPoint > 8) //TODO: SET properly!
{
Console.WriteLine("Invalid spawn point: {0}", s);
Log.Write("server", "Invalid spawn point: {0}", s);
return false;
}
@@ -314,11 +374,108 @@ namespace OpenRA.Server
s =>
{
var c = s.Split(',').Select(cc => int.Parse(cc)).ToArray();
GetClient(conn).Color1 = System.Drawing.Color.FromArgb(c[0],c[1],c[2]);
GetClient(conn).Color2 = System.Drawing.Color.FromArgb(c[3],c[4],c[5]);
GetClient(conn).Color1 = Color.FromArgb(c[0],c[1],c[2]);
GetClient(conn).Color2 = Color.FromArgb(c[3],c[4],c[5]);
SyncLobbyInfo();
return true;
}},
{ "slot",
s =>
{
int slot;
if (!int.TryParse(s, out slot)) { Log.Write("server", "Invalid slot: {0}", s ); return false; }
var slotData = lobbyInfo.Slots.FirstOrDefault( x => x.Index == slot );
if (slotData == null || slotData.Closed || slotData.Bot != null
|| lobbyInfo.Clients.Any( c => c.Slot == slot ))
return false;
GetClient(conn).Slot = slot;
SyncLobbyInfo();
return true;
}},
{ "slot_close",
s =>
{
int slot;
if (!int.TryParse(s, out slot)) { Log.Write("server", "Invalid slot: {0}", s ); return false; }
var slotData = lobbyInfo.Slots.FirstOrDefault( x => x.Index == slot );
if (slotData == null)
return false;
if (conn.PlayerIndex != 0)
{
SendChatTo( conn, "Only the host can alter slots" );
return true;
}
slotData.Closed = true;
slotData.Bot = null;
/* kick any player that's in the slot */
var occupant = lobbyInfo.Clients.FirstOrDefault( c => c.Slot == slotData.Index );
if (occupant != null)
{
var occupantConn = conns.FirstOrDefault( c => c.PlayerIndex == occupant.Index );
if (occupantConn != null)
DropClient( occupantConn, new Exception() );
}
SyncLobbyInfo();
return true;
}},
{ "slot_open",
s =>
{
int slot;
if (!int.TryParse(s, out slot)) { Log.Write("server", "Invalid slot: {0}", s ); return false; }
var slotData = lobbyInfo.Slots.FirstOrDefault( x => x.Index == slot );
if (slotData == null)
return false;
if (conn.PlayerIndex != 0)
{
SendChatTo( conn, "Only the host can alter slots" );
return true;
}
slotData.Closed = false;
slotData.Bot = null;
SyncLobbyInfo();
return true;
}},
{ "slot_bot",
s =>
{
var parts = s.Split(' ');
if (parts.Length != 2)
{
SendChatTo( conn, "Malformed slot_bot command" );
return true;
}
int slot;
if (!int.TryParse(parts[0], out slot)) { Log.Write("server", "Invalid slot: {0}", s ); return false; }
var slotData = lobbyInfo.Slots.FirstOrDefault( x => x.Index == slot );
if (slotData == null)
return false;
if (conn.PlayerIndex != 0)
{
SendChatTo( conn, "Only the host can alter slots" );
return true;
}
slotData.Bot = parts[1];
SyncLobbyInfo();
return true;
}},
{ "map",
s =>
{
@@ -328,6 +485,8 @@ namespace OpenRA.Server
return true;
}
lobbyInfo.GlobalSettings.Map = s;
LoadMap();
foreach(var client in lobbyInfo.Clients)
{
client.SpawnPoint = 0;
@@ -368,7 +527,7 @@ namespace OpenRA.Server
if (!dict.TryGetValue(cmdName, out a))
return false;
Console.WriteLine( "Client {0} sent server command: {1}", conn.PlayerIndex, cmd );
Log.Write("server", "Client {0} sent server command: {1}", conn.PlayerIndex, cmd );
return a(cmdValue);
}
@@ -391,16 +550,18 @@ namespace OpenRA.Server
{
if(GameStarted)
SendChatTo(conn, "Cannot change state when game started.");
else if (GetClient(conn).State == Session.ClientState.Ready && so.Data != "ready")
else if (GetClient(conn).State == Session.ClientState.Ready && !(so.Data == "ready" || so.Data == "startgame") )
SendChatTo(conn, "Cannot change state when marked as ready.");
else if (!InterpretCommand(conn, so.Data))
{
Console.WriteLine("Bad server command: {0}", so.Data);
Log.Write("server", "Bad server command: {0}", so.Data);
SendChatTo(conn, "Bad server command.");
};
}
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;
@@ -414,8 +575,6 @@ namespace OpenRA.Server
public static void DropClient(Connection toDrop, Exception e)
{
Console.WriteLine("Client dropped: {0}.", toDrop.socket.RemoteEndPoint);
conns.Remove(toDrop);
SendChat(toDrop, "Connection Dropped");
@@ -429,50 +588,57 @@ 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();
}
static volatile bool isBusy;
static Queue<string> masterServerMessages = new Queue<string>();
static void PingMasterServer()
{
if (wc.IsBusy || !isInternetServer) return;
var url = "ping.php?port={0}&name={1}&state={2}&players={3}&mods={4}&map={5}";
wc.DownloadDataCompleted += PingMasterServerResponse;
if (isInitialPing)
{
url += "&new=1";
isInitialPing = false;
}
else
wc.DownloadDataCompleted -= PingMasterServerResponse;
wc.DownloadDataAsync(new Uri(
masterServerUrl + url.F(
ExternalPort, Uri.EscapeUriString(Name),
GameStarted ? 2 : 1, // todo: post-game states, etc.
lobbyInfo.Clients.Count,
string.Join(",", lobbyInfo.GlobalSettings.Mods),
lobbyInfo.GlobalSettings.Map)));
if (isBusy || !isInternetServer) return;
lastPing = Environment.TickCount;
}
isBusy = true;
static void PingMasterServerResponse(object sender, DownloadDataCompletedEventArgs e)
{
string s = Encoding.UTF8.GetString(e.Result);
int gameId;
if (int.TryParse(s.Trim(), out gameId))
Game.SetGameId(gameId);
Log.Write("debug", "Game ID: {0}", gameId);
Action a = () =>
{
try
{
var url = "ping.php?port={0}&name={1}&state={2}&players={3}&mods={4}&map={5}";
if (isInitialPing) url += "&new=1";
using (var wc = new WebClient())
{
wc.DownloadData(
masterServerUrl + url.F(
ExternalPort, Uri.EscapeUriString(Name),
GameStarted ? 2 : 1, // todo: post-game states, etc.
lobbyInfo.Clients.Count,
string.Join(",", lobbyInfo.GlobalSettings.Mods),
lobbyInfo.GlobalSettings.Map));
if (isInitialPing)
{
isInitialPing = false;
lock (masterServerMessages)
masterServerMessages.Enqueue("Master server communication established.");
}
}
}
catch(Exception ex)
{
Log.Write("server", ex.ToString());
lock( masterServerMessages )
masterServerMessages.Enqueue( "Master server communication failed." );
}
isBusy = false;
};
a.BeginInvoke(null, null);
}
}
}

Some files were not shown because too many files have changed in this diff Show More