Compare commits

..

422 Commits

Author SHA1 Message Date
Chris Forbes
a265d11a5f make JoinServer public [at Gecko's request] 2010-11-07 08:41:16 +13:00
Bob
8a96c5f7b5 fix infantry entering buildings 2010-11-06 22:27:28 +13:00
Chris Forbes
5e5456191c fix missing assembly reference FileFormats -> System.Windows.Forms in makefile 2010-11-06 20:33:12 +13:00
Chris Forbes
aae7f56e7d extend RevealMapCrateAction to optionally reveal for allies 2010-11-06 20:18:18 +13:00
Chris Forbes
3645b3ee13 increase ParaBomb spread 3 -> 6 2010-11-06 20:02:01 +13:00
Chris Forbes
e0f357390c clean up usings in AirstrikePower 2010-11-06 19:58:41 +13:00
Chris Forbes
9c3cb62838 reduce redeye damage 50 -> 40 2010-11-06 19:55:53 +13:00
Chris Forbes
14225b8e79 reduce dragon damage from 60 to 50 2010-11-06 19:55:10 +13:00
Chris Forbes
e01bf735d9 reduce cooldown on Chronoshift and Invulnerability to 2min each; reduce invuln duration to 30 seconds 2010-11-06 19:51:57 +13:00
Chris Forbes
1904f8ced1 fix weap door never closing 2010-11-06 17:47:11 +13:00
Caleb Anderson
3caca8c323 some noise on shellmap 2010-11-06 17:00:36 +13:00
Chris Forbes
dd7ce2d45e fix dumb crash in UIM 2010-11-06 16:56:50 +13:00
geckosoft
f793f4548b Added: A PasswordField widget (based on the TextField widget) 2010-11-06 16:49:46 +13:00
Chris Forbes
56675a61f5 rationalize the mess of Sound.Play[ToPlayer] functions. 2010-11-06 16:49:08 +13:00
Bob
3dd27fcb77 fix crash involving crushing and dead walls 2010-11-06 16:35:51 +13:00
Chris Forbes
13d76f8e9c removed editor -> ra dep; added EditorAppearance for inconvenient bits that the editor must have _some_ understanding of, but can't see 2010-11-06 15:10:29 +13:00
Bob
9a2cdcde11 move Selectable.Radius into Health 2010-11-06 14:35:26 +13:00
Bob
155e7320cb add support for decimal to fieldLoader. use decimal for speedmodifiers 2010-11-06 14:10:56 +13:00
Chris Forbes
750fd84494 reinstate order validation for ra & cnc 2010-11-06 13:36:53 +13:00
Bob
924adc68a9 fix aircraft desync 2010-11-06 13:36:11 +13:00
Bob
0f8d9d7fb3 fix heli repusion from grounded helis. cleanup flight code 2010-11-06 12:44:25 +13:00
Bob
801aa1156f move aircraft code into Mods.RA/Air/ 2010-11-06 12:18:04 +13:00
Bob
aa0c14c214 Fix crash when generating a syncreport when blaming something other than Tick. 2010-11-06 12:08:33 +13:00
Bob
8fcbb670d8 rename AllowOreAt => AllowResourceAt 2010-11-06 11:53:41 +13:00
Bob
85a26ecdf7 fix invincible aircraft, infantry 2010-11-06 11:06:12 +13:00
Bob
480c5edd75 move Building et al into Mods/ 2010-11-06 11:05:45 +13:00
Bob
fdfa1ddf97 extract DrawGrid from DrawBuildingGrid 2010-11-05 19:50:18 +13:00
Bob
80caf1818b fix off-by-one in IsInRange. use CenterLocation in combat code where appropriate 2010-11-05 19:50:17 +13:00
Bob
5c0cd50797 remove unnecessary parameters from Aircraft.MovementSpeedForCell, and rename to MovementSpeed 2010-11-05 19:50:17 +13:00
Bob
8129d5d7dc use CenterLocation, not Location, to determine facing during attack 2010-11-05 19:50:17 +13:00
Bob
a77b7af5fd add Target overload for IsInRange 2010-11-05 19:50:16 +13:00
Bob
ecf41722c3 rename TargetableSquares -> TargetableCells 2010-11-05 19:50:16 +13:00
Bob
39b09780f6 refactor Targetable into Targetable{Unit,Building} and ITargetable 2010-11-05 19:50:15 +13:00
Bob
98dec6dc8e revert sanity check that building is entirely within map (trees break it) 2010-11-05 19:50:15 +13:00
Bob
50b1ba3acc attack if in range of any cell of a building 2010-11-05 19:50:15 +13:00
Bob
c3fc7b98f3 use UIM (was BIM) to determine what cells a building blocks 2010-11-05 19:50:14 +13:00
geckosoft
f5b8b18d86 Core: Added trait 'SurrenderOnDisconnect' and the core changes required to make this work 2010-11-05 19:48:32 +13:00
geckosoft
e7c61fac5c Core: Added the attacker to IDamageModifier' GetDamageModifier 2010-11-05 19:48:31 +13:00
geckosoft
2640603f6c Added 'Health' and 'PreviousHealth' to the AttackInfo (so we can know how many 'real' damage is done!) 2010-11-05 19:48:31 +13:00
Chris Forbes
1a011276bf (gecko) add more overloads for Renderable, to support scaling 2010-11-05 19:48:31 +13:00
Chris Forbes
bc7cf09287 (gecko) Add support for custom order generators 2010-11-05 19:48:30 +13:00
Chris Forbes
7762241653 (gecko) Send damage notifications to the owner's player actor too. 2010-11-05 19:48:30 +13:00
geckosoft
9489196911 Added a new trait : Scale 2010-11-05 19:48:29 +13:00
Chris Forbes
e32e060d37 (gecko) make production queue content accessible to mod code 2010-11-05 19:48:29 +13:00
Chris Forbes
5e3fce6820 (gecko) Return the ISound when playing sounds, so you can stop it later 2010-11-05 19:48:28 +13:00
Chris Forbes
6655b6ba6a (gecko) make IonCannon and NukeLaunch effects public 2010-11-05 19:48:28 +13:00
Chris Forbes
0b4b003c10 (gecko) Make Parachute public so it can be used in other mods 2010-11-05 19:48:27 +13:00
geckosoft
380e7e8b20 Core: Made stuff public so people (read: openrg) can have its own lobby, based on the core lobby 2010-11-05 19:48:27 +13:00
geckosoft
9b3533abc0 Core: Made Corpse public 2010-11-05 19:48:27 +13:00
Chris Forbes
1409016cbd (gecko) Allow mods to override order validation 2010-11-05 19:48:26 +13:00
Chris Forbes
4aa1fb4b86 refactor mouse position label hooking for Surface 2010-11-02 11:16:30 +13:00
Chris Forbes
40126c52e8 render actors using correct palettes in editor 2010-11-02 10:30:22 +13:00
Chris Forbes
dd600d4590 indent, etc 2010-11-02 09:50:58 +13:00
Chris Forbes
532f420338 build 8bpp bitmaps in OpenRA.Editor 2010-11-02 09:39:35 +13:00
Chris Forbes
e25878e78d ai: dont build defenses if in low power; dont build random stuff if no power-producing item is available on the same queue 2010-11-02 07:51:07 +13:00
Chris Forbes
002cc4842a start reducing duplication in HackyAI 2010-11-02 07:27:31 +13:00
Chris Forbes
eac548ac8b disable bot debug by default 2010-11-01 20:38:28 +13:00
Chris Forbes
3fc4d1b219 fix cnc crashing on startup 2010-11-01 20:31:12 +13:00
Chris Forbes
80e3b8be0d more cleanups 2010-11-01 20:28:28 +13:00
Chris Forbes
fa35f6caa4 cleanups 2010-11-01 20:25:03 +13:00
Chris Forbes
526cf6059a remove Misc.cs; put its contents in sensible places. 2010-11-01 20:15:32 +13:00
Caleb Anderson
1527e472b6 potential fix for bug 312 : PlaceBuilding.ResolveOrder does not validate the requested build location 2010-11-01 18:42:51 +13:00
Bob
94f7c6db97 determine which sprites to render during Render, not Tick 2010-11-01 18:39:50 +13:00
Bob
e3ddb8f757 cache world.LocalPlayer instead of fetching it repeatedly within loops 2010-11-01 18:39:44 +13:00
Chris Forbes
d7d0d371c6 (bob) refactor input dispatch; remove public Dispatch*Input from game; (chris) fix build failures due to rebase past gecko 2010-11-01 18:39:37 +13:00
Chris Forbes
527c60daa7 some cleanup on gecko's stuff 2010-11-01 18:34:44 +13:00
geckosoft
c30050396a Fixed: Possible crash 2010-11-01 17:55:33 +13:00
geckosoft
60a8acf4d4 Added: Another hook for server extensions 2010-11-01 17:55:32 +13:00
geckosoft
5164c3cd7d Hack: Changed the ChooseFreePlayerIndex (hack on its own..) to loop till 256 instead of 8 (ie increasing max player support to 256) 2010-11-01 17:55:32 +13:00
geckosoft
6276e659cd Fixed: A crash when the host disconnects 2010-11-01 17:55:31 +13:00
geckosoft
298314626e Added: Dedicated server support
Added: The ability to not render anything when using the client as a dedicated server
Added: The basic server extension (NullServerExtension)
Exposed: Made some fields public, so that the server extension can access it
2010-11-01 17:55:31 +13:00
geckosoft
0112bc4df7 Added : Some missing changes to make WorldGameOver work (ie missing RejoinLobby etc)
Warning: Please add the following manually (if you cant find it):
	in Game.cs
		find
			'case ConnectionState.Connected:'
		add
			if (ConnectedToLobby != null) ConnectedToLobby();

Tried my best to add it in the patch but it failed :(
2010-11-01 17:55:31 +13:00
geckosoft
c8dbed938a Enable: WorldGameOver at the 'ra' mod (ie after 10 second delay, it'll return all players to the lobby) 2010-11-01 17:55:30 +13:00
geckosoft
278f35e4aa Added: If the 'Owner' ('who' said something) @ chat is null or "" it'll just render the text 2010-11-01 17:55:30 +13:00
geckosoft
dd38e45f2e Added: Lobby server command "spectator" - will attempt to put the player in an available spectator slot 2010-11-01 17:55:29 +13:00
geckosoft
3e08f9b1b1 Added: LocalAssemblies / LocalRules to Manifest.cs (can be used to add a custom set of rules / assemblies to be loaded, other than the ones defined in the mod) 2010-11-01 17:55:29 +13:00
geckosoft
8392a44314 Added: Support for not-synced traits (ITraitNotSynced) 2010-11-01 17:55:28 +13:00
geckosoft
d050d1a4b9 Added : OpenRA.Renderer.Null 2010-11-01 03:50:21 +01:00
geckosoft
56598ce2ff Fixed: crash when starting a new game after disconnecting from own server 2010-11-01 03:46:38 +01:00
geckosoft
2fb72155eb Added: Missing IngameObserverChromeDelegate.cs file (for spectator)
Fixed: Some spectator gui issues (@ gamelobby.yaml / LobbyDelegate.cs)
2010-11-01 03:35:55 +01:00
geckosoft
030bd4b28d Core: Added basic support for Spectators
TODO: Someone modify the files for cnc (chrome / rules)
2010-10-31 04:03:31 +01:00
Matthew Bowra-Dean
b9c40ad3ce Cleanup
Removed OpenRAUploader (long since obsolete) and master server hack from master server upgrade.
2010-10-28 23:03:14 +13:00
Matthew Bowra-Dean
4490a90332 Changed IFolder implementations' priorities to be based on listing order in mod.yaml 2010-10-28 22:21:54 +13:00
Matthew
15d6facdb9 Fix crash in WorldInteractionControllerWidget. 2010-10-25 17:14:14 +13:00
Chris Forbes
b80962b365 fix ore/tib growth -- we can always grow in a cell that already contains our resource type. buildable/terraintype checks only come into it when growing into *new* cells 2010-10-25 11:34:31 +13:00
Chris Forbes
814c6a8713 aircraft crash nicely now 2010-10-25 11:34:30 +13:00
Chris Forbes
eea89c1d33 make FallsToEarth significantly more flexible 2010-10-25 11:34:29 +13:00
Chris Forbes
9ec0367ecb add support for an explosion on aircraft falling out of the sky 2010-10-25 11:34:28 +13:00
Bob
f5fe1013ee remove AttackDefault; use AttackFrontal instead 2010-10-25 09:51:14 +13:00
Bob
c338d28d35 fix 'decided on Attack but ordered Heal' message on medic 2010-10-25 09:14:49 +13:00
Chris Forbes
2f962452e5 fix compile failure in prev 2010-10-25 09:14:22 +13:00
Bob
10f5e68f7f fix #166 (shroud artifacting at bounds) 2010-10-25 09:06:23 +13:00
Chris Forbes
1cde37f32b update cnc to work with updated engine (AttackBase -> AttackDefault) 2010-10-25 08:45:49 +13:00
Chris Forbes
291899de8a glitch-free. 2010-10-25 08:32:14 +13:00
Caleb Anderson
cc70669f1a use player color option 2010-10-25 08:29:55 +13:00
Caleb Anderson
c568dfa486 Contrail trait 2010-10-25 08:29:54 +13:00
Caleb Anderson
ff7daf8727 MoveViewport takes a float to allow for smoother panning. Added more scripting to shellmap 2010-10-25 08:16:08 +13:00
Caleb Anderson
fc72066ed2 Fixing last mouse position commit :( 2010-10-25 08:16:08 +13:00
Caleb Anderson
253ccd6d9b Status bar with mouse position in editor 2010-10-25 08:16:07 +13:00
Bob
01accaeb38 fix cargo 2010-10-25 08:03:13 +13:00
Bob
46c3bc09a1 fix harv bug in c&c 2010-10-25 08:03:12 +13:00
Bob
2f0b7566e5 partial fix for tsla 2010-10-25 08:03:12 +13:00
Bob
9b8ec714d2 add AutoTarget to v2 2010-10-25 08:03:11 +13:00
Bob
5d408fe3c7 make AttackMove use an activity 2010-10-25 08:03:11 +13:00
Bob
aa0b7bedf0 pass target to DoAttack 2010-10-25 08:03:10 +13:00
Bob
c5358f7c82 call DoAttack from activities instead of from Tick in AttackPlane/AttackHeli 2010-10-25 08:03:10 +13:00
Bob
158d6e5647 move CheckFire in Weapon 2010-10-25 08:03:09 +13:00
Bob
e091781104 use new AttackDefault trait instead of AttackBase; make AttackBase abstract 2010-10-25 08:03:09 +13:00
Bob
25582cb9f8 move CancelableActivity into its own file 2010-10-25 08:03:08 +13:00
Bob
fece294cc6 move RemoveSelf activity into Mods/ 2010-10-25 08:03:07 +13:00
Bob
c3501f68e3 move TurnActivity into Mods/ 2010-10-25 08:03:07 +13:00
Bob
c4ee5fbd41 add self param to OnCancel 2010-10-25 08:03:06 +13:00
Caleb Anderson
9a54074b1b motd failure avoidance 2010-10-23 21:27:36 +13:00
Chris Forbes
10e918c375 increase ftrk range 6 -> 8; spread 3 -> 5 2010-10-23 19:14:53 +13:00
Chris Forbes
bbcad99fa7 increase agun range 6 -> 10; increase zsu-23 spread 3 -> 5 2010-10-23 18:52:04 +13:00
Chris Forbes
c8198a1a1c reduce hind cost 2000 -> 1500 2010-10-23 18:52:03 +13:00
Chris Forbes
8d5241f419 reduce hpad cost 1500 -> 300; increase heli,hind cost 1200 -> 2000 2010-10-23 18:52:03 +13:00
Chris Forbes
448b681c5b reduce AFLD cost 1100 -> 300; increase YAK cost 800 -> 1000; increase MIG cost 1200 -> 2000 2010-10-23 18:52:02 +13:00
Chris Forbes
826f7a29b5 remove dead .mpr maps 2010-10-23 18:52:02 +13:00
Chris Forbes
0629b1e2d2 quick hack to fix aircraft fail in prev 2010-10-23 18:49:42 +13:00
Bob
0d9cf63dd2 move Mobile et al into Mods/ 2010-10-22 11:31:13 +13:00
Bob
6513bd5fe0 don't use Move directly 2010-10-22 11:10:32 +13:00
Bob
f933e3de3f fix entering buildings from the east or south 2010-10-22 10:20:34 +13:00
Bob
88a8d84153 use new Enter activity for engy, spy, c4 2010-10-21 20:58:07 +13:00
Bob
8e4f5da791 fix ironcurtain 2010-10-21 18:29:43 +13:00
Bob
a61d21e501 fix sync-checking in replays 2010-10-21 15:33:24 +13:00
Matthew Bowra-Dean
2bcf33661a Give folders higher priority than packages in FileSystem. Also fixes bug where folder contents were not being cached with the correct hash. 2010-10-21 07:44:26 +13:00
Matthew Bowra-Dean
f6df7a06f2 fixes #323 2010-10-21 07:43:52 +13:00
Bob
de92a2fc0c remove UIM-debug from settings (now in DeveloperMode trait) 2010-10-20 20:49:10 +13:00
Bob
6012f1d592 fix a water-walk bug 2010-10-20 20:39:18 +13:00
Chris Forbes
caf676dc33 fix client quits 2010-10-20 20:33:28 +13:00
Chris Forbes
e3ae1bec75 add Yellowstone II map 2010-10-19 21:04:00 +13:00
Chris Forbes
b98b517e35 add some decent 4p cnc maps 2010-10-19 19:25:05 +13:00
Chris Forbes
5d9f25eef5 add Slendermang's 'Into the River Below' C&C map 2010-10-18 18:54:16 +13:00
Chris Forbes
5bf69eb539 strip some dead crap from UnitOrders 2010-10-18 18:44:47 +13:00
Chris Forbes
09f7778294 fix bogus base-cycler 2010-10-18 18:07:08 +13:00
Bob
c8ec5f3579 fix sync 2010-10-17 13:38:16 +13:00
Chris Forbes
a54c7ecc18 reduce ra AFLD power requirement from 50 to 20 2010-10-17 12:34:21 +13:00
Chris Forbes
0064caf1f4 fix cnc missiles a bit; add trails to cnc sam missiles 2010-10-17 12:34:02 +13:00
Chris Forbes
fc2fbd4689 nerf v2 reload slightly (250 -> 280) 2010-10-17 12:27:49 +13:00
Chris Forbes
0f0793cde5 buff arty slightly 2010-10-17 12:27:12 +13:00
Chris Forbes
2fed6f61e9 fix version string showing up twice in bottom-right corner 2010-10-17 10:42:25 +13:00
Bob
3df310df6e halve bandwidth usage 2010-10-16 22:25:31 +13:00
Bob
c2b3a749ca change IConnection interface wrt sending 2010-10-16 21:05:14 +13:00
Chris Forbes
2d2220f38f issue a warning on the debug, rather than exploding on double-quit 2010-10-16 19:09:38 +13:00
Chris Forbes
7509bf85aa #309 add Tiberian's 'chokepoint' cnc 1v1 map 2010-10-16 18:54:04 +13:00
Chris Forbes
64e8088ae4 cnc: nerf apc speed 15 -> 11 2010-10-16 18:54:03 +13:00
Chris Forbes
15584b9b34 c&c unit tuning: orca various nerfs, bggy/jeep/bike speed nerfs 2010-10-16 18:54:03 +13:00
Matthew
79c1d1b0a6 Updated linux post-install scripts to use OpenRA.Utility.exe 2010-10-16 18:48:42 +13:00
Matthew
50860614cf Downloaded packages extracted. Added SharpZipLib in order to extract them. 2010-10-16 18:48:42 +13:00
Matthew
90d1d3e053 Makefile rule for OpenRA.Utility 2010-10-16 18:48:42 +13:00
Matthew Bowra-Dean
3bf83b52fb Download packages for mods action. 2010-10-16 18:48:42 +13:00
Matthew Bowra-Dean
5f57dd7a62 Options to install scores.mix from RA and CnC CDs 2010-10-16 18:48:42 +13:00
Matthew Bowra-Dean
8e1185b56f --mod-info now accepts a comma separated list of mods. 2010-10-16 18:48:41 +13:00
Matthew Bowra-Dean
26098df2a1 Added mod metadata listing to OpenRA.Utility 2010-10-16 18:48:41 +13:00
Matthew Bowra-Dean
9ac9d83745 Moved Mod class into OpenRA.FileFormats. Added OpenRA.Utility project, a CLI program for utility actions on a game install. 2010-10-16 18:48:41 +13:00
Chris Forbes
51edd5a3f4 #314 reduce effectiveness of GUN vs unarmored, 30% -> 20% 2010-10-16 17:19:04 +13:00
Paul Chote
fc07f90f91 Fix editor 2010-10-16 12:06:29 +13:00
Paul Chote
b64dcbf502 Fix HackyAI in cnc 2010-10-16 11:49:27 +13:00
Paul Chote
5694c113be undo testing changes 2010-10-16 09:56:01 +13:00
Paul Chote
01cf5c21a7 Add `roof' to lst 2010-10-16 09:56:01 +13:00
Paul Chote
4206d2e131 Add Katzsmile's transport remake; make it controllable on GDI01 while testing. 2010-10-16 09:56:01 +13:00
Paul Chote
83968553aa Allow maps to override and enforce race/colour selection 2010-10-16 09:56:01 +13:00
Paul Chote
aa2aba1250 Allow map slots to disallow bots 2010-10-16 09:56:00 +13:00
Chris Forbes
bba9c4b976 #313 fixed -- there's unsynced code that runs in Tick, too. 2010-10-16 09:48:39 +13:00
Chris Forbes
999eef2ec9 #157 explosions weren't taking altitude into account 2010-10-16 09:30:32 +13:00
Chris Forbes
f28c8903aa #297 planes get stuck on map edge -- fixed 2010-10-16 09:09:39 +13:00
Chris Forbes
f58eabe667 fix base cycling (and free mcv logic) for cnc 2010-10-15 18:51:25 +13:00
Chris Forbes
86695dfe29 #299 Updated version of 'The Sentinel' 1v1 map 2010-10-15 18:48:39 +13:00
Chris Forbes
9ae452e8d6 make GiveMcvCrateAction less stupid, if the GiveUnitCrateAction says it can't do it. 2010-10-15 18:38:09 +13:00
Chris Forbes
3e547102d2 #217 avoid dumping tanks in the sea, and ships on dry land 2010-10-15 18:36:34 +13:00
pdovy
e883e63c87 fixed bug where cloakable units were not targetable when uncloaked. removed ITargetable interface in favor of using the Targetable trait. fixed warnings in child classes of Targetable. 2010-10-15 18:13:01 +13:00
Caleb Anderson
a904047a16 Toggle-able teamchat on shift-enter 2010-10-15 18:07:36 +13:00
Chris Forbes
ad1af792e6 add CashTrickler trait, to support oil derricks, etc 2010-10-15 17:56:28 +13:00
Chris Forbes
6c9527d9dc #304 engineer goldwrench cursors were backwards 2010-10-15 17:44:18 +13:00
Chris Forbes
da2d461aa8 use fixed missile silo icon 2010-10-15 17:36:56 +13:00
Paul Chote
49bfd69707 Fix voice crash on adding a 3rd side (thanks raminator for finding this) 2010-10-15 12:33:15 +13:00
Paul Chote
2a02df9411 Fix crash on fmv completion 2010-10-15 00:03:15 +13:00
Paul Chote
185ba80e99 Fix FMV player widget display 2010-10-14 23:30:48 +13:00
Paul Chote
127cbf3f96 Start pulling out useful scripting components - stalled by widget changes having broken the fmv player. 2010-10-14 22:16:18 +13:00
Paul Chote
4bdf675def Mute shellmap combat 2010-10-14 20:03:36 +13:00
Paul Chote
9c944924de Fix a pile of compile warnings and debug log spam. Fix <playername> (Dead) in chat 2010-10-14 19:40:00 +13:00
Paul Chote
28f79533eb Customisable mcv crates 2010-10-14 19:27:43 +13:00
Paul Chote
5a834c9500 Add mcv, napalm, nuke crates to cnc. Reduce levelup and cash shares. 2010-10-14 19:27:43 +13:00
Chris Forbes
3255218d5c #298 MINE overlaps units wrong -- fixed 2010-10-14 19:22:16 +13:00
Chris Forbes
9befe377d5 fix invisible crates in cnc (where did that patch go?) 2010-10-14 19:15:37 +13:00
Chris Forbes
f39b4f0750 fix putting units back into badr/c17 and cursors 2010-10-14 18:09:59 +13:00
Chris Forbes
1794625cea #284 double-destroy in same frame fixed 2010-10-14 18:02:15 +13:00
Chris Forbes
42092fe6dd fix #291 2010-10-14 14:41:43 +13:00
Chris Forbes
1a451fe813 fix crash in TiberiumRefineryDockAction.CancelDock 2010-10-13 22:17:55 +13:00
Paul Chote
0e845a9f3a Make tundra slightly less boring 2010-10-13 21:44:14 +13:00
Chris Forbes
2a3c149769 rename oasis map, since that wasn't what it was. 2010-10-13 21:18:26 +13:00
Chris Forbes
7cfcd94e23 #181, #286 fixed -- sharedrandom seed from server is now used. 2010-10-13 21:00:27 +13:00
Chris Forbes
247a8c9717 add Tiberian's TD map: 'The Sentinel (1v1)' 2010-10-13 20:52:40 +13:00
Chris Forbes
4afd8ad783 #289 lobby title should show servername -- fixed 2010-10-13 20:45:03 +13:00
Paul Chote
dca77956cb Revert to saving only non-default settings. 2010-10-13 19:57:53 +13:00
Paul Chote
22c6c3b3f6 Remove the default westwood maps from cnc. 2010-10-13 19:57:52 +13:00
Paul Chote
1e8e5c8f5c Remove aftermath mod - it has bitrotted beyond all usefulness. 2010-10-13 19:57:52 +13:00
Paul Chote
c0fcc862d6 Add some more mod metadata for beedee. Todo: enforce dependencies and standalone during game init. 2010-10-13 19:57:52 +13:00
Paul Chote
5a3ccdf0c5 fix ssm launcher rockets 2010-10-13 19:57:52 +13:00
Paul Chote
1bd802c386 Add tech building definitions to cnc; noncapturable map decorations until they have behavior implemented. 2010-10-13 19:57:52 +13:00
Paul Chote
3a4e2abedd Fix snow theatre 2010-10-13 19:57:18 +13:00
Paul Chote
f13343ef2e Fix tilesets colours for snow and blue tib, fix unit speed over tib, fix lst pathability. 2010-10-13 19:57:18 +13:00
Paul Chote
2b1ac6a60f WIP 1v1 map 2010-10-13 19:57:18 +13:00
Chris Forbes
8aa180c1f4 cleaned up unused bits in attackmove 2010-10-13 19:20:17 +13:00
Chris Forbes
260e420f85 fix crash in attackmove 2010-10-13 19:12:20 +13:00
Chris Forbes
1a203afd89 fix all the buggy crap around chat/teamchat... still hacked 2010-10-13 08:13:21 +13:00
max621
c150fd9475 Added attack move 2010-10-13 08:13:20 +13:00
Chris Forbes
566303a8f1 #276 fixed 2010-10-12 21:19:48 +13:00
Chris Forbes
10f8364b99 #240 declaring war should set the reciprocal state too 2010-10-12 21:19:48 +13:00
Chris Forbes
7c31f72db6 quick hack around the teamchat crash in lobby 2010-10-12 21:19:27 +13:00
Bob
6b6c9639f9 fix crash wrt chat in lobby 2010-10-12 20:37:36 +13:00
Bob
7ca9fcdac9 run the shellmap even while the lobby shows. remove Widget.SpecialOneArg 2010-10-12 18:33:16 +13:00
Bob
20276291ce move Game.world onto OrderManager. use call IssueOrder on world and/or on orderManager, not on Game 2010-10-12 17:30:27 +13:00
Bob
34fc207a6c FUCK YES 2010-10-12 17:30:24 +13:00
Bob
c974e61680 remove refs to Game.world in widgets 2010-10-12 17:30:22 +13:00
Bob
09db76f89f remove ref to Game.world in Move 2010-10-12 17:29:11 +13:00
Bob
6bbf878314 remove ref to Game.world in Viewport.ShroudBounds 2010-10-12 17:29:09 +13:00
Bob
4cd3195f9f call refreshPalette in the right place. make Game.worldRenderer PRIVATE (yay) 2010-10-12 17:29:06 +13:00
Bob
6ea2a06e4b pass WorldRenderer to Widget.Draw, DrawInner 2010-10-12 17:27:08 +13:00
Bob
597dba8584 untangling WorldRenderer from World 2010-10-12 17:24:23 +13:00
Bob
1c1483377c remove ref to Game.world in Combat 2010-10-12 17:24:19 +13:00
Bob
0873741983 don't pass world to Widget.Tick, either 2010-10-12 17:24:17 +13:00
Bob
e0afc08e5f move an access of Viewport into Game 2010-10-12 17:24:14 +13:00
Bob
cb1deacbb2 pass world into CheckSync. don't pass world into Widget.DrawInner 2010-10-12 17:24:12 +13:00
Bob
ab1e930ba3 pass worldRenderer around as necessary 2010-10-12 17:24:07 +13:00
Bob
10bf85f57e move Game.Current{Host,Port} into orderManager 2010-10-12 17:24:03 +13:00
Bob
17990ab8b7 move LobbyInfo onto OrderManager 2010-10-12 17:24:00 +13:00
Bob
beecb8aeb1 remove redundant parameter from ObjectCreator.Param attribute 2010-10-12 17:23:57 +13:00
Bob
6a25d989a7 remove many references to Game.world 2010-10-12 17:23:55 +13:00
Bob
f8776d773d extract FrameData from OrderManager. fix disconnect bug in NetworkConnection 2010-10-12 17:22:17 +13:00
Bob
3724f46a3e remove world arg to SyncLobbyInfo and HandleInput 2010-10-12 17:22:14 +13:00
Bob
915ad7fb7b extract replay saving from OrderManager 2010-10-12 17:22:12 +13:00
Chris Forbes
07023b6c35 add yaml setup for blue tiberium 2010-10-12 16:59:58 +13:00
Chris Forbes
b78b5b22a3 add blue tiberium art 2010-10-12 16:59:58 +13:00
Bob
eca098b0b4 use a getter for Aircraft.Location 2010-10-12 07:59:40 +13:00
Bob
011a20e8b4 add IHasLocation 2010-10-12 07:59:40 +13:00
Bob
9c362f7d41 remove setters on Mobile.{from,to}Cell. use SetLocation instead 2010-10-12 07:59:39 +13:00
Chris Forbes
99d92ee095 move ColorFromHSL out of the ui code 2010-10-11 19:52:46 +13:00
Chris Forbes
b905d343af fix mishandled CancelProduction 2010-10-11 19:37:45 +13:00
Chris Forbes
024b564b8c maybe this works better 2010-10-11 19:10:34 +13:00
Chris Forbes
9c0e3ac4c9 allow AppearsOnRadar to use Location rather than OccupiedCells, for things that don't occupy any cells 2010-10-11 18:54:13 +13:00
Chris Forbes
a2205e9031 fix some dumb (nonfatal) crashes in the editor with nothing loaded 2010-10-11 18:21:41 +13:00
Chris Forbes
0df9815d2c fix base-cycling for mac build, too? 2010-10-10 15:11:53 +13:00
Chris Forbes
79d6eb4a2b some widgets cleanup 2010-10-10 15:09:26 +13:00
Chris Forbes
0d8557eadb fix NRE in CancelableActivity 2010-10-10 11:04:39 +13:00
Matthew Bowra-Dean
70f6fc7a7a Removed git describe from checkout-and-build.sh 2010-10-09 22:26:17 +13:00
Paul Chote
11f6a8b945 Revert mono 2.8 deps; causes desyncs. 2010-10-09 20:05:37 +13:00
Chris Forbes
e3d71acb05 fix crash in CrateEffect 2010-10-09 13:40:57 +13:00
Chris Forbes
580f1cfe97 bot and humans hate each other, even if the host is on a team. 2010-10-09 13:40:55 +13:00
Andrew Riedi
3d7434f42e Fixed under VS 2010. 2010-10-09 13:40:52 +13:00
Chris Forbes
ef96604f9e #231 scroll jumping fixed 2010-10-09 13:40:49 +13:00
Chris Forbes
465f5d295b force master server url update, if old. 2010-10-09 13:40:47 +13:00
Chris Forbes
1feb377d43 fix bogus target lines being drawn to 0,0 2010-10-09 13:40:33 +13:00
Caleb Anderson
a065fb464e bandage to fix dogs not killing infantry in certain cases 2010-10-09 13:40:21 +13:00
Chris Forbes
e39917ca19 trim bbox for ftrk 2010-10-08 19:09:18 +13:00
Chris Forbes
318f496bf9 #202 fixed 2010-10-08 19:03:01 +13:00
Chris Forbes
ed8a155249 if c17 gets confused, just fly away 2010-10-08 18:57:14 +13:00
Chris Forbes
40ac5f7b6e FallsToEarth for cnc helis too 2010-10-08 18:45:52 +13:00
Chris Forbes
517754027e 8Inch and SubMissile minimum ranges temporarily disabled, until combat bugs are fixed 2010-10-08 18:33:38 +13:00
Caleb Anderson
ef4f478e10 Strip newlines from scrolling text. Frame-friendly update of scrolling text 2010-10-08 18:29:20 +13:00
Chris Forbes
92c30b89f8 FACT hp 1000 -> 1500 2010-10-08 18:26:25 +13:00
Chris Forbes
7331a9c4fb prevent aircraft shooting while landed 2010-10-08 18:25:26 +13:00
Chris Forbes
d2b9e150f1 add FallsToEarth for helicopters dying 2010-10-08 18:24:08 +13:00
Chris Forbes
8ba329f1be reduce cost of CA 3200 -> 2400 2010-10-08 18:03:34 +13:00
Chris Forbes
8868eab247 nerf hind chaingun from [x2] 40@3 to 20@10 2010-10-08 18:03:13 +13:00
Caleb Anderson
f12889e684 Sensible scroll cursor positions for cnc. RA seems to be totally different, didn't touch atm 2010-10-08 17:39:00 +13:00
Caleb Anderson
5c095bc174 more politic default motd text for cnc 2010-10-08 17:38:55 +13:00
Caleb Anderson
0c24a08436 sane default scroll speed 2010-10-08 17:38:48 +13:00
Chris Forbes
9762b540b0 fix bogus reverse-enter-transport (in ResolveOrder this time) 2010-10-08 17:38:07 +13:00
Paul Chote
fd34f2ba99 Fix #225 and some other uses of a.IsInWorld / a.IsDead() 2010-10-08 10:56:50 +13:00
Paul Chote
ad6481c8e8 Change master server url. Won't work until dns is set correctly. 2010-10-07 22:46:44 +13:00
Paul Chote
7426d47cd5 Fix some compile warnings 2010-10-07 22:46:44 +13:00
pdovy
761f62292f missing files from previous commit 2010-10-07 22:46:44 +13:00
unknown
63b8555bc9 Fix bug that prevented ground units from attacking landed aircraft. 2010-10-07 22:46:44 +13:00
Bob
3209da4a4a fixed PlaceBuilding and Chronosphere ordergenerators 2010-10-07 22:07:13 +13:00
Bob
aebef4f1c8 rename IIssueOrder2 -> IIssueOrder 2010-10-07 22:07:13 +13:00
Bob
d3244184c1 implement order targeter for everything else 2010-10-07 22:07:13 +13:00
Bob
39e62354a8 implement order targeter for passenger 2010-10-07 22:07:12 +13:00
Bob
f525c3808e implement order targeter for cargo 2010-10-07 22:07:12 +13:00
Bob
87a0b52ce5 more order targeters 2010-10-07 22:07:12 +13:00
Bob
4bc9e01516 use new orders system in various traits 2010-10-07 22:07:12 +13:00
Bob
711d05da98 use IIssueOrder2 in AttackBase 2010-10-07 22:07:12 +13:00
Bob
3d805ff40d added IIssueOrder2. most orders are broken, but Minelayer is fixed 2010-10-07 22:07:12 +13:00
Bob
0cd140849b fix some support powers 2010-10-07 22:07:12 +13:00
Bob
d6110b9ef0 add Sync.AssertUnsynced. use it in OrderGenerator.set 2010-10-07 22:07:12 +13:00
Bob
26d1db778e push the check-synchash-doesn't-change pattern into a utility fn. furthur reduce the number of uses on Game.world 2010-10-07 22:07:11 +13:00
Bob
f41aa474aa remove more uses of Game.world 2010-10-07 22:07:11 +13:00
Paul Chote
0002e80a19 Use new deps package, built on mono 2.8. Enable new sgen garbage collector. 2010-10-07 22:05:43 +13:00
Chris Forbes
759a52d86e fix build failure from bad merge in prev 2010-10-07 18:24:40 +13:00
max621
44fe0396bb Added shift+right click on build menu cancels 5 orders. Added ctrl+shift+right click on build menu cancels all orders 2010-10-07 18:23:26 +13:00
rasco
dd6d8d916e hackyAI 2010-10-07 07:42:40 +13:00
Chris Forbes
5af8f5e2d9 bots choose random colors 2010-10-07 07:41:14 +13:00
Caleb Anderson
c85503811c Clamp, scroll, scrollspeed, sliders
Reduced clamp duplication
Fixed scrolling speed issue
Modified scrollspeed slider to use a range
Fixed scrollspeed, volume, and sound sliders not showing current setting.
2010-10-06 20:53:56 +13:00
Chris Forbes
ab431fe9ee fix crash on warhead=null 2010-10-06 20:17:03 +13:00
Chris Forbes
7ec9958d47 hackyai should only attack humans 2010-10-06 20:04:36 +13:00
rasco
cfc74a6dee HackyAI: builds defense now. rally points are rechosen so units are more scattered in the base. builds only e1-3 and 1-3tnks. 2010-10-06 19:14:29 +13:00
Chris Forbes
cecdd73e08 destroy ore in all smudged cells 2010-10-06 18:37:32 +13:00
Chris Forbes
381e080b11 tweak nuke timing back 2010-10-06 18:32:33 +13:00
Chris Forbes
c6a047cb1a fix silent nukes 2010-10-06 18:29:05 +13:00
Chris Forbes
4c51733e04 actually add the cratenuke warhead 2010-10-06 18:25:07 +13:00
Chris Forbes
8f613b80e8 ban cloak crate on infantry 2010-10-06 18:16:09 +13:00
Chris Forbes
10de282daa add mechanism to exclude particular actor types from picking up any crate 2010-10-06 18:13:29 +13:00
Chris Forbes
915e123956 add minimum ranges for mig/yak/ca/msub/hind/v2rl/arty 2010-10-06 18:05:31 +13:00
Chris Forbes
8cb7a7b8ce add support for WeaponInfo.MinRange 2010-10-06 17:41:52 +13:00
Chris Forbes
ce5cf93077 prevent infantry going prone due to tib damage 2010-10-06 17:37:23 +13:00
Chris Forbes
1390bb7428 ra: reduce parabombs charge to 1min 2010-10-06 17:32:04 +13:00
Chris Forbes
3c5b136216 ra: reduce nuke charge time 13min -> 9 min 2010-10-06 17:29:45 +13:00
Chris Forbes
66785e606a ra: brik hp 1500 => 1000 2010-10-06 17:23:12 +13:00
Chris Forbes
e2239fa50c #215 obli misses moving units fixed 2010-10-06 17:22:11 +13:00
Chris Forbes
7f7712aa64 #203 dogs eating walls fixed 2010-10-06 17:20:20 +13:00
Chris Forbes
b1605e115a #206 badr/badr.bomber appear in tooltips 2010-10-06 17:15:52 +13:00
Chris Forbes
69b2da86be RA: +20% 4tnk HP 2010-10-06 17:13:39 +13:00
Chris Forbes
18e965e5aa fix desync on destroying ore storage buildings 2010-10-06 10:59:46 +13:00
Chris Forbes
24f0c28f56 fix massive player/client confusion after people drop 2010-10-06 10:59:45 +13:00
Paul Chote
25af51b4ac Prevent a race condition 2010-10-05 19:00:36 +13:00
Paul Chote
ecd7064cc3 Add an example mod that adds a soviet supply truck 2010-10-05 18:48:00 +13:00
Chris Forbes
477a21e782 fix 'minv/minp' showing up in tooltips 2010-10-05 17:58:30 +13:00
Chris Forbes
899d9af62b CNC: +25% HP on FACT/MCV 2010-10-05 17:58:29 +13:00
Paul Chote
17eca983ef Fix wall sell exploit 2010-10-05 17:56:30 +13:00
max621
2fc219ecd5 Fixed auto attack not working properly due to code expecting 'Idle' activity but most units use 'IdleAnimation' in RA mod 2010-10-05 17:45:29 +13:00
Chris Forbes
49a645cd2d fix ubuntu-64 support (Tao.FreeType was quite bogus.) 2010-10-05 17:42:13 +13:00
Matthew
7cb8da411d Updated git url to point to new upstream location. 2010-10-05 17:42:13 +13:00
Matthew
4bcdf3cf11 Netcode discussion from Etherpad. 2010-10-05 17:36:03 +13:00
Matthew
1b525ff809 Export FTP server and added username/password to VERSION file upload. 2010-10-05 17:36:03 +13:00
Caleb Anderson
cdec3fce26 Don't tell all players about my yaks 2010-10-05 17:25:30 +13:00
Caleb Anderson
7bdf6a953f New slider Range parameter. Palette modifications. Potential crash fix. Clamp function.
Range parameter added to slider. Supports returning a range of values
rather than just 0-1. Allows you to not have to post process the offset.
Modified palette selector to not have full range, which was causing
blown out units.
Introduced exension method Clamp<T>(min, max)
Fixed crash deserializing out of bound color value using above
extension.
2010-10-05 17:25:25 +13:00
Chris Forbes
06b20c8ba5 fix blatant wrongness in parallel builds 2010-10-02 20:55:43 +13:00
Paul Chote
9b484b53ec Show mod info in server browser 2010-10-02 20:37:25 +13:00
Paul Chote
9620b4ed46 Add mod metadata, and filter valid mods on startup. 2010-10-02 20:37:22 +13:00
Caleb Anderson
d8908c44d0 Nicer default ticker text 2010-10-02 01:13:55 -05:00
Caleb Anderson
cfe705531a Async motd grab. Client and server version in MasterServerQuery 2010-10-02 01:13:53 -05:00
Caleb Anderson
9a2fd38ab6 MOTD ticker. ScrollingText Widget 2010-10-02 01:13:51 -05:00
Chris Forbes
911e7f62de fix retardedly putting everyone in slot 0. 2010-10-02 18:33:29 +13:00
Chris Forbes
403b81bdc9 apply the balance stick to BIKE 2010-10-02 15:49:42 +13:00
Chris Forbes
a0714b00b3 a bit of cleanup 2010-10-02 15:49:42 +13:00
Chris Forbes
d5239ee77a add tib and spawns to new map 2010-10-02 15:49:41 +13:00
Chris Forbes
03ec97f302 unfinished 2v2 cnc map 2010-10-02 15:49:41 +13:00
Chris Forbes
3255f95ee9 ffs, more paths 2010-10-02 15:35:48 +13:00
Chris Forbes
4c8fd5e73d oops 2010-10-02 15:30:16 +13:00
Chris Forbes
7179ef0f45 fix packaging script 2010-10-02 15:19:14 +13:00
Alli
6d5918b11d Remove compiler warnings 2010-10-02 14:30:54 +13:00
Matthew Bowra-Dean
81c484d1c9 Fixed debian package prereqs 2010-10-02 13:28:22 +13:00
Chris Forbes
de1044c24c hack around imagelist deserialization being completely busted across platforms 2010-10-02 13:22:54 +13:00
Paul Chote
e4c31939d9 Save all settings to settings.yaml, not just non-defaults. 2010-10-02 12:15:15 +13:00
Paul Chote
7f48d6796e Fix scroll ticks 2010-10-02 12:01:11 +13:00
Matthew Bowra-Dean
4fd77aec8e Only add 'Latest:' text if query succeeds. 2010-10-02 11:39:56 +13:00
Matthew Bowra-Dean
92fece01de Added latest version information underneath current version in main menu. 2010-10-02 11:39:54 +13:00
geckosoft
d8d987f844 Fixed Settings menu issue (overlapping text) 2010-10-02 11:38:42 +13:00
unknown
de429a4c62 Added new setting : Scroll Speed (added to cnc & ra) 2010-10-02 11:38:39 +13:00
Chris Forbes
c0ca35a4ff fix AI jam in cnc 2010-10-02 11:33:50 +13:00
Chris Forbes
1bff8559fb pull HasAdequatePower out into a function 2010-10-02 11:33:47 +13:00
Chris Forbes
de98274165 fix #184 crashes while placing minefield 2010-10-02 11:33:04 +13:00
Chris Forbes
59e2228b2a shader fallback path for cg-2.1, which misinterprets 'latest' 2010-10-02 11:32:14 +13:00
geckosoft
96d1408d45 Fixed crash bug #197 2010-10-02 11:31:55 +13:00
Paul Chote
ff45ae2d16 Fix direct connect in cnc 2010-10-02 11:31:19 +13:00
Chris Forbes
47950c9113 Revert "remove setters on Mobile.{from,to}Cell. use SetLocation instead"
This reverts commit 911db3feb1.
2010-09-28 07:45:14 +13:00
Chris Forbes
f402ec7898 Revert "add IHasLocation"
This reverts commit 699b4b1154.
2010-09-28 07:43:49 +13:00
Chris Forbes
a3c0448e15 fix broken mac packaging script 2010-09-27 21:44:23 +13:00
Bob
699b4b1154 add IHasLocation 2010-09-26 18:17:23 +12:00
Bob
911db3feb1 remove setters on Mobile.{from,to}Cell. use SetLocation instead 2010-09-26 16:46:22 +12:00
Paul Chote
c790db8e84 Fix and bulletproof osx packaging script; cleanup some obsolete .gitignore entries 2010-09-26 10:18:22 +13:00
Bob
d6dd392028 use Invariant culture for float parsing (workaround a mono bug) 2010-09-26 09:11:26 +12:00
Bob
636b2a8ea7 fix Direct Connect window 2010-09-25 19:26:02 +12:00
Chris Forbes
959d3f8bd7 fix crash in giving a harv DeliverOre order after its proc has died, but it hasnt noticed yet 2010-09-25 07:34:51 +12:00
Matthew
193cb929f1 Fixing error with Windows package script launching 2010-09-24 22:12:26 +12:00
Chris Forbes
f93e270fe4 reduce v2 range and damage slightly 2010-09-24 21:49:26 +12:00
Chris Forbes
f19953c39a nerf ftrk heavily vs most ground, and reduce hp from 300 to 120 2010-09-24 21:44:57 +12:00
Matthew
59d5ce1bc8 +x permissions for checkout-and-build.sh 2010-09-24 20:42:29 +12:00
Matthew
43cf7cd074 Yet another packaging script. 2010-09-24 20:42:26 +12:00
Chris Forbes
aba76f4b50 don't look up resource type every cell for ore, too 2010-09-24 20:40:56 +12:00
Bob
8021fc3b20 Deduplicate shroud rendering code 2010-09-24 18:11:11 +12:00
Bob
7bf4cb85fa fix perf in ShroudRenderer 2010-09-24 18:11:08 +12:00
Bob
6dd03bb339 bugfix in ResourceLayer 2010-09-24 18:11:06 +12:00
Bob
14e517cab5 Autoflush renderer. Sprite.DrawAt convenience function. 2010-09-24 18:11:03 +12:00
Bob
cdcfeb6276 render perf improvement: BufferSubData, and don't use the same buffer back-to-back 2010-09-24 18:11:00 +12:00
Matthew
c77c63a380 Fix #130.
Windows installer now checks version of OpenAL32.dll and downloads and updates if necessary.
2010-09-24 16:40:55 +12:00
Matthew
9921fe8fad Linux package parallelisation. 2010-09-24 16:40:55 +12:00
Matthew
f848c5d7d4 Parallel building of packages. 2010-09-24 16:40:55 +12:00
Matthew
62c47e3b12 Reordered packaging platforms due to popular demand. 2010-09-24 16:40:55 +12:00
Matthew
094986d066 Tag prefixes change upload location for packages. 2010-09-24 16:40:55 +12:00
Chris Forbes
d87e02ab41 more tweaks, make DD a detector again (wtf?) 2010-09-23 22:23:39 +12:00
Chris Forbes
3f415a8fdf tweak torps more 2010-09-23 22:09:56 +12:00
Chris Forbes
846371cf3e homing torps, bubble trail 2010-09-23 22:09:56 +12:00
Chris Forbes
45c77e64ee fix various desyncs when using cheats 2010-09-23 22:09:55 +12:00
Bob
66493031c8 fix crash with idle units and pathdebug 2010-09-23 21:06:44 +12:00
Bob
384b26db60 fix fieldloader bug caused by foreach language-bug 2010-09-23 17:24:06 +12:00
Bob
d66dbeb312 removing unused stuff from TraitsInterfaces 2010-09-23 15:43:34 +12:00
Chris Forbes
ba9611f544 un-FP Building.cs power stuff 2010-09-22 21:43:04 +12:00
Chris Forbes
b6e56560d4 fix a crash in RepairIndicator with a dead building 2010-09-22 20:48:45 +12:00
Chris Forbes
1f047d439f back out editor icon hack -- it breaks the packaged editor on windows 2010-09-22 19:26:09 +12:00
Chris Forbes
5233ae4770 remove spam from Util.GetFacing 2010-09-22 19:21:22 +12:00
Chris Forbes
562e07264a remove lots of debug spam 2010-09-22 19:19:18 +12:00
Chris Forbes
4dab4ed73f reenable crates 2010-09-22 19:15:45 +12:00
Chris Forbes
a97ddffa53 fix cnc movement (argh) 2010-09-22 19:00:07 +12:00
Bob
8e589e3c16 fix infantry anims 2010-09-22 13:32:58 +12:00
Bob
96f72ce842 new map in ra_perf 2010-09-22 13:08:02 +12:00
Bob
d8de477edb fix IdleAnimation. add IsAttacking to AttackBase 2010-09-22 12:21:49 +12:00
Bob
694fb6831a fix target line on Move that has not yet calculated path 2010-09-22 12:03:45 +12:00
Bob
c16a515224 make more activities cancelable. remove many uses of CurrentActivity is T 2010-09-22 11:53:58 +12:00
Bob
e2eae7973b removing warning 2010-09-22 11:18:47 +12:00
Bob
9e3c938706 removing cancelable from Move. remove unnecessary qualified names from Production 2010-09-22 10:46:54 +12:00
Bob
ef665df2e9 refactor activity queueing 2010-09-22 10:13:13 +12:00
Bob
271a3eea8d fix harv 2010-09-22 08:49:56 +12:00
Bob
2f6315b816 make the pathfinder use integers 2010-09-22 08:04:52 +12:00
Bob
ac8d408ba7 allow queuing non-buildings while the queue is ready 2010-09-22 08:02:20 +12:00
Bob
0fdd49c96a MovePart becomes an Activity 2010-09-22 08:02:17 +12:00
Bob
e390cf8ab0 fix real-ra map importer when map isn't in game root 2010-09-22 08:02:15 +12:00
Bob
64d700cd70 make c&c work too 2010-09-22 08:02:12 +12:00
Bob
d3db9d3710 yes, i do want += 2010-09-22 08:02:09 +12:00
Bob
9eb05a43f9 show perf widget 2010-09-22 08:02:06 +12:00
Bob
3165ec5359 create widgets on demand 2010-09-22 08:02:03 +12:00
Bob
f4699132d6 made OpenWindow and CloseWindow static 2010-09-22 08:02:00 +12:00
Bob
086bdfb4bd new object creation logic 2010-09-22 08:01:57 +12:00
Chris Forbes
61fd12c7da oops; prev didnt run 2010-09-22 07:19:18 +12:00
Chris Forbes
9979209c34 more useful debug, less debug 2010-09-21 22:42:31 +12:00
Chris Forbes
b0c06d4cd9 force consistent conversion to float in a few places 2010-09-21 22:08:40 +12:00
Chris Forbes
3a617f8934 sync the last path generated, to try and catch this MUCH earlier 2010-09-21 21:43:16 +12:00
Chris Forbes
b35a7d9f8d stacktrace on set_Facing too 2010-09-21 21:06:37 +12:00
Chris Forbes
67fa079658 more debug 2010-09-21 21:05:28 +12:00
Chris Forbes
c9edbd8a80 report the tick in debug 2010-09-21 18:10:05 +12:00
Chris Forbes
4b49bf03dc reset local tick # at gamestart 2010-09-21 18:05:54 +12:00
532 changed files with 16606 additions and 16575 deletions

11
.gitignore vendored
View File

@@ -12,20 +12,11 @@ mods/*/*.dll
# Red Alert binary files
mods/*/packages/*.[mM][iI][xX]
# Crap generated by OpenRa
sheet-*.png
log.txt
*.rep
#binary stuff
/*.dll
*.pdb
*.mdb
*.exe
OpenRA
OpenRA.app
*.vqa
# backup files by various editors
*~
@@ -33,10 +24,8 @@ OpenRA.app
# dependency DLLs (different for every platform!)
cg.dll
cgGL.dll
glfw.dll
/OpenRa.Gl.dll
settings.ini
#monodevelop
*.pidb

View File

@@ -6,4 +6,5 @@ The OpenRA developers are:
* Matthew Bowra-Dean
* Paul Chote
* Alli Witheford
* Joakim Lindberg
* Joakim Lindberg
* Andrew Riedi

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 seqed editor ralint filex tsbuild
PROGRAMS =fileformats gl game ra cnc seqed editor ralint filex tsbuild utility
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 thirdparty/WindowsBase.dll
fileformats_LIBS = $(COMMON_LIBS) thirdparty/Tao/Tao.Sdl.dll System.Windows.Forms.dll thirdparty/WindowsBase.dll
gl_SRCS = $(shell find OpenRA.Gl/ -iname '*.cs')
gl_TARGET = OpenRA.Gl.dll
@@ -62,13 +62,13 @@ editor_EXTRA = -resource:OpenRA.Editor.Form1.resources -resource:OpenRA.Editor.
ralint_SRCS = $(shell find RALint/ -iname '*.cs')
ralint_TARGET = RALint.exe
ralint_KIND = winexe
ralint_KIND = exe
ralint_DEPS = $(fileformats_TARGET) $(game_TARGET)
ralint_LIBS = $(COMMON_LIBS) $(ralint_DEPS)
filex_SRCS = $(shell find FileExtractor/ -iname '*.cs')
filex_TARGET = FileExtractor.exe
filex_KIND = winexe
filex_KIND = exe
filex_DEPS = $(fileformats_TARGET)
filex_LIBS = $(COMMON_LIBS) $(filex_DEPS)
@@ -79,19 +79,26 @@ tsbuild_DEPS = $(fileformats_TARGET) $(game_TARGET)
tsbuild_LIBS = $(COMMON_LIBS) $(tsbuild_DEPS) System.Windows.Forms.dll
tsbuild_EXTRA = -resource:OpenRA.TilesetBuilder.Form1.resources
utility_SRCS = $(shell find OpenRA.Utility/ -iname '*.cs')
utility_TARGET = OpenRA.Utility.exe
utility_KIND = exe
utility_DEPS = $(fileformats_TARGET) thirdparty/ICSharpCode.SharpZipLib.dll
utility_LIBS = $(COMMON_LIBS) $(utility_DEPS)
# -platform:x86
.SUFFIXES:
.PHONY: clean all game tool default mods mod_ra mod_cnc install uninstall editor_res editor tsbuild ralint seqed filex
.PHONY: clean all game tool default mods mod_ra mod_cnc install uninstall editor_res editor tsbuild ralint seqed filex utility
game: $(fileformats_TARGET) $(gl_TARGET) $(game_TARGET) $(ra_TARGET) $(cnc_TARGET)
game: $(fileformats_TARGET) $(gl_TARGET) $(game_TARGET) $(ra_TARGET) $(cnc_TARGET) $(utility_TARGET)
clean:
@-rm *.exe *.dll *.mdb mods/**/*.dll mods/**/*.mdb *.resources
distclean: clean
CORE = fileformats gl game editor
CORE = fileformats gl game editor utility
install: all
@-echo "Installing OpenRA to $(INSTALL_DIR)"
@@ -123,6 +130,7 @@ install: all
@cp *.ttf $(INSTALL_DIR)
@cp --parents -r thirdparty/Tao $(INSTALL_DIR)
@$(INSTALL_PROGRAM) thirdparty/WindowsBase.dll $(INSTALL_DIR)
@$(INSTALL_PROGRAM) thirdparty/ICSharpCode.SharpZipLib.dll $(INSTALL_DIR)
@-$(INSTALL_PROGRAM) VERSION $(INSTALL_DIR)
@echo "#!/bin/sh" > openra
@@ -132,8 +140,8 @@ install: all
@$(INSTALL_PROGRAM) -m +rx openra $(BIN_INSTALL_DIR)
@echo "OpenRA is now installed. You will now want to download"
@echo "http://open-ra.org/get-dependency.php?ra-packages and"
@echo "http://open-ra.org/get-dependency.php?cnc-packages"
@echo "http://open-ra.org/get-dependency.php?file=ra-packages and"
@echo "http://open-ra.org/get-dependency.php?file=cnc-packages"
@echo "and extract their contents to"
@echo "$(INSTALL_DIR)/mods/ra/packages and "
@echo "$(INSTALL_DIR)/mods/cnc/packages respectively."

View File

@@ -18,7 +18,7 @@ namespace OpenRA.Editor
{
public Bitmap Bitmap;
public ActorInfo Info;
public bool Centered;
public EditorAppearanceInfo Appearance;
}
class BrushTemplate

877
OpenRA.Editor/Form1.Designer.cs generated Normal file → Executable file
View File

@@ -28,221 +28,204 @@
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
this.toolStripContainer1 = new System.Windows.Forms.ToolStripContainer();
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
this.splitContainer2 = new System.Windows.Forms.SplitContainer();
this.pmMiniMap = new System.Windows.Forms.PictureBox();
this.tabControl1 = new System.Windows.Forms.TabControl();
this.tabPage1 = new System.Windows.Forms.TabPage();
this.tilePalette = new System.Windows.Forms.FlowLayoutPanel();
this.tabPage2 = new System.Windows.Forms.TabPage();
this.actorPalette = new System.Windows.Forms.FlowLayoutPanel();
this.tabPage3 = new System.Windows.Forms.TabPage();
this.resourcePalette = new System.Windows.Forms.FlowLayoutPanel();
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.mnuExport = new System.Windows.Forms.ToolStripMenuItem();
this.mnuMinimapToPNG = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
this.exotToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.mapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
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.toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.layersFloaterToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.tt = new System.Windows.Forms.ToolTip(this.components);
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
this.surface1 = new OpenRA.Editor.Surface();
this.saveFileDialog = new System.Windows.Forms.SaveFileDialog();
this.toolStripContainer1.ContentPanel.SuspendLayout();
this.toolStripContainer1.TopToolStripPanel.SuspendLayout();
this.toolStripContainer1.SuspendLayout();
this.splitContainer1.Panel1.SuspendLayout();
this.splitContainer1.Panel2.SuspendLayout();
this.splitContainer1.SuspendLayout();
this.splitContainer2.Panel1.SuspendLayout();
this.splitContainer2.Panel2.SuspendLayout();
this.splitContainer2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.pmMiniMap)).BeginInit();
this.tabControl1.SuspendLayout();
this.tabPage1.SuspendLayout();
this.tabPage2.SuspendLayout();
this.tabPage3.SuspendLayout();
this.menuStrip1.SuspendLayout();
this.SuspendLayout();
//
// toolStripContainer1
//
//
// toolStripContainer1.ContentPanel
//
this.toolStripContainer1.ContentPanel.Controls.Add(this.splitContainer1);
this.toolStripContainer1.ContentPanel.Size = new System.Drawing.Size(985, 727);
this.toolStripContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
this.toolStripContainer1.Location = new System.Drawing.Point(0, 0);
this.toolStripContainer1.Name = "toolStripContainer1";
this.toolStripContainer1.Padding = new System.Windows.Forms.Padding(0, 0, 0, 22);
this.toolStripContainer1.Size = new System.Drawing.Size(985, 773);
this.toolStripContainer1.TabIndex = 1;
this.toolStripContainer1.Text = "toolStripContainer1";
//
// toolStripContainer1.TopToolStripPanel
//
this.toolStripContainer1.TopToolStripPanel.Controls.Add(this.menuStrip1);
//
// splitContainer1
//
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainer1.Location = new System.Drawing.Point(0, 0);
this.splitContainer1.Name = "splitContainer1";
//
// splitContainer1.Panel1
//
this.splitContainer1.Panel1.Controls.Add(this.splitContainer2);
//
// splitContainer1.Panel2
//
this.splitContainer1.Panel2.Controls.Add(this.surface1);
this.splitContainer1.Size = new System.Drawing.Size(985, 727);
this.splitContainer1.SplitterDistance = 198;
this.splitContainer1.TabIndex = 0;
//
// splitContainer2
//
this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainer2.Location = new System.Drawing.Point(0, 0);
this.splitContainer2.Name = "splitContainer2";
this.splitContainer2.Orientation = System.Windows.Forms.Orientation.Horizontal;
//
// splitContainer2.Panel1
//
this.splitContainer2.Panel1.Controls.Add(this.pmMiniMap);
//
// splitContainer2.Panel2
//
this.splitContainer2.Panel2.Controls.Add(this.tabControl1);
this.splitContainer2.Size = new System.Drawing.Size(198, 727);
this.splitContainer2.SplitterDistance = 160;
this.splitContainer2.TabIndex = 1;
//
// pmMiniMap
//
this.pmMiniMap.BackColor = System.Drawing.Color.Black;
this.pmMiniMap.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.pmMiniMap.Dock = System.Windows.Forms.DockStyle.Fill;
this.pmMiniMap.Location = new System.Drawing.Point(0, 0);
this.pmMiniMap.Name = "pmMiniMap";
this.pmMiniMap.Size = new System.Drawing.Size(198, 160);
this.pmMiniMap.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.pmMiniMap.TabIndex = 1;
this.pmMiniMap.TabStop = false;
//
// tabControl1
//
this.tabControl1.Controls.Add(this.tabPage1);
this.tabControl1.Controls.Add(this.tabPage2);
this.tabControl1.Controls.Add(this.tabPage3);
this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tabControl1.Location = new System.Drawing.Point(0, 0);
this.tabControl1.Margin = new System.Windows.Forms.Padding(3, 3, 3, 0);
this.tabControl1.Multiline = true;
this.tabControl1.Name = "tabControl1";
this.tabControl1.Padding = new System.Drawing.Point(6, 0);
this.tabControl1.SelectedIndex = 0;
this.tabControl1.Size = new System.Drawing.Size(198, 563);
this.tabControl1.TabIndex = 0;
//
// tabPage1
//
this.tabPage1.Controls.Add(this.tilePalette);
this.tabPage1.Location = new System.Drawing.Point(4, 20);
this.tabPage1.Name = "tabPage1";
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
this.tabPage1.Size = new System.Drawing.Size(190, 539);
this.tabPage1.TabIndex = 0;
this.tabPage1.Text = "Templates";
this.tabPage1.UseVisualStyleBackColor = true;
//
// tilePalette
//
this.tilePalette.AutoScroll = true;
this.tilePalette.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.tilePalette.Dock = System.Windows.Forms.DockStyle.Fill;
this.tilePalette.Location = new System.Drawing.Point(3, 3);
this.tilePalette.Name = "tilePalette";
this.tilePalette.Size = new System.Drawing.Size(184, 533);
this.tilePalette.TabIndex = 1;
//
// tabPage2
//
this.tabPage2.Controls.Add(this.actorPalette);
this.tabPage2.Location = new System.Drawing.Point(4, 20);
this.tabPage2.Name = "tabPage2";
this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
this.tabPage2.Size = new System.Drawing.Size(190, 539);
this.tabPage2.TabIndex = 1;
this.tabPage2.Text = "Actors";
this.tabPage2.UseVisualStyleBackColor = true;
//
// actorPalette
//
this.actorPalette.AutoScroll = true;
this.actorPalette.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.actorPalette.Dock = System.Windows.Forms.DockStyle.Fill;
this.actorPalette.Location = new System.Drawing.Point(3, 3);
this.actorPalette.Name = "actorPalette";
this.actorPalette.Size = new System.Drawing.Size(184, 533);
this.actorPalette.TabIndex = 2;
//
// tabPage3
//
this.tabPage3.Controls.Add(this.resourcePalette);
this.tabPage3.Location = new System.Drawing.Point(4, 20);
this.tabPage3.Name = "tabPage3";
this.tabPage3.Size = new System.Drawing.Size(190, 539);
this.tabPage3.TabIndex = 2;
this.tabPage3.Text = "Resources";
this.tabPage3.UseVisualStyleBackColor = true;
//
// resourcePalette
//
this.resourcePalette.AutoScroll = true;
this.resourcePalette.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.resourcePalette.Dock = System.Windows.Forms.DockStyle.Fill;
this.resourcePalette.Location = new System.Drawing.Point(0, 0);
this.resourcePalette.Name = "resourcePalette";
this.resourcePalette.Size = new System.Drawing.Size(190, 539);
this.resourcePalette.TabIndex = 3;
//
// menuStrip1
//
this.menuStrip1.Dock = System.Windows.Forms.DockStyle.None;
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
this.splitContainer2 = new System.Windows.Forms.SplitContainer();
this.pmMiniMap = new System.Windows.Forms.PictureBox();
this.tabControl1 = new System.Windows.Forms.TabControl();
this.tabPage1 = new System.Windows.Forms.TabPage();
this.tilePalette = new System.Windows.Forms.FlowLayoutPanel();
this.tabPage2 = new System.Windows.Forms.TabPage();
this.actorPalette = new System.Windows.Forms.FlowLayoutPanel();
this.tabPage3 = new System.Windows.Forms.TabPage();
this.resourcePalette = new System.Windows.Forms.FlowLayoutPanel();
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.mnuExport = new System.Windows.Forms.ToolStripMenuItem();
this.mnuMinimapToPNG = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
this.exotToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.mapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
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.toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.layersFloaterToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.tt = new System.Windows.Forms.ToolTip(this.components);
this.saveFileDialog = new System.Windows.Forms.SaveFileDialog();
this.splitContainer3 = new System.Windows.Forms.SplitContainer();
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
this.toolStripStatusLabelFiller = new System.Windows.Forms.ToolStripStatusLabel();
this.toolStripStatusLabelMousePosition = new System.Windows.Forms.ToolStripStatusLabel();
this.surface1 = new OpenRA.Editor.Surface();
this.splitContainer1.Panel1.SuspendLayout();
this.splitContainer1.Panel2.SuspendLayout();
this.splitContainer1.SuspendLayout();
this.splitContainer2.Panel1.SuspendLayout();
this.splitContainer2.Panel2.SuspendLayout();
this.splitContainer2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.pmMiniMap)).BeginInit();
this.tabControl1.SuspendLayout();
this.tabPage1.SuspendLayout();
this.tabPage2.SuspendLayout();
this.tabPage3.SuspendLayout();
this.menuStrip1.SuspendLayout();
this.splitContainer3.Panel1.SuspendLayout();
this.splitContainer3.Panel2.SuspendLayout();
this.splitContainer3.SuspendLayout();
this.statusStrip1.SuspendLayout();
this.SuspendLayout();
//
// splitContainer1
//
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainer1.Location = new System.Drawing.Point(0, 0);
this.splitContainer1.Name = "splitContainer1";
//
// splitContainer1.Panel1
//
this.splitContainer1.Panel1.Controls.Add(this.splitContainer2);
//
// splitContainer1.Panel2
//
this.splitContainer1.Panel2.Controls.Add(this.surface1);
this.splitContainer1.Size = new System.Drawing.Size(985, 744);
this.splitContainer1.SplitterDistance = 198;
this.splitContainer1.TabIndex = 0;
//
// splitContainer2
//
this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainer2.Location = new System.Drawing.Point(0, 0);
this.splitContainer2.Name = "splitContainer2";
this.splitContainer2.Orientation = System.Windows.Forms.Orientation.Horizontal;
//
// splitContainer2.Panel1
//
this.splitContainer2.Panel1.Controls.Add(this.pmMiniMap);
//
// splitContainer2.Panel2
//
this.splitContainer2.Panel2.Controls.Add(this.tabControl1);
this.splitContainer2.Size = new System.Drawing.Size(198, 744);
this.splitContainer2.SplitterDistance = 164;
this.splitContainer2.TabIndex = 1;
//
// pmMiniMap
//
this.pmMiniMap.BackColor = System.Drawing.Color.Black;
this.pmMiniMap.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.pmMiniMap.Dock = System.Windows.Forms.DockStyle.Fill;
this.pmMiniMap.Location = new System.Drawing.Point(0, 0);
this.pmMiniMap.Name = "pmMiniMap";
this.pmMiniMap.Size = new System.Drawing.Size(198, 164);
this.pmMiniMap.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.pmMiniMap.TabIndex = 1;
this.pmMiniMap.TabStop = false;
//
// tabControl1
//
this.tabControl1.Controls.Add(this.tabPage1);
this.tabControl1.Controls.Add(this.tabPage2);
this.tabControl1.Controls.Add(this.tabPage3);
this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tabControl1.Location = new System.Drawing.Point(0, 0);
this.tabControl1.Margin = new System.Windows.Forms.Padding(3, 3, 3, 0);
this.tabControl1.Multiline = true;
this.tabControl1.Name = "tabControl1";
this.tabControl1.Padding = new System.Drawing.Point(6, 0);
this.tabControl1.SelectedIndex = 0;
this.tabControl1.Size = new System.Drawing.Size(198, 576);
this.tabControl1.TabIndex = 0;
//
// tabPage1
//
this.tabPage1.Controls.Add(this.tilePalette);
this.tabPage1.Location = new System.Drawing.Point(4, 20);
this.tabPage1.Name = "tabPage1";
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
this.tabPage1.Size = new System.Drawing.Size(190, 552);
this.tabPage1.TabIndex = 0;
this.tabPage1.Text = "Templates";
this.tabPage1.UseVisualStyleBackColor = true;
//
// tilePalette
//
this.tilePalette.AutoScroll = true;
this.tilePalette.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.tilePalette.Dock = System.Windows.Forms.DockStyle.Fill;
this.tilePalette.Location = new System.Drawing.Point(3, 3);
this.tilePalette.Name = "tilePalette";
this.tilePalette.Size = new System.Drawing.Size(184, 546);
this.tilePalette.TabIndex = 1;
//
// tabPage2
//
this.tabPage2.Controls.Add(this.actorPalette);
this.tabPage2.Location = new System.Drawing.Point(4, 20);
this.tabPage2.Name = "tabPage2";
this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
this.tabPage2.Size = new System.Drawing.Size(190, 552);
this.tabPage2.TabIndex = 1;
this.tabPage2.Text = "Actors";
this.tabPage2.UseVisualStyleBackColor = true;
//
// actorPalette
//
this.actorPalette.AutoScroll = true;
this.actorPalette.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.actorPalette.Dock = System.Windows.Forms.DockStyle.Fill;
this.actorPalette.Location = new System.Drawing.Point(3, 3);
this.actorPalette.Name = "actorPalette";
this.actorPalette.Size = new System.Drawing.Size(184, 546);
this.actorPalette.TabIndex = 2;
//
// tabPage3
//
this.tabPage3.Controls.Add(this.resourcePalette);
this.tabPage3.Location = new System.Drawing.Point(4, 20);
this.tabPage3.Name = "tabPage3";
this.tabPage3.Size = new System.Drawing.Size(190, 552);
this.tabPage3.TabIndex = 2;
this.tabPage3.Text = "Resources";
this.tabPage3.UseVisualStyleBackColor = true;
//
// resourcePalette
//
this.resourcePalette.AutoScroll = true;
this.resourcePalette.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.resourcePalette.Dock = System.Windows.Forms.DockStyle.Fill;
this.resourcePalette.Location = new System.Drawing.Point(0, 0);
this.resourcePalette.Name = "resourcePalette";
this.resourcePalette.Size = new System.Drawing.Size(190, 552);
this.resourcePalette.TabIndex = 3;
//
// menuStrip1
//
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.fileToolStripMenuItem,
this.mapToolStripMenuItem,
this.toolsToolStripMenuItem});
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
this.menuStrip1.Name = "menuStrip1";
this.menuStrip1.Size = new System.Drawing.Size(985, 24);
this.menuStrip1.TabIndex = 1;
this.menuStrip1.Text = "menuStrip1";
//
// fileToolStripMenuItem
//
this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
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";
//
// fileToolStripMenuItem
//
this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.newToolStripMenuItem,
this.toolStripSeparator1,
this.openToolStripMenuItem,
@@ -253,232 +236,267 @@
this.mnuExport,
this.toolStripSeparator3,
this.exotToolStripMenuItem});
this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
this.fileToolStripMenuItem.Size = new System.Drawing.Size(35, 20);
this.fileToolStripMenuItem.Text = "&File";
//
// newToolStripMenuItem
//
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);
//
// toolStripSeparator1
//
this.toolStripSeparator1.Name = "toolStripSeparator1";
this.toolStripSeparator1.Size = new System.Drawing.Size(122, 6);
//
// openToolStripMenuItem
//
this.openToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("openToolStripMenuItem.Image")));
this.openToolStripMenuItem.Name = "openToolStripMenuItem";
this.openToolStripMenuItem.Size = new System.Drawing.Size(125, 22);
this.openToolStripMenuItem.Text = "&Open...";
this.openToolStripMenuItem.Click += new System.EventHandler(this.OpenClicked);
//
// saveToolStripMenuItem
//
this.saveToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("saveToolStripMenuItem.Image")));
this.saveToolStripMenuItem.Name = "saveToolStripMenuItem";
this.saveToolStripMenuItem.Size = new System.Drawing.Size(125, 22);
this.saveToolStripMenuItem.Text = "&Save";
this.saveToolStripMenuItem.Click += new System.EventHandler(this.SaveClicked);
//
// saveAsToolStripMenuItem
//
this.saveAsToolStripMenuItem.Name = "saveAsToolStripMenuItem";
this.saveAsToolStripMenuItem.Size = new System.Drawing.Size(125, 22);
this.saveAsToolStripMenuItem.Text = "Save &As...";
this.saveAsToolStripMenuItem.Click += new System.EventHandler(this.SaveAsClicked);
//
// toolStripSeparator2
//
this.toolStripSeparator2.Name = "toolStripSeparator2";
this.toolStripSeparator2.Size = new System.Drawing.Size(122, 6);
//
// toolStripMenuItem1
//
this.toolStripMenuItem1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
this.fileToolStripMenuItem.Text = "&File";
//
// newToolStripMenuItem
//
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(123, 22);
this.newToolStripMenuItem.Text = "&New...";
this.newToolStripMenuItem.Click += new System.EventHandler(this.NewClicked);
//
// toolStripSeparator1
//
this.toolStripSeparator1.Name = "toolStripSeparator1";
this.toolStripSeparator1.Size = new System.Drawing.Size(120, 6);
//
// openToolStripMenuItem
//
this.openToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("openToolStripMenuItem.Image")));
this.openToolStripMenuItem.Name = "openToolStripMenuItem";
this.openToolStripMenuItem.Size = new System.Drawing.Size(123, 22);
this.openToolStripMenuItem.Text = "&Open...";
this.openToolStripMenuItem.Click += new System.EventHandler(this.OpenClicked);
//
// saveToolStripMenuItem
//
this.saveToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("saveToolStripMenuItem.Image")));
this.saveToolStripMenuItem.Name = "saveToolStripMenuItem";
this.saveToolStripMenuItem.Size = new System.Drawing.Size(123, 22);
this.saveToolStripMenuItem.Text = "&Save";
this.saveToolStripMenuItem.Click += new System.EventHandler(this.SaveClicked);
//
// saveAsToolStripMenuItem
//
this.saveAsToolStripMenuItem.Name = "saveAsToolStripMenuItem";
this.saveAsToolStripMenuItem.Size = new System.Drawing.Size(123, 22);
this.saveAsToolStripMenuItem.Text = "Save &As...";
this.saveAsToolStripMenuItem.Click += new System.EventHandler(this.SaveAsClicked);
//
// toolStripSeparator2
//
this.toolStripSeparator2.Name = "toolStripSeparator2";
this.toolStripSeparator2.Size = new System.Drawing.Size(120, 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(125, 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(185, 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(185, 22);
this.bitmapToolStripMenuItem.Text = "&Bitmap...";
this.bitmapToolStripMenuItem.Visible = false;
//
// mnuExport
//
this.mnuExport.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.toolStripMenuItem1.Name = "toolStripMenuItem1";
this.toolStripMenuItem1.Size = new System.Drawing.Size(123, 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...";
this.bitmapToolStripMenuItem.Visible = false;
//
// mnuExport
//
this.mnuExport.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.mnuMinimapToPNG});
this.mnuExport.Name = "mnuExport";
this.mnuExport.Size = new System.Drawing.Size(152, 22);
this.mnuExport.Text = "&Export";
//
// mnuMinimapToPNG
//
this.mnuMinimapToPNG.Image = ((System.Drawing.Image)(resources.GetObject("mnuMinimapToPNG.Image")));
this.mnuMinimapToPNG.Name = "mnuMinimapToPNG";
this.mnuMinimapToPNG.Size = new System.Drawing.Size(152, 22);
this.mnuMinimapToPNG.Text = "Minimap to PNG";
this.mnuMinimapToPNG.Click += new System.EventHandler(this.mnuMinimapToPNG_Click);
//
// toolStripSeparator3
//
this.toolStripSeparator3.Name = "toolStripSeparator3";
this.toolStripSeparator3.Size = new System.Drawing.Size(122, 6);
//
// exotToolStripMenuItem
//
this.exotToolStripMenuItem.Name = "exotToolStripMenuItem";
this.exotToolStripMenuItem.Size = new System.Drawing.Size(125, 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.mnuExport.Name = "mnuExport";
this.mnuExport.Size = new System.Drawing.Size(123, 22);
this.mnuExport.Text = "&Export";
//
// mnuMinimapToPNG
//
this.mnuMinimapToPNG.Image = ((System.Drawing.Image)(resources.GetObject("mnuMinimapToPNG.Image")));
this.mnuMinimapToPNG.Name = "mnuMinimapToPNG";
this.mnuMinimapToPNG.Size = new System.Drawing.Size(163, 22);
this.mnuMinimapToPNG.Text = "Minimap to PNG";
this.mnuMinimapToPNG.Click += new System.EventHandler(this.mnuMinimapToPNG_Click);
//
// toolStripSeparator3
//
this.toolStripSeparator3.Name = "toolStripSeparator3";
this.toolStripSeparator3.Size = new System.Drawing.Size(120, 6);
//
// exotToolStripMenuItem
//
this.exotToolStripMenuItem.Name = "exotToolStripMenuItem";
this.exotToolStripMenuItem.Size = new System.Drawing.Size(123, 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(39, 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(135, 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(135, 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(132, 6);
//
// spawnpointsToolStripMenuItem
//
this.spawnpointsToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("spawnpointsToolStripMenuItem.Image")));
this.spawnpointsToolStripMenuItem.Name = "spawnpointsToolStripMenuItem";
this.spawnpointsToolStripMenuItem.Size = new System.Drawing.Size(135, 22);
this.spawnpointsToolStripMenuItem.Text = "&Spawnpoints";
this.spawnpointsToolStripMenuItem.Click += new System.EventHandler(this.SpawnPointsClicked);
//
// toolsToolStripMenuItem
//
this.toolsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
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);
//
// toolsToolStripMenuItem
//
this.toolsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.layersFloaterToolStripMenuItem});
this.toolsToolStripMenuItem.Name = "toolsToolStripMenuItem";
this.toolsToolStripMenuItem.Size = new System.Drawing.Size(44, 20);
this.toolsToolStripMenuItem.Text = "Tools";
this.toolsToolStripMenuItem.Visible = false;
//
// layersFloaterToolStripMenuItem
//
this.layersFloaterToolStripMenuItem.Name = "layersFloaterToolStripMenuItem";
this.layersFloaterToolStripMenuItem.Size = new System.Drawing.Size(141, 22);
this.layersFloaterToolStripMenuItem.Text = "Layers floater";
this.layersFloaterToolStripMenuItem.Click += new System.EventHandler(this.layersFloaterToolStripMenuItem_Click);
//
// tt
//
this.tt.ShowAlways = true;
//
// statusStrip1
//
this.statusStrip1.Location = new System.Drawing.Point(0, 751);
this.statusStrip1.Name = "statusStrip1";
this.statusStrip1.Size = new System.Drawing.Size(985, 22);
this.statusStrip1.TabIndex = 2;
this.statusStrip1.Text = "statusStrip1";
//
// surface1
//
this.surface1.BackColor = System.Drawing.Color.Black;
this.surface1.Dock = System.Windows.Forms.DockStyle.Fill;
this.surface1.Location = new System.Drawing.Point(0, 0);
this.surface1.Name = "surface1";
this.surface1.Size = new System.Drawing.Size(783, 727);
this.surface1.TabIndex = 5;
this.surface1.Text = "surface1";
this.surface1.Click += new System.EventHandler(this.OnSurfaceClicked);
//
// saveFileDialog
//
this.saveFileDialog.DefaultExt = "*.png";
this.saveFileDialog.Filter = "PNG Image (*.png)|";
this.saveFileDialog.Title = "Export minimap to PNG";
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(985, 773);
this.Controls.Add(this.statusStrip1);
this.Controls.Add(this.toolStripContainer1);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.KeyPreview = true;
this.MainMenuStrip = this.menuStrip1;
this.Name = "Form1";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "OpenRA Editor";
this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyUp);
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.OnFormClosing);
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
this.toolStripContainer1.ContentPanel.ResumeLayout(false);
this.toolStripContainer1.TopToolStripPanel.ResumeLayout(false);
this.toolStripContainer1.TopToolStripPanel.PerformLayout();
this.toolStripContainer1.ResumeLayout(false);
this.toolStripContainer1.PerformLayout();
this.splitContainer1.Panel1.ResumeLayout(false);
this.splitContainer1.Panel2.ResumeLayout(false);
this.splitContainer1.ResumeLayout(false);
this.splitContainer2.Panel1.ResumeLayout(false);
this.splitContainer2.Panel2.ResumeLayout(false);
this.splitContainer2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.pmMiniMap)).EndInit();
this.tabControl1.ResumeLayout(false);
this.tabPage1.ResumeLayout(false);
this.tabPage2.ResumeLayout(false);
this.tabPage3.ResumeLayout(false);
this.menuStrip1.ResumeLayout(false);
this.menuStrip1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
this.toolsToolStripMenuItem.Name = "toolsToolStripMenuItem";
this.toolsToolStripMenuItem.Size = new System.Drawing.Size(48, 20);
this.toolsToolStripMenuItem.Text = "Tools";
this.toolsToolStripMenuItem.Visible = false;
//
// layersFloaterToolStripMenuItem
//
this.layersFloaterToolStripMenuItem.Name = "layersFloaterToolStripMenuItem";
this.layersFloaterToolStripMenuItem.Size = new System.Drawing.Size(144, 22);
this.layersFloaterToolStripMenuItem.Text = "Layers floater";
this.layersFloaterToolStripMenuItem.Click += new System.EventHandler(this.layersFloaterToolStripMenuItem_Click);
//
// tt
//
this.tt.ShowAlways = true;
//
// saveFileDialog
//
this.saveFileDialog.DefaultExt = "*.png";
this.saveFileDialog.Filter = "PNG Image (*.png)|";
this.saveFileDialog.Title = "Export minimap to PNG";
//
// splitContainer3
//
this.splitContainer3.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainer3.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
this.splitContainer3.IsSplitterFixed = true;
this.splitContainer3.Location = new System.Drawing.Point(0, 0);
this.splitContainer3.Name = "splitContainer3";
this.splitContainer3.Orientation = System.Windows.Forms.Orientation.Horizontal;
//
// splitContainer3.Panel1
//
this.splitContainer3.Panel1.Controls.Add(this.menuStrip1);
//
// splitContainer3.Panel2
//
this.splitContainer3.Panel2.Controls.Add(this.splitContainer1);
this.splitContainer3.Size = new System.Drawing.Size(985, 773);
this.splitContainer3.SplitterDistance = 25;
this.splitContainer3.TabIndex = 6;
//
// statusStrip1
//
this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.toolStripStatusLabelFiller,
this.toolStripStatusLabelMousePosition});
this.statusStrip1.Location = new System.Drawing.Point(0, 751);
this.statusStrip1.Name = "statusStrip1";
this.statusStrip1.Size = new System.Drawing.Size(985, 22);
this.statusStrip1.TabIndex = 7;
this.statusStrip1.Text = "statusStrip1";
//
// toolStripStatusLabelFiller
//
this.toolStripStatusLabelFiller.Name = "toolStripStatusLabelFiller";
this.toolStripStatusLabelFiller.Size = new System.Drawing.Size(948, 17);
this.toolStripStatusLabelFiller.Spring = true;
//
// toolStripStatusLabelMousePosition
//
this.toolStripStatusLabelMousePosition.Name = "toolStripStatusLabelMousePosition";
this.toolStripStatusLabelMousePosition.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
this.toolStripStatusLabelMousePosition.Size = new System.Drawing.Size(22, 17);
this.toolStripStatusLabelMousePosition.Text = "0,0";
//
// surface1
//
this.surface1.BackColor = System.Drawing.Color.Black;
this.surface1.Dock = System.Windows.Forms.DockStyle.Fill;
this.surface1.Location = new System.Drawing.Point(0, 0);
this.surface1.Name = "surface1";
this.surface1.Size = new System.Drawing.Size(783, 744);
this.surface1.TabIndex = 5;
this.surface1.Text = "surface1";
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(985, 773);
this.Controls.Add(this.statusStrip1);
this.Controls.Add(this.splitContainer3);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.KeyPreview = true;
this.MainMenuStrip = this.menuStrip1;
this.Name = "Form1";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "OpenRA Editor";
this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyUp);
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.OnFormClosing);
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
this.splitContainer1.Panel1.ResumeLayout(false);
this.splitContainer1.Panel2.ResumeLayout(false);
this.splitContainer1.ResumeLayout(false);
this.splitContainer2.Panel1.ResumeLayout(false);
this.splitContainer2.Panel2.ResumeLayout(false);
this.splitContainer2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.pmMiniMap)).EndInit();
this.tabControl1.ResumeLayout(false);
this.tabPage1.ResumeLayout(false);
this.tabPage2.ResumeLayout(false);
this.tabPage3.ResumeLayout(false);
this.menuStrip1.ResumeLayout(false);
this.menuStrip1.PerformLayout();
this.splitContainer3.Panel1.ResumeLayout(false);
this.splitContainer3.Panel1.PerformLayout();
this.splitContainer3.Panel2.ResumeLayout(false);
this.splitContainer3.ResumeLayout(false);
this.statusStrip1.ResumeLayout(false);
this.statusStrip1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.ToolStripContainer toolStripContainer1;
private System.Windows.Forms.SplitContainer splitContainer1;
private System.Windows.Forms.ToolTip tt;
private System.Windows.Forms.TabControl tabControl1;
@@ -506,15 +524,18 @@
private System.Windows.Forms.ToolStripMenuItem propertiesToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem resizeToolStripMenuItem;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator4;
private System.Windows.Forms.ToolStripMenuItem spawnpointsToolStripMenuItem;
private System.Windows.Forms.StatusStrip statusStrip1;
private System.Windows.Forms.ToolStripMenuItem spawnpointsToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem toolsToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem layersFloaterToolStripMenuItem;
private System.Windows.Forms.PictureBox pmMiniMap;
private System.Windows.Forms.SplitContainer splitContainer2;
private System.Windows.Forms.ToolStripMenuItem mnuExport;
private System.Windows.Forms.ToolStripMenuItem mnuMinimapToPNG;
private System.Windows.Forms.SaveFileDialog saveFileDialog;
private System.Windows.Forms.SaveFileDialog saveFileDialog;
private System.Windows.Forms.SplitContainer splitContainer3;
private System.Windows.Forms.StatusStrip statusStrip1;
private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabelMousePosition;
private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabelFiller;
}
}

23
OpenRA.Editor/Form1.cs Normal file → Executable file
View File

@@ -35,10 +35,14 @@ namespace OpenRA.Editor
Rules.LoadRules(Game.modData.Manifest, new Map());
surface1.AfterChange += MakeDirty;
string path = Directory.GetCurrentDirectory();
Icon = new Icon(path + Path.DirectorySeparatorChar + "OpenRA.Editor" + Path.DirectorySeparatorChar + "OpenRA.Editor.Icon.ico");
surface1.AfterChange += OnMapChanged;
surface1.MousePositionChanged += s => toolStripStatusLabelMousePosition.Text = s;
}
void OnMapChanged()
{
MakeDirty();
pmMiniMap.Image = Minimap.AddStaticResources(surface1.Map, Minimap.TerrainBitmap(surface1.Map, true));
}
void MakeDirty() { dirty = true; }
@@ -58,7 +62,7 @@ namespace OpenRA.Editor
Game.modData = new ModData(currentMod);
// load the map
var map = new Map(new Folder(mapname));
var map = new Map(new Folder(mapname, 0));
// upgrade maps that have no player definitions. editor doesnt care,
// but this breaks the game pretty badly.
@@ -237,7 +241,6 @@ namespace OpenRA.Editor
else
{
surface1.Map.PlayerCount = surface1.Map.Waypoints.Count;
surface1.Map.Package = new Folder(loadedMapName);
surface1.Map.Save(loadedMapName);
dirty = false;
}
@@ -349,9 +352,11 @@ namespace OpenRA.Editor
void ImportLegacyMapClicked(object sender, EventArgs e)
{
var currentDirectory = Directory.GetCurrentDirectory();
using (var ofd = new OpenFileDialog { Filter = "Legacy maps (*.ini;*.mpr)|*.ini;*.mpr" })
if (DialogResult.OK == ofd.ShowDialog())
{
Directory.SetCurrentDirectory( currentDirectory );
/* massive hack: we should be able to call NewMap() with the imported Map object,
* but something's not right internally in it, unless loaded via the real maploader */
@@ -359,7 +364,6 @@ namespace OpenRA.Editor
Directory.CreateDirectory(savePath);
var map = LegacyMapImporter.Import(ofd.FileName);
map.Package = new Folder(savePath);
map.Players.Add("Neutral", new PlayerReference("Neutral",
Rules.Info["world"].Traits.WithInterface<CountryInfo>().First().Race, true, true));
@@ -391,11 +395,6 @@ namespace OpenRA.Editor
pb.Show();
}
private void OnSurfaceClicked(object sender, EventArgs e)
{
pmMiniMap.Image = Minimap.AddStaticResources(surface1.Map, Minimap.TerrainBitmap(surface1.Map, true));
}
private void mnuMinimapToPNG_Click(object sender, EventArgs e)
{
try

6
OpenRA.Editor/Form1.resx Normal file → Executable file
View File

@@ -308,12 +308,12 @@
<metadata name="tt.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>307, 17</value>
</metadata>
<metadata name="saveFileDialog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>76, 17</value>
</metadata>
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>313, 17</value>
</metadata>
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>45</value>
</metadata>

View File

@@ -17,6 +17,7 @@ using OpenRA;
using OpenRA.FileFormats;
using OpenRA.Traits;
using System.Drawing;
using System.Globalization;
namespace OpenRA.Editor
{
@@ -401,7 +402,7 @@ namespace OpenRA.Editor
{
new LocationInit(new int2(loc % MapSize, loc / MapSize)),
new OwnerInit(parts[0]),
new HealthInit(float.Parse(parts[2])/256),
new HealthInit(float.Parse(parts[2], NumberFormatInfo.InvariantInfo)/256),
new FacingInit((section == "INFANTRY") ? int.Parse(parts[6]) : int.Parse(parts[4])),
new ActorStanceInit(stance),
};

View File

@@ -52,11 +52,13 @@
this.txtTitle = new System.Windows.Forms.TextBox();
this.lblMapName = new System.Windows.Forms.Label();
this.lblMinimap = new System.Windows.Forms.Label();
this.pictureBox1 = new System.Windows.Forms.PictureBox();
((System.ComponentModel.ISupportInitialize)(this.pbMinimap)).BeginInit();
this.pnlBottom.SuspendLayout();
this.splitContainer1.Panel1.SuspendLayout();
this.splitContainer1.Panel2.SuspendLayout();
this.splitContainer1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.SuspendLayout();
//
// MapList
@@ -85,9 +87,9 @@
//
// MapIconsList
//
this.MapIconsList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("MapIconsList.ImageStream")));
this.MapIconsList.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
this.MapIconsList.ImageSize = new System.Drawing.Size(24, 24);
this.MapIconsList.TransparentColor = System.Drawing.Color.Transparent;
this.MapIconsList.Images.SetKeyName(0, "mapicon");
//
// btnCancel
//
@@ -141,6 +143,7 @@
//
// pnlBottom
//
this.pnlBottom.Controls.Add(this.pictureBox1);
this.pnlBottom.Controls.Add(this.txtPathOut);
this.pnlBottom.Controls.Add(this.lblPathOut);
this.pnlBottom.Controls.Add(this.lblPath);
@@ -301,6 +304,16 @@
this.lblMinimap.TabIndex = 6;
this.lblMinimap.Text = "Map preview:";
//
// pictureBox1
//
this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image")));
this.pictureBox1.Location = new System.Drawing.Point(336, -9);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(54, 35);
this.pictureBox1.TabIndex = 7;
this.pictureBox1.TabStop = false;
this.pictureBox1.Visible = false;
//
// MapSelect
//
this.AcceptButton = this.btnOk;
@@ -327,6 +340,7 @@
this.splitContainer1.Panel2.ResumeLayout(false);
this.splitContainer1.Panel2.PerformLayout();
this.splitContainer1.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.ResumeLayout(false);
}
@@ -356,5 +370,6 @@
public System.Windows.Forms.Label lblPathOut;
public System.Windows.Forms.Label lblPath;
public System.Windows.Forms.TextBox txtPathOut;
private System.Windows.Forms.PictureBox pictureBox1;
}
}

View File

@@ -1,13 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using OpenRA.FileFormats;
using System.Windows.Forms;
using System.IO;
using System.Windows.Forms;
using OpenRA.FileFormats;
using OpenRA.Graphics;
namespace OpenRA.Editor
@@ -19,11 +13,11 @@ namespace OpenRA.Editor
public MapSelect()
{
InitializeComponent();
MapIconsList.Images.Add(pictureBox1.Image);
}
private void MapSelect_Load(object sender, EventArgs e)
void MapSelect_Load(object sender, EventArgs e)
{
DirectoryInfo directory = new DirectoryInfo(MapFolderPath);
DirectoryInfo[] directories = directory.GetDirectories();
MapList.Items.Clear();
@@ -33,25 +27,19 @@ namespace OpenRA.Editor
ListViewItem map1 = new ListViewItem(subDirectory.Name);
map1.ImageIndex = 0;
MapList.Items.Add(map1);
}
if (txtNew.Text == "unnamed")
{
//dumb indian code
}
else
{
// hack
if (txtNew.Text != "unnamed")
MapList.Items[0].Selected = true;
}
}
private void MapList_SelectedIndexChanged(object sender, EventArgs e)
void MapList_SelectedIndexChanged(object sender, EventArgs e)
{
if (MapList.SelectedItems.Count == 1)
{
txtNew.Text = MapList.SelectedItems[0].Text;
var map = new Map(new Folder(Path.Combine(MapFolderPath, MapList.SelectedItems[0].Text)));
var map = new Map(new Folder(Path.Combine(MapFolderPath, MapList.SelectedItems[0].Text), 0));
txtTitle.Text = map.Title;
txtAuthor.Text = map.Author;
txtTheater.Text = map.Theater;
@@ -69,7 +57,7 @@ namespace OpenRA.Editor
}
}
private void txtPathOut_TextChanged(object sender, EventArgs e)
void txtPathOut_TextChanged(object sender, EventArgs e)
{
MapFolderPath = txtPathOut.Text;
}

View File

@@ -120,73 +120,58 @@
<metadata name="MapIconsList.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<data name="MapIconsList.ImageStream" mimetype="application/x-microsoft.net.object.binary.base64">
<assembly alias="System.Drawing" name="System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="pictureBox1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAABW
DgAAAk1TRnQBSQFMAwEBAAEoAQABKAEAARgBAAEYAQAE/wEhAQAI/wFCAU0BNgcAATYDAAEoAwABYAMA
ARgDAAEBAQABIAYAASQhAAEBAx8BLAM/AW8DSQGJAzYBWQMYASIDAQECDAADHQEqA0gBhQNCAXYDDwEU
AwEBAgMNARIDMwFSA0MBdwNRAacDDwEU/wAwAAEBAwMBBAM0BFQBwgFlAVMBUgH0AV4CWAHYA0oBjQMW
AR4DAgEDAwABAQMCAQMDGgElA00BlAEIAQkBJgH9A1ABpAM1AVcDQQFzA1MBugJDAVMB4wJTAVQBsgMA
AQH/ADAAAQEDDAEQA0IBdAFVAVQBUQHYAZkBiAGBAf8BoAGMAYUB/wF8AXABZQH4AzoBYQMKAQ4DCgEN
Ax0BKgM/AW0DVAG7ARkCGgH5AQABBQG/Af8BFwEbAUcB+QE5AUYBSwHrAQABBQFEAf8CCAFOAf0DDQER
/wAxAAMDAQQDHQEpA1ABpAFdAVMBUQHtAZsBiwGDAf8BnAGMAYQB/wGWAYQBdwH/AVsBWgFYAdgDMwFT
AzQBVANPAZ0CUwFSAdMCTAFOAfMBMAGeAbsB/wEUAXMBqwH/ARQBdQG/Af8BEAFmAbYB/wEXAY0BwgH/
AUgBYQFpAeEDAQEC/wAwAAEBAwkBDAMwAUwBVgJVAcMBewFqAWAB+gGoAZkBjwH/AZMBhAF3Af8BjAF3
AXAB/wGHAXEBbAH/A1YBvgNTAbwBTAFIAUYB5QF2AVsBUgH6AYMBbQFlAf8BUQFIAUcB/wEOAUsBpAH/
AQMBGgG4Af8BDQFRAZUB/wEZAZUBwAH/AUABagGBAesDMwFSAw0BEgMCAQP/ACUAAwEBAgMYASEDSwGP
A1EB3AGbAY4BhQH9AaEBkQGJAf8BhwFzAWsB/wGBAW4BZQH/AXUBZwFgAf8BUwFKAUMB/QFHAT4BOwH8
AZYBhgF5Af8BowGSAYoB/wFTAUoBUwH/AQMBGAHzAf8BEgFgAaYB/wEVAXYBlQH/AS8B/QH+Af8BBwEy
AZQB/wEVAXoByAH/AgABVAH/A00BlAMZASP/ACUAAwYBCAMtAUYDUwG8AWoBZQFiAfQBqgGbAZEB/wGJ
AXQBbQH/AYwBdwFuAf8BmgGJAYEB/wGLAXcBbwH/AXUBZQFeAf8BhgFxAWkB/wGVAYYBegH/AUEBPAFK
Af8BEwEfAYgB/wEKAREBrgH/ARwBegGVAf8BKgHfAeIB/wEOAVMBdwH/AQkBOQG8Af8BGQGBAaoB/wEK
ARIBqQH/ASABJQFSAfcDUQGg/wAlAAMQARUDQQFyA08B1gGTAYQBcgH9AZoBiQGDAf8BjwGAAXMB/wGW
AYYBeAH/AZ8BjQGEAf8BlgGFAXkB/wGUAYQBdwH/AZQBgwF3Af8BlwGHAYAB/wFqAV8BXAH/AWMBVwFW
Af8BYAFVAVMB/wFTAVcBWAH/ASIBYwGaAf8BDQFLAZ0B/wEgAY4BsAH/AUwBnAGvAf8BaAGaAa4B/wE7
AUsBWgH+A1ABn/8AJQADGAEiA00BlQF8AXUBbwH8AagBmAGOAf8BkQGBAXUB/wGNAXcBcAH/AZoBiQGB
Af8BkwGDAXYB/wGZAYgBgQH/AZwBiwGDAf8BlQGGAXgB/wGWAYYBeQH/AaABjwGHAf8BlgGFAXgB/wF2
AW8BbAH/AUwBbQGBAf8BHgE7AV0B/wEdAZ4BhAH/AVMBlwGpAf8BhgHFAd8B/wGIAb4B1wH/AUUBXwFi
AfsDQQFz/wAlAAMYASIDTQGVAZ0BjwGGAf8BmwGLAYMB/wGGAXEBagH/AYUBbwFoAf8BiAFzAWwB/wGJ
AXQBbAH/AZABdwFvAf8BlQGEAXcB/wGWAYUBeAH/AZ8BjwGGAf8BmQGLAYQB/wFtAYABhgH/AVYBkwGw
Af8BWgGmAccB/wFMAYMBpAH/AQUBFgGgAf8BYwGaAbAB/wGCAb8B2QH/AXEBrQHIAf8BXQFpAXIB3gMq
AUD/ACEAAwMBBAMmATgDUAGjAZoBjAGEAf8BpwGWAY4B/wGXAYYBegH/AZQBggF2Af8BlAGDAXcB/wGW
AYUBeAH/AZIBeQFyAf8BjwF5AXIB/wGVAYQBeAH/AY8BdgFrAf8BcAGAAYcB/wFaAZcBswH/AU8BmgHC
Af8BZQG1AdQB/wFlAaQBvgH/AR4BKgE1Af8BeAG6AdMB/wGAAbsB1QH/AYEBvwHaAf8BVQFWAVkBtQMP
ART/ACEAAwoBDQNBAXMDUgHPAasBmgGRAf8BmgGJAYIB/wGOAXgBcQH/AZEBgQF1Af8BlQGEAXcB/wGY
AYgBgAH/AYsBdQFuAf8BkQF6AXMB/wF4AW4BagH/AW8BrwHIAf8BWQGoAc0B/wFZAasBzAH/AVUBpgHJ
Af8BdQHDAeAB/wF6AcIB3wH/AYMBwAHbAf8BggHBAd0B/wFvAa4ByAH/AYsBvwHbAf8DSAGGAwUBB/8A
IQADHgErA1EBoAFSAVABTQHtAYMBbwFoAf8BgwFuAWcB/wGBAWwBZQH/AYEBbQFmAf8BiAFzAWsB/wGH
AXIBawH/AZQBhAF4Af8BhQF2AXMB/wFUAWwBgwH/AU0BhAGmAf8BZgGwAdAB/wFdAaoBywH/AWEBsQHR
Af8BdgG/Ad0B/wGFAcgB4wH/AYEBxAHfAf8BiAHHAeIB/wF3AbYB0QH/AW0BngG2Af8DPgFrAwIBA/8A
IQADOAFcA1UByAFrAlwB+AGRAYABdAH/AZgBiAGAAf8BlQGEAXgB/wGNAXcBcAH/AZUBhAF4Af8BmAGH
AYAB/wGTAYMBdwH/AWgBgAGLAf8BVgGQAbEB/wE3AUoBagH/AWMBqQHKAf8BYAGuAc8B/wFhAawBzAH/
AXYBvwHcAf8BiQHMAecB/wGDAcYB4gH/AYUBxQHgAf8BhwHDAd4B/wFIAYMBmwH/AzwBaAMEAQX/ACEA
A0kBiQFXAVYBVQHiAaQBkwGKAf8BrAGcAZMB/wGlAZUBjAH/AZYBhgF5Af8BkAF5AXQB/wGZAYgBgQH/
AaIBkQGJAf8BbgFdAVUB/wFkAZUBqwH/AWgBtQHVAf8BcgHBAd0B/wF0AbsB2wH/AV4BrAHOAf8BXwGt
AcwB/wF4AcMB4AH/AYgBywHmAf8BhQHGAeQB/wGDAcYB4gH/AYcBvwHdAf8BQwFVAWkB7gMbASb/ACUA
A08BpQFiAlgB7wGjAZMBigH/AZgBhwGAAf8BnwGOAYYB/wGdAYwBhAH/AZABeQFzAf8BiAFyAWsB/wGI
AXYBbwH/AWoBhQGPAf8BaAGwAdAB/wFvAbwB2gH/AWYBtgHWAf8BXwGxAdAB/wFdAa8B0AH/AXEBvQHa
Af8BcAG7AdoB/wGAAcQB3wH/AY8BzQHpAf8BigHJAeUB/wFbAZcBrAH9A0QBeQMAAQH/ACUAA1MBsQFs
Al8B8wGjAZIBigH/AZUBgwF4Af8BlAGCAXcB/wGUAYUBdwH/AYQBiwGNAf8BWwFzAYYB/wFcAYcBnQH/
AWEBrwHSAf8BbwG7AdoB/wF3AcEB3gH/AWwBuQHXAf8BYQGzAdIB/wFpAbkB1gH/AYABxQHhAf8BeAHC
Ad8B/wGAAcUB4QH/AYUBxAHhAf8BiQHGAeIB/wFSAXMBhAHzAzEBTv8AKQADUQHHAXsBcQFnAfgBqwGb
AZIB/wGQAYABdQH/AYQBbwFmAf8BcgGaAasB/wFiAasBzwH/AWQBsgHTAf8BbQG8AdoB/wFrAbgB1QH/
AXEBvgHbAf8BdgG/Ad0B/wFwAb4B2wH/AWgBuQHWAf8BgAHHAeEB/wGJAcsB5AH/AYoBzAHnAf8BjQHR
AewB/wGFAckB5gH/AW0BswHUAf8DTgGYAzABTAMAAQEDAAEB/wAhAAM+AWwBYgFfAV4ByQGJAYIBfAH1
AaQBkwGLAf8BhgGeAakB/wFnAa8B0gH/AXgBxAHgAf8BdgHCAd4B/wF2AcIB3gH/AXEBvwHbAf8BcwG8
AdoB/wFyAbsB2AH/AXQBuwHZAf8BdAG/Ad0B/wFhAZ0BwwH/AUEBYwGUAf8BbQGnAcgB/wGHAcQB4AH/
AYgBzQHpAf8BXwFxAXkB4AMsAUQDBwEJAwQBBgMAAQH/ACEAAwQBBQMZASMDNAFUA0oBiQFYAlsBwQFh
AXIBeQHiAW4BlwGoAfUBfQG0Ac8B/gGAAcUB4gH/AXIBvAHaAf8BcQG8AdoB/wFwAbsB2AH/AXUBvAHZ
Af8BjgHPAesB/wFtAa8B0gH/AU8BdQGkAf8BbAGrAc0B/wFuAa8BzwH/AY8BygHnAf8BWAFjAWYB0QMi
ATH/AD0AAwEBAgMJAQwDGgElA0IBdgFSAVwBZQHqAYEBxAHgAf4BigHHAeQB/wF5AcAB3QH/AYoBxgHi
Af8BlgHUAe4B/wF5AbgB1wH/AWEBoQHIAf8BaQGmAcwB/wFZAZIBvwH/AX8BwwHdAf4CRgFHAYEDAwEE
/wBJAAMCAQMDEAEWAzIBUANKAY0DUAGdA1IBoQFmAXUBewHgAXIBrwHFAf0BYAGXAb4B/wFgAZQBwAH/
AW8BqAHOAf8BQAF5AaoB/wM1AVYDAAEB/wBVAAMCAQMDBgEIAwoBDQMoAT0DQAFxAU8CUQGcAVsBYgFo
AdYBZQF/AZMB8gFKAX0BoQH8AyABL/8AcAABAQMIAQsDJAE2A0YBgQMDAQT/ADEAAUIBTQE+BwABPgMA
ASgDAAFgAwABGAMAAQEBAAEBBQABIAEBFgAD/wEAAeABOAEBCQABwAEAAQEJAAHAAQABAwkAAcABAAED
CQABgAsAAYALAAGACwABgAsAAYALAAGAPQABAQsAAQELAAEDIwABBwkAAfABAAEHCQAB/gEAAQcJAAH/
AcABDwkAAf8B/gEPCQAL
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAArjSURBVFhHrVcJUFRXFmVpkE0Wg0ZEMoIBl8RodGSMmVES
R0NiChVxCyIIAmFfhe6maeh9AZodZHFpUFQWcQmKisiuLKLEKFFEcIuljtZMYmI5Uc/c14EZYyapmpq8
qlP/9/+/373v3HPu+19P7/cf+jSlEWHCOEuzZfNn2Yvo3Pj3D/PTjCwYAwtgyeFwFk163Tos2tvtWJZw
zeUjmYFP6vL9fhw31vRPI8/9LnmMrtCWZhtPcJ7hbC/W8DwfnS0LR0t5NKoFazHcqsaprE3YGbMQLrYW
W/7fBAxogjGEd5wcXuPK4jxa60qD75VrfIbKMrYMD7VlPX/Ul49rp8RoyPbFIaEnhpuU6NRGoVa2HKuX
zEj/X8vAVmlIsDY2Nna2sjBd+ee5b7Yq4tfgfm8a7pxNw60OFa6e4uFmhxr/uFSIexfz0VsZh7PaIJzb
F42u8hAcV2/ANz3KF8Hub52kucb+Fv+jAccbm5quWOfxYX5y6Mcnq/IDh1oreX/vOcx9drdbjTudUgye
Tsb1piT0H4/DACUw0JiEh33ZePhlFi7UxOCLtI1o2x6IW2cUqM/egLtdediV9MnjSTYm60bE+Ys8rMzN
zd1c3/vLgVRV3lN1XjkqyzNoNaEUjIvrLXwMtQsw1JqEy/UxuHg0DJcp+KXj8bh0IobOucSGDA8vpqE6
+VNINixBU0kQbp5JJ8jxDSXdeyAE5arlAxTZ/BfRra2tS8Tqwh9L9tQhq6gSabm7odLkoP8oF90V/rjR
LsRgExc32oS40ZFCiQgw2Mgj+gW42pBI4OPSsUh6Jgn1mX4ojXJH865IXDzCx50uDe6fV+P8oQQ4Tx6r
pOATmVteKgdjXu/Mhk1h328rO4TMwn3gp2YhmquEX7gAfYfjcb4qGH0HQ3D5WAiunU7ApfpofH0ymhIS
UwkSqQRb6VoYvm6IZvXG7W4VBVbjwQU1/tanIQZkGDqtwOmSjzHXxXaLsZnZpy85QpdAnfunax/mllRD
ma2FQJyHxORMJAqzwBVpUJgtRLF8E7bxlqKjzA991QEYaIijgFwMt6VggJK60hCFr46F4sqprbjbLaf6
S0icIh1rwy1i3OvNQff+GEgj3r87/c1JLIGfjX1LP/F6KJDlIy1vN4SyQohVRdAU7IVIXQxuSiZ8/OOw
JYyP8EiqfV04Lh2JoHIwMSbiwoFgtO1ah97aTSTMKGIjDl/WhONWl5jKlYrbZ9OJGRWVIg/+axf8SK5a
8WoC5fPm//l2qqIIOaVVECm3QZG5C7L0HZCklYJLTCQKNUiWFYAnykJYRDRaywNxrTGRyhKNKyciiIEY
dFd743z1ZioT6eJkHK41kXDbRFSKbHzTpcJwsxoHc71fTLGzyBix+L/zUFlaWX+VTcFZ4NzSamQXV1Lw
EggkeeBRAsnSfKgyy6DKKcNnxEahIgRfHg5B/7EoDDYn4ma7FD0VAThfG4Cemi3EDI90IKWVq3GbHHKz
nUpCbrh6QokEr5mPTEw4773MQij9aPZY5fNYrC6hEhRAnrkTjJEkcS5ieWokS/IhUZdCrtlJbGQiIjQQ
/YeD0VMVhoTVbmgs8iVBctG11xtdlUHkDGpQpIN7velUAhmG2+W43spclI6K9JWY6TyxlGKOGxWjG53s
5HBMBpZ7+iI6UY44vhoiVTH4ohxCLoTyArqu1OlBll6CZHE20mI9EOjuis3L5uFCbTh6D4Wgu8offYeC
qSRccgcPD/qyMEiBb55VYahFgSuNckhjlr2YaGMqH2ntOiKmEAQGBgbnNviGPg2LFSM4UoiAUD78ghLo
yEVojBhRiQqEx0nAI5sKiBH/z+OgVfuic7c/Lh+NwZndfughDfQdCtL1hKFmEW51ymn1EjpSAh1ytFCL
Pl+nhKebY8XLTYn16DWEgzY2ttdzyI6yjB06+uP4acRANokvhzRRjBR5oe4aN4WupeYgNCIWZ2vj0b17
I/UKf3Tt34jOis9otTyyILmkORUDTRKyqxj9DSI07Y0k26Yhes38Roo3YVQH7OVhGiGR0K7O0epEqCRB
shKwRIQkQrZqMelAIM6ncihII9uQSCXZEi2HSMjDXvVqXKSGdTLbg6z6OVkwGcfLQvHB4jkYbJMS/UL0
1aeSM6So1Hg/oljTR93AupEVYSXhGE+soZa8X+cIlgALzFhg5/HJ6YhIzACXVs9EKiHRMmckSfMQGx0G
rXIldU4/dJSvJx3EoSDZC7OszHByTxTOfZGEq00y9B3lo70iElamhp4Ujy1eN8wIrEOd0BRW6DpiWEwq
giKSdYiIl5IOUskRKh39AmKECZNP7Mg0O6Ci59Pzd4O/NQK7FBvQULyGOmMUpBsX4KqjGTL5qzHQIqJy
iNG6Px5dtYlY7jqhmeKxvUE32C7FGKiP58uf5W6v0ekgPkmNrYIMor4YqcoinQBFdGQrl2ZsB4/cwBJR
Zml/+k0sxcQnQZzgDWHIIni9NQHSHU5IS15EPSEN11ok6G9UQs1dh/lvv/GdkZHR3NEETOhkIUE7zvb1
wRRFwYv0vD3UBTORJCEdSHMhydgFCXVHVnuWEGvZrDewjqmzKbHDNjHmFl5KBlasWokiFwtop5nhyL6p
2JO/CN2HBdSgMpHJ88GH82c8s7b4T1vmUPA3CFxCp19QzBPdtpxdRqon2kkDKRSYXeOJcyBUFMI7IJ70
kYsEgRLTXGbgrzNnwmWqC2Y4OsLZZjx43El4ggXI1zohy24MAFd4u9tj8YLpmD71DTiMtwGHY1A7ygB7
52N29CLULV7qeT+HnFCoPairu87/RC9rTjE8JYJoY/p4lR8lVQAp7Rc+/pHY7OKE61NN8a21MbQpfwC+
m4fbQ7MQNdcCfWMMsTPWDi2yyRgyNkDhOGPYGhpeo97jP5oAcwJjYQ4hk3DOyzvwn9v3HtUJjHXAzSFc
RHHlCAznIyAsCZa2k56u3xT2gtHPmGHCdXtnHh47mKBqjS1K5Q7ITrCjlf8RqXPM0bDUCr3bp2C9nj70
DDinKMYSwmujCbAjY4G9ZrN3tyMLP/D4IT1vP/J21CBeoKaa55MTUhAeK8J7i91v0DMdxqZmgw7Oc74N
iFQ8T5YX6ZzisWI9Uiebo8rKCOcsjLAv1Z72BydUxE5ElcTuKf2P9RunEeGzmD8bTIzsI0I7wc7xdgyf
NiayWVoueZ1qz7zPT8l5McnBqZieYZsYO/a4zF7yaKWP4HlASMKLj9xX4WrXWzhdMxUFlkaI1tPDAZE9
Htc4wWuhOas5Y/oXgUezYDdZdmK2N6zzCfuB1V1GFkulNswT5cFllms73f+QMIWwjCDjGJnUW1rbfu2z
wvXJk85pUH5gieNJdugvd8QV7RSc0TjgiMwevkvH3np1xa/+ZplZE9jecNh14UcPmO8VmnLyfzGr/feU
mB/dY7VjybItdSZhrb6+fvEYY6PuMWOMn7ZbGeILfQNYmnN+mO1o/OBdCw74HAMsMjR8NjL/r+Yx+p3H
XhjKZs93uxMZr4Z/uOb54qWf3bGf4pw3EpC1UPYsS3g0EU9K4uBsI86zYHPOY3MOp4zuRRLyDfT1O941
Mrzvb86Bob4h+0r6zcEmdiFICA2vvT55kLFBYOJhnYt9or06TOmCJ7HTbGxgADpndmaJMU29QxAQKvTo
vhnHkN1nX12/OlgCNoT3CZsIvgRWc9ao2IT/bTD3+BID7N1/zisBWCKOBB/CagKf8PboJP8CoUmu3yhA
ga8AAAAASUVORK5CYII=
</value>
</data>
</root>

View File

@@ -8,6 +8,7 @@
*/
#endregion
using System;
using System.Drawing;
using System.Drawing.Imaging;
using OpenRA.FileFormats;
@@ -17,19 +18,34 @@ namespace OpenRA.Editor
{
static class RenderUtils
{
public static ColorPalette MakeSystemPalette(Palette p)
{
ColorPalette pal;
using (var b = new Bitmap(1, 1, PixelFormat.Format8bppIndexed))
pal = b.Palette;
for (var i = 0; i < 256; i++)
pal.Entries[i] = p.GetColor(i);
return pal;
}
public static Bitmap RenderTemplate(TileSet ts, ushort n, Palette p)
{
var template = ts.Templates[n];
var tile = ts.Tiles[n];
var bitmap = new Bitmap(ts.TileSize * template.Size.X, ts.TileSize * template.Size.Y);
var bitmap = new Bitmap(ts.TileSize * template.Size.X, ts.TileSize * template.Size.Y,
PixelFormat.Format8bppIndexed);
bitmap.Palette = MakeSystemPalette(p);
var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
unsafe
{
int* q = (int*)data.Scan0.ToPointer();
var stride = data.Stride >> 2;
byte* q = (byte*)data.Scan0.ToPointer();
var stride = data.Stride;
for (var u = 0; u < template.Size.X; u++)
for (var v = 0; v < template.Size.Y; v++)
@@ -38,13 +54,13 @@ namespace OpenRA.Editor
var rawImage = tile.TileBitmapBytes[u + v * template.Size.X];
for (var i = 0; i < ts.TileSize; i++)
for (var j = 0; j < ts.TileSize; j++)
q[(v * ts.TileSize + j) * stride + u * ts.TileSize + i] = p.GetColor(rawImage[i + ts.TileSize * j]).ToArgb();
q[(v * ts.TileSize + j) * stride + u * ts.TileSize + i] = rawImage[i + ts.TileSize * j];
}
else
{
for (var i = 0; i < ts.TileSize; i++)
for (var j = 0; j < ts.TileSize; j++)
q[(v * ts.TileSize + j) * stride + u * ts.TileSize + i] = Color.Transparent.ToArgb();
q[(v * ts.TileSize + j) * stride + u * ts.TileSize + i] = 0;
}
}
@@ -56,18 +72,21 @@ namespace OpenRA.Editor
{
var frame = shp[0];
var bitmap = new Bitmap(shp.Width, shp.Height);
var bitmap = new Bitmap(shp.Width, shp.Height, PixelFormat.Format8bppIndexed);
bitmap.Palette = MakeSystemPalette(p);
var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
unsafe
{
int* q = (int*)data.Scan0.ToPointer();
var stride2 = data.Stride >> 2;
byte* q = (byte*)data.Scan0.ToPointer();
var stride2 = data.Stride;
for (var i = 0; i < shp.Width; i++)
for (var j = 0; j < shp.Height; j++)
q[j * stride2 + i] = p.GetColor(frame.Image[i + shp.Width * j]).ToArgb();
q[j * stride2 + i] = frame.Image[i + shp.Width * j];
}
bitmap.UnlockBits(data);
@@ -102,7 +121,12 @@ namespace OpenRA.Editor
}
catch { }
return new ActorTemplate { Bitmap = bitmap, Info = info, Centered = !info.Traits.Contains<BuildingInfo>() };
return new ActorTemplate
{
Bitmap = bitmap,
Info = info,
Appearance = info.Traits.GetOrDefault<EditorAppearanceInfo>()
};
}
}
@@ -114,18 +138,19 @@ namespace OpenRA.Editor
var shp = new ShpReader(s);
var frame = shp[shp.ImageCount - 1];
var bitmap = new Bitmap(shp.Width, shp.Height);
var bitmap = new Bitmap(shp.Width, shp.Height, PixelFormat.Format8bppIndexed);
bitmap.Palette = MakeSystemPalette(p);
var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
unsafe
{
int* q = (int*)data.Scan0.ToPointer();
var stride = data.Stride >> 2;
byte* q = (byte*)data.Scan0.ToPointer();
var stride = data.Stride;
for (var i = 0; i < shp.Width; i++)
for (var j = 0; j < shp.Height; j++)
q[j * stride + i] = p.GetColor(frame.Image[i + shp.Width * j]).ToArgb();
q[j * stride + i] = frame.Image[i + shp.Width * j];
}
bitmap.UnlockBits(data);

View File

@@ -15,6 +15,7 @@ using System.Drawing.Imaging;
using System.Linq;
using System.Windows.Forms;
using OpenRA.FileFormats;
using OpenRA.Traits;
namespace OpenRA.Editor
{
@@ -34,6 +35,7 @@ namespace OpenRA.Editor
public bool IsPanning;
public event Action AfterChange = () => { };
public event Action<string> MousePositionChanged = _ => { };
Dictionary<string, ActorTemplate> ActorTemplates = new Dictionary<string, ActorTemplate>();
Dictionary<int, ResourceTemplate> ResourceTemplates = new Dictionary<int, ResourceTemplate>();
@@ -44,6 +46,7 @@ namespace OpenRA.Editor
TileSet = ts;
Palette = p;
Brush = null;
PlayerPalettes = null;
Chunks.Clear();
}
@@ -87,6 +90,8 @@ namespace OpenRA.Editor
{
base.OnMouseWheel(e);
if (Map == null) return;
Zoom *= e.Delta > 0 ? 4.0f / 3.0f : .75f;
Invalidate();
@@ -114,8 +119,11 @@ namespace OpenRA.Editor
{
base.OnMouseMove(e);
if (Map == null) return;
var oldMousePos = MousePos;
MousePos = new int2(e.Location);
MousePositionChanged(GetBrushLocation().ToString());
if (e.Button == MouseButtons.Middle || (e.Button != MouseButtons.None && IsPanning))
Scroll(oldMousePos - MousePos);
@@ -167,7 +175,7 @@ namespace OpenRA.Editor
{
for (; ; )
{
var q = p+d;
var q = p + d;
if (!Map.IsInMap(q)) return p;
if (!Map.MapTiles[q.X, q.Y].Equals(replace)) return p;
p = q;
@@ -199,7 +207,7 @@ namespace OpenRA.Editor
{
type = Brush.N,
index = template.PickAny ? byte.MaxValue : (byte)z,
image = template.PickAny ? (byte)((u + pos.X) % 4 + ((v + pos.Y) % 4)*4) : (byte)z,
image = template.PickAny ? (byte)((u + pos.X) % 4 + ((v + pos.Y) % 4) * 4) : (byte)z,
};
var ch = new int2((pos.X + u) / ChunkSize, (pos.Y + v) / ChunkSize);
@@ -332,6 +340,8 @@ namespace OpenRA.Editor
{
base.OnMouseDown(e);
if (Map == null) return;
if (!IsPanning)
{
if (e.Button == MouseButtons.Right) Erase();
@@ -401,19 +411,25 @@ namespace OpenRA.Editor
return new int2(vX / TileSet.TileSize, vY / TileSet.TileSize);
}
void DrawActor(System.Drawing.Graphics g, int2 p, ActorTemplate t)
void DrawActor(System.Drawing.Graphics g, int2 p, ActorTemplate t, ColorPalette cp)
{
float OffsetX = t.Centered ? t.Bitmap.Width / 2 - TileSet.TileSize/2 : 0;
var centered = t.Appearance == null || !t.Appearance.RelativeToTopLeft;
float OffsetX = centered ? t.Bitmap.Width / 2 - TileSet.TileSize / 2 : 0;
float DrawX = TileSet.TileSize * p.X * Zoom + Offset.X - OffsetX;
float OffsetY = t.Centered ? t.Bitmap.Height / 2 - TileSet.TileSize/2 : 0;
float OffsetY = centered ? t.Bitmap.Height / 2 - TileSet.TileSize / 2 : 0;
float DrawY = TileSet.TileSize * p.Y * Zoom + Offset.Y - OffsetY;
float width = t.Bitmap.Width * Zoom;
float height = t.Bitmap.Height * Zoom;
RectangleF sourceRect = new RectangleF(0, 0, t.Bitmap.Width, t.Bitmap.Height);
RectangleF destRect = new RectangleF(DrawX, DrawY, width, height);
var restorePalette = t.Bitmap.Palette;
if (cp != null) t.Bitmap.Palette = cp;
g.DrawImage(t.Bitmap, destRect, sourceRect, GraphicsUnit.Pixel);
if (cp != null) t.Bitmap.Palette = restorePalette;
}
void DrawImage(System.Drawing.Graphics g, Bitmap bmp, int2 location)
@@ -433,35 +449,55 @@ namespace OpenRA.Editor
void DrawActorBorder(System.Drawing.Graphics g, int2 p, ActorTemplate t)
{
float OffsetX = t.Centered ? t.Bitmap.Width / 2 - TileSet.TileSize / 2 : 0;
var centered = t.Appearance == null || !t.Appearance.RelativeToTopLeft;
float OffsetX = centered ? t.Bitmap.Width / 2 - TileSet.TileSize / 2 : 0;
float DrawX = TileSet.TileSize * p.X * Zoom + Offset.X - OffsetX;
float OffsetY = t.Centered ? t.Bitmap.Height / 2 - TileSet.TileSize / 2 : 0;
float OffsetY = centered ? t.Bitmap.Height / 2 - TileSet.TileSize / 2 : 0;
float DrawY = TileSet.TileSize * p.Y * Zoom + Offset.Y - OffsetY;
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,
DrawX, DrawY,
t.Bitmap.Width * Zoom, t.Bitmap.Height * Zoom);
}
ColorPalette GetPaletteForPlayer(string name)
{
var pr = Map.Players[name];
var pcpi = Rules.Info["player"].Traits.Get<PlayerColorPaletteInfo>();
var remap = new PlayerColorRemap(pr.Color, pr.Color2, pcpi.PaletteFormat);
return RenderUtils.MakeSystemPalette(new Palette(Palette, remap));
}
Cache<string, ColorPalette> PlayerPalettes;
ColorPalette GetPaletteForActor(ActorReference ar)
{
if (PlayerPalettes == null)
PlayerPalettes = new Cache<string, ColorPalette>(GetPaletteForPlayer);
var ownerInit = ar.InitDict.GetOrDefault<OwnerInit>();
if (ownerInit == null)
return null;
return PlayerPalettes[ownerInit.PlayerName];
}
protected override void OnPaint(PaintEventArgs e)
{
if (Map == null) return;
if (TileSet == null) return;
for( var u = 0; u < Map.MapSize.X; u += ChunkSize )
for (var u = 0; u < Map.MapSize.X; u += ChunkSize)
for (var v = 0; v < Map.MapSize.Y; v += ChunkSize)
{
var x = new int2(u/ChunkSize,v/ChunkSize);
var x = new int2(u / ChunkSize, v / ChunkSize);
if (!Chunks.ContainsKey(x)) Chunks[x] = RenderChunk(u / ChunkSize, v / ChunkSize);
Bitmap bmp = Chunks[x];
float DrawX = TileSet.TileSize* 1f * (float)ChunkSize * (float)x.X * Zoom + Offset.X;
float DrawX = TileSet.TileSize * 1f * (float)ChunkSize * (float)x.X * Zoom + Offset.X;
float DrawY = TileSet.TileSize * 1f * (float)ChunkSize * (float)x.Y * Zoom + Offset.Y;
RectangleF sourceRect = new RectangleF(0, 0, bmp.Width, bmp.Height);
RectangleF destRect = new RectangleF(DrawX, DrawY, bmp.Width * Zoom, bmp.Height * Zoom);
@@ -475,7 +511,8 @@ namespace OpenRA.Editor
Map.Height * TileSet.TileSize * Zoom);
foreach (var ar in Map.Actors)
DrawActor(e.Graphics, ar.Value.Location(), ActorTemplates[ar.Value.Type]);
DrawActor(e.Graphics, ar.Value.Location(), ActorTemplates[ar.Value.Type],
GetPaletteForActor(ar.Value));
foreach (var wp in Map.Waypoints)
e.Graphics.DrawRectangle(Pens.LimeGreen,
@@ -491,7 +528,8 @@ namespace OpenRA.Editor
Brush.Bitmap.Height * Zoom);
if (Actor != null)
DrawActor(e.Graphics, GetBrushLocation(), Actor);
DrawActor(e.Graphics, GetBrushLocation(), Actor, null); /* todo: include the player
* in the brush so we can color new buildings too */
if (Resource != null)
DrawImage(e.Graphics, Resource.Bitmap, GetBrushLocation());

View File

@@ -21,9 +21,11 @@ namespace OpenRA.FileFormats
readonly uint[] hashes;
readonly Stream s;
readonly ZipPackage pkg;
int priority;
public CompressedPackage(string filename)
public CompressedPackage(string filename, int priority)
{
this.priority = priority;
s = FileSystem.Open(filename);
pkg = (ZipPackage)ZipPackage.Open(s, FileMode.Open);
hashes = pkg.GetParts()
@@ -41,5 +43,11 @@ namespace OpenRA.FileFormats
{
return hashes.Contains(PackageEntry.HashFilename(filename));
}
public int Priority
{
get { return 500 + priority; }
}
}
}

22
OpenRA.FileFormats/Exts.cs Normal file → Executable file
View File

@@ -73,5 +73,27 @@ namespace OpenRA
{
return mi.GetCustomAttributes(typeof(T), true).Length != 0;
}
public static T[] GetCustomAttributes<T>( this MemberInfo mi, bool inherit )
where T : class
{
return (T[])mi.GetCustomAttributes( typeof( T ), inherit );
}
public static T[] GetCustomAttributes<T>( this ParameterInfo mi )
where T : class
{
return (T[])mi.GetCustomAttributes( typeof( T ), true );
}
public static T Clamp<T>(this T val, T min, T max) where T : IComparable<T>
{
if (val.CompareTo(min) < 0)
return min;
else if (val.CompareTo(max) > 0)
return max;
else
return val;
}
}
}

29
OpenRA.FileFormats/FieldLoader.cs Normal file → Executable file
View File

@@ -13,6 +13,7 @@ using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Globalization;
namespace OpenRA.FileFormats
{
@@ -99,11 +100,19 @@ namespace OpenRA.FileFormats
else if (fieldType == typeof(float))
{
float res;
if (float.TryParse(x.Replace("%",""), out res))
if (float.TryParse(x.Replace("%",""), System.Globalization.NumberStyles.Any, NumberFormatInfo.InvariantInfo, out res))
return res * (x.Contains( '%' ) ? 0.01f : 1f);
return InvalidValueAction(x,fieldType, field);
}
else if (fieldType == typeof(decimal))
{
decimal res;
if (decimal.TryParse(x.Replace("%",""), System.Globalization.NumberStyles.Any, NumberFormatInfo.InvariantInfo, out res))
return res * (x.Contains( '%' ) ? 0.01m : 1m);
return InvalidValueAction(x,fieldType, field);
}
else if (fieldType == typeof(string))
return x;
@@ -111,9 +120,9 @@ namespace OpenRA.FileFormats
{
var parts = x.Split(',');
if (parts.Length == 3)
return Color.FromArgb(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]));
return Color.FromArgb(int.Parse(parts[0]).Clamp(0, 255), int.Parse(parts[1]).Clamp(0, 255), int.Parse(parts[2]).Clamp(0, 255));
if (parts.Length == 4)
return Color.FromArgb(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), int.Parse(parts[3]));
return Color.FromArgb(int.Parse(parts[0]).Clamp(0, 255), int.Parse(parts[1]).Clamp(0, 255), int.Parse(parts[2]).Clamp(0, 255), int.Parse(parts[3]).Clamp(0, 255));
return InvalidValueAction(x,fieldType, field);
}
@@ -177,11 +186,12 @@ namespace OpenRA.FileFormats
{
var ret = new Dictionary<FieldInfo, Func<string, Type, MiniYaml, object>>();
foreach( var field in type.GetFields() )
foreach( var ff in type.GetFields() )
{
var load = (LoadAttribute[])field.GetCustomAttributes( typeof( LoadAttribute ), false );
var loadUsing = (LoadUsingAttribute[])field.GetCustomAttributes( typeof( LoadUsingAttribute ), false );
var fromYamlKey = (FieldFromYamlKeyAttribute[])field.GetCustomAttributes( typeof( FieldFromYamlKeyAttribute ), false );
var field = ff;
var load = field.GetCustomAttributes<LoadAttribute>( false );
var loadUsing = field.GetCustomAttributes<LoadUsingAttribute>( false );
var fromYamlKey = field.GetCustomAttributes<FieldFromYamlKeyAttribute>( false );
if( loadUsing.Length != 0 )
ret[ field ] = ( _1, fieldType, yaml ) => loadUsing[ 0 ].LoaderFunc( field )( yaml );
else if( fromYamlKey.Length != 0 )
@@ -262,7 +272,10 @@ namespace OpenRA.FileFormats
if (f.FieldType == typeof(Color))
{
var c = (Color)v;
return "{0},{1},{2},{3}".F(c.A,c.R,c.G,c.B);
return "{0},{1},{2},{3}".F(((int)c.A).Clamp(0, 255),
((int)c.R).Clamp(0, 255),
((int)c.G).Clamp(0, 255),
((int)c.B).Clamp(0, 255));
}
return f.FieldType.IsArray

View File

@@ -12,6 +12,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Linq;
namespace OpenRA.FileFormats
{
@@ -33,14 +34,16 @@ namespace OpenRA.FileFormats
}
}
static int order = 0;
static IFolder OpenPackage(string filename)
{
if (filename.EndsWith(".mix"))
return new Package(filename);
return new Package(filename, order++);
else if (filename.EndsWith(".zip"))
return new CompressedPackage(filename);
return new CompressedPackage(filename, order++);
else
return new Folder(filename);
return new Folder(filename, order++);
}
public static void Mount(string name)
@@ -73,9 +76,14 @@ namespace OpenRA.FileFormats
static Stream GetFromCache( Cache<uint, List<IFolder>> index, string filename )
{
foreach( var folder in index[ PackageEntry.HashFilename( filename ) ] )
if (folder.Exists(filename))
return folder.GetContent(filename);
var folder = index[PackageEntry.HashFilename(filename)]
.Where(x => x.Exists(filename))
.OrderBy(x => x.Priority)
.FirstOrDefault();
if (folder != null)
return folder.GetContent(filename);
return null;
}
@@ -88,11 +96,13 @@ namespace OpenRA.FileFormats
return ret;
}
foreach( IFolder folder in mountedFolders )
{
if (folder.Exists(filename))
return folder.GetContent(filename);
}
var folder = mountedFolders
.Where(x => x.Exists(filename))
.OrderByDescending(x => x.Priority)
.FirstOrDefault();
if (folder != null)
return folder.GetContent(filename);
throw new FileNotFoundException( string.Format( "File not found: {0}", filename ), filename );
}

View File

@@ -17,7 +17,9 @@ namespace OpenRA.FileFormats
{
readonly string path;
public Folder(string path) { this.path = path; }
int priority;
public Folder(string path, int priority) { this.path = path; this.priority = priority; }
public Stream GetContent(string filename)
{
@@ -28,12 +30,18 @@ namespace OpenRA.FileFormats
public IEnumerable<uint> AllFileHashes()
{
foreach( var filename in Directory.GetFiles( path, "*", SearchOption.TopDirectoryOnly ) )
yield return PackageEntry.HashFilename( filename );
yield return PackageEntry.HashFilename( Path.GetFileName(filename) );
}
public bool Exists(string filename)
{
return File.Exists(Path.Combine(path,filename));
}
public int Priority
{
get { return priority; }
}
}
}

View File

@@ -37,10 +37,8 @@ namespace OpenRA.FileFormats.Graphics
Size WindowSize { get; }
void Begin();
void End();
void Clear( Color color );
void Present();
void Present( IInputHandler inputHandler );
void DrawIndexedPrimitives( PrimitiveType type, Range<int> vertexRange, Range<int> indexRange );
void DrawIndexedPrimitives( PrimitiveType type, int vertexPool, int numPrimitives );
@@ -52,13 +50,13 @@ namespace OpenRA.FileFormats.Graphics
public interface IVertexBuffer<T>
{
void Bind();
void SetData( T[] vertices );
void SetData( T[] vertices, int length );
}
public interface IIndexBuffer
{
void Bind();
void SetData( ushort[] indices );
void SetData( ushort[] indices, int length );
}
public interface IShader

View File

@@ -1,4 +1,4 @@
#region Copyright & License Information
#region Copyright & License Information
/*
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
@@ -9,16 +9,33 @@
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace OpenRA
{
public interface IInputHandler
{
void ModifierKeys( Modifiers mods );
void OnKeyInput( KeyInput input );
void OnMouseInput( MouseInput input );
}
public struct MouseInput
{
public MouseInputEvent Event;
public int2 Location;
public MouseButton Button;
public int2 Location;
public Modifiers Modifiers;
public MouseInput( MouseInputEvent ev, MouseButton button, int2 location, Modifiers mods )
{
this.Event = ev;
this.Button = button;
this.Location = location;
this.Modifiers = mods;
}
}
public enum MouseInputEvent { Down, Move, Up };

View File

@@ -18,15 +18,17 @@ namespace OpenRA.FileFormats
public class Manifest
{
public readonly string[]
Folders, Packages, Rules,
Mods, Folders, Packages, Rules,
Sequences, Cursors, Chrome, Assemblies, ChromeLayout,
Weapons, Voices, Music, Movies, TileSets;
public string[] LocalRules = new string[0];
public string[] LocalAssemblies = new string[0];
public readonly string ShellmapUid, LoadScreen;
public readonly int TileSize = 24;
public Manifest(string[] mods)
{
Mods = mods;
var yaml = mods
.Select(m => MiniYaml.FromFile("mods/" + m + "/mod.yaml"))
.Aggregate(MiniYaml.Merge);

View File

@@ -17,10 +17,10 @@ namespace OpenRA.FileFormats
{
public class MapStub
{
public readonly IFolder Package;
public IFolder Package { get; protected set; }
// Yaml map data
public readonly string Uid;
public string Uid { get; protected set; }
[FieldLoader.Load] public bool Selectable;
[FieldLoader.Load] public string Title;
@@ -37,7 +37,8 @@ namespace OpenRA.FileFormats
[FieldLoader.Load] public int2 BottomRight;
public int Width { get { return BottomRight.X - TopLeft.X; } }
public int Height { get { return BottomRight.Y - TopLeft.Y; } }
public MapStub() {} // Hack for the editor - not used for anything important
public MapStub(IFolder package)
{
Package = package;

View File

@@ -16,11 +16,16 @@ namespace OpenRA.FileFormats
{
public string Name;
public string Palette;
public string Race;
public bool OwnsWorld = false;
public bool NonCombatant = false;
public bool Playable = false;
public bool DefaultStartingUnits = false;
public bool AllowBots = true;
public bool LockRace = false;
public string Race;
public bool LockColor = false;
public Color Color = Color.FromArgb(238,238,238);
public Color Color2 = Color.FromArgb(44,28,24);

39
OpenRA.FileFormats/Mod.cs Normal file
View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace OpenRA.FileFormats
{
public class Mod
{
public string Title;
public string Description;
public string Version;
public string Author;
public string[] RequiresMods;
public bool Standalone = false;
public static readonly Dictionary<string, Mod> AllMods = ValidateMods(Directory.GetDirectories("mods").Select(x => x.Substring(5)).ToArray());
public static Dictionary<string, Mod> ValidateMods(string[] mods)
{
var ret = new Dictionary<string, Mod>();
foreach (var m in mods)
{
if (!File.Exists("mods" + Path.DirectorySeparatorChar + m + Path.DirectorySeparatorChar + "mod.yaml"))
continue;
var yaml = new MiniYaml(null, MiniYaml.FromFile("mods" + Path.DirectorySeparatorChar + m + Path.DirectorySeparatorChar + "mod.yaml"));
if (!yaml.NodesDict.ContainsKey("Metadata"))
{
continue;
}
ret.Add(m, FieldLoader.Load<Mod>(yaml.NodesDict["Metadata"]));
}
return ret;
}
}
}

View File

@@ -45,6 +45,7 @@
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="Tao.Sdl, Version=1.2.13.0, Culture=neutral, PublicKeyToken=9c7a200e36c0094e">
<SpecificVersion>False</SpecificVersion>
@@ -61,9 +62,11 @@
<Compile Include="FileSystem.cs" />
<Compile Include="Folder.cs" />
<Compile Include="Graphics\IGraphicsDevice.cs" />
<Compile Include="Graphics\IInputHandler.cs" />
<Compile Include="Graphics\Vertex.cs" />
<Compile Include="Manifest.cs" />
<Compile Include="MiniYaml.cs" />
<Compile Include="Mod.cs" />
<Compile Include="PackageEntry.cs" />
<Compile Include="Package.cs" />
<Compile Include="PackageWriter.cs" />

View File

@@ -20,6 +20,7 @@ namespace OpenRA.FileFormats
Stream GetContent(string filename);
bool Exists(string filename);
IEnumerable<uint> AllFileHashes();
int Priority { get; }
}
public class Package : IFolder
@@ -28,9 +29,11 @@ namespace OpenRA.FileFormats
readonly bool isRmix, isEncrypted;
readonly long dataStart;
readonly Stream s;
int priority;
public Package(string filename)
public Package(string filename, int priority)
{
this.priority = priority;
s = FileSystem.Open(filename);
BinaryReader reader = new BinaryReader(s);
@@ -149,6 +152,12 @@ namespace OpenRA.FileFormats
{
return index.ContainsKey(PackageEntry.HashFilename(filename));
}
public int Priority
{
get { return 1000 + priority; }
}
}
[Flags]

View File

@@ -8,11 +8,8 @@
*/
#endregion
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System;
namespace OpenRA.FileFormats
{

31
OpenRA.FileFormats/PlayerColorRemap.cs Normal file → Executable file
View File

@@ -32,7 +32,7 @@ namespace OpenRA.FileFormats
.ToDictionary(u => u.First, u => u.Second);
}
static Color ColorLerp(float t, Color c1, Color c2)
public static Color ColorLerp(float t, Color c1, Color c2)
{
return Color.FromArgb(255,
(int)(t * c2.R + (1 - t) * c1.R),
@@ -46,5 +46,34 @@ namespace OpenRA.FileFormats
return remapColors.TryGetValue(index, out c)
? c : original;
}
// hk is hue in the range [0,1] instead of [0,360]
public static Color ColorFromHSL(float hk, float s, float l)
{
// Convert from HSL to RGB
var q = (l < 0.5f) ? l * (1 + s) : l + s - (l * s);
var p = 2 * l - q;
float[] trgb = { hk + 1 / 3.0f,
hk,
hk - 1/3.0f };
float[] rgb = { 0, 0, 0 };
for (int k = 0; k < 3; k++)
{
while (trgb[k] < 0) trgb[k] += 1.0f;
while (trgb[k] > 1) trgb[k] -= 1.0f;
}
for (int k = 0; k < 3; k++)
{
if (trgb[k] < 1 / 6.0f) { rgb[k] = (p + ((q - p) * 6 * trgb[k])); }
else if (trgb[k] >= 1 / 6.0f && trgb[k] < 0.5) { rgb[k] = q; }
else if (trgb[k] >= 0.5f && trgb[k] < 2.0f / 3) { rgb[k] = (p + ((q - p) * 6 * (2.0f / 3 - trgb[k]))); }
else { rgb[k] = p; }
}
return Color.FromArgb((int)(rgb[0] * 255), (int)(rgb[1] * 255), (int)(rgb[2] * 255));
}
}
}

View File

@@ -62,7 +62,8 @@ namespace OpenRA
Constrain(Y, min.Y, max.Y));
}
public static float2 operator *(float a, float2 b) { return new float2(a * b.X, a * b.Y); }
public static float2 operator *(float a, float2 b) { return new float2(a * b.X, a * b.Y); }
public static float2 operator *(float2 b, float a) { return new float2(a * b.X, a * b.Y); }
public static float2 operator *( float2 a, float2 b ) { return new float2( a.X * b.X, a.Y * b.Y ); }
public static float2 operator /( float2 a, float2 b ) { return new float2( a.X / b.X, a.Y / b.Y ); }

View File

@@ -25,6 +25,9 @@ namespace OpenRA
public static int2 operator -(int2 a, int2 b) { return new int2(a.X - b.X, a.Y - b.Y); }
public static int2 operator *(int a, int2 b) { return new int2(a * b.X, a * b.Y); }
public static int2 operator *(int2 b, int a) { return new int2(a * b.X, a * b.Y); }
public static int2 operator /(int2 a, int b) { return new int2(a.X / b, a.Y / b); }
public static int2 operator -(int2 a) { return new int2(-a.X, -a.Y); }
public static bool operator ==(int2 me, int2 other) { return (me.X == other.X && me.Y == other.Y); }
public static bool operator !=(int2 me, int2 other) { return !(me == other); }
@@ -59,5 +62,15 @@ namespace OpenRA
{
return (uint)((orig & 0xff000000) >> 24) | ((orig & 0x00ff0000) >> 8) | ((orig & 0x0000ff00) << 8) | ((orig & 0x000000ff) << 24);
}
public static int Lerp( int a, int b, int mul, int div )
{
return a + ( b - a ) * mul / div;
}
public static int2 Lerp( int2 a, int2 b, int mul, int div )
{
return a + ( b - a ) * mul / div;
}
}
}

View File

@@ -26,7 +26,8 @@ namespace OpenRA
public readonly World World;
public readonly uint ActorID;
public int2 Location { get { return Trait<IOccupySpace>().TopLeft; } }
public int2 Location { get { return Trait<IOccupySpace>().TopLeft; } }
public float2 CenterLocation { get { return Trait<IHasLocation>().PxPosition; } }
[Sync]
public Player Owner;
@@ -52,9 +53,6 @@ namespace OpenRA
AddTrait(trait.Create(init));
}
if( CenterLocation == float2.Zero && HasTrait<IOccupySpace>() )
CenterLocation = Traits.Util.CenterOfCell(Location);
Size = Lazy.New(() =>
{
var si = Info.Traits.GetOrDefault<SelectableInfo>();
@@ -63,8 +61,8 @@ namespace OpenRA
// auto size from render
var firstSprite = TraitsImplementing<IRender>().SelectMany(x => x.Render(this)).FirstOrDefault();
if (firstSprite.Sprite == null) return float2.Zero;
return firstSprite.Sprite.size;
if (firstSprite.Sprite == null) return float2.Zero;
return firstSprite.Sprite.size * firstSprite.Scale;
});
}
@@ -99,8 +97,6 @@ namespace OpenRA
get { return currentActivity == null || currentActivity is Idle; }
}
public float2 CenterLocation;
OpenRA.FileFormats.Lazy<float2> Size;
public IEnumerable<Renderable> Render()
@@ -110,28 +106,17 @@ namespace OpenRA
return mods.Aggregate(sprites, (m, p) => p.ModifyRender(this, m));
}
public Order Order( int2 xy, MouseInput mi, Actor underCursor )
{
if (Owner != World.LocalPlayer)
return null;
if (!World.Map.IsInMap(xy.X, xy.Y))
return null;
if (Destroyed)
return null;
return TraitsImplementing<IIssueOrder>()
.OrderByDescending( x => x.OrderPriority( this, xy, mi, underCursor ) )
.Select( x => x.IssueOrder( this, xy, mi, underCursor ) )
.FirstOrDefault( x => x != null );
}
public RectangleF GetBounds(bool useAltitude)
{
var si = Info.Traits.GetOrDefault<SelectableInfo>();
var size = Size.Value;
var size = Size.Value;
/* apply scaling */
var scale = this.TraitOrDefault<Scale>();
if (scale != null && scale.Info.Value != 1)
size = size*scale.Info.Value;
var loc = CenterLocation - 0.5f * size;
if (si != null && si.Bounds != null && si.Bounds.Length > 2)
@@ -151,16 +136,9 @@ namespace OpenRA
public void QueueActivity( IActivity nextActivity )
{
if( currentActivity == null )
{
currentActivity = nextActivity;
return;
}
var act = currentActivity;
while( act.NextActivity != null )
{
act = act.NextActivity;
}
act.NextActivity = nextActivity;
else
currentActivity.Queue( nextActivity );
}
public void CancelActivity()
@@ -222,6 +200,8 @@ namespace OpenRA
{
World.AddFrameEndTask( w =>
{
if (Destroyed) return;
World.Remove( this );
World.traitDict.RemoveActor( this );
Destroyed = true;

View File

@@ -20,9 +20,9 @@ namespace OpenRA
sequence = CursorProvider.GetCursorSequence(cursor);
}
public void Draw(int frame, float2 pos)
public void Draw(WorldRenderer wr, int frame, float2 pos)
{
Game.Renderer.SpriteRenderer.DrawSprite(sequence.GetSprite(frame), pos - sequence.Hotspot, sequence.Palette);
sequence.GetSprite(frame).DrawAt(wr, pos - sequence.Hotspot, sequence.Palette);
}
}
}

21
OpenRA.Game/Exts.cs Normal file → Executable file
View File

@@ -12,7 +12,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Support;
using OpenRA.Traits;
namespace OpenRA
{
@@ -34,17 +33,17 @@ namespace OpenRA
return xs.Aggregate(1f, (a, x) => a * x);
}
public static V GetOrAdd<K, V>( this Dictionary<K, V> d, K k )
public static V GetOrAdd<K, V>(this Dictionary<K, V> d, K k)
where V : new()
{
return d.GetOrAdd( k, _ => new V() );
return d.GetOrAdd(k, _ => new V());
}
public static V GetOrAdd<K, V>( this Dictionary<K, V> d, K k, Func<K, V> createFn )
public static V GetOrAdd<K, V>(this Dictionary<K, V> d, K k, Func<K, V> createFn)
{
V ret;
if( !d.TryGetValue( k, out ret ) )
d.Add( k, ret = createFn( k ) );
if (!d.TryGetValue(k, out ret))
d.Add(k, ret = createFn(k));
return ret;
}
@@ -54,18 +53,18 @@ namespace OpenRA
return xs[r.Next(xs.Length)];
}
public static void DoTimed<T>( this IEnumerable<T> e, Action<T> a, string text, double time )
public static void DoTimed<T>(this IEnumerable<T> e, Action<T> a, string text, double time)
{
var sw = new Stopwatch();
e.Do( x =>
e.Do(x =>
{
var t = sw.ElapsedTime();
a( x );
a(x);
var dt = sw.ElapsedTime() - t;
if( dt > time )
if (dt > time)
Log.Write("perf", text, x, dt * 1000, Game.LocalTick);
} );
});
}
}
}

501
OpenRA.Game/Game.cs Normal file → Executable file
View File

@@ -6,18 +6,20 @@
* as published by the Free Software Foundation. For more information,
* see LICENSE.
*/
#endregion
#endregion
using System;
using System.Drawing;
using System.IO;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using System.Net;
using System.Windows.Forms;
using OpenRA.FileFormats;
using OpenRA.GameRules;
using OpenRA.Graphics;
using OpenRA.Network;
using OpenRA.Server;
using OpenRA.GameRules;
using OpenRA.Graphics;
using OpenRA.Network;
using OpenRA.Server;
using OpenRA.Support;
using OpenRA.Widgets;
@@ -30,7 +32,8 @@ namespace OpenRA
public static int CellSize { get { return modData.Manifest.TileSize; } }
public static ModData modData;
public static World world;
private static WorldRenderer worldRenderer;
public static Viewport viewport;
public static Settings Settings;
@@ -39,37 +42,25 @@ namespace OpenRA
public static XRandom CosmeticRandom = new XRandom(); // not synced
public static Renderer Renderer;
public static Session LobbyInfo = new Session();
public static bool HasInputFocus = false;
static void LoadMap(string uid)
{
var map = modData.PrepareMap(uid);
viewport = new Viewport(new float2(Renderer.Resolution), map.TopLeft, map.BottomRight, Renderer);
world = null; // trying to access the old world will NRE, rather than silently doing it wrong.
world = new World(modData.Manifest, map);
}
public static void MoveViewport(int2 loc)
public static void MoveViewport(float2 loc)
{
viewport.Center(loc);
}
internal static string CurrentHost = "";
internal static int CurrentPort = 0;
internal static void JoinServer(string host, int port)
public static void JoinServer(string host, int port)
{
if (orderManager != null) orderManager.Dispose();
CurrentHost = host;
CurrentPort = port;
var replayFilename = ChooseReplayFilename();
string path = Path.Combine( Game.SupportDir, "Replays" );
if( !Directory.Exists( path ) ) Directory.CreateDirectory( path );
var replayFile = File.Create( Path.Combine( path, replayFilename ) );
orderManager = new OrderManager( host, port, new ReplayRecorderConnection( new NetworkConnection( host, port ), replayFile ) );
lastConnectionState = ConnectionState.PreConnecting;
ConnectionStateChanged();
orderManager = new OrderManager(new NetworkConnection(host, port), ChooseReplayFilename());
ConnectionStateChanged(orderManager);
}
static string ChooseReplayFilename()
@@ -79,71 +70,36 @@ namespace OpenRA
static void JoinLocal()
{
lastConnectionState = ConnectionState.PreConnecting;
ConnectionStateChanged();
if (orderManager != null) orderManager.Dispose();
orderManager = new OrderManager(new EchoConnection());
}
static int lastTime = Environment.TickCount;
static void ResetTimer()
{
lastTime = Environment.TickCount;
orderManager = new OrderManager("<no server>", -1, new EchoConnection());
lastConnectionState = ConnectionState.PreConnecting;
ConnectionStateChanged( orderManager );
}
internal static int RenderFrame = 0;
internal static int LocalTick = 0;
internal static int LocalTick { get { return orderManager.LocalFrameNumber; } }
const int NetTickScale = 3; // 120ms net tick for 40ms local tick
public static event Action ConnectionStateChanged = () => { };
static ConnectionState lastConnectionState = ConnectionState.PreConnecting;
public static event Action<OrderManager> ConnectionStateChanged = _ => { };
static ConnectionState lastConnectionState = ConnectionState.PreConnecting;
public static int LocalClientId { get { return orderManager.Connection.LocalClientId; } }
static void Tick()
static void Tick( OrderManager orderManager, Viewport viewPort )
{
if (orderManager.Connection.ConnectionState != lastConnectionState)
{
lastConnectionState = orderManager.Connection.ConnectionState;
ConnectionStateChanged();
}
int t = Environment.TickCount;
int dt = t - lastTime;
if (dt >= Settings.Game.Timestep)
if (orderManager.Connection.ConnectionState != lastConnectionState)
{
using (new PerfSample("tick_time"))
{
lastTime += Settings.Game.Timestep;
Widget.DoTick(world);
Sound.Tick();
orderManager.TickImmediate(world);
var isNetTick = LocalTick % NetTickScale == 0;
if (!isNetTick || orderManager.IsReadyForNextFrame)
{
++LocalTick;
if (isNetTick) orderManager.Tick(world);
world.OrderGenerator.Tick(world);
world.Selection.Tick(world);
world.Tick();
PerfHistory.Tick();
}
else
if (orderManager.FrameNumber == 0)
lastTime = Environment.TickCount;
}
lastConnectionState = orderManager.Connection.ConnectionState;
ConnectionStateChanged( orderManager );
}
Tick( orderManager );
if( orderManager.world != worldRenderer.world )
Tick( worldRenderer.world.orderManager );
using (new PerfSample("render"))
{
++RenderFrame;
viewport.DrawRegions(world);
viewport.DrawRegions(worldRenderer, new DefaultInputHandler( orderManager.world ));
Sound.SetListenerPosition(viewport.Location + .5f * new float2(viewport.Width, viewport.Height));
}
@@ -155,64 +111,73 @@ namespace OpenRA
MasterServerQuery.Tick();
}
internal static event Action LobbyInfoChanged = () => { };
internal static void SyncLobbyInfo(string data)
private static void Tick( OrderManager orderManager )
{
LobbyInfo = Session.Deserialize(data);
int t = Environment.TickCount;
int dt = t - orderManager.LastTickTime;
if (dt >= Settings.Game.Timestep)
using( new PerfSample( "tick_time" ) )
{
orderManager.LastTickTime += Settings.Game.Timestep;
Widget.DoTick();
var world = orderManager.world;
if( orderManager.GameStarted && world.LocalPlayer != null )
++Viewport.TicksSinceLastMove;
Sound.Tick();
Sync.CheckSyncUnchanged( world, () => { orderManager.TickImmediate(); } );
if( !world.GameHasStarted )
world.SharedRandom = new XRandom( LobbyInfo.GlobalSettings.RandomSeed );
var isNetTick = LocalTick % NetTickScale == 0;
if (orderManager.Connection.ConnectionState == ConnectionState.Connected)
world.SetLocalPlayer(orderManager.Connection.LocalClientId);
if( !isNetTick || orderManager.IsReadyForNextFrame )
{
++orderManager.LocalFrameNumber;
if (orderManager.FramesAhead != LobbyInfo.GlobalSettings.OrderLatency
&& !orderManager.GameStarted)
{
orderManager.FramesAhead = LobbyInfo.GlobalSettings.OrderLatency;
Debug("Order lag is now {0} frames.".F(LobbyInfo.GlobalSettings.OrderLatency));
}
Log.Write( "debug", "--Tick: {0} ({1})", LocalTick, isNetTick ? "net" : "local" );
if( isNetTick ) orderManager.Tick();
Sync.CheckSyncUnchanged(world, () =>
{
world.OrderGenerator.Tick(world);
world.Selection.Tick(world);
});
world.Tick();
PerfHistory.Tick();
}
else
if( orderManager.NetFrameNumber == 0 )
orderManager.LastTickTime = Environment.TickCount;
}
}
public static event Action LobbyInfoChanged = () => { };
public static event Action ConnectedToLobby = () => { };
internal static void SyncLobbyInfo()
{
LobbyInfoChanged();
}
public static void IssueOrder(Order o) { orderManager.IssueOrder(o); } /* avoid exposing the OM to mod code */
public static event Action AfterGameStart = () => {};
public static event Action<World> AfterGameStart = _ => {};
public static event Action BeforeGameStart = () => {};
internal static void StartGame(string map)
internal static void StartGame(string mapUID)
{
BeforeGameStart();
LoadMap(map);
var map = modData.PrepareMap(mapUID);
viewport = new Viewport(new int2(Renderer.Resolution), map.TopLeft, map.BottomRight, Renderer);
orderManager.world = new World(modData.Manifest, map, orderManager);
worldRenderer = new WorldRenderer(orderManager.world);
if (orderManager.GameStarted) return;
Widget.SelectedWidget = null;
Widget.SelectedWidget = null;
orderManager.StartGame();
viewport.RefreshPalette();
AfterGameStart();
}
public static void DispatchMouseInput(MouseInputEvent ev, MouseEventArgs e, Modifiers modifierKeys)
{
if (world == null)
return;
int sync = world.SyncHash();
var initialWorld = world;
var mi = new MouseInput
{
Button = (MouseButton)(int)e.Button,
Event = ev,
Location = new int2(e.Location),
Modifiers = modifierKeys,
};
Widget.HandleInput(world, mi);
if (sync != world.SyncHash() && world == initialWorld)
throw new InvalidOperationException("Desync in DispatchMouseInput");
orderManager.LocalFrameNumber = 0;
orderManager.StartGame();
worldRenderer.RefreshPalette();
AfterGameStart( orderManager.world );
}
public static bool IsHost
@@ -220,62 +185,175 @@ namespace OpenRA
get { return orderManager.Connection.LocalClientId == 0; }
}
internal static Session.Client LocalClient
{
get { return LobbyInfo.Clients.FirstOrDefault(c => c.Index == orderManager.Connection.LocalClientId); }
}
public static void HandleKeyEvent(KeyInput e)
{
if (world == null)
return;
int sync = world.SyncHash();
if (Widget.HandleKeyPress(e))
return;
if (sync != Game.world.SyncHash())
throw new InvalidOperationException("Desync in OnKeyPress");
}
static Modifiers modifiers;
public static Modifiers GetModifierKeys() { return modifiers; }
public static void HandleModifierKeys(Modifiers mods) { modifiers = mods; }
internal static void HandleModifierKeys(Modifiers mods) { modifiers = mods; }
internal static void Initialize(Arguments args)
{
AppDomain.CurrentDomain.AssemblyResolve += FileSystem.ResolveAssembly;
var defaultSupport = Environment.GetFolderPath(Environment.SpecialFolder.Personal)
+ Path.DirectorySeparatorChar + "OpenRA";
SupportDir = args.GetValue("SupportDir", defaultSupport);
Settings = new Settings(SupportDir + "settings.yaml", args);
Log.LogPath = SupportDir + "Logs" + Path.DirectorySeparatorChar;
Log.AddChannel("perf", "perf.log");
Log.AddChannel("debug", "debug.log");
Log.AddChannel("sync", "syncreport.log");
AppDomain.CurrentDomain.AssemblyResolve += FileSystem.ResolveAssembly;
var defaultSupport = Environment.GetFolderPath(Environment.SpecialFolder.Personal)
+ Path.DirectorySeparatorChar + "OpenRA";
SupportDir = args.GetValue("SupportDir", defaultSupport);
Settings = new Settings(SupportDir + "settings.yaml", args);
Settings.Save();
Log.LogPath = SupportDir + "Logs" + Path.DirectorySeparatorChar;
Log.AddChannel("perf", "perf.log");
Log.AddChannel("debug", "debug.log");
Log.AddChannel("sync", "syncreport.log");
FileSystem.Mount("."); // Needed to access shaders
Renderer.Initialize( Game.Settings.Graphics.Mode );
Renderer.SheetSize = Settings.Game.SheetSize;
Renderer = new Renderer();
LobbyInfo.GlobalSettings.Mods = Settings.Game.Mods;
modData = new ModData( LobbyInfo.GlobalSettings.Mods );
Console.WriteLine("Available mods:");
foreach(var mod in Mod.AllMods)
Console.WriteLine("\t{0}: {1} ({2})", mod.Key, mod.Value.Title, mod.Value.Version);
// Discard any invalid mods
var mods = Settings.Game.Mods.Where( m => Mod.AllMods.ContainsKey( m ) ).ToArray();
Console.WriteLine("Loading mods: {0}",string.Join(",",mods));
modData = new ModData( mods );
// when this client is running in dedicated mode ...
if (Settings.Server.IsDedicated)
{
// it may specify a yaml extension file (to add non synced traits)
if (!string.IsNullOrEmpty(Settings.Server.ExtensionYaml))
{
var r = modData.Manifest.LocalRules.ToList();
r.Add(Settings.Server.ExtensionYaml);
modData.Manifest.LocalRules = r.ToArray();
}
// and a dll to the assemblies (to add those non synced traits)
if (!string.IsNullOrEmpty(Settings.Server.ExtensionDll))
{
var r = modData.Manifest.LocalAssemblies.ToList();
r.Add(Settings.Server.ExtensionDll);
modData.Manifest.LocalAssemblies = r.ToArray();
}
if (!string.IsNullOrEmpty(Settings.Server.ExtensionClass))
Settings.Server.Extension = modData.ObjectCreator.CreateObject<IServerExtension>(Settings.Server.ExtensionClass);
}
Sound.Initialize();
PerfHistory.items["render"].hasNormalTick = false;
PerfHistory.items["batches"].hasNormalTick = false;
PerfHistory.items["text"].hasNormalTick = false;
PerfHistory.items["cursor"].hasNormalTick = false;
JoinLocal();
PerfHistory.items["cursor"].hasNormalTick = false;
if (!Settings.Graphics.UseNullRenderer)
{
JoinLocal();
StartGame(modData.Manifest.ShellmapUid);
Game.ConnectionStateChanged += om =>
{
Widget.CloseWindow();
switch (om.Connection.ConnectionState)
{
case ConnectionState.PreConnecting:
Widget.OpenWindow("MAINMENU_BG");
break;
case ConnectionState.Connecting:
Widget.OpenWindow("CONNECTING_BG",
new Dictionary<string, object>
{{"host", om.Host}, {"port", om.Port}});
break;
case ConnectionState.NotConnected:
Widget.OpenWindow("CONNECTION_FAILED_BG",
new Dictionary<string, object>
{{"host", om.Host}, {"port", om.Port}});
break;
case ConnectionState.Connected:
var lobby = Widget.OpenWindow("SERVER_LOBBY",
new Dictionary<string, object>
{{"orderManager", om}});
lobby.GetWidget<ChatDisplayWidget>("CHAT_DISPLAY").ClearChat();
lobby.GetWidget("CHANGEMAP_BUTTON").Visible = true;
lobby.GetWidget("LOCKTEAMS_CHECKBOX").Visible = true;
lobby.GetWidget("DISCONNECT_BUTTON").Visible = true;
// Inform whoever is willing to hear it that the player is connected to the lobby
if (ConnectedToLobby != null)
ConnectedToLobby();
if (Settings.Server.IsDedicated)
{
// Force spectator as a default
Game.orderManager.IssueOrder(Order.Command("spectator"));
}
if (Game.Settings.Server.Extension != null)
Game.Settings.Server.Extension.OnLobbyUp();
break;
}
};
modData.WidgetLoader.LoadWidget(new Dictionary<string, object>(), Widget.RootWidget, "PERF_BG");
Widget.OpenWindow("MAINMENU_BG");
}else
{
JoinLocal();
StartGame(modData.Manifest.ShellmapUid);
Game.ConnectionStateChanged += om =>
{
Widget.CloseWindow();
switch (om.Connection.ConnectionState)
{
case ConnectionState.PreConnecting:
Widget.OpenWindow("MAINMENU_BG");
break;
case ConnectionState.Connecting:
Widget.OpenWindow("CONNECTING_BG",
new Dictionary<string, object> { { "host", om.Host }, { "port", om.Port } });
break;
case ConnectionState.NotConnected:
Widget.OpenWindow("CONNECTION_FAILED_BG",
new Dictionary<string, object> { { "host", om.Host }, { "port", om.Port } });
break;
case ConnectionState.Connected:
var lobby = Widget.OpenWindow("SERVER_LOBBY",
new Dictionary<string, object> { { "orderManager", om } });
// Inform whoever is willing to hear it that the player is connected to the lobby
if (ConnectedToLobby != null)
ConnectedToLobby();
if (Settings.Server.IsDedicated)
{
// Force spectator as a default
Game.orderManager.IssueOrder(Order.Command("spectator"));
}
if (Game.Settings.Server.Extension != null)
Game.Settings.Server.Extension.OnLobbyUp();
break;
}
};
modData.WidgetLoader.LoadWidget(new Dictionary<string, object>(), Widget.RootWidget, "PERF_BG");
Widget.OpenWindow("MAINMENU_BG");
}
if (Settings.Server.IsDedicated)
{
// Auto-host
var map = Game.modData.AvailableMaps.FirstOrDefault(m => m.Value.Selectable).Key;
Server.Server.ServerMain(Game.modData, Settings, map);
Game.JoinServer(IPAddress.Loopback.ToString(), Settings.Server.ListenPort);
}
Game.orderManager.LastTickTime = Environment.TickCount;
}
static bool quit;
@@ -283,55 +361,90 @@ namespace OpenRA
{
while (!quit)
{
{
Tick( orderManager, viewport );
Application.DoEvents();
}
}
public static void Exit() { quit = true; }
public static void Exit() { quit = true; }
public static Action<Color,string,string> AddChatLine = (c,n,s) => {};
public static void Debug(string s, params object[] args)
{
public static void Debug(string s, params object[] args)
{
AddChatLine(Color.White, "Debug", String.Format(s,args));
}
public static void Disconnect()
{
if (IsHost)
Server.Server.StopListening();
orderManager.Dispose();
var shellmap = modData.Manifest.ShellmapUid;
var shellmap = modData.Manifest.ShellmapUid;
LobbyInfo = new Session();
JoinLocal();
StartGame(shellmap);
Widget.RootWidget.CloseWindow();
Widget.RootWidget.OpenWindow("MAINMENU_BG");
}
static string baseSupportDir = null;
public static string SupportDir
{
set
{
var dir = value;
// Expand paths relative to the personal directory
if (dir.ElementAt(0) == '~')
dir = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + dir.Substring(1);
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
Widget.CloseWindow();
Widget.OpenWindow("MAINMENU_BG");
}
static string baseSupportDir = null;
public static string SupportDir
{
set
{
var dir = value;
// Expand paths relative to the personal directory
if (dir.ElementAt(0) == '~')
dir = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + dir.Substring(1);
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
baseSupportDir = dir + Path.DirectorySeparatorChar;
}
}
get { return baseSupportDir; }
}
public static T CreateObject<T>( string name )
{
return modData.ObjectCreator.CreateObject<T>( name );
}
public static void RejoinLobby(World world)
{
if (Game.IsHost && Game.Settings.Server.Extension != null)
Game.Settings.Server.Extension.OnRejoinLobby(world);
var map = orderManager.LobbyInfo.GlobalSettings.Map;
var host = orderManager.Host;
var port = orderManager.Port;
var isHost = Game.IsHost;
Disconnect();
ConnectedToLobby += () =>
{
if (world.LocalPlayer != null)
{
/* Try to get back the old slot */
Game.orderManager.IssueOrder(Order.Command("race " + world.LocalPlayer.Country.Race));
Game.orderManager.IssueOrder(Order.Command("slot " + world.LobbyInfo.ClientWithIndex(world.LocalPlayer.ClientIndex).Slot));
}else /* a spectator */
{
Game.orderManager.IssueOrder(Order.Command("spectator"));
}
ConnectedToLobby = null;
};
if (isHost)
{
Server.Server.ServerMain(Game.modData, Settings, map);
JoinServer(IPAddress.Loopback.ToString(), Settings.Server.ListenPort);
}
else
JoinServer(host, port);
}
}
}
}

View File

@@ -66,21 +66,21 @@ namespace OpenRA
var ret = new List<ITraitInfo>();
var t = Traits.WithInterface<ITraitInfo>().ToList();
int index = 0;
while( t.Count != 0 )
while (t.Count != 0)
{
if( index >= t.Count )
throw new InvalidOperationException( "Trait prerequisites not satisfied (or prerequisite loop) Actor={0} Unresolved={1}".F(
Name, string.Join( ",", t.Select( x => x.GetType().Name ).ToArray())));
var prereqs = PrerequisitesOf( t[ index ] );
if( prereqs.Count == 0 || prereqs.All( n => ret.Any( x => x.GetType() == n || x.GetType().IsSubclassOf( n ) ) ) )
var prereqs = PrerequisitesOf(t[index]);
var unsatisfied = prereqs.Where(n => !ret.Any(x => x.GetType() == n || x.GetType().IsSubclassOf(n)));
if (!unsatisfied.Any())
{
ret.Add( t[ index ] );
t.RemoveAt( index );
ret.Add(t[index]);
t.RemoveAt(index);
index = 0;
}
else
++index;
else if (++index >= t.Count)
throw new InvalidOperationException("Trait prerequisites not satisfied (or prerequisite loop) Actor={0} Unresolved={1} Missing={2}".F(
Name,
string.Join(",", t.Select(x => x.GetType().Name).ToArray()),
string.Join(",", unsatisfied.Select(x => x.Name).ToArray())));
}
return ret;

View File

@@ -27,7 +27,9 @@ namespace OpenRA
public static void LoadRules(Manifest m, Map map)
{
Info = LoadYamlRules(m.Rules, map.Rules, (k, y) => new ActorInfo(k.Key.ToLowerInvariant(), k.Value, y));
// Added support to extend the list of rules (add it to m.LocalRules)
// Should only be used to add ITraitNoSync traits! (otherwise BOOM)
Info = LoadYamlRules(m.Rules.Concat(m.LocalRules).ToArray(), map.Rules, (k, y) => new ActorInfo(k.Key.ToLowerInvariant(), k.Value, y));
Weapons = LoadYamlRules(m.Weapons, new List<MiniYamlNode>(), (k, _) => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value));
Voices = LoadYamlRules(m.Voices, new List<MiniYamlNode>(), (k, _) => new VoiceInfo(k.Value));
Music = LoadYamlRules(m.Music, new List<MiniYamlNode>(), (k, _) => new MusicInfo(k.Key, k.Value));

View File

@@ -8,13 +8,14 @@
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using OpenRA.FileFormats;
using OpenRA.FileFormats.Graphics;
using System;
using OpenRA.Server;
namespace OpenRA.GameRules
{
@@ -24,24 +25,34 @@ namespace OpenRA.GameRules
public int ListenPort = 1234;
public int ExternalPort = 1234;
public bool AdvertiseOnline = true;
public string MasterServer = "http://open-ra.org/master/";
public string MasterServer = "http://master.open-ra.org/";
public bool AllowCheats = false;
public string ExtensionDll = "";
public string ExtensionClass = "";
/* not storeable */
public IServerExtension Extension { get; set; }
public string ExtensionYaml { get; set; }
public bool IsDedicated { get; set; }
}
public class DebugSettings
{
public bool BotDebug = false;
public bool PerfGraph = false;
public bool RecordSyncReports = true;
public bool ShowCollisions = false;
public float LongTickThreshold = 0.001f;
}
public class GraphicSettings
{
public WindowMode Mode = WindowMode.PseudoFullscreen;
public int2 FullscreenSize = new int2(Screen.PrimaryScreen.Bounds.Width,Screen.PrimaryScreen.Bounds.Height);
public int2 WindowedSize = new int2(1024,768);
public int2 FullscreenSize = new int2(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
public int2 WindowedSize = new int2(1024, 768);
public readonly int2 MinResolution = new int2(800, 600);
public readonly string RenderEngine = "OpenRA.Gl.dll";
public readonly string NullRenderEngine = "OpenRA.Renderer.Null.dll";
public bool UseNullRenderer { get; set; }
}
public class SoundSettings
@@ -66,9 +77,13 @@ namespace OpenRA.GameRules
public string[] Mods = { "ra" };
public bool MatchTimer = true;
// Chat settings
public bool TeamChatToggle = false;
// Behaviour settings
public bool ViewportEdgeScroll = true;
public bool InverseDragScroll = false;
public float ViewportEdgeScrollStep = 10f;
// Internal game settings
public int Timestep = 40;
@@ -100,18 +115,23 @@ namespace OpenRA.GameRules
{"Debug", Debug}
};
// Should we run in dedicated mode (use the server extension)
Server.IsDedicated = args.GetValue("Server.IsDedicated", false);
if (Server.IsDedicated)
Graphics.UseNullRenderer = args.GetValue("Graphics.UseNullRenderer", false);
// 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 ) );
Console.WriteLine( "Ignoring unknown field `{0}` on `{1}`".F( s, f.Name ) );
};
if (File.Exists(SettingsFile))
{
System.Console.WriteLine("Loading settings file {0}",SettingsFile);
Console.WriteLine("Loading settings file {0}",SettingsFile);
var yaml = MiniYaml.DictFromFile(SettingsFile);
foreach (var kv in Sections)
@@ -123,7 +143,7 @@ namespace OpenRA.GameRules
foreach (var kv in Sections)
foreach (var f in kv.Value.GetType().GetFields())
if (args.Contains(kv.Key+"."+f.Name))
OpenRA.FileFormats.FieldLoader.LoadField( kv.Value, f.Name, args.GetValue(kv.Key+"."+f.Name, "") );
FieldLoader.LoadField( kv.Value, f.Name, args.GetValue(kv.Key+"."+f.Name, "") );
FieldLoader.UnknownFieldAction = err1;
FieldLoader.InvalidValueAction = err2;
@@ -133,16 +153,11 @@ namespace OpenRA.GameRules
{
var root = new List<MiniYamlNode>();
foreach( var kv in Sections )
root.Add( new MiniYamlNode( kv.Key, SectionYaml( kv.Value ) ) );
root.Add( new MiniYamlNode( kv.Key, FieldSaver.SaveDifferences(kv.Value, Activator.CreateInstance(kv.Value.GetType())) ) );
root.WriteToFile(SettingsFile);
}
MiniYaml SectionYaml(object section)
{
return FieldSaver.SaveDifferences(section, Activator.CreateInstance(section.GetType()));
}
void LoadSectionYaml(MiniYaml yaml, object section)
{
object defaults = Activator.CreateInstance(section.GetType());

View File

@@ -31,7 +31,7 @@ namespace OpenRA.GameRules
: new Dictionary<string, string[]>();
}
public readonly Lazy<Dictionary<string, VoicePool>> Pools;
public readonly OpenRA.FileFormats.Lazy<Dictionary<string, VoicePool>> Pools;
public VoiceInfo( MiniYaml y )
{
@@ -41,6 +41,9 @@ namespace OpenRA.GameRules
if (!Voices.ContainsKey("Attack"))
Voices.Add("Attack", Voices["Move"]);
if (!Voices.ContainsKey("AttackMove"))
Voices.Add("AttackMove", Voices["Move"]);
Pools = Lazy.New(() => Voices.ToDictionary( a => a.Key, a => new VoicePool(a.Value) ));
}

View File

@@ -32,6 +32,7 @@ namespace OpenRA.GameRules
[FieldLoader.Load] public readonly int Damage = 0; // how much (raw) damage to deal
[FieldLoader.Load] public readonly int Delay = 0; // delay in ticks before dealing the damage. 0=instant (old model)
[FieldLoader.Load] public readonly DamageModel DamageModel = DamageModel.Normal; // which damage model to use
[FieldLoader.Load] public readonly bool PreventProne = false; // whether we should prevent prone response in infantry.
public float EffectivenessAgainst(Actor self)
{
@@ -92,6 +93,7 @@ namespace OpenRA.GameRules
[FieldLoader.Load] public readonly bool Underwater = false;
[FieldLoader.Load] public readonly string[] ValidTargets = { "Ground" };
[FieldLoader.Load] public readonly int BurstDelay = 5;
[FieldLoader.Load] public readonly float MinRange = 0;
[FieldLoader.LoadUsing( "LoadProjectile" )] public IProjectileInfo Projectile;
[FieldLoader.LoadUsing( "LoadWarheads" )] public List<WarheadInfo> Warheads;

View File

@@ -27,7 +27,7 @@ namespace OpenRA.Graphics
}
Sprite[] LoadCursors(string filename)
{
{
try
{
var shp = new Dune2ShpReader(FileSystem.OpenWithExts(filename, exts));

View File

@@ -13,45 +13,46 @@ using OpenRA.FileFormats.Graphics;
namespace OpenRA.Graphics
{
public class LineRenderer
public class LineRenderer : Renderer.IBatchRenderer
{
Renderer renderer;
IVertexBuffer<Vertex> vertexBuffer;
IIndexBuffer indexBuffer; /* kindof a waste of space, but the GPU likes indexing, oh well */
const int linesPerBatch = 1024;
Vertex[] vertices = new Vertex[ 2 * linesPerBatch ];
ushort[] indices = new ushort[ 2 * linesPerBatch ];
int lines = 0;
Vertex[] vertices = new Vertex[ Renderer.TempBufferSize ];
ushort[] indices = new ushort[ Renderer.TempBufferSize ];
int nv = 0, ni = 0;
public LineRenderer( Renderer renderer )
{
this.renderer = renderer;
vertexBuffer = renderer.Device.CreateVertexBuffer(vertices.Length );
indexBuffer = renderer.Device.CreateIndexBuffer( indices.Length );
}
public void Flush()
{
if( lines > 0 )
if( ni > 0 )
{
renderer.LineShader.Render( () =>
{
vertexBuffer.SetData( vertices );
indexBuffer.SetData( indices );
renderer.DrawBatch( vertexBuffer, indexBuffer,
var vb = renderer.GetTempVertexBuffer();
var ib = renderer.GetTempIndexBuffer();
vb.SetData( vertices, nv );
ib.SetData( indices, ni );
renderer.DrawBatch( vb, ib,
nv, ni / 2, PrimitiveType.LineList );
} );
nv = 0; ni = 0;
lines = 0;
}
}
public void DrawLine( float2 start, float2 end, Color startColor, Color endColor )
{
Renderer.CurrentBatchRenderer = this;
if( ni + 2 > Renderer.TempBufferSize )
Flush();
if( nv + 2 > Renderer.TempBufferSize )
Flush();
indices[ ni++ ] = (ushort)nv;
vertices[ nv++ ] = new Vertex( start,
@@ -63,9 +64,6 @@ namespace OpenRA.Graphics
vertices[ nv++ ] = new Vertex( end,
new float2( endColor.R / 255.0f, endColor.G / 255.0f ),
new float2( endColor.B / 255.0f, endColor.A / 255.0f ) );
if( ++lines >= linesPerBatch )
Flush();
}
public void FillRect( RectangleF r, Color color )

View File

@@ -135,10 +135,12 @@ namespace OpenRA.Graphics
unsafe
{
int* c = (int*)bitmapData.Scan0;
var player = world.LocalPlayer;
foreach (var t in world.Queries.WithTraitMultiple<IRadarSignature>())
{
if (!t.Actor.IsVisible(world.LocalPlayer))
if (!t.Actor.IsVisible(player))
continue;
var color = t.Trait.RadarSignatureColor(t.Actor);
@@ -162,7 +164,9 @@ namespace OpenRA.Graphics
var shroud = Color.Black.ToArgb();
var fog = Color.FromArgb(128, Color.Black).ToArgb();
var playerShroud = world.LocalPlayer.Shroud;
unsafe
{
int* c = (int*)bitmapData.Scan0;
@@ -172,9 +176,9 @@ namespace OpenRA.Graphics
{
var mapX = x + map.TopLeft.X;
var mapY = y + map.TopLeft.Y;
if (!world.LocalPlayer.Shroud.IsExplored(mapX, mapY))
if (!playerShroud.IsExplored(mapX, mapY))
*(c + (y * bitmapData.Stride >> 2) + x) = shroud;
else if (!world.LocalPlayer.Shroud.IsVisible(mapX,mapY))
else if (!playerShroud.IsVisible(mapX,mapY))
*(c + (y * bitmapData.Stride >> 2) + x) = fog;
}
}

View File

@@ -9,13 +9,14 @@
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using OpenRA.FileFormats;
using OpenRA.FileFormats.Graphics;
using OpenRA.Support;
using System.Windows.Forms;
namespace OpenRA.Graphics
{
@@ -23,10 +24,10 @@ namespace OpenRA.Graphics
{
internal static int SheetSize;
public IShader SpriteShader { get; private set; } /* note: shared shader params */
public IShader LineShader { get; private set; }
public IShader RgbaSpriteShader { get; private set; }
public IShader WorldSpriteShader { get; private set; }
internal IShader SpriteShader { get; private set; } /* note: shared shader params */
internal IShader LineShader { get; private set; }
internal IShader RgbaSpriteShader { get; private set; }
internal IShader WorldSpriteShader { get; private set; }
public SpriteRenderer SpriteRenderer { get; private set; }
public SpriteRenderer RgbaSpriteRenderer { get; private set; }
@@ -37,6 +38,12 @@ namespace OpenRA.Graphics
public readonly SpriteFont RegularFont, BoldFont, TitleFont;
internal const int TempBufferSize = 8192;
const int TempBufferCount = 8;
Queue<IVertexBuffer<Vertex>> tempBuffersV = new Queue<IVertexBuffer<Vertex>>();
Queue<IIndexBuffer> tempBuffersI = new Queue<IIndexBuffer>();
public Renderer()
{
SpriteShader = device.CreateShader(FileSystem.Open("shaders/world-shp.fx"));
@@ -52,13 +59,18 @@ namespace OpenRA.Graphics
RegularFont = new SpriteFont("FreeSans.ttf", 14);
BoldFont = new SpriteFont("FreeSansBold.ttf", 14);
TitleFont = new SpriteFont("titles.ttf", 48);
for( int i = 0 ; i < TempBufferCount ; i++ )
{
tempBuffersV.Enqueue( device.CreateVertexBuffer( TempBufferSize ) );
tempBuffersI.Enqueue( device.CreateIndexBuffer( TempBufferSize ) );
}
}
public IGraphicsDevice Device { get { return device; } }
internal IGraphicsDevice Device { get { return device; } }
public void BeginFrame(float2 scroll)
{
device.Begin();
device.Clear(Color.Black);
float2 r1 = new float2(2f/Resolution.Width, -2f/Resolution.Height);
@@ -73,16 +85,16 @@ namespace OpenRA.Graphics
private void SetShaderParams( IShader s, float2 r1, float2 r2, float2 scroll )
{
s.SetValue( "Palette", PaletteTexture );
s.SetValue( "Scroll", scroll.X, scroll.Y );
s.SetValue( "Scroll", (int) scroll.X, (int) scroll.Y );
s.SetValue( "r1", r1.X, r1.Y );
s.SetValue( "r2", r2.X, r2.Y );
s.Commit();
}
public void EndFrame()
public void EndFrame( IInputHandler inputHandler )
{
device.End();
device.Present();
Flush();
device.Present( inputHandler );
}
public void DrawBatch<T>(IVertexBuffer<T> vertices, IIndexBuffer indices,
@@ -111,15 +123,9 @@ namespace OpenRA.Graphics
public void Flush()
{
WorldSpriteRenderer.Flush();
RgbaSpriteRenderer.Flush();
LineRenderer.Flush();
CurrentBatchRenderer = null;
}
static IGraphicsDevice device;
public static Size Resolution { get { return device.WindowSize; } }
@@ -127,7 +133,11 @@ namespace OpenRA.Graphics
internal static void Initialize( OpenRA.FileFormats.Graphics.WindowMode windowMode )
{
var resolution = GetResolution( windowMode );
device = CreateDevice( Assembly.LoadFile( Path.GetFullPath( "OpenRA.Gl.dll" ) ), resolution.Width, resolution.Height, windowMode, false );
if (Game.Settings.Graphics.UseNullRenderer)
device = CreateDevice(Assembly.LoadFile(Path.GetFullPath(Game.Settings.Graphics.NullRenderEngine)), resolution.Width, resolution.Height, windowMode, false);
else
device = CreateDevice( Assembly.LoadFile( Path.GetFullPath( Game.Settings.Graphics.RenderEngine ) ), resolution.Width, resolution.Height, windowMode, false );
}
static Size GetResolution(WindowMode windowmode)
@@ -154,5 +164,49 @@ namespace OpenRA.Graphics
}
throw new NotImplementedException();
}
internal IVertexBuffer<Vertex> GetTempVertexBuffer()
{
var ret = tempBuffersV.Dequeue();
tempBuffersV.Enqueue( ret );
return ret;
}
internal IIndexBuffer GetTempIndexBuffer()
{
var ret = tempBuffersI.Dequeue();
tempBuffersI.Enqueue( ret );
return ret;
}
public interface IBatchRenderer
{
void Flush();
}
static IBatchRenderer currentBatchRenderer;
public static IBatchRenderer CurrentBatchRenderer
{
get { return currentBatchRenderer; }
set
{
if( currentBatchRenderer == value ) return;
if( currentBatchRenderer != null )
currentBatchRenderer.Flush();
currentBatchRenderer = value;
}
}
public void EnableScissor(int left, int top, int width, int height)
{
Flush();
Device.EnableScissor( left, top, width, height );
}
public void DisableScissor()
{
Flush();
Device.DisableScissor();
}
}
}

View File

@@ -49,6 +49,26 @@ namespace OpenRA.Graphics
{
return uvhax[ k ];
}
public void DrawAt( WorldRenderer wr, float2 location, string palette )
{
Game.Renderer.SpriteRenderer.DrawSprite( this, location, wr, palette, this.size );
}
public void DrawAt( float2 location, int paletteIndex )
{
Game.Renderer.SpriteRenderer.DrawSprite( this, location, paletteIndex, this.size );
}
public void DrawAt(float2 location, int paletteIndex, float scale)
{
Game.Renderer.SpriteRenderer.DrawSprite(this, location, paletteIndex, this.size * scale);
}
public void DrawAt( float2 location, int paletteIndex, float2 size )
{
Game.Renderer.SpriteRenderer.DrawSprite( this, location, paletteIndex, size );
}
}
public enum TextureChannel

View File

@@ -80,17 +80,20 @@ namespace OpenRA.Graphics
GlyphInfo CreateGlyph(Pair<char,Color> c)
{
var index = FT.FT_Get_Char_Index(face, (uint)c.First);
FT.FT_Load_Glyph(face, index, FT.FT_LOAD_RENDER);
if (0 != FT.FT_Load_Glyph(face, index, FT.FT_LOAD_RENDER))
throw new InvalidOperationException( "FT_Load_Glyph failed." );
var _face = (FT_FaceRec)Marshal.PtrToStructure(face, typeof(FT_FaceRec));
var _glyph = (FT_GlyphSlotRec)Marshal.PtrToStructure(_face.glyph, typeof(FT_GlyphSlotRec));
var s = builder.Allocate(new Size(_glyph.metrics.width >> 6, _glyph.metrics.height >> 6));
var s = builder.Allocate(
new Size(_glyph.metrics.width.ToInt32() >> 6,
_glyph.metrics.height.ToInt32() >> 6));
var g = new GlyphInfo
{
Sprite = s,
Advance = _glyph.metrics.horiAdvance / 64f,
Advance = _glyph.metrics.horiAdvance.ToInt32() / 64f,
Offset = { X = _glyph.bitmap_left, Y = -_glyph.bitmap_top }
};

View File

@@ -12,28 +12,20 @@ using OpenRA.FileFormats.Graphics;
namespace OpenRA.Graphics
{
public class SpriteRenderer
public class SpriteRenderer : Renderer.IBatchRenderer
{
IVertexBuffer<Vertex> vertexBuffer;
IIndexBuffer indexBuffer;
Renderer renderer;
IShader shader;
const int spritesPerBatch = 1024;
Vertex[] vertices = new Vertex[4 * spritesPerBatch];
ushort[] indices = new ushort[6 * spritesPerBatch];
Vertex[] vertices = new Vertex[Renderer.TempBufferSize];
ushort[] indices = new ushort[Renderer.TempBufferSize];
Sheet currentSheet = null;
int sprites = 0;
int nv = 0, ni = 0;
public SpriteRenderer(Renderer renderer, IShader shader)
{
this.renderer = renderer;
this.shader = shader;
vertexBuffer = renderer.Device.CreateVertexBuffer( vertices.Length );
indexBuffer = renderer.Device.CreateIndexBuffer( indices.Length );
}
public SpriteRenderer(Renderer renderer)
@@ -41,14 +33,16 @@ namespace OpenRA.Graphics
public void Flush()
{
if (sprites > 0)
if (ni > 0)
{
shader.SetValue( "DiffuseTexture", currentSheet.Texture );
shader.Render(() =>
{
vertexBuffer.SetData(vertices);
indexBuffer.SetData(indices);
renderer.DrawBatch(vertexBuffer, indexBuffer,
var vb = renderer.GetTempVertexBuffer();
var ib = renderer.GetTempIndexBuffer();
vb.SetData(vertices, nv);
ib.SetData(indices, ni);
renderer.DrawBatch(vb, ib,
new Range<int>(0, nv),
new Range<int>(0, ni),
PrimitiveType.TriangleList,
@@ -57,30 +51,34 @@ namespace OpenRA.Graphics
nv = 0; ni = 0;
currentSheet = null;
sprites = 0;
}
}
public void DrawSprite(Sprite s, float2 location, string palette)
public void DrawSprite(Sprite s, float2 location, WorldRenderer wr, string palette)
{
DrawSprite(s, location, Game.world.WorldRenderer.GetPaletteIndex(palette), s.size);
DrawSprite(s, location, wr.GetPaletteIndex(palette), s.size);
}
public void DrawSprite(Sprite s, float2 location, string palette, float2 size)
public void DrawSprite(Sprite s, float2 location, WorldRenderer wr, string palette, float2 size)
{
DrawSprite(s, location, Game.world.WorldRenderer.GetPaletteIndex(palette), size);
DrawSprite(s, location, wr.GetPaletteIndex(palette), size);
}
public void DrawSprite(Sprite s, float2 location, int paletteIndex, float2 size)
{
Renderer.CurrentBatchRenderer = this;
if (s.sheet != currentSheet)
Flush();
if( nv + 4 > Renderer.TempBufferSize )
Flush();
if( ni + 6 > Renderer.TempBufferSize )
Flush();
currentSheet = s.sheet;
Util.FastCreateQuad(vertices, indices, location.ToInt2(), s, paletteIndex, nv, ni, size);
nv += 4; ni += 6;
if (++sprites >= spritesPerBatch)
Flush();
}

View File

@@ -56,13 +56,13 @@ namespace OpenRA.Graphics
}
vertexBuffer = Game.Renderer.Device.CreateVertexBuffer( vertices.Length );
vertexBuffer.SetData( vertices );
vertexBuffer.SetData( vertices, nv );
indexBuffer = Game.Renderer.Device.CreateIndexBuffer( indices.Length );
indexBuffer.SetData( indices );
indexBuffer.SetData( indices, ni );
}
public void Draw( Viewport viewport )
public void Draw( WorldRenderer wr, Viewport viewport )
{
int indicesPerRow = map.Width * 6;
int verticesPerRow = map.Width * 4;
@@ -98,7 +98,7 @@ namespace OpenRA.Graphics
PrimitiveType.TriangleList, Game.Renderer.SpriteShader));
foreach (var r in world.WorldActor.TraitsImplementing<IRenderOverlay>())
r.Render();
r.Render( wr );
}
}
}

58
OpenRA.Game/Graphics/Viewport.cs Normal file → Executable file
View File

@@ -11,7 +11,6 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Support;
using OpenRA.Traits;
using OpenRA.Widgets;
@@ -19,16 +18,16 @@ namespace OpenRA.Graphics
{
public class Viewport
{
readonly float2 screenSize;
float2 scrollPosition;
readonly int2 screenSize;
int2 scrollPosition;
readonly Renderer renderer;
readonly int2 mapStart;
readonly int2 mapEnd;
public float2 Location { get { return scrollPosition; } }
public int Width { get { return (int)screenSize.X; } }
public int Height { get { return (int)screenSize.Y; } }
public int Width { get { return screenSize.X; } }
public int Height { get { return screenSize.Y; } }
float cursorFrame = 0f;
@@ -42,7 +41,8 @@ namespace OpenRA.Graphics
public void Scroll(float2 delta, bool ignoreBorders)
{
float2 newScrollPosition = scrollPosition + delta;
var d = delta.ToInt2();
var newScrollPosition = scrollPosition + d;
if(!ignoreBorders)
newScrollPosition = this.NormalizeScrollPosition(newScrollPosition);
@@ -50,10 +50,10 @@ namespace OpenRA.Graphics
scrollPosition = newScrollPosition;
}
private float2 NormalizeScrollPosition(float2 newScrollPosition)
private int2 NormalizeScrollPosition(int2 newScrollPosition)
{
float2 topLeftBorder = (Game.CellSize* mapStart).ToFloat2();
float2 bottomRightBorder = (Game.CellSize* mapEnd).ToFloat2();
var topLeftBorder = Game.CellSize* mapStart;
var bottomRightBorder = Game.CellSize* mapEnd;
if(newScrollPosition.Y < topLeftBorder.Y - screenSize.Y/2)
newScrollPosition.Y = topLeftBorder.Y - screenSize.Y/2;
@@ -86,7 +86,7 @@ namespace OpenRA.Graphics
return blockedDirections;
}
public Viewport(float2 screenSize, int2 mapStart, int2 mapEnd, Renderer renderer)
public Viewport(int2 screenSize, int2 mapStart, int2 mapEnd, Renderer renderer)
{
this.screenSize = screenSize;
this.renderer = renderer;
@@ -96,34 +96,23 @@ namespace OpenRA.Graphics
this.scrollPosition = Game.CellSize* mapStart;
}
public void DrawRegions( World world )
public void DrawRegions( WorldRenderer wr, IInputHandler inputHandler )
{
renderer.BeginFrame(scrollPosition);
world.WorldRenderer.Draw();
wr.Draw();
Widget.DoDraw(world);
Widget.DoDraw( wr );
var cursorName = Widget.RootWidget.GetCursorOuter(Viewport.LastMousePos) ?? "default";
var c = new Cursor(cursorName);
c.Draw((int)cursorFrame, Viewport.LastMousePos + Location);
c.Draw(wr, (int)cursorFrame, Viewport.LastMousePos + Location);
renderer.RgbaSpriteRenderer.Flush();
renderer.SpriteRenderer.Flush();
renderer.WorldSpriteRenderer.Flush();
renderer.EndFrame();
}
public void RefreshPalette()
{
Game.world.WorldRenderer.palette.Update(
Game.world.WorldActor.TraitsImplementing<IPaletteModifier>());
renderer.EndFrame( inputHandler );
}
public void Tick()
{
cursorFrame += 0.5f;
RefreshPalette();
}
public float2 ViewToWorld(int2 loc)
@@ -135,9 +124,9 @@ namespace OpenRA.Graphics
return ViewToWorld(mi.Location);
}
public void Center(int2 loc)
public void Center(float2 loc)
{
scrollPosition = this.NormalizeScrollPosition(Game.CellSize*loc - .5f * new float2(Width, Height));
scrollPosition = this.NormalizeScrollPosition((Game.CellSize*loc - screenSize / 2).ToInt2());
}
public void Center(IEnumerable<Actor> actors)
@@ -148,15 +137,16 @@ namespace OpenRA.Graphics
.Select(a => a.CenterLocation)
.Aggregate((a, b) => a + b);
scrollPosition = this.NormalizeScrollPosition((avgPos - .5f * new float2(Width, Height)));
scrollPosition = this.NormalizeScrollPosition((avgPos.ToInt2() - screenSize / 2));
}
public Rectangle? ShroudBounds()
public Rectangle ShroudBounds( World world )
{
var localPlayer = Game.world.LocalPlayer;
if (localPlayer == null) return null;
if (localPlayer.Shroud.Disabled) return null;
return localPlayer.Shroud.Bounds;
var localPlayer = world.LocalPlayer;
if( localPlayer == null ) return world.Map.Bounds;
if( localPlayer.Shroud.Disabled ) return world.Map.Bounds;
if( !localPlayer.Shroud.Bounds.HasValue ) return world.Map.Bounds;
return Rectangle.Intersect( localPlayer.Shroud.Bounds.Value, world.Map.Bounds );
}
public Rectangle ViewBounds()

View File

@@ -19,7 +19,7 @@ namespace OpenRA.Graphics
{
public class WorldRenderer
{
readonly World world;
public readonly World world;
internal readonly TerrainRenderer terrainRenderer;
public readonly UiOverlay uiOverlay;
internal readonly HardwarePalette palette;
@@ -31,6 +31,9 @@ namespace OpenRA.Graphics
terrainRenderer = new TerrainRenderer(world, this);
uiOverlay = new UiOverlay();
palette = new HardwarePalette(world.Map);
foreach( var pal in world.traitDict.ActorsWithTraitMultiple<IPalette>( world ) )
pal.Trait.InitPalette( this );
}
public int GetPaletteIndex(string name) { return palette.GetPaletteIndex(name); }
@@ -67,8 +70,7 @@ namespace OpenRA.Graphics
return new Rectangle(0, 0, Game.viewport.Width, Game.viewport.Height);
}
Renderable[] worldSprites = { };
public void Tick()
IEnumerable<Renderable> SpritesToRender()
{
var bounds = GetBoundsRect();
var comparer = new SpriteComparer();
@@ -84,38 +86,37 @@ namespace OpenRA.Graphics
var effects = world.Effects.SelectMany(e => e.Render());
worldSprites = renderables.Concat(effects).ToArray();
return renderables.Concat(effects);
}
public void Draw()
{
RefreshPalette();
var bounds = GetBoundsRect();
Game.Renderer.Device.EnableScissor(bounds.Left, bounds.Top, bounds.Width, bounds.Height);
Game.Renderer.EnableScissor(bounds.Left, bounds.Top, bounds.Width, bounds.Height);
terrainRenderer.Draw(Game.viewport);
terrainRenderer.Draw(this, Game.viewport);
if (world.OrderGenerator != null)
world.OrderGenerator.RenderBeforeWorld(world);
world.OrderGenerator.RenderBeforeWorld(this, world);
foreach (var image in SpritesToRender() )
image.Sprite.DrawAt(image.Pos, this.GetPaletteIndex(image.Palette), image.Scale);
uiOverlay.Draw(this, world);
Game.Renderer.SpriteRenderer.Flush();
Game.Renderer.LineRenderer.Flush();
foreach (var image in worldSprites)
Game.Renderer.SpriteRenderer.DrawSprite(image.Sprite, image.Pos, image.Palette);
uiOverlay.Draw(world);
Game.Renderer.SpriteRenderer.Flush();
// added for contrails
foreach (var a in world.Actors)
if (!a.Destroyed)
foreach (var t in a.TraitsImplementing<IPostRender>())
t.RenderAfterWorld(this, a);
if (world.OrderGenerator != null)
world.OrderGenerator.RenderAfterWorld(world);
world.OrderGenerator.RenderAfterWorld(this, world);
if (world.LocalPlayer != null)
world.LocalPlayer.Shroud.Draw();
world.LocalPlayer.Shroud.Draw( this );
Game.Renderer.SpriteRenderer.Flush();
Game.Renderer.Device.DisableScissor();
Game.Renderer.LineRenderer.Flush();
Game.Renderer.DisableScissor();
}
void DrawBox(RectangleF r, Color color)
@@ -193,5 +194,10 @@ namespace OpenRA.Graphics
prev = pos;
}
}
public void RefreshPalette()
{
palette.Update( world.WorldActor.TraitsImplementing<IPaletteModifier>() );
}
}
}

52
OpenRA.Game/InputHandler.cs Executable file
View File

@@ -0,0 +1,52 @@
#region Copyright & License Information
/*
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see LICENSE.
*/
#endregion
using OpenRA.Widgets;
namespace OpenRA
{
public class NullInputHandler : IInputHandler
{
// ignore all input
public void ModifierKeys( Modifiers mods ) { }
public void OnKeyInput( KeyInput input ) { }
public void OnMouseInput( MouseInput input ) { }
}
public class DefaultInputHandler : IInputHandler
{
readonly World world;
public DefaultInputHandler( World world )
{
this.world = world;
}
public void ModifierKeys( Modifiers mods )
{
Game.HandleModifierKeys( mods );
}
public void OnKeyInput( KeyInput input )
{
Sync.CheckSyncUnchanged( world, () =>
{
Widget.HandleKeyPress( input );
} );
}
public void OnMouseInput( MouseInput input )
{
Sync.CheckSyncUnchanged( world, () =>
{
Widget.HandleInput( input );
} );
}
}
}

View File

@@ -19,24 +19,14 @@ using OpenRA.FileFormats;
namespace OpenRA
{
public class Map
public class Map : MapStub
{
public IFolder Package;
public string Uid;
// Yaml map data
[FieldLoader.Load] public bool Selectable = true;
[FieldLoader.Load] public int MapFormat;
[FieldLoader.Load] public string Title;
[FieldLoader.Load] public string Description;
[FieldLoader.Load] public string Author;
[FieldLoader.Load] public int PlayerCount;
[FieldLoader.Load] public string Tileset;
public Dictionary<string, PlayerReference> Players = new Dictionary<string, PlayerReference>();
public Dictionary<string, ActorReference> Actors = new Dictionary<string, ActorReference>();
public List<SmudgeReference> Smudges = new List<SmudgeReference>();
public Dictionary<string, int2> Waypoints = new Dictionary<string, int2>();
// Rules overrides
public List<MiniYamlNode> Rules = new List<MiniYamlNode>();
@@ -45,8 +35,6 @@ namespace OpenRA
public byte TileFormat = 1;
[FieldLoader.Load] public int2 MapSize;
[FieldLoader.Load] public int2 TopLeft;
[FieldLoader.Load] public int2 BottomRight;
public TileReference<ushort, byte>[,] MapTiles;
public TileReference<byte, byte>[,] MapResources;
@@ -55,10 +43,7 @@ namespace OpenRA
// Temporary compat hacks
public int XOffset { get { return TopLeft.X; } }
public int YOffset { get { return TopLeft.Y; } }
public int Width { get { return BottomRight.X - TopLeft.X; } }
public int Height { get { return BottomRight.Y - TopLeft.Y; } }
public string Theater { get { return Tileset; } }
public IEnumerable<int2> SpawnPoints { get { return Waypoints.Select(kv => kv.Value); } }
public Rectangle Bounds { get { return Rectangle.FromLTRB(TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y); } }
public Map()
@@ -97,19 +82,13 @@ namespace OpenRA
}
public Map(IFolder package)
: base(package)
{
Package = package;
var yaml = new MiniYaml( null, MiniYaml.FromStream( Package.GetContent( "map.yaml" ) ) );
// 'Simple' metadata
FieldLoader.Load( this, yaml );
// Waypoints
foreach (var wp in yaml.NodesDict["Waypoints"].NodesDict)
{
string[] loc = wp.Value.Value.Split(',');
Waypoints.Add(wp.Key, new int2(int.Parse(loc[0]), int.Parse(loc[1])));
}
// Players & Actors -- this has changed several times.
// - Be backwards compatible wherever possible.
@@ -199,12 +178,13 @@ namespace OpenRA
Rules = yaml.NodesDict["Rules"].Nodes;
CustomTerrain = new string[MapSize.X, MapSize.Y];
LoadUid();
LoadBinaryData();
}
public void Save(string filepath)
{
// Todo: save to a zip file in the support dir by default
Package = new Folder(filepath, 0);
MapFormat = 3;
var root = new List<MiniYamlNode>();
@@ -314,11 +294,6 @@ namespace OpenRA
File.Move(filepath + ".tmp", filepath);
}
public void LoadUid()
{
Uid = Package.GetContent("map.uid").ReadAllText();
}
public void SaveUid(string filename)
{
// UID is calculated by taking an SHA1 of the yaml and binary data

View File

@@ -13,7 +13,6 @@ using System.IO;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Support;
namespace OpenRA
{
@@ -24,10 +23,11 @@ namespace OpenRA
public readonly SheetBuilder SheetBuilder;
public readonly CursorSheetBuilder CursorSheetBuilder;
public readonly Dictionary<string, MapStub> AvailableMaps;
public readonly WidgetLoader WidgetLoader;
public ILoadScreen LoadScreen = null;
public ModData( params string[] mods )
{
{
Manifest = new Manifest( mods );
ObjectCreator = new ObjectCreator( Manifest );
LoadScreen = ObjectCreator.CreateObject<ILoadScreen>(Manifest.LoadScreen);
@@ -39,6 +39,7 @@ namespace OpenRA
SheetBuilder = new SheetBuilder( TextureChannel.Red );
CursorSheetBuilder = new CursorSheetBuilder( this );
AvailableMaps = FindMaps( mods );
WidgetLoader = new WidgetLoader( this );
}
// TODO: Do this nicer
@@ -48,7 +49,7 @@ namespace OpenRA
.Where(p => Directory.Exists(p))
.SelectMany(p => Directory.GetDirectories(p)).ToList();
return paths.Select(p => new MapStub(new Folder(p))).ToDictionary(m => m.Uid);
return paths.Select(p => new MapStub(new Folder(p, 0))).ToDictionary(m => m.Uid);
}
string cachedTheatre = null;

View File

@@ -18,7 +18,7 @@ using OpenRA.Support;
namespace OpenRA.Network
{
enum ConnectionState
public enum ConnectionState
{
PreConnecting,
NotConnected,
@@ -26,11 +26,13 @@ namespace OpenRA.Network
Connected,
}
interface IConnection
public interface IConnection : IDisposable
{
int LocalClientId { get; }
ConnectionState ConnectionState { get; }
void Send( byte[] packet );
void Send( int frame, List<byte[]> orders );
void SendImmediate( List<byte[]> orders );
void SendSync( int frame, byte[] syncData );
void Receive( Action<int, byte[]> packetFn );
}
@@ -53,7 +55,33 @@ namespace OpenRA.Network
get { return ConnectionState.PreConnecting; }
}
public virtual void Send( byte[] packet )
public virtual void Send( int frame, List<byte[]> orders )
{
var ms = new MemoryStream();
ms.Write( BitConverter.GetBytes( frame ) );
foreach( var o in orders )
ms.Write( o );
Send( ms.ToArray() );
}
public virtual void SendImmediate( List<byte[]> orders )
{
var ms = new MemoryStream();
ms.Write( BitConverter.GetBytes( (int)0 ) );
foreach( var o in orders )
ms.Write( o );
Send( ms.ToArray() );
}
public virtual void SendSync( int frame, byte[] syncData )
{
var ms = new MemoryStream();
ms.Write( BitConverter.GetBytes( frame ) );
ms.Write( syncData );
Send( ms.ToArray() );
}
protected virtual void Send( byte[] packet )
{
if( packet.Length == 0 )
throw new NotImplementedException();
@@ -73,9 +101,11 @@ namespace OpenRA.Network
foreach( var p in packets )
packetFn( p.FromClient, p.Data );
}
public virtual void Dispose() { }
}
class NetworkConnection : EchoConnection, IDisposable
class NetworkConnection : EchoConnection
{
TcpClient socket;
int clientId;
@@ -112,12 +142,13 @@ namespace OpenRA.Network
receivedPackets.Add( new ReceivedPacket { FromClient = client, Data = buf } );
}
}
catch( SocketException )
catch { }
finally
{
connectionState = ConnectionState.NotConnected;
if( socket != null )
socket.Close();
}
catch ( IOException ) { socket.Close(); }
catch (ThreadAbortException ) { socket.Close(); }
}
) { IsBackground = true };
t.Start();
@@ -126,26 +157,42 @@ namespace OpenRA.Network
public override int LocalClientId { get { return clientId; } }
public override ConnectionState ConnectionState { get { return connectionState; } }
public override void Send( byte[] packet )
List<byte[]> queuedSyncPackets = new List<byte[]>();
public override void SendSync( int frame, byte[] syncData )
{
var ms = new MemoryStream();
ms.Write( BitConverter.GetBytes( frame ) );
ms.Write( syncData );
queuedSyncPackets.Add( ms.ToArray() );
}
protected override void Send( byte[] packet )
{
base.Send( packet );
try
{
var ms = new MemoryStream();
ms.Write(BitConverter.GetBytes((int)packet.Length));
ms.Write(packet);
foreach( var q in queuedSyncPackets )
{
ms.Write( BitConverter.GetBytes( (int)q.Length ) );
ms.Write( q );
base.Send( q );
}
queuedSyncPackets.Clear();
ms.WriteTo(socket.GetStream());
}
catch (SocketException) { /* drop this on the floor; we'll pick up the disconnect from the reader thread */ }
catch (ObjectDisposedException) { /* ditto */ }
catch (InvalidOperationException) { /* ditto */ }
}
bool disposed = false;
public void Dispose ()
public override void Dispose ()
{
if (disposed) return;
disposed = true;
@@ -160,48 +207,4 @@ namespace OpenRA.Network
~NetworkConnection() { Dispose(); }
}
class ReplayConnection : IConnection
{
//uint nextFrame = 1;
FileStream replayStream;
public ReplayConnection( string replayFilename )
{
replayStream = File.OpenRead( replayFilename );
}
public int LocalClientId
{
get { return 0; }
}
public ConnectionState ConnectionState
{
get { return ConnectionState.Connected; }
}
public void Send( byte[] packet )
{
// do nothing; ignore locally generated orders
}
public void Receive( Action<int, byte[]> packetFn )
{
if( replayStream == null ) return;
var reader = new BinaryReader( replayStream );
while( replayStream.Position < replayStream.Length )
{
var client = reader.ReadInt32();
var packetLen = reader.ReadInt32();
var packet = reader.ReadBytes( packetLen );
packetFn( client, packet );
if( !Game.orderManager.GameStarted )
return;
}
replayStream = null;
}
}
}

View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace OpenRA.Network
{
class FrameData
{
public struct ClientOrder
{
public int Client;
public Order Order;
}
readonly Dictionary<int, int> clientQuitTimes = new Dictionary<int, int>();
readonly Dictionary<int, Dictionary<int, byte[]>> framePackets = new Dictionary<int, Dictionary<int, byte[]>>();
public IEnumerable<int> ClientsPlayingInFrame( int frame )
{
return clientQuitTimes
.Where( x => frame <= x.Value )
.Select( x => x.Key )
.OrderBy( x => x );
}
public void ClientQuit( int clientId, int lastClientFrame )
{
clientQuitTimes[clientId] = lastClientFrame;
}
public void AddFrameOrders( int clientId, int frame, byte[] orders )
{
var frameData = framePackets.GetOrAdd( frame );
frameData.Add( clientId, orders );
}
public bool IsReadyForFrame( int frame )
{
var frameData = framePackets.GetOrAdd( frame );
return ClientsPlayingInFrame( frame )
.All( client => frameData.ContainsKey( client ) );
}
public IEnumerable<ClientOrder> OrdersForFrame( World world, int frame )
{
var frameData = framePackets[ frame ];
var clientData = ClientsPlayingInFrame( frame )
.ToDictionary( k => k, v => frameData[ v ] );
return clientData
.SelectMany( x => x.Value
.ToOrderList( world )
.Select( o => new ClientOrder { Client = x.Key, Order = o } ) );
}
}
}

View File

@@ -132,7 +132,7 @@ namespace OpenRA
{
return "OrderString: \"{0}\" \n\t Subject: \"{1}\". \n\t TargetActor: \"{2}\" \n\t TargetLocation: {3}." +
"\n\t TargetString: \"{4}\".\n\t IsImmediate: {5}.\n\t Player(PlayerName): {6}\n".F(
OrderString, Subject, TargetActor.Info.Name , TargetLocation, TargetString, IsImmediate, Player.PlayerName);
OrderString, Subject, TargetActor != null ? TargetActor.Info.Name : null , TargetLocation, TargetString, IsImmediate, Player != null ? Player.PlayerName : null);
}
static uint UIntFromActor(Actor a)
@@ -187,9 +187,9 @@ namespace OpenRA
return new Order("PauseProduction", subject, new int2( pause ? 1 : 0, 0 ), item);
}
public static Order CancelProduction(Actor subject, string item)
public static Order CancelProduction(Actor subject, string item, int count)
{
return new Order("CancelProduction", subject, item);
return new Order("CancelProduction", subject, new int2( count, 0 ), item);
}
}
}

View File

@@ -22,22 +22,6 @@ namespace OpenRA.Network
s.Write(buf, 0, buf.Length);
}
public static void WriteFrameData(this Stream s, IEnumerable<Order> orders, int frameNumber)
{
var bytes = Serialize( orders, frameNumber );
s.Write( BitConverter.GetBytes( (int)bytes.Length ) );
s.Write( bytes );
}
public static byte[] Serialize( this IEnumerable<Order> orders, int frameNumber )
{
var ms = new MemoryStream();
ms.Write( BitConverter.GetBytes( frameNumber ) );
foreach( var o in orders.Select( o => o.Serialize() ) )
ms.Write( o );
return ms.ToArray();
}
public static List<Order> ToOrderList(this byte[] bytes, World world)
{
var ms = new MemoryStream(bytes, 4, bytes.Length - 4);
@@ -52,12 +36,11 @@ namespace OpenRA.Network
return ret;
}
public static byte[] SerializeSync( this List<int> sync, int frameNumber )
public static byte[] SerializeSync( this List<int> sync )
{
var ms = new MemoryStream();
using( var writer = new BinaryWriter( ms ) )
{
writer.Write( frameNumber );
writer.Write( (byte)0x65 );
foreach( var s in sync )
writer.Write( s );

View File

@@ -16,47 +16,46 @@ using OpenRA.FileFormats;
namespace OpenRA.Network
{
class OrderManager : IDisposable
public class OrderManager : IDisposable
{
SyncReport syncReport = new SyncReport();
readonly SyncReport syncReport;
readonly FrameData frameData = new FrameData();
public int FrameNumber { get; private set; }
public Session LobbyInfo = new Session( Game.Settings.Game.Mods );
public Session.Client LocalClient { get { return LobbyInfo.ClientWithIndex( Connection.LocalClientId ); } }
public World world;
public readonly string Host;
public readonly int Port;
public int NetFrameNumber { get; private set; }
public int LocalFrameNumber;
public int FramesAhead = 0;
public bool GameStarted { get { return FrameNumber != 0; } }
public int LastTickTime = Environment.TickCount;
public bool GameStarted { get { return NetFrameNumber != 0; } }
public IConnection Connection { get; private set; }
public readonly int SyncHeaderSize = 9;
Dictionary<int, int> clientQuitTimes = new Dictionary<int, int>();
Dictionary<int, Dictionary<int, byte[]>> frameClientData =
new Dictionary<int, Dictionary<int, byte[]>>();
List<Order> localOrders = new List<Order>();
FileStream replaySaveFile;
public void StartGame()
{
if (GameStarted) return;
FrameNumber = 1;
for( int i = FrameNumber ; i <= FramesAhead ; i++ )
Connection.Send( new List<Order>().Serialize( i ) );
NetFrameNumber = 1;
for( int i = NetFrameNumber ; i <= FramesAhead ; i++ )
Connection.Send( i, new List<byte[]>() );
}
public OrderManager( IConnection conn )
public OrderManager( string host, int port, IConnection conn )
{
this.Host = host;
this.Port = port;
Connection = conn;
}
public OrderManager( IConnection conn, string replayFilename )
: this( conn )
{
string path = Game.SupportDir + "Replays" + Path.DirectorySeparatorChar;
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
replaySaveFile = File.Create( path + replayFilename );
syncReport = new SyncReport( this );
}
public void IssueOrders( Order[] orders )
@@ -70,11 +69,11 @@ namespace OpenRA.Network
localOrders.Add( order );
}
public void TickImmediate( World world )
public void TickImmediate()
{
var immediateOrders = localOrders.Where( o => o.IsImmediate ).ToList();
if( immediateOrders.Count != 0 )
Connection.Send( immediateOrders.Serialize( 0 ) );
Connection.SendImmediate( immediateOrders.Select( o => o.Serialize() ).ToList() );
localOrders.RemoveAll( o => o.IsImmediate );
var immediatePackets = new List<Pair<int, byte[]>>();
@@ -84,26 +83,23 @@ namespace OpenRA.Network
{
var frame = BitConverter.ToInt32( packet, 0 );
if( packet.Length == 5 && packet[ 4 ] == 0xBF )
clientQuitTimes[ clientId ] = frame;
frameData.ClientQuit( clientId, frame );
else if( packet.Length >= 5 && packet[ 4 ] == 0x65 )
CheckSync( packet );
else if( frame == 0 )
immediatePackets.Add( Pair.New( clientId, packet ) );
else
frameClientData.GetOrAdd( frame ).Add( clientId, packet );
frameData.AddFrameOrders( clientId, frame, packet );
} );
foreach( var p in immediatePackets )
{
foreach( var o in p.Second.ToOrderList( world ) )
UnitOrders.ProcessOrder( world, p.First, o );
WriteImmediateToReplay( immediatePackets );
}
UnitOrders.ProcessOrder( this, world, p.First, o );
}
Dictionary<int, byte[]> syncForFrame = new Dictionary<int, byte[]>();
void CheckSync(byte[] packet)
void CheckSync( byte[] packet )
{
var frame = BitConverter.ToInt32(packet, 0);
byte[] existingSync;
@@ -134,14 +130,9 @@ namespace OpenRA.Network
syncForFrame.Add(frame, packet);
}
void OutOfSync( int frame , int index)
{
var frameData = clientQuitTimes
.Where( x => frame <= x.Value )
.OrderBy( x => x.Key )
.ToDictionary( k => k.Key, v => frameClientData[ FrameNumber ][ v.Key ] );
var order = frameData.SelectMany( o => o.Value.ToOrderList( Game.world ).Select( a => new { Client = o.Key, Order = a } ) ).ElementAt(index);
void OutOfSync(int frame, int index)
{
var order = frameData.OrdersForFrame( world, frame ).ElementAt(index);
throw new InvalidOperationException("Out of sync in frame {0}.\n {1}".F(frame, order.Order.ToString()));
}
@@ -157,72 +148,32 @@ namespace OpenRA.Network
public bool IsReadyForNextFrame
{
get
{
return FrameNumber > 0 &&
clientQuitTimes
.Where( x => FrameNumber <= x.Value )
.All( x => frameClientData.GetOrAdd( FrameNumber ).ContainsKey( x.Key ) );
}
get { return NetFrameNumber >= 1 && frameData.IsReadyForFrame( NetFrameNumber ); }
}
public void Tick( World world )
public void Tick()
{
if( !IsReadyForNextFrame )
throw new InvalidOperationException();
Connection.Send( localOrders.Serialize( FrameNumber + FramesAhead ) );
Connection.Send( NetFrameNumber + FramesAhead, localOrders.Select( o => o.Serialize() ).ToList() );
localOrders.Clear();
var frameData = clientQuitTimes
.Where( x => FrameNumber <= x.Value )
.OrderBy( x => x.Key )
.ToDictionary( k => k.Key, v => frameClientData[ FrameNumber ][ v.Key ] );
var sync = new List<int>();
sync.Add( world.SyncHash() );
foreach( var order in frameData.SelectMany( o => o.Value.ToOrderList( world ).Select( a => new { Client = o.Key, Order = a } ) ) )
foreach( var order in frameData.OrdersForFrame( world, NetFrameNumber) )
{
UnitOrders.ProcessOrder( world, order.Client, order.Order );
UnitOrders.ProcessOrder( this, world, order.Client, order.Order );
sync.Add( world.SyncHash() );
}
var ss = sync.SerializeSync( FrameNumber );
Connection.Send( ss );
WriteToReplay( frameData, ss );
var ss = sync.SerializeSync();
Connection.SendSync( NetFrameNumber, ss );
syncReport.UpdateSyncReport();
CheckSync( ss );
++FrameNumber;
}
void WriteToReplay( Dictionary<int, byte[]> frameData, byte[] syncData )
{
if( replaySaveFile == null ) return;
foreach( var f in frameData )
{
replaySaveFile.Write( BitConverter.GetBytes( f.Key ) );
replaySaveFile.Write( BitConverter.GetBytes( f.Value.Length ) );
replaySaveFile.Write( f.Value );
}
replaySaveFile.Write( BitConverter.GetBytes( (int)0 ) );
replaySaveFile.Write( BitConverter.GetBytes( (int)syncData.Length ) );
replaySaveFile.Write( syncData );
}
void WriteImmediateToReplay( List<Pair<int, byte[]>> immediatePackets )
{
if( replaySaveFile == null ) return;
foreach( var i in immediatePackets )
{
replaySaveFile.Write( BitConverter.GetBytes( i.First ) );
replaySaveFile.Write( BitConverter.GetBytes( i.Second.Length ) );
replaySaveFile.Write( i.Second );
}
++NetFrameNumber;
}
bool disposed;
@@ -230,11 +181,7 @@ namespace OpenRA.Network
{
if (disposed) return;
if (replaySaveFile != null)
replaySaveFile.Dispose();
var disposableConnection = Connection as IDisposable;
if (disposableConnection != null) disposableConnection.Dispose();
Connection.Dispose();
disposed = true;
GC.SuppressFinalize(this);

View File

@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace OpenRA.Network
{
class ReplayConnection : IConnection
{
//uint nextFrame = 1;
FileStream replayStream;
public ReplayConnection( string replayFilename )
{
replayStream = File.OpenRead( replayFilename );
}
public int LocalClientId
{
get { return 0; }
}
public ConnectionState ConnectionState
{
get { return ConnectionState.Connected; }
}
// do nothing; ignore locally generated orders
public void Send( int frame, List<byte[]> orders ) { }
public void SendImmediate( List<byte[]> orders ) { }
public void SendSync( int frame, byte[] syncData )
{
var ms = new MemoryStream();
ms.Write( BitConverter.GetBytes( frame ) );
ms.Write( syncData );
sync.Add( ms.ToArray() );
}
List<byte[]> sync = new List<byte[]>();
public void Receive( Action<int, byte[]> packetFn )
{
while( sync.Count != 0 )
{
packetFn( LocalClientId, sync[ 0 ] );
sync.RemoveAt( 0 );
}
if( replayStream == null ) return;
var reader = new BinaryReader( replayStream );
while( replayStream.Position < replayStream.Length )
{
var client = reader.ReadInt32();
var packetLen = reader.ReadInt32();
var packet = reader.ReadBytes( packetLen );
packetFn( client, packet );
}
replayStream = null;
}
public void Dispose() { }
}
class ReplayRecorderConnection : IConnection
{
IConnection inner;
BinaryWriter writer;
public ReplayRecorderConnection( IConnection inner, FileStream replayFile )
{
this.inner = inner;
this.writer = new BinaryWriter( replayFile );
}
public int LocalClientId { get { return inner.LocalClientId; } }
public ConnectionState ConnectionState { get { return inner.ConnectionState; } }
public void Send( int frame, List<byte[]> orders ) { inner.Send( frame, orders ); }
public void SendImmediate( List<byte[]> orders ) { inner.SendImmediate( orders ); }
public void SendSync( int frame, byte[] syncData ) { inner.SendSync( frame, syncData ); }
public void Receive( Action<int, byte[]> packetFn )
{
inner.Receive( ( client, data ) =>
{
writer.Write( client );
writer.Write( data.Length );
writer.Write( data );
packetFn( client, data );
} );
}
bool disposed;
public void Dispose()
{
if( disposed )
return;
writer.Close();
disposed = true;
}
~ReplayRecorderConnection()
{
Dispose();
}
}
}

View File

@@ -10,6 +10,7 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
namespace OpenRA.Network
@@ -20,10 +21,21 @@ namespace OpenRA.Network
public List<Slot> Slots = new List<Slot>();
public Global GlobalSettings = new Global();
public Client ClientWithIndex(int clientID)
{
return Clients.SingleOrDefault(c => c.Index == clientID);
}
public Client ClientInSlot(Slot slot)
{
return Clients.SingleOrDefault(c => c.Slot == slot.Index);
}
public enum ClientState
{
NotReady,
Ready
Ready,
Disconnected = 1000
}
public class Client
@@ -44,13 +56,14 @@ namespace OpenRA.Network
public int Index;
public string Bot; // trait name of the bot to initialize in this slot, or null otherwise.
public bool Closed; // host has explicitly closed this slot.
public string MapPlayer; // playerReference to bind against.
public string MapPlayer; // playerReference to bind against.
public bool Spectator = false; // Spectating or not
// todo: more stuff?
}
public class Global
{
public string ServerName;
public string Map;
public string[] Mods = { "ra" }; // mod names
public int OrderLatency = 3;
@@ -59,32 +72,36 @@ namespace OpenRA.Network
public bool AllowCheats = false;
}
public Session(string[] mods)
{
this.GlobalSettings.Mods = mods.ToArray();
}
public string Serialize()
{
var clientData = new List<MiniYamlNode>();
foreach( var client in Clients )
clientData.Add( new MiniYamlNode( "Client@{0}".F( client.Index ), FieldSaver.Save( client ) ) );
foreach (var client in Clients)
clientData.Add(new MiniYamlNode("Client@{0}".F(client.Index), FieldSaver.Save(client)));
foreach( var slot in Slots )
clientData.Add( new MiniYamlNode( "Slot@{0}".F( slot.Index ), FieldSaver.Save( slot ) ) );
foreach (var slot in Slots)
clientData.Add(new MiniYamlNode("Slot@{0}".F(slot.Index), FieldSaver.Save(slot)));
clientData.Add( new MiniYamlNode( "GlobalSettings", FieldSaver.Save( GlobalSettings ) ) );
clientData.Add(new MiniYamlNode("GlobalSettings", FieldSaver.Save(GlobalSettings)));
return clientData.WriteToString();
}
public static Session Deserialize(string data)
{
var session = new Session();
session.GlobalSettings.Mods = Game.Settings.Game.Mods;
var session = new Session(Game.Settings.Game.Mods);
var ys = MiniYaml.FromString(data);
foreach (var y in ys)
{
var yy = y.Key.Split('@');
switch( yy[0] )
switch (yy[0])
{
case "GlobalSettings":
FieldLoader.Load(session.GlobalSettings, y.Value);
@@ -95,7 +112,7 @@ namespace OpenRA.Network
break;
case "Slot":
session.Slots.Add(FieldLoader.Load<Session.Slot>(y.Value ));
session.Slots.Add(FieldLoader.Load<Session.Slot>(y.Value));
break;
}
}

View File

@@ -4,17 +4,20 @@ using System.Linq;
using System.Text;
using OpenRA.FileFormats;
using OpenRA.Support;
using OpenRA.Traits;
namespace OpenRA.Network
{
class SyncReport
{
readonly OrderManager orderManager;
const int numSyncReports = 5;
Report[] syncReports = new Report[numSyncReports];
int curIndex = 0;
public SyncReport()
public SyncReport( OrderManager orderManager )
{
this.orderManager = orderManager;
for (var i = 0; i < numSyncReports; i++)
syncReports[i] = new SyncReport.Report();
}
@@ -30,11 +33,12 @@ namespace OpenRA.Network
void GenerateSyncReport(Report report)
{
report.Frame = Game.orderManager.FrameNumber;
report.SyncedRandom = Game.world.SharedRandom.Last;
report.Frame = orderManager.NetFrameNumber;
report.SyncedRandom = orderManager.world.SharedRandom.Last;
report.Traits.Clear();
foreach (var a in Game.world.Queries.WithTraitMultiple<object>())
foreach (var a in orderManager.world.Queries.WithTraitMultiple<object>())
{
if (a.Trait is ITraitNotSynced ) continue;
var sync = Sync.CalculateSyncHash(a.Trait);
if (sync != 0)
report.Traits.Add(new TraitReport()

View File

@@ -16,92 +16,150 @@ namespace OpenRA.Network
{
static class UnitOrders
{
static Session.Client FindClientById(int id)
static Player FindPlayerByClient(this World world, Session.Client c)
{
return Game.LobbyInfo.Clients.FirstOrDefault(c => c.Index == id);
/* todo: this is still a hack.
* the cases we're trying to avoid are the extra players on the host's client -- Neutral, other MapPlayers,
* bots,.. */
return world.players.Values.FirstOrDefault(
p => p.ClientIndex == c.Index && p.PlayerName == c.Name);
}
static Player FindPlayerByClientId(int id)
public static void ProcessOrder(OrderManager orderManager, World world, int clientId, Order order)
{
/* todo: find the interactive player. */
return Game.world.players.Values.FirstOrDefault(p => p.ClientIndex == id);
}
public static void ProcessOrder( World world, int clientId, Order order )
{
// Drop exploiting orders
if (order.Subject != null && order.Subject.Owner.ClientIndex != clientId)
if (world != null)
{
Game.Debug("Detected exploit order from {0}: {1}".F(clientId, order.OrderString));
return;
if (!world.WorldActor.TraitsImplementing<IValidateOrder>().All(vo =>
vo.OrderValidation(orderManager, world, clientId, order)))
return;
}
switch( order.OrderString )
switch (order.OrderString)
{
case "Chat":
{
var client = FindClientById(clientId);
if (client != null)
case "Chat":
{
var player = FindPlayerByClientId(clientId);
if (player != null && player.WinState == WinState.Lost)
Game.AddChatLine(client.Color1, client.Name + " (Dead)", order.TargetString);
else
Game.AddChatLine(client.Color1, client.Name, order.TargetString);
}
break;
}
case "TeamChat":
{
var client = FindClientById(clientId);
if (client != null)
{
var player = FindPlayerByClientId(clientId);
var display = (world.GameHasStarted) ?
player != null && (world.LocalPlayer != null && player.Stances[world.LocalPlayer] == Stance.Ally
|| player.WinState == WinState.Lost) :
client == Game.LocalClient || (client.Team == Game.LocalClient.Team && client.Team != 0);
if (display)
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
if (Game.IsHost && Game.Settings.Server.Extension != null && !Game.Settings.Server.Extension.OnIngameChat(client, order.TargetString, false))
break;
if (client != null)
{
var suffix = (player != null && player.WinState == WinState.Lost) ? " (Dead)" : " (Team)";
var player = world != null ? world.FindPlayerByClient(client) : null;
var suffix = (player != null && player.WinState == WinState.Lost) ? " (Dead)" : "";
Game.AddChatLine(client.Color1, client.Name + suffix, order.TargetString);
}
else
Game.AddChatLine(Color.White, "(player {0})".F(clientId), order.TargetString);
break;
}
case "Disconnected": /* reports that the target player disconnected */
{
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
if (client != null)
{
client.State = Session.ClientState.Disconnected;
}
break;
}
case "TeamChat":
{
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
if (Game.IsHost && Game.Settings.Server.Extension != null && !Game.Settings.Server.Extension.OnIngameChat(client, order.TargetString, true))
break;
if (client != null)
{
if (world == null)
{
if (client.Team == orderManager.LocalClient.Team)
Game.AddChatLine(client.Color1, client.Name + " (Team)",
order.TargetString);
}
else
{
var player = world.FindPlayerByClient(client);
var display = player != null
&&
(world.LocalPlayer != null &&
player.Stances[world.LocalPlayer] == Stance.Ally
|| player.WinState == WinState.Lost);
if (display)
{
var suffix = (player != null && player.WinState == WinState.Lost)
? " (Dead)"
: " (Team)";
Game.AddChatLine(client.Color1, client.Name + suffix, order.TargetString);
}
}
}
break;
}
case "StartGame":
{
Game.AddChatLine(Color.White, "Server", "The game has started.");
Game.StartGame(orderManager.LobbyInfo.GlobalSettings.Map);
break;
}
case "SyncInfo":
{
orderManager.LobbyInfo = Session.Deserialize(order.TargetString);
if (orderManager.FramesAhead != orderManager.LobbyInfo.GlobalSettings.OrderLatency
&& !orderManager.GameStarted)
{
orderManager.FramesAhead = orderManager.LobbyInfo.GlobalSettings.OrderLatency;
Game.Debug(
"Order lag is now {0} frames.".F(orderManager.LobbyInfo.GlobalSettings.OrderLatency));
}
Game.SyncLobbyInfo();
break;
}
case "SetStance":
{
var targetPlayer = order.Player.World.players[order.TargetLocation.X];
var newStance = (Stance)order.TargetLocation.Y;
if (Game.IsHost && Game.Settings.Server.Extension != null)
Game.Settings.Server.Extension.OnIngameSetStance(order.Player, targetPlayer, newStance);
SetPlayerStance(world, order.Player, targetPlayer, newStance);
// automatically declare war reciprocally
if (newStance == Stance.Enemy)
SetPlayerStance(world, targetPlayer, order.Player, newStance);
Game.Debug("{0} has set diplomatic stance vs {1} to {2}".F(
order.Player.PlayerName, targetPlayer.PlayerName, newStance));
break;
}
default:
{
if( !order.IsImmediate )
{
var self = order.Subject;
var health = self.TraitOrDefault<Health>();
if( health == null || !health.IsDead )
foreach( var t in self.TraitsImplementing<IResolveOrder>() )
t.ResolveOrder( self, order );
}
break;
}
break;
}
case "StartGame":
{
Game.AddChatLine(Color.White, "Server", "The game has started.");
Game.StartGame(Game.LobbyInfo.GlobalSettings.Map);
break;
}
case "SyncInfo":
{
Game.SyncLobbyInfo(order.TargetString);
break;
}
case "SetStance":
{
var targetPlayer = order.Player.World.players[order.TargetLocation.X];
var oldStance = order.Player.Stances[targetPlayer];
order.Player.Stances[targetPlayer] = (Stance)order.TargetLocation.Y;
if (targetPlayer == world.LocalPlayer)
world.WorldActor.Trait<Shroud>().UpdatePlayerStance(world, order.Player, oldStance, order.Player.Stances[targetPlayer]);
Game.Debug("{0} has set diplomatic stance vs {1} to {2}".F(
order.Player.PlayerName, targetPlayer.PlayerName, order.Player.Stances[targetPlayer]));
break;
}
default:
{
if( !order.IsImmediate )
foreach (var t in order.Subject.TraitsImplementing<IResolveOrder>())
t.ResolveOrder(order.Subject, order);
break;
}
}
}
static void SetPlayerStance(World w, Player a, Player b, Stance s)
{
var oldStance = a.Stances[b];
a.Stances[b] = s;
if (b == w.LocalPlayer)
w.WorldActor.Trait<Shroud>().UpdatePlayerStance(w, b, oldStance, s);
}
}
}

View File

@@ -1,4 +1,15 @@
using System;
#region Copyright & License Information
/*
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see LICENSE.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
@@ -18,7 +29,7 @@ namespace OpenRA
.ToList();
// Namespaces from each mod assembly
foreach (var a in manifest.Assemblies)
foreach (var a in manifest.Assemblies.Concat(manifest.LocalAssemblies))
{
var asm = Assembly.LoadFile(Path.GetFullPath(a));
asms.AddRange(asm.GetNamespaces().Select(ns => Pair.New(asm, ns)));
@@ -30,19 +41,63 @@ namespace OpenRA
public static Action<string> MissingTypeAction =
s => { throw new InvalidOperationException("Cannot locate type: {0}".F(s)); };
public T CreateObject<T>(string classname)
public T CreateObject<T>(string className)
{
foreach (var mod in ModAssemblies)
{
var fullTypeName = mod.Second + "." + classname;
var obj = mod.First.CreateInstance(fullTypeName);
if (obj != null)
return (T)obj;
}
return CreateObject<T>( className, new Dictionary<string, object>() );
}
MissingTypeAction(classname);
public T CreateObject<T>( string className, Dictionary<string, object> args )
{
foreach( var mod in ModAssemblies )
{
var type = mod.First.GetType( mod.Second + "." + className, false );
if( type == null ) continue;
var ctors = type.GetConstructors( BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance ).Where( x => x.HasAttribute<UseCtorAttribute>() ).ToList();
if( ctors.Count == 0 )
return (T)CreateBasic( type );
else if( ctors.Count == 1 )
return (T)CreateUsingArgs( ctors[ 0 ], args );
else
throw new InvalidOperationException( "ObjectCreator: UseCtor on multiple constructors; invalid." );
}
MissingTypeAction(className);
return default(T);
}
public object CreateBasic( Type type )
{
return type.GetConstructor( new Type[ 0 ] ).Invoke( new object[ 0 ] );
}
public object CreateUsingArgs( ConstructorInfo ctor, Dictionary<string, object> args )
{
var p = ctor.GetParameters();
var a = new object[ p.Length ];
for( int i = 0 ; i < p.Length ; i++ )
{
var attrs = p[ i ].GetCustomAttributes<ParamAttribute>();
if( attrs.Length != 1 ) throw new InvalidOperationException( "ObjectCreator: argument in [UseCtor] doesn't have [Param]" );
a[ i ] = args[ attrs[ 0 ].ParamName ?? p[i].Name ];
}
return ctor.Invoke( a );
}
[AttributeUsage( AttributeTargets.Parameter )]
public class ParamAttribute : Attribute
{
public string ParamName { get; private set; }
public ParamAttribute() { }
public ParamAttribute( string paramName )
{
ParamName = paramName;
}
}
[AttributeUsage( AttributeTargets.Constructor )]
public class UseCtorAttribute : Attribute
{
}
}
}

View File

@@ -79,7 +79,6 @@
<Compile Include="Group.cs" />
<Compile Include="Orders\GenericSelectTarget.cs" />
<Compile Include="Server\ProtocolVersion.cs" />
<Compile Include="Traits\BaseBuilding.cs" />
<Compile Include="Traits\LintAttributes.cs" />
<Compile Include="Traits\Player\PlayerResources.cs" />
<Compile Include="Traits\World\Shroud.cs" />
@@ -106,7 +105,6 @@
<Compile Include="Network\Connection.cs" />
<Compile Include="Network\OrderIO.cs" />
<Compile Include="Network\OrderManager.cs" />
<Compile Include="PathSearch.cs" />
<Compile Include="Selection.cs" />
<Compile Include="Server\Connection.cs" />
<Compile Include="Server\Exts.cs" />
@@ -117,12 +115,10 @@
<Compile Include="Sound.cs" />
<Compile Include="Support\PerfHistory.cs" />
<Compile Include="Sync.cs" />
<Compile Include="Traits\CustomSellValue.cs" />
<Compile Include="Traits\World\SpatialBins.cs" />
<Compile Include="Traits\World\Country.cs" />
<Compile Include="Actor.cs" />
<Compile Include="Cursor.cs" />
<Compile Include="GameRules\Footprint.cs" />
<Compile Include="GameRules\Rules.cs" />
<Compile Include="Graphics\Animation.cs" />
<Compile Include="Game.cs" />
@@ -131,19 +127,14 @@
<Compile Include="Graphics\LineRenderer.cs" />
<Compile Include="Graphics\WorldRenderer.cs" />
<Compile Include="Traits\Activities\Idle.cs" />
<Compile Include="Traits\Activities\RemoveSelf.cs" />
<Compile Include="Traits\Activities\Sell.cs" />
<Compile Include="Orders\IOrderGenerator.cs" />
<Compile Include="Player.cs" />
<Compile Include="Graphics\Sheet.cs" />
<Compile Include="PathFinder.cs" />
<Compile Include="Graphics\Sequence.cs" />
<Compile Include="Network\Order.cs" />
<Compile Include="Graphics\SequenceProvider.cs" />
<Compile Include="Graphics\SheetBuilder.cs" />
<Compile Include="Graphics\HardwarePalette.cs" />
<Compile Include="MainWindow.cs">
</Compile>
<Compile Include="Support\Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Graphics\Renderer.cs" />
@@ -151,15 +142,10 @@
<Compile Include="Graphics\SpriteRenderer.cs" />
<Compile Include="Graphics\SpriteSheetBuilder.cs" />
<Compile Include="Graphics\TerrainRenderer.cs" />
<Compile Include="Traits\Activities\Move.cs" />
<Compile Include="Traits\Activities\Turn.cs" />
<Compile Include="Traits\Building.cs" />
<Compile Include="Traits\World\BuildingInfluence.cs" />
<Compile Include="Traits\World\PlayerColorPalette.cs" />
<Compile Include="Traits\World\ResourceLayer.cs" />
<Compile Include="Traits\World\ResourceType.cs" />
<Compile Include="Traits\Selectable.cs" />
<Compile Include="Traits\Mobile.cs" />
<Compile Include="Traits\Render\RenderSimple.cs" />
<Compile Include="Traits\TraitsInterfaces.cs" />
<Compile Include="Traits\World\UnitInfluence.cs" />
@@ -169,12 +155,10 @@
<Compile Include="Graphics\Util.cs" />
<Compile Include="Graphics\Viewport.cs" />
<Compile Include="Orders\UnitOrderGenerator.cs" />
<Compile Include="Widgets\WorldTooltipWidget.cs" />
<Compile Include="World.cs" />
<Compile Include="WorldUtils.cs" />
<Compile Include="Traits\Player\EvaAlerts.cs" />
<Compile Include="Traits\World\ScreenShaker.cs" />
<Compile Include="Traits\LineBuild.cs" />
<Compile Include="Widgets\WidgetLoader.cs" />
<Compile Include="Widgets\ButtonWidget.cs" />
<Compile Include="Widgets\Widget.cs" />
@@ -200,9 +184,7 @@
<Compile Include="Widgets\ViewportScrollControllerWidget.cs" />
<Compile Include="Traits\Player\DeveloperMode.cs" />
<Compile Include="Traits\RevealsShroud.cs" />
<Compile Include="Traits\Targetable.cs" />
<Compile Include="Traits\Health.cs" />
<Compile Include="Traits\Activities\Drag.cs" />
<Compile Include="Widgets\VqaPlayerWidget.cs" />
<Compile Include="Widgets\Delegates\VideoPlayerDelegate.cs" />
<Compile Include="GameRules\Settings.cs" />
@@ -210,8 +192,6 @@
<Compile Include="Traits\ActorStance.cs" />
<Compile Include="Traits\Armor.cs" />
<Compile Include="Graphics\CursorProvider.cs" />
<Compile Include="Traits\Player\TechTree.cs" />
<Compile Include="Traits\Player\PowerManager.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
@@ -220,16 +200,26 @@
</ProjectReference>
<Compile Include="ActorInitializer.cs" />
<Compile Include="ActorReference.cs" />
<Compile Include="InputHandler.cs" />
<Compile Include="ModData.cs" />
<Compile Include="Map.cs" />
<Compile Include="Network\FrameData.cs" />
<Compile Include="Network\ReplayConnection.cs" />
<Compile Include="Network\Session.cs" />
<Compile Include="ObjectCreator.cs" />
<Compile Include="Network\SyncReport.cs" />
<Compile Include="Server\IServerExtension.cs" />
<Compile Include="Server\NullServerExtension.cs" />
<Compile Include="Traits\BaseBuilding.cs" />
<Compile Include="Traits\EditorAppearance.cs" />
<Compile Include="Traits\ValidateOrder.cs" />
<Compile Include="Traits\Scale.cs" />
<Compile Include="TraitDictionary.cs" />
<Compile Include="Traits\Activities\CancelableActivity.cs" />
<Compile Include="Traits\SharesCell.cs" />
<Compile Include="Traits\Valued.cs" />
<Compile Include="Traits\World\BibLayer.cs" />
<Compile Include="Widgets\PasswordFieldWidget.cs" />
<Compile Include="Widgets\Delegates\DeveloperModeDelegate.cs" />
<Compile Include="Widgets\ScrollingTextWidget.cs" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Framework.2.0">

View File

@@ -10,18 +10,27 @@
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Orders
{
public class GenericSelectTarget : IOrderGenerator
{
readonly Actor subject;
readonly IEnumerable<Actor> subjects;
readonly string order;
readonly string cursor;
public GenericSelectTarget(IEnumerable<Actor> subjects, string order, string cursor)
{
this.subjects = subjects;
this.order = order;
this.cursor = cursor;
}
public GenericSelectTarget(Actor subject, string order, string cursor)
{
this.subject = subject;
this.subjects = new Actor[] { subject };
this.order = order;
this.cursor = cursor;
}
@@ -36,12 +45,34 @@ namespace OpenRA.Orders
IEnumerable<Order> OrderInner(World world, int2 xy, MouseInput mi)
{
if (mi.Button == MouseButton.Left && world.Map.IsInMap(xy))
yield return new Order(order, subject, xy);
{
world.CancelInputMode();
foreach (var subject in subjects)
yield return new Order(order, subject, xy);
}
}
public virtual void Tick(World world) { }
public void RenderAfterWorld(World world) { }
public void RenderBeforeWorld(World world) { }
public void RenderBeforeWorld(WorldRenderer wr, World world)
{
foreach (var a in world.Selection.Actors)
if (!a.Destroyed)
foreach (var t in a.TraitsImplementing<IPreRenderSelection>())
t.RenderBeforeWorld(wr, a);
Game.Renderer.Flush();
}
public void RenderAfterWorld(WorldRenderer wr, World world)
{
foreach (var a in world.Selection.Actors)
if (!a.Destroyed)
foreach (var t in a.TraitsImplementing<IPostRenderSelection>())
t.RenderAfterWorld(wr, a);
Game.Renderer.Flush();
}
public string GetCursor(World world, int2 xy, MouseInput mi) { return world.Map.IsInMap(xy) ? cursor : "generic-blocked"; }
}
@@ -63,5 +94,4 @@ namespace OpenRA.Orders
world.CancelInputMode();
}
}
}

View File

@@ -9,6 +9,7 @@
#endregion
using System.Collections.Generic;
using OpenRA.Graphics;
namespace OpenRA
{
@@ -16,8 +17,8 @@ namespace OpenRA
{
IEnumerable<Order> Order(World world, int2 xy, MouseInput mi);
void Tick(World world);
void RenderBeforeWorld(World world);
void RenderAfterWorld(World world);
void RenderBeforeWorld(WorldRenderer wr, World world);
void RenderAfterWorld(WorldRenderer wr, World world);
string GetCursor(World world, int2 xy, MouseInput mi);
}
}

View File

@@ -6,78 +6,189 @@
* as published by the Free Software Foundation. For more information,
* see LICENSE.
*/
#endregion
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Orders
{
class UnitOrderGenerator : IOrderGenerator
{
public IEnumerable<Order> Order( World world, int2 xy, MouseInput mi )
{
var underCursor = world.FindUnitsAtMouse(mi.Location)
.Where(a => a.Info.Traits.Contains<TargetableInfo>())
.OrderByDescending(a => a.Info.Traits.Contains<SelectableInfo>() ? a.Info.Traits.Get<SelectableInfo>().Priority : int.MinValue)
.FirstOrDefault();
var orders = world.Selection.Actors
.Select(a => a.Order(xy, mi, underCursor))
.Where(o => o != null)
.ToArray();
var actorsInvolved = orders.Select(o => o.Subject).Distinct();
if (actorsInvolved.Any())
yield return new Order("CreateGroup", actorsInvolved.First().Owner.PlayerActor,
string.Join(",", actorsInvolved.Select(a => a.ActorID.ToString()).ToArray()));
foreach (var o in orders)
yield return o;
public IEnumerable<Order> Order( World world, int2 xy, MouseInput mi )
{
var custom = world.WorldActor.TraitOrDefault<ICustomUnitOrderGenerator>();
if (custom != null)
{
var customOrders = custom.Order(world, xy, mi);
foreach (var o in customOrders)
yield return o;
}
else
{
var underCursor = world.FindUnitsAtMouse(mi.Location)
.Where(a => a.HasTrait<ITargetable>())
.OrderByDescending(
a =>
a.Info.Traits.Contains<SelectableInfo>()
? a.Info.Traits.Get<SelectableInfo>().Priority
: int.MinValue)
.FirstOrDefault();
var orders = world.Selection.Actors
.Select(a => OrderForUnit(a, xy, mi, underCursor))
.Where(o => o != null)
.ToArray();
var actorsInvolved = orders.Select(o => o.self).Distinct();
if (actorsInvolved.Any())
yield return new Order("CreateGroup", actorsInvolved.First().Owner.PlayerActor,
string.Join(",", actorsInvolved.Select(a => a.ActorID.ToString()).ToArray()))
;
foreach (var o in orders)
yield return CheckSameOrder(o.iot, o.trait.IssueOrder(o.self, o.iot, o.target));
}
}
public void Tick( World world ) {}
public void Tick( World world )
{
var custom = world.WorldActor.TraitOrDefault<ICustomUnitOrderGenerator>();
if (custom != null)
{
custom.Tick(world);
}
}
public void RenderBeforeWorld( WorldRenderer wr, World world )
{
var custom = world.WorldActor.TraitOrDefault<ICustomUnitOrderGenerator>();
if (custom != null)
{
custom.RenderBeforeWorld(wr, world);
return;
}
public void RenderBeforeWorld(World world)
{
foreach (var a in world.Selection.Actors)
if (!a.Destroyed)
foreach (var t in a.TraitsImplementing<IPreRenderSelection>())
t.RenderBeforeWorld(a);
t.RenderBeforeWorld( wr, a );
Game.Renderer.Flush();
}
public void RenderAfterWorld( World world )
{
foreach (var a in world.Selection.Actors)
public void RenderAfterWorld( WorldRenderer wr, World world )
{
var custom = world.WorldActor.TraitOrDefault<ICustomUnitOrderGenerator>();
if (custom != null)
{
custom.RenderAfterWorld(wr, world);
return;
}
foreach (var a in world.Selection.Actors)
if (!a.Destroyed)
foreach (var t in a.TraitsImplementing<IPostRenderSelection>())
t.RenderAfterWorld(a);
t.RenderAfterWorld( wr, a );
Game.Renderer.Flush();
}
public string GetCursor( World world, int2 xy, MouseInput mi )
{
if (mi.Modifiers.HasModifier(Modifiers.Shift) || !world.Selection.Actors.Any())
{
var underCursor = world.FindUnitsAtMouse(mi.Location)
.Where(a => a.Info.Traits.Contains<SelectableInfo>())
.Any();
if (underCursor)
return "select";
}
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);
public string GetCursor( World world, int2 xy, MouseInput mi )
{
var custom = world.WorldActor.TraitOrDefault<ICustomUnitOrderGenerator>();
if (custom != null)
{
return custom.GetCursor(world, xy, mi);
}
return c ?? "default";
var underCursor = world.FindUnitsAtMouse(mi.Location)
.Where(a => a.HasTrait<ITargetable>())
.OrderByDescending(a => a.Info.Traits.Contains<SelectableInfo>() ? a.Info.Traits.Get<SelectableInfo>().Priority : int.MinValue)
.FirstOrDefault();
if( mi.Modifiers.HasModifier( Modifiers.Shift ) || !world.Selection.Actors.Any() )
if( underCursor != null )
return "select";
var orders = world.Selection.Actors
.Select(a => OrderForUnit(a, xy, mi, underCursor))
.Where(o => o != null)
.ToArray();
if( orders.Length == 0 ) return "default";
return orders[ 0 ].cursor ?? "default";
}
static UnitOrderResult OrderForUnit( Actor self, int2 xy, MouseInput mi, Actor underCursor )
{
if (self.Owner != self.World.LocalPlayer)
return null;
if (!self.World.Map.IsInMap(xy.X, xy.Y))
return null;
if (self.Destroyed)
return null;
//var old = self.TraitsImplementing<IIssueOrder>()
// .OrderByDescending( x => x.OrderPriority( self, xy, mi, underCursor ) )
// .Select( x => x.IssueOrder( self, xy, mi, underCursor ) )
// .FirstOrDefault( x => x != null );
//if( old != null )
// return old;
if( mi.Button == MouseButton.Right )
{
var uim = self.World.WorldActor.Trait<UnitInfluence>();
foreach( var o in self.TraitsImplementing<IIssueOrder>()
.SelectMany( trait => trait.Orders
.Select( x => new { Trait = trait, Order = x } ) )
.OrderByDescending( x => x.Order.OrderPriority ) )
{
var actorsAt = uim.GetUnitsAt( xy ).ToList();
string cursor = null;
if( underCursor != null )
if( o.Order.CanTargetUnit( self, underCursor, mi.Modifiers.HasModifier( Modifiers.Ctrl ), mi.Modifiers.HasModifier( Modifiers.Alt ), ref cursor ) )
return new UnitOrderResult( self, o.Order, o.Trait, cursor, Target.FromActor( underCursor ) );
if( o.Order.CanTargetLocation( self, xy, actorsAt, mi.Modifiers.HasModifier( Modifiers.Ctrl ), mi.Modifiers.HasModifier( Modifiers.Alt ), ref cursor ) )
return new UnitOrderResult( self, o.Order, o.Trait, cursor, Target.FromCell( xy ) );
}
}
return null;
}
static Order CheckSameOrder( IOrderTargeter iot, Order order )
{
if( order == null && iot.OrderID != null )
Game.Debug( "BUG: in order targeter - decided on {0} but then didn't order", iot.OrderID );
else if( iot.OrderID != order.OrderString )
Game.Debug( "BUG: in order targeter - decided on {0} but ordered {1}", iot.OrderID, order.OrderString );
return order;
}
class UnitOrderResult
{
public readonly Actor self;
public readonly IOrderTargeter iot;
public readonly IIssueOrder trait;
public readonly string cursor;
public readonly Target target;
public UnitOrderResult( Actor self, IOrderTargeter iot, IIssueOrder trait, string cursor, Target target )
{
this.self = self;
this.iot = iot;
this.trait = trait;
this.cursor = cursor;
this.target = target;
}
}
}
}

View File

@@ -13,93 +13,86 @@ using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Network;
using OpenRA.Traits;
namespace OpenRA
{
public enum PowerState { Normal, Low, Critical };
public enum WinState { Won, Lost, Undefined };
public class Player
{
public Actor PlayerActor;
public int Kills;
using OpenRA.Traits;
namespace OpenRA
{
public enum PowerState { Normal, Low, Critical };
public enum WinState { Won, Lost, Undefined };
public class Player
{
public Actor PlayerActor;
public int Kills;
public int Deaths;
public WinState WinState = WinState.Undefined;
public readonly string Palette;
public readonly Color Color;
public readonly Color Color2;
public readonly string PlayerName;
public readonly string InternalName;
public readonly CountryInfo Country;
public readonly int Index;
public WinState WinState = WinState.Undefined;
public readonly string Palette;
public readonly Color Color;
public readonly Color Color2;
public readonly string PlayerName;
public readonly string InternalName;
public readonly CountryInfo Country;
public readonly int Index;
public readonly bool NonCombatant = false;
public readonly int ClientIndex;
public readonly PlayerReference PlayerRef;
public ShroudRenderer Shroud;
public World World { get; private set; }
public Player( World world, PlayerReference pr, int index )
{
World = world;
Shroud = new ShroudRenderer(this, world.Map);
Index = index;
Palette = "player"+index;
Color = pr.Color;
public readonly PlayerReference PlayerRef;
public bool IsBot;
public ShroudRenderer Shroud;
public World World { get; private set; }
public Player(World world, PlayerReference pr, int index)
{
World = world;
Shroud = new ShroudRenderer(this, world.Map);
Index = index;
Palette = "player" + index;
Color = pr.Color;
Color2 = pr.Color2;
ClientIndex = 0; /* it's a map player, "owned" by host */
PlayerName = InternalName = pr.Name;
NonCombatant = pr.NonCombatant;
Country = world.GetCountries()
ClientIndex = 0; /* it's a map player, "owned" by host */
PlayerName = InternalName = pr.Name;
NonCombatant = pr.NonCombatant;
Country = world.GetCountries()
.FirstOrDefault(c => pr.Race == c.Race)
?? world.GetCountries().Random(world.SharedRandom);
PlayerRef = pr;
RegisterPlayerColor(world, Palette);
PlayerActor = world.CreateActor("Player", new TypeDictionary{ new OwnerInit( this ) });
}
public Player( World world, Session.Client client, PlayerReference pr, int index )
{
World = world;
Shroud = new ShroudRenderer(this, world.Map);
PlayerRef = pr;
PlayerActor = world.CreateActor("Player", new TypeDictionary { new OwnerInit(this) });
}
public Player(World world, Session.Client client, PlayerReference pr, int index)
{
World = world;
Shroud = new ShroudRenderer(this, world.Map);
Index = index;
Palette = "player"+index;
Color = client.Color1;
Color2 = client.Color2;
Palette = "player" + index;
Color = client.Color1;
Color2 = client.Color2;
PlayerName = client.Name;
InternalName = pr.Name;
Country = world.GetCountries()
.FirstOrDefault(c => client != null && client.Country == c.Race)
InternalName = pr.Name;
Country = world.GetCountries()
.FirstOrDefault(c => client != null && client.Country == c.Race)
?? world.GetCountries().Random(world.SharedRandom);
ClientIndex = client.Index;
PlayerRef = pr;
RegisterPlayerColor(world, Palette);
PlayerActor = world.CreateActor("Player", new TypeDictionary{ new OwnerInit( this ) });
}
public void RegisterPlayerColor(World world, string palette)
{
var info = Rules.Info["world"].Traits.Get<PlayerColorPaletteInfo>();
var newpal = new Palette(world.WorldRenderer.GetPalette(info.BasePalette),
new PlayerColorRemap(Color, Color2, info.PaletteFormat));
world.WorldRenderer.AddPalette(palette, newpal);
}
public void GiveAdvice(string advice)
{
Sound.PlayToPlayer(this, advice);
}
public Dictionary<Player, Stance> Stances = new Dictionary<Player, Stance>();
}
PlayerRef = pr;
PlayerActor = world.CreateActor("Player", new TypeDictionary { new OwnerInit(this) });
}
public void GiveAdvice(string advice)
{
Sound.PlayToPlayer(this, advice);
}
public Dictionary<Player, Stance> Stances = new Dictionary<Player, Stance>();
}
}

View File

@@ -42,7 +42,7 @@ namespace OpenRA
else
actors = (isCombine ? oldSelection.Union(newSelection) : newSelection).ToList();
var voicedUnit = actors.FirstOrDefault(a => a.Owner == world.LocalPlayer && a.HasVoice());
var voicedUnit = actors.FirstOrDefault(a => a.Owner == world.LocalPlayer && a.IsInWorld && a.HasVoice());
if (voicedUnit != null)
Sound.PlayVoice("Select", voicedUnit, voicedUnit.Owner.Country.Race);
@@ -56,6 +56,10 @@ namespace OpenRA
public void Tick(World world)
{
actors.RemoveAll(a => !a.IsInWorld);
foreach (var cg in controlGroups.Values)
cg.RemoveAll(a => a.Destroyed); // note: NOT `!a.IsInWorld`, since that would remove things
// that are in transports.
}
Cache<int, List<Actor>> controlGroups = new Cache<int, List<Actor>>(_ => new List<Actor>());

View File

@@ -15,20 +15,20 @@ using System.Net.Sockets;
namespace OpenRA.Server
{
class Connection
public class Connection
{
public Socket socket;
public List<byte> data = new List<byte>();
public ReceiveState State = ReceiveState.Header;
public int ExpectLength = 8;
public int Frame = 0;
internal Socket socket;
internal List<byte> data = new List<byte>();
internal ReceiveState State = ReceiveState.Header;
internal int ExpectLength = 8;
internal int Frame = 0;
public int MostRecentFrame = 0;
internal int MostRecentFrame = 0;
/* client data */
public int PlayerIndex;
public int PlayerIndex { get; internal set; }
public byte[] PopBytes(int n)
internal byte[] PopBytes(int n)
{
var result = data.GetRange(0, n);
data.RemoveRange(0, n);
@@ -65,7 +65,7 @@ namespace OpenRA.Server
return true;
}
public void ReadData()
internal void ReadData()
{
if (ReadDataInner())
while (data.Count >= ExpectLength)

1
OpenRA.Game/Server/Exts.cs Normal file → Executable file
View File

@@ -11,6 +11,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System;
namespace OpenRA.Server
{

View File

@@ -0,0 +1,84 @@
#region Copyright & License Information
/*
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see LICENSE.
*/
#endregion
using System.Drawing;
using OpenRA.Network;
using OpenRA.Traits;
namespace OpenRA.Server
{
public interface IServerExtension
{
/// <summary>
/// Return true to use the built-in handling
/// </summary>
bool OnReadyUp(Connection conn, Session.Client client);
void OnStartGame();
/// <summary>
/// Return true to use the built-in handling
/// </summary>
bool OnNickChange(Connection conn, Session.Client client, string newName);
/// <summary>
/// Return true to use the built-in handling
/// </summary>
bool OnRaceChange(Connection conn, Session.Client client, string newRace);
/// <summary>
/// Return true to use the built-in handling
/// </summary>
bool OnSlotChange(Connection conn, Session.Client client, Session.Slot slot, Map map);
/// <summary>
/// Return true to use the built-in handling
/// </summary>
bool OnTeamChange(Connection conn, Session.Client getClient, int team);
/// <summary>
/// Return true to use the built-in handling
/// </summary>
bool OnSpawnpointChange(Connection conn, Session.Client getClient, int spawnPoint);
/// <summary>
/// Return true to use the built-in handling
/// </summary>
bool OnColorChange(Connection conn, Session.Client getClient, Color fromArgb, Color color);
/// <summary>
/// Return true to use the built-in handling
/// </summary>
bool OnChat(Connection conn, string message, bool teamChat);
void OnServerStart();
void OnServerStop(bool forced);
void OnLoadMap(Map map);
/// <summary>
/// Return false to drop the connection
/// </summary>
bool OnValidateConnection(bool gameStarted, Connection newConn);
void OnLobbySync(Session lobbyInfo, bool gameStarted);
/// <summary>
/// Return true to use the built-in handling
/// </summary>
bool OnPingMasterServer(Session lobbyInfo, bool gameStarted);
/// <summary>
/// Return true to use the built-in handling
/// </summary>
bool OnIngameChat(Session.Client client, string message, bool teamChat);
void OnIngameSetStance(Player player, Player stanceForPlayer, Stance newStance);
void OnLobbyUp();
void OnRejoinLobby(World world);
}
}

69
OpenRA.Game/Server/MasterServerQuery.cs Normal file → Executable file
View File

@@ -14,15 +14,20 @@ using System.Net;
using System.Text;
using System.Threading;
using OpenRA.FileFormats;
using OpenRA.Widgets;
namespace OpenRA.Server
{
static class MasterServerQuery
public static class MasterServerQuery
{
public static event Action<GameServer[]> OnComplete = _ => { };
public static event Action<string> OnVersion = _ => { };
static GameServer[] Games = { };
public static string ClientVersion = "";
public static string ServerVersion = "";
static AutoResetEvent ev = new AutoResetEvent(false);
static AutoResetEvent ev2 = new AutoResetEvent(false);
public static void Refresh(string masterServerUrl)
{
@@ -30,9 +35,7 @@ namespace OpenRA.Server
{
try
{
var wc = new WebClient();
var data = wc.DownloadData(new Uri(masterServerUrl + "list.php"));
var str = Encoding.UTF8.GetString(data);
var str = GetData(new Uri(masterServerUrl + "list.php"));
var yaml = MiniYaml.FromString(str);
@@ -48,14 +51,70 @@ namespace OpenRA.Server
}).Start();
}
public static void GetMOTD(string masterServerUrl)
{
var motd = Widget.RootWidget.GetWidget<ScrollingTextWidget>("MOTD_SCROLLER");
// Runs in a separate thread to prevent dns lookup hitches
new Thread(() =>
{
if (motd != null)
{
try
{
string motdText = GetData(new Uri(masterServerUrl + "motd.php?v=" + ClientVersion));
string[] p = motdText.Split('|');
if (p.Length == 2 && p[1].Length == int.Parse(p[0]))
{
motd.SetText(p[1]);
motd.ResetScroll();
}
}
catch
{
motd.SetText("Welcome to OpenRA. MOTD unable to be loaded from server.");
motd.ResetScroll();
}
}
ev.Set();
}).Start();
}
public static void Tick()
{
if (ev.WaitOne(TimeSpan.FromMilliseconds(0)))
OnComplete(Games);
if (ev2.WaitOne(TimeSpan.FromMilliseconds(0)))
OnVersion(ServerVersion);
}
static string GetData(Uri uri)
{
var wc = new WebClient();
var data = wc.DownloadData(uri);
return Encoding.UTF8.GetString(data);
}
public static void GetCurrentVersion(string masterServerUrl)
{
new Thread(() =>
{
try
{
ServerVersion = GetData(new Uri(masterServerUrl + "VERSION"));
}
catch
{
ServerVersion = "";
}
ev2.Set();
}).Start();
}
}
class GameServer
public class GameServer
{
public readonly int Id = 0;
public readonly string Name = null;

View File

@@ -0,0 +1,40 @@
#region Copyright & License Information
/*
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see LICENSE.
*/
#endregion
using System.Drawing;
using OpenRA.Network;
using OpenRA.Traits;
namespace OpenRA.Server
{
public class NullServerExtension : IServerExtension
{
public virtual bool OnReadyUp(Connection conn, Session.Client client) { return true; }
public virtual void OnStartGame() { }
public virtual bool OnNickChange(Connection conn, Session.Client client, string newName) { return true; }
public virtual bool OnRaceChange(Connection conn, Session.Client client, string newRace) { return true; }
public virtual bool OnSlotChange(Connection conn, Session.Client client, Session.Slot slot, Map map) { return true; }
public virtual bool OnTeamChange(Connection conn, Session.Client getClient, int team) { return true; }
public virtual bool OnSpawnpointChange(Connection conn, Session.Client getClient, int spawnPoint) { return true; }
public virtual bool OnColorChange(Connection conn, Session.Client getClient, Color fromArgb, Color color) { return true; }
public virtual bool OnChat(Connection conn, string message, bool teamChat) { return true; }
public virtual void OnServerStart() { }
public virtual void OnServerStop(bool forced) { }
// Good spot to manipulate number of spectators! ie set Server.MaxSpectators
public virtual void OnLoadMap(Map map) { }
public virtual bool OnValidateConnection(bool gameStarted, Connection newConn) { return true; }
public virtual void OnLobbySync(Session lobbyInfo, bool gameStarted) { }
public virtual bool OnPingMasterServer(Session lobbyInfo, bool gameStarted) { return true; }
public virtual bool OnIngameChat(Session.Client client, string message, bool teamChat) { return true; }
public virtual void OnIngameSetStance(Player player, Player stanceForPlayer, Stance newStance) { }
public virtual void OnLobbyUp() { }
public virtual void OnRejoinLobby(World world) { }
}
}

View File

@@ -25,12 +25,17 @@ namespace OpenRA.Server
{
static class Server
{
public static Connection[] Connections
{
get { return conns.ToArray(); }
}
static List<Connection> conns = new List<Connection>();
static TcpListener listener = null;
static Dictionary<int, List<Connection>> inFlightFrames
= new Dictionary<int, List<Connection>>();
static Session lobbyInfo;
static bool GameStarted = false;
internal static bool GameStarted = false;
static string Name;
static int ExternalPort;
static int randomSeed;
@@ -40,6 +45,9 @@ namespace OpenRA.Server
const int MasterPingInterval = 60 * 3; // 3 minutes. server has a 5 minute TTL for games, so give ourselves a bit
// of leeway.
public static int MaxSpectators = 4; // How many spectators to allow // @todo Expose this as an option
static int lastPing = 0;
static bool isInternetServer;
static string masterServerUrl;
@@ -47,6 +55,27 @@ namespace OpenRA.Server
static ModData ModData;
static Map Map;
public static void StopListening()
{
conns.Clear();
GameStarted = false;
try { listener.Stop(); }
catch { }
E(e => e.OnServerStop(true));
}
public static void E(Action<IServerExtension> f)
{
E(g => { f(g); return true; });
}
public static bool E(Func<IServerExtension, bool> f)
{
return Game.Settings.Server.Extension == null ||
f(Game.Settings.Server.Extension);
}
public static void ServerMain(ModData modData, Settings settings, string map)
{
Log.AddChannel("server", "server.log");
@@ -54,17 +83,18 @@ namespace OpenRA.Server
isInitialPing = true;
Server.masterServerUrl = settings.Server.MasterServer;
isInternetServer = settings.Server.AdvertiseOnline;
listener = new TcpListener(IPAddress.Any, settings.Server.ListenPort);
Name = settings.Server.Name;
ExternalPort = settings.Server.ExternalPort;
randomSeed = (int)DateTime.Now.ToBinary();
ModData = modData;
lobbyInfo = new Session();
lobbyInfo.GlobalSettings.Mods = settings.Game.Mods;
lobbyInfo = new Session( settings.Game.Mods );
lobbyInfo.GlobalSettings.RandomSeed = randomSeed;
lobbyInfo.GlobalSettings.Map = map;
lobbyInfo.GlobalSettings.AllowCheats = settings.Server.AllowCheats;
lobbyInfo.GlobalSettings.ServerName = settings.Server.Name;
LoadMap(); // populates the Session's slots, too.
@@ -83,6 +113,8 @@ namespace OpenRA.Server
throw new InvalidOperationException( "Unable to start server: port is already in use" );
}
E(e => e.OnServerStart());
new Thread( _ =>
{
for( ; ; )
@@ -95,7 +127,7 @@ namespace OpenRA.Server
foreach( Socket s in checkRead )
if( s == listener.Server ) AcceptConnection();
else conns.Single( c => c.socket == s ).ReadData();
else if (conns.Count > 0) conns.Single( c => c.socket == s ).ReadData();
if (Environment.TickCount - lastPing > MasterPingInterval * 1000)
PingMasterServer();
@@ -108,6 +140,7 @@ namespace OpenRA.Server
{
listener.Stop();
GameStarted = false;
E(e => e.OnServerStop(false));
break;
}
}
@@ -135,41 +168,66 @@ namespace OpenRA.Server
.Where(s => s != null)
.Select((s, i) => { s.Index = i; return s; })
.ToList();
E(e => e.OnLoadMap(Map));
// Generate slots for spectators
for (int i = 0; i < MaxSpectators; i++)
lobbyInfo.Slots.Add(new Session.Slot
{
Spectator = true,
Index = lobbyInfo.Slots.Count(),
MapPlayer = null,
Bot = null
});
}
/* lobby rework todo:
*
* - auto-assign players to slots
* - show all the slots in the lobby ui.
* - rework the game start so we actually use the slots.
* - all players should be able to click an empty slot to move to it
* - host should be able to choose whether a slot is open/closed/bot, with
* potentially more than one choice of bot class.
* - host should be able to kick a client from the lobby by closing its slot.
* - change lobby commands so the host can configure bots, rather than
* just configuring itself.
* - "teams together" option for team games -- will eliminate most need
* for manual spawnpoint choosing.
* - pick sensible non-conflicting colors for bots.
* - 256 max players is a dirty hack
*/
static int ChooseFreePlayerIndex()
{
for (var i = 0; i < 8; i++)
for (var i = 0; i < 256; i++)
if (conns.All(c => c.PlayerIndex != i))
return i;
throw new InvalidOperationException("Already got 8 players");
throw new InvalidOperationException("Already got 256 players");
}
static int ChooseFreeSlot()
{
return lobbyInfo.Slots.First(s => !s.Closed && s.Bot == null).Index;
return lobbyInfo.Slots.First(s => !s.Closed && s.Bot == null
&& !lobbyInfo.Clients.Any( c => c.Slot == s.Index )).Index;
}
static void AcceptConnection()
{
var newConn = new Connection { socket = listener.AcceptSocket() };
Socket newSocket = null;
try
{
if (!listener.Server.IsBound) return;
newSocket = listener.AcceptSocket();
}catch
{
/* could have an exception here when listener 'goes away' when calling AcceptConnection! */
/* alternative would be to use locking but the listener doesnt go away without a reason */
return;
}
var newConn = new Connection { socket = newSocket };
if (!E(e => e.OnValidateConnection(GameStarted, newConn)))
{
DropClient(newConn, new Exception() );
return;
}
try
{
if (GameStarted)
@@ -190,19 +248,25 @@ namespace OpenRA.Server
conns.Add(newConn);
var defaults = new GameRules.PlayerSettings();
lobbyInfo.Clients.Add(
new Session.Client()
{
Index = newConn.PlayerIndex,
Color1 = defaults.Color1,
Color2 = defaults.Color2,
Name = defaults.Name,
Country = "random",
State = Session.ClientState.NotReady,
SpawnPoint = 0,
Team = 0,
Slot = ChooseFreeSlot(),
});
var client = new Session.Client()
{
Index = newConn.PlayerIndex,
Color1 = defaults.Color1,
Color2 = defaults.Color2,
Name = defaults.Name,
Country = "random",
State = Session.ClientState.NotReady,
SpawnPoint = 0,
Team = 0,
Slot = ChooseFreeSlot(),
};
var slotData = lobbyInfo.Slots.FirstOrDefault( x => x.Index == client.Slot );
if (slotData != null)
SyncClientToPlayerReference(client, Map.Players[slotData.MapPlayer]);
lobbyInfo.Clients.Add(client);
Log.Write("server", "Client {0}: Accepted connection from {1}",
newConn.PlayerIndex, newConn.socket.RemoteEndPoint);
@@ -274,7 +338,20 @@ namespace OpenRA.Server
catch (NotImplementedException) { }
}
static bool InterpretCommand(Connection conn, string cmd)
public static void SyncClientToPlayerReference(Session.Client c, PlayerReference pr)
{
if (pr == null)
return;
if (pr.LockColor)
{
c.Color1 = pr.Color;
c.Color2 = pr.Color2;
}
if (pr.LockRace)
c.Country = pr.Race;
}
public static bool InterpretCommand(Connection conn, string cmd)
{
var dict = new Dictionary<string, Func<string, bool>>
{
@@ -294,9 +371,12 @@ namespace OpenRA.Server
SyncLobbyInfo();
if (conns.Count > 0 && conns.All(c => GetClient(c).State == Session.ClientState.Ready))
InterpretCommand(conn, "startgame");
if (Game.Settings.Server.Extension== null || Game.Settings.Server.Extension.OnReadyUp(conn, client))
{
if (conns.Count > 0 && conns.All(c => GetClient(c).State == Session.ClientState.Ready))
InterpretCommand(conn, "startgame");
}
return true;
}},
{ "startgame",
@@ -310,6 +390,8 @@ namespace OpenRA.Server
DispatchOrders(null, 0,
new ServerOrder("StartGame", "").Serialize());
E(e => e.OnStartGame());
PingMasterServer();
return true;
}},
@@ -317,7 +399,10 @@ namespace OpenRA.Server
s =>
{
Log.Write("server", "Player@{0} is now known as {1}", conn.socket.RemoteEndPoint, s);
GetClient(conn).Name = s;
if (E(e => e.OnNickChange(conn, GetClient(conn), s)))
GetClient(conn).Name = s;
SyncLobbyInfo();
return true;
}},
@@ -335,8 +420,29 @@ namespace OpenRA.Server
}},
{ "race",
s =>
{
GetClient(conn).Country = s;
{
if (E(e => e.OnRaceChange(conn, GetClient(conn), s)))
GetClient(conn).Country = s;
SyncLobbyInfo();
return true;
}},
{ "spectator",
s =>
{
var slotData = lobbyInfo.Slots.Where(ax => ax.Spectator && !lobbyInfo.Clients.Any(l => l.Slot == ax.Index)).FirstOrDefault();
if (slotData == null)
return true;
var cl = GetClient(conn);
if (E(e => e.OnSlotChange(conn, cl, slotData, Map)))
{
cl.Slot = slotData.Index;
SyncClientToPlayerReference(cl, slotData.MapPlayer != null
? Map.Players[slotData.MapPlayer] : null);
}
SyncLobbyInfo();
return true;
}},
@@ -345,8 +451,10 @@ namespace OpenRA.Server
{
int team;
if (!int.TryParse(s, out team)) { Log.Write("server", "Invalid team: {0}", s ); return false; }
GetClient(conn).Team = team;
if (E(e => e.OnTeamChange(conn, GetClient(conn), team)))
{
GetClient(conn).Team = team;
}
SyncLobbyInfo();
return true;
}},
@@ -365,8 +473,10 @@ namespace OpenRA.Server
SendChatTo( conn, "You can't be at the same spawn point as another player" );
return true;
}
GetClient(conn).SpawnPoint = spawnPoint;
if (E(e => e.OnSpawnpointChange(conn, GetClient(conn), spawnPoint)))
{
GetClient(conn).SpawnPoint = spawnPoint;
}
SyncLobbyInfo();
return true;
}},
@@ -374,8 +484,14 @@ namespace OpenRA.Server
s =>
{
var c = s.Split(',').Select(cc => int.Parse(cc)).ToArray();
GetClient(conn).Color1 = Color.FromArgb(c[0],c[1],c[2]);
GetClient(conn).Color2 = Color.FromArgb(c[3],c[4],c[5]);
var c1 = Color.FromArgb(c[0], c[1], c[2]);
var c2 = Color.FromArgb(c[3], c[4], c[5]);
if (E(e => e.OnColorChange(conn, GetClient(conn), c1, c2)))
{
GetClient(conn).Color1 = c1;
GetClient(conn).Color2 = c2;
}
SyncLobbyInfo();
return true;
}},
@@ -390,7 +506,15 @@ namespace OpenRA.Server
|| lobbyInfo.Clients.Any( c => c.Slot == slot ))
return false;
GetClient(conn).Slot = slot;
var cl = GetClient(conn);
if (E(e => e.OnSlotChange(conn, cl, slotData, Map)))
{
cl.Slot = slot;
SyncClientToPlayerReference(cl, slotData.MapPlayer != null
? Map.Players[slotData.MapPlayer] : null);
}
SyncLobbyInfo();
return true;
}},
@@ -490,18 +614,13 @@ namespace OpenRA.Server
foreach(var client in lobbyInfo.Clients)
{
client.SpawnPoint = 0;
var slotData = lobbyInfo.Slots.FirstOrDefault( x => x.Index == client.Slot );
if (slotData != null)
SyncClientToPlayerReference(client, Map.Players[slotData.MapPlayer]);
client.State = Session.ClientState.NotReady;
}
SyncLobbyInfo();
return true;
}},
{ "mods",
s =>
{
var args = s.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList();
lobbyInfo.GlobalSettings.Mods = args.GetRange(0,args.Count - 1).ToArray();
lobbyInfo.GlobalSettings.Map = args.Last();
SyncLobbyInfo();
return true;
}},
@@ -537,34 +656,41 @@ namespace OpenRA.Server
new ServerOrder("Chat", text).Serialize());
}
static void SendChat(Connection asConn, string text)
{
DispatchOrders(asConn, 0, new ServerOrder("Chat", text).Serialize());
}
static void SendChat(Connection asConn, string text)
{
DispatchOrders(asConn, 0, new ServerOrder("Chat", text).Serialize());
}
static void SendDisconnected(Connection asConn)
{
DispatchOrders(asConn, 0, new ServerOrder("Disconnected", "").Serialize());
}
static void InterpretServerOrder(Connection conn, ServerOrder so)
{
switch (so.Name)
{
case "Command":
{
if(GameStarted)
SendChatTo(conn, "Cannot change state when game started.");
else if (GetClient(conn).State == Session.ClientState.Ready && !(so.Data == "ready" || so.Data == "startgame") )
SendChatTo(conn, "Cannot change state when marked as ready.");
else if (!InterpretCommand(conn, so.Data))
{
Log.Write("server", "Bad server command: {0}", so.Data);
SendChatTo(conn, "Bad server command.");
};
}
break;
if (GameStarted)
SendChatTo(conn, "Cannot change state when game started.");
else if (GetClient(conn).State == Session.ClientState.Ready && !(so.Data == "ready" || so.Data == "startgame"))
SendChatTo(conn, "Cannot change state when marked as ready.");
else if (!InterpretCommand(conn, so.Data))
{
Log.Write("server", "Bad server command: {0}", so.Data);
SendChatTo(conn, "Bad server command.");
};
}
break;
case "Chat":
case "Chat":
case "TeamChat":
foreach (var c in conns.Except(conn).ToArray())
DispatchOrdersToClient(c, GetClient(conn).Index, 0, so.Serialize());
break;
case "Disconnected":
if (E(e => e.OnChat(conn, so.Data, so.Name == "TeamChat")))
foreach (var c in conns.Except(conn).ToArray())
DispatchOrdersToClient(c, GetClient(conn).Index, 0, so.Serialize());
break;
}
}
@@ -578,6 +704,9 @@ namespace OpenRA.Server
conns.Remove(toDrop);
SendChat(toDrop, "Connection Dropped");
if (GameStarted)
SendDisconnected(toDrop); /* Report disconnection */
lobbyInfo.Clients.RemoveAll(c => c.Index == toDrop.PlayerIndex);
DispatchOrders( toDrop, toDrop.MostRecentFrame, new byte[] { 0xbf } );
@@ -588,8 +717,11 @@ namespace OpenRA.Server
static void SyncLobbyInfo()
{
DispatchOrders(null, 0,
new ServerOrder("SyncInfo", lobbyInfo.Serialize()).Serialize());
E(e => e.OnLobbySync(lobbyInfo, GameStarted));
if (!GameStarted) /* don't do this while the game is running, it breaks things. */
DispatchOrders(null, 0,
new ServerOrder("SyncInfo", lobbyInfo.Serialize()).Serialize());
PingMasterServer();
}
@@ -600,6 +732,9 @@ namespace OpenRA.Server
{
if (isBusy || !isInternetServer) return;
if (!E(e => e.OnPingMasterServer(lobbyInfo, GameStarted)))
return;
lastPing = Environment.TickCount;
isBusy = true;

View File

@@ -9,7 +9,6 @@
#endregion
using System.Drawing;
using OpenRA.FileFormats;
using OpenRA.Graphics;
namespace OpenRA
@@ -128,7 +127,7 @@ namespace OpenRA
return shadowBits[SpecialShroudTiles[u ^ uSides][v]];
}
internal void Draw()
internal void Draw( WorldRenderer wr )
{
if (disabled)
return;
@@ -151,65 +150,39 @@ namespace OpenRA
var minx = clipRect.Left;
var maxx = clipRect.Right;
var shroudPalette = "fog";
DrawShroud( wr, minx, miny, maxx, maxy, fogSprites, "fog" );
DrawShroud( wr, minx, miny, maxx, maxy, sprites, "shroud" );
}
void DrawShroud( WorldRenderer wr, int minx, int miny, int maxx, int maxy, Sprite[,] s, string pal )
{
var shroudPalette = wr.GetPaletteIndex(pal);
for (var j = miny; j < maxy; j++)
{
var starti = minx;
for (var i = minx; i < maxx; i++)
{
if (fogSprites[i, j] == shadowBits[0x0f])
if (s[i, j] == shadowBits[0x0f])
continue;
if (starti != i)
{
Game.Renderer.SpriteRenderer.DrawSprite(fogSprites[starti, j],
Game.CellSize * new float2(starti, j),
shroudPalette,
new float2(Game.CellSize * (i - starti), Game.CellSize));
starti = i+1;
}
Game.Renderer.SpriteRenderer.DrawSprite(fogSprites[i, j],
Game.CellSize * new float2(i, j),
shroudPalette);
starti = i+1;
}
if (starti < maxx)
Game.Renderer.SpriteRenderer.DrawSprite(fogSprites[starti, j],
Game.CellSize * new float2(starti, j),
shroudPalette,
new float2(Game.CellSize * (maxx - starti), Game.CellSize));
}
shroudPalette = "shroud";
for (var j = miny; j < maxy; j++)
{
var starti = minx;
for (var i = minx; i < maxx; i++)
{
if (sprites[i, j] == shadowBits[0x0f])
continue;
if (starti != i)
{
Game.Renderer.SpriteRenderer.DrawSprite(sprites[starti, j],
s[starti, j].DrawAt(
Game.CellSize * new float2(starti, j),
shroudPalette,
new float2(Game.CellSize * (i - starti), Game.CellSize));
starti = i + 1;
}
Game.Renderer.SpriteRenderer.DrawSprite(sprites[i, j],
s[i, j].DrawAt(
Game.CellSize * new float2(i, j),
shroudPalette);
starti = i + 1;
}
if (starti < maxx)
Game.Renderer.SpriteRenderer.DrawSprite(sprites[starti, j],
s[starti, j].DrawAt(
Game.CellSize * new float2(starti, j),
shroudPalette,
new float2(Game.CellSize * (maxx - starti), Game.CellSize));

View File

@@ -48,60 +48,49 @@ namespace OpenRA
public static void SetListenerPosition(float2 position) { soundEngine.SetListenerPosition(position); }
public static void Play(string name)
static ISound Play(Player player, string name, bool headRelative, float2 pos, float volumeModifier)
{
if (player != null && player != player.World.LocalPlayer)
return null;
if (name == "" || name == null)
return;
return null;
var sound = sounds[name];
soundEngine.Play2D(sound, false, true, float2.Zero, SoundVolume);
}
public static void Play(string name, float2 pos)
{
if (name == "" || name == null)
return;
var sound = sounds[name];
soundEngine.Play2D(sound, false, false, pos, SoundVolume);
}
public static void PlayToPlayer(Player player, string name)
{
if( player == player.World.LocalPlayer )
Play( name );
}
public static void PlayToPlayer(Player player, string name, float2 pos)
{
if (player == player.World.LocalPlayer)
Play(name, pos);
return soundEngine.Play2D(sounds[name],
false, headRelative, pos,
InternalSoundVolume * volumeModifier);
}
public static ISound Play(string name) { return Play(null, name, true, float2.Zero, 1); }
public static ISound Play(string name, float2 pos) { return Play(null, name, false, pos, 1); }
public static ISound Play(string name, float volumeModifier) { return Play(null, name, true, float2.Zero, volumeModifier); }
public static ISound Play(string name, float2 pos, float volumeModifier) { return Play(null, name, false, pos, volumeModifier); }
public static ISound PlayToPlayer(Player player, string name) { return Play( player, name, true, float2.Zero, 1); }
public static ISound PlayToPlayer(Player player, string name, float2 pos) { return Play(player, name, false, pos, 1); }
public static void PlayVideo(byte[] raw)
{
rawSource = LoadSoundRaw(raw);
video = soundEngine.Play2D(rawSource, false, true, float2.Zero, SoundVolume);
video = soundEngine.Play2D(rawSource, false, true, float2.Zero, InternalSoundVolume);
}
public static void PlayVideo()
{
if (video != null)
soundEngine.PauseSound(video, false);
}
public static void PauseVideo()
{
if (video != null)
soundEngine.PauseSound(video, true);
}
public static void StopVideo()
{
if (video != null)
soundEngine.StopSound(video);
}
public static void Tick()
{
// Song finished
@@ -111,13 +100,13 @@ namespace OpenRA
OnMusicComplete();
}
}
static Action OnMusicComplete;
public static bool MusicPlaying { get; private set; }
public static void PlayMusic(string name)
{
PlayMusicThen(name, () => {});
PlayMusicThen(name, () => { });
}
public static void PlayMusicThen(string name, Action then)
{
@@ -132,13 +121,13 @@ namespace OpenRA
return;
}
StopMusic();
currentMusic = name;
MusicPlaying = true;
var sound = sounds[name];
music = soundEngine.Play2D(sound, false, true, float2.Zero, MusicVolume);
}
public static void PlayMusic()
{
if (music == null)
@@ -147,20 +136,26 @@ namespace OpenRA
soundEngine.PauseSound(music, false);
}
public static void StopSound(ISound sound)
{
if (sound != null)
soundEngine.StopSound(sound);
}
public static void StopMusic()
{
if (music != null)
soundEngine.StopSound(music);
MusicPlaying = false;
currentMusic = null;
}
public static void PauseMusic()
{
if (music == null)
return;
MusicPlaying = false;
soundEngine.PauseSound(music, true);
}
@@ -168,16 +163,28 @@ namespace OpenRA
public static float GlobalVolume
{
get { return soundEngine.Volume; }
set { soundEngine.Volume = value;}
set { soundEngine.Volume = value; }
}
static float soundVolumeModifier = 1.0f;
public static float SoundVolumeModifier
{
get { return soundVolumeModifier; }
set
{
soundVolumeModifier = value;
soundEngine.SetSoundVolume(InternalSoundVolume, music, video);
}
}
static float InternalSoundVolume { get { return SoundVolume * soundVolumeModifier; } }
public static float SoundVolume
{
get { return Game.Settings.Sound.SoundVolume; }
set
{
Game.Settings.Sound.SoundVolume = value;
soundEngine.SetSoundVolume(value, music, video);
soundEngine.SetSoundVolume(InternalSoundVolume, music, video);
}
}
@@ -191,7 +198,7 @@ namespace OpenRA
music.Volume = value;
}
}
public static float VideoVolume
{
get { return Game.Settings.Sound.VideoVolume; }
@@ -202,17 +209,17 @@ namespace OpenRA
video.Volume = value;
}
}
public static float MusicSeekPosition
{
get { return (music != null)? music.SeekPosition : 0; }
get { return (music != null) ? music.SeekPosition : 0; }
}
public static float VideoSeekPosition
{
get { return (video != null)? video.SeekPosition : 0; }
get { return (video != null) ? video.SeekPosition : 0; }
}
// Returns true if it played a phrase
public static bool PlayVoice(string phrase, Actor voicedUnit, string variant)
{
@@ -228,9 +235,9 @@ namespace OpenRA
var clip = vi.Pools.Value[phrase].GetNext();
if (clip == null)
return false;
var variantext = (vi.Variants.ContainsKey(variant) && !vi.DisableVariants.Contains(phrase))?
vi.Variants[variant][voicedUnit.ActorID % vi.Variants.Count] : vi.DefaultVariant;
var variantext = (vi.Variants.ContainsKey(variant) && !vi.DisableVariants.Contains(phrase)) ?
vi.Variants[variant][voicedUnit.ActorID % vi.Variants[variant].Length] : vi.DefaultVariant;
Play(clip + variantext);
return true;
}
@@ -249,8 +256,9 @@ namespace OpenRA
void SetSoundVolume(float volume, ISound music, ISound video);
}
interface ISoundSource {}
interface ISound
interface ISoundSource { }
public interface ISound
{
float Volume { get; set; }
float SeekPosition { get; }
@@ -283,7 +291,7 @@ namespace OpenRA
Log.Write("debug", "Failed generating OpenAL source {0}", i);
return;
}
sourcePool.Add(source, false);
}
}
@@ -335,10 +343,10 @@ namespace OpenRA
get { return volume; }
set { Al.alListenerf(Al.AL_GAIN, volume = value); }
}
public void PauseSound(ISound sound, bool paused)
{
int key = ((OpenAlSound) sound).source;
int key = ((OpenAlSound)sound).source;
int state;
Al.alGetSourcei(key, Al.AL_SOURCE_STATE, out state);
if (state == Al.AL_PLAYING && paused)
@@ -346,9 +354,9 @@ namespace OpenRA
else if (state == Al.AL_PAUSED && !paused)
Al.alSourcePlay(key);
}
public void SetAllSoundsPaused(bool paused)
{
{
foreach (int key in sourcePool.Keys)
{
int state;
@@ -357,35 +365,35 @@ namespace OpenRA
Al.alSourcePause(key);
else if (state == Al.AL_PAUSED && !paused)
Al.alSourcePlay(key);
}
}
public void SetSoundVolume(float volume, ISound music, ISound video)
{
var sounds = sourcePool.Select(s => s.Key).Where( b =>
{
var sounds = sourcePool.Select(s => s.Key).Where(b =>
{
int state;
Al.alGetSourcei(b, Al.AL_SOURCE_STATE, out state);
return ((state == Al.AL_PLAYING || state == Al.AL_PAUSED) &&
((music != null)? b != ((OpenAlSound) music).source : true) &&
((video != null)? b != ((OpenAlSound) video).source : true));
return ((state == Al.AL_PLAYING || state == Al.AL_PAUSED) &&
((music != null) ? b != ((OpenAlSound)music).source : true) &&
((video != null) ? b != ((OpenAlSound)video).source : true));
}).ToList();
foreach (var s in sounds)
{
Al.alSourcef(s, Al.AL_GAIN, volume);
}
}
public void StopSound(ISound sound)
{
int key = ((OpenAlSound) sound).source;
int key = ((OpenAlSound)sound).source;
int state;
Al.alGetSourcei(key, Al.AL_SOURCE_STATE, out state);
if (state == Al.AL_PLAYING || state == Al.AL_PAUSED)
Al.alSourceStop(key);
}
public void StopAllSounds()
{
foreach (int key in sourcePool.Keys)
@@ -399,7 +407,7 @@ namespace OpenRA
public void SetListenerPosition(float2 position)
{
var orientation = new [] { 0f, 0f, 1f, 0f, -1f, 0f };
var orientation = new[] { 0f, 0f, 1f, 0f, -1f, 0f };
Al.alListener3f(Al.AL_POSITION, position.X, position.Y, 50);
Al.alListenerfv(Al.AL_ORIENTATION, ref orientation[0]);
@@ -451,26 +459,26 @@ namespace OpenRA
public float Volume
{
get { return volume; }
set
set
{
if (source != -1)
Al.alSourcef(source, Al.AL_GAIN, volume = value);
Al.alSourcef(source, Al.AL_GAIN, volume = value);
}
}
public float SeekPosition
{
get
{
float pos;
Al.alGetSourcef(source, Al.AL_SAMPLE_OFFSET, out pos);
return pos/22050f;
return pos / 22050f;
}
}
public bool Playing
{
get
get
{
int state;
Al.alGetSourcei(source, Al.AL_SOURCE_STATE, out state);

View File

@@ -90,7 +90,7 @@ namespace OpenRA.Support
}
}
class PerfSample : IDisposable
public class PerfSample : IDisposable
{
readonly Stopwatch sw = new Stopwatch();
readonly string Item;

View File

@@ -129,5 +129,36 @@ namespace OpenRA
return p.Index * 0x567;
return 0;
}
public static void CheckSyncUnchanged( World world, Action fn )
{
CheckSyncUnchanged( world, () => { fn(); return true; } );
}
static bool inUnsyncedCode = false;
public static T CheckSyncUnchanged<T>( World world, Func<T> fn )
{
if( world == null ) return fn();
int sync = world.SyncHash();
bool prevInUnsyncedCode = inUnsyncedCode;
inUnsyncedCode = true;
try
{
return fn();
}
finally
{
inUnsyncedCode = prevInUnsyncedCode;
if( sync != world.SyncHash() )
throw new InvalidOperationException( "CheckSyncUnchanged: sync-changing code may not run here" );
}
}
public static void AssertUnsynced( string message )
{
if( !inUnsyncedCode )
throw new InvalidOperationException( message );
}
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace OpenRA.Traits
{
public abstract class CancelableActivity : IActivity
{
protected IActivity NextActivity { get; private set; }
protected bool IsCanceled { get; private set; }
public abstract IActivity Tick( Actor self );
protected virtual bool OnCancel( Actor self ) { return true; }
public void Cancel( Actor self )
{
IsCanceled = OnCancel( self );
if( IsCanceled )
NextActivity = null;
else if (NextActivity != null)
NextActivity.Cancel( self );
}
public void Queue( IActivity activity )
{
if( NextActivity != null )
NextActivity.Queue( activity );
else
NextActivity = activity;
}
public virtual IEnumerable<float2> GetCurrentPath()
{
yield break;
}
}
}

View File

@@ -10,11 +10,8 @@
namespace OpenRA.Traits.Activities
{
class Idle : IActivity
public class Idle : CancelableActivity
{
public IActivity NextActivity { get; set; }
public IActivity Tick(Actor self) { return NextActivity; }
public void Cancel(Actor self) {}
public override IActivity Tick(Actor self) { return NextActivity; }
}
}

View File

@@ -8,6 +8,8 @@
*/
#endregion
using OpenRA.Traits;
namespace OpenRA.Traits
{
/* tag trait for "bases": mcv/fact */

View File

@@ -1,107 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see LICENSE.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Effects;
using OpenRA.GameRules;
using OpenRA.Traits.Activities;
namespace OpenRA.Traits
{
public class BuildingInfo : ITraitInfo
{
public readonly int Power = 0;
public readonly bool BaseNormal = true;
public readonly bool WaterBound = false;
public readonly int Adjacent = 2;
public readonly bool Capturable = false;
public readonly string Footprint = "x";
public readonly int2 Dimensions = new int2(1, 1);
public readonly bool Unsellable = false;
public readonly float RefundPercent = 0.5f;
public readonly string[] BuildSounds = {"placbldg.aud", "build5.aud"};
public readonly string[] SellSounds = {"cashturn.aud"};
public readonly string DamagedSound = "kaboom1.aud";
public readonly string DestroyedSound = "kaboom22.aud";
public object Create(ActorInitializer init) { return new Building(init); }
}
public class Building : INotifyDamage, IResolveOrder, IOccupySpace
{
readonly Actor self;
public readonly BuildingInfo Info;
[Sync]
readonly int2 topLeft;
readonly PowerManager PlayerPower;
public Building(ActorInitializer init)
{
this.self = init.self;
this.topLeft = init.Get<LocationInit,int2>();
Info = self.Info.Traits.Get<BuildingInfo>();
self.CenterLocation = Game.CellSize
* ((float2)topLeft + .5f * (float2)Info.Dimensions);
PlayerPower = init.self.Owner.PlayerActor.Trait<PowerManager>();
}
public int GetPowerUsage()
{
if (Info.Power <= 0)
return Info.Power;
var health = self.TraitOrDefault<Health>();
var healthFraction = (health == null) ? 1f : health.HPFraction;
return (int)(healthFraction * Info.Power);
}
public void Damaged(Actor self, AttackInfo e)
{
// Power plants lose power with damage
if (Info.Power > 0)
{
var health = self.TraitOrDefault<Health>();
var healthFraction = (health == null) ? 1f : health.HPFraction;
PlayerPower.UpdateActor(self, (int)(healthFraction * Info.Power));
}
if (e.DamageState == DamageState.Dead)
{
self.World.WorldActor.Trait<ScreenShaker>().AddEffect(10, self.CenterLocation, 1);
Sound.Play(Info.DestroyedSound, self.CenterLocation);
}
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "Sell")
{
self.CancelActivity();
self.QueueActivity(new Sell());
}
}
public int2 TopLeft
{
get { return topLeft; }
}
public IEnumerable<int2> OccupiedCells()
{
return Footprint.UnpathableTiles( self.Info.Name, Info, TopLeft );
}
}
}

View File

@@ -8,8 +8,8 @@
*/
#endregion
using System.Drawing;
using OpenRA.Traits.Activities;
using System.Drawing;
using OpenRA.Graphics;
namespace OpenRA.Traits
{
@@ -45,7 +45,7 @@ namespace OpenRA.Traits
this.c = c;
}
public void RenderAfterWorld(Actor self)
public void RenderAfterWorld(WorldRenderer wr, Actor self)
{
if (self.IsIdle) return;
@@ -53,6 +53,9 @@ namespace OpenRA.Traits
if ((lifetime <= 0 || --lifetime <= 0) && !force)
return;
if (!target.IsValid)
return;
var p = target.CenterLocation;
Game.Renderer.LineRenderer.DrawLine(self.CenterLocation, p, c, c);

View File

@@ -0,0 +1,19 @@
#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.Traits
{
public class EditorAppearanceInfo : TraitInfo<EditorAppearance>
{
public readonly bool RelativeToTopLeft = false;
}
public class EditorAppearance { }
}

23
OpenRA.Game/Traits/Health.cs Normal file → Executable file
View File

@@ -22,6 +22,7 @@ namespace OpenRA.Traits
public class HealthInfo : ITraitInfo
{
public readonly int HP = 0;
public readonly float Radius = 10;
public virtual object Create(ActorInitializer init) { return new Health(init, this); }
}
@@ -80,15 +81,15 @@ namespace OpenRA.Traits
var oldState = this.DamageState;
/* apply the damage modifiers, if we have any. */
var modifier = (float)self.TraitsImplementing<IDamageModifier>()
.Select(t => t.GetDamageModifier(warhead)).Product();
/* apply the damage modifiers, if we have any. */
var modifier = (float)self.TraitsImplementing<IDamageModifier>().Concat(self.Owner.PlayerActor.TraitsImplementing<IDamageModifier>())
.Select(t => t.GetDamageModifier(attacker, warhead)).Product();
damage = (int)(damage * modifier);
hp -= damage;
foreach (var nd in self.TraitsImplementing<INotifyDamage>())
hp -= damage;
foreach (var nd in self.TraitsImplementing<INotifyDamage>().Concat(self.Owner.PlayerActor.TraitsImplementing<INotifyDamage>()))
nd.Damaged(self, new AttackInfo
{
Attacker = attacker,
@@ -96,7 +97,9 @@ namespace OpenRA.Traits
DamageState = this.DamageState,
PreviousDamageState = oldState,
DamageStateChanged = this.DamageState != oldState,
Warhead = warhead
Warhead = warhead,
PreviousHealth = hp + damage < 0 ? 0 : hp + damage,
Health = hp
});
if (hp <= 0)
@@ -140,6 +143,8 @@ namespace OpenRA.Traits
{
public static bool IsDead(this Actor self)
{
if (self.Destroyed) return true;
var health = self.TraitOrDefault<Health>();
return (health == null) ? true : health.IsDead;
}
@@ -161,7 +166,9 @@ namespace OpenRA.Traits
{
var health = self.TraitOrDefault<Health>();
if (health == null) return;
health.InflictDamage(self, attacker, health.HP, null);
/* hack. Fix for proper */
health.InflictDamage(self, attacker, int.MaxValue, null);
}
}
}

View File

@@ -17,7 +17,8 @@ namespace OpenRA.Traits
public bool FastBuild = false;
public bool FastCharge = false;
public bool DisableShroud = false;
public bool PathDebug = false;
public bool PathDebug = false;
public bool UnitInfluenceDebug = false;
public object Create (ActorInitializer init) { return new DeveloperMode(this); }
}
@@ -30,19 +31,21 @@ namespace OpenRA.Traits
[Sync] public bool FastBuild;
[Sync] public bool DisableShroud;
[Sync] public bool PathDebug;
[Sync] public bool UnitInfluenceDebug;
public DeveloperMode(DeveloperModeInfo info)
{
Info = info;
FastBuild = Info.FastBuild;
FastCharge = Info.FastCharge;
DisableShroud = Info.DisableShroud;
PathDebug = Info.PathDebug;
PathDebug = Info.PathDebug;
UnitInfluenceDebug = info.UnitInfluenceDebug;
}
public void ResolveOrder (Actor self, Order order)
{
if (!Game.LobbyInfo.GlobalSettings.AllowCheats) return;
if (!self.World.LobbyInfo.GlobalSettings.AllowCheats) return;
switch(order.OrderString)
{
@@ -68,35 +71,31 @@ namespace OpenRA.Traits
}
case "DevShroud":
{
DisableShroud ^= true;
if (self.World.LocalPlayer == self.Owner)
{
DisableShroud ^= true;
Game.world.LocalPlayer.Shroud.Disabled = DisableShroud;
}
self.World.LocalPlayer.Shroud.Disabled = DisableShroud;
break;
}
case "DevPathDebug":
{
if (self.World.LocalPlayer == self.Owner)
PathDebug ^= true;
PathDebug ^= true;
break;
}
case "DevUnitDebug":
{
UnitInfluenceDebug ^= true;
break;
}
case "DevGiveExploration":
{
if (self.World.LocalPlayer == self.Owner)
Game.Settings.Debug.ShowCollisions ^= true;
self.World.WorldActor.Trait<Shroud>().ExploreAll(self.World);
break;
}
case "DevGiveExploration":
{
if (self.World.LocalPlayer == self.Owner)
self.World.WorldActor.Trait<Shroud>().ExploreAll(self.World);
break;
}
default:
default:
return;
}
}
Game.Debug("Cheat used: {0} by {1}"
.F(order.OrderString, self.Owner.PlayerName));
}

View File

@@ -0,0 +1,42 @@
using System.Collections.Generic;
namespace OpenRA.Traits
{
public class ScaleInfo : ITraitInfo
{
public readonly float Value = 1f; /* default */
public ScaleInfo() { } /* only because we have other ctors */
public object Create(ActorInitializer init) { return new Scale(init.self, this); }
}
public class Scale : IRenderModifier
{
Actor self;
public ScaleInfo Info { get; protected set; }
public Scale(Actor self, ScaleInfo info)
{
this.Info = info;
this.self = self;
}
public IEnumerable<Renderable> ModifyRender(Actor self, IEnumerable<Renderable> r)
{
var r2 = new List<Renderable>(r);
var r3 = new List<Renderable>();
for (int i = 0; i < r2.Count;i++)
{
var renderable = r2[i];
renderable.Scale = Info.Value;
r3.Add(renderable);
// yield return renderable;
}
return r3;
}
}
}

View File

@@ -10,7 +10,6 @@
using System.Drawing;
using OpenRA.Graphics;
using System.Linq;
namespace OpenRA.Traits
{
@@ -20,7 +19,6 @@ namespace OpenRA.Traits
public readonly int[] Bounds = null;
[VoiceReference]
public readonly string Voice = null;
public readonly float Radius = 10;
}
public class Selectable : IPostRenderSelection
@@ -29,7 +27,7 @@ namespace OpenRA.Traits
static readonly string[] pipStrings = { "pip-empty", "pip-green", "pip-yellow", "pip-red", "pip-gray" };
static readonly string[] tagStrings = { "", "tag-fake", "tag-primary" };
public void RenderAfterWorld (Actor self)
public void RenderAfterWorld (WorldRenderer wr, Actor self)
{
var bounds = self.GetBounds(true);
@@ -40,9 +38,9 @@ namespace OpenRA.Traits
DrawSelectionBox(self, xy, Xy, xY, XY, Color.White);
DrawHealthBar(self, xy, Xy);
DrawControlGroup(self, xy);
DrawPips(self, xY);
DrawTags(self, new float2(.5f * (bounds.Left + bounds.Right), bounds.Top));
DrawControlGroup(wr, self, xy);
DrawPips(wr, self, xY);
DrawTags(wr, self, new float2(.5f * (bounds.Left + bounds.Right), bounds.Top));
DrawUnitPath(self);
}
@@ -61,9 +59,10 @@ namespace OpenRA.Traits
void DrawHealthBar(Actor self, float2 xy, float2 Xy)
{
if (!self.IsInWorld) return;
var health = self.TraitOrDefault<Health>();
if (self.IsDead() || health == null)
return;
if (health == null || health.IsDead) return;
var c = Color.Gray;
Game.Renderer.LineRenderer.DrawLine(xy + new float2(0, -2), xy + new float2(0, -4), c, c);
@@ -88,7 +87,7 @@ namespace OpenRA.Traits
Game.Renderer.LineRenderer.DrawLine(xy + new float2(0, -4), z + new float2(0, -4), healthColor2, healthColor2);
}
void DrawControlGroup(Actor self, float2 basePosition)
void DrawControlGroup(WorldRenderer wr, Actor self, float2 basePosition)
{
var group = self.World.Selection.GetControlGroupForActor(self);
if (group == null) return;
@@ -96,10 +95,10 @@ namespace OpenRA.Traits
var pipImages = new Animation("pips");
pipImages.PlayFetchIndex("groups", () => (int)group);
pipImages.Tick();
Game.Renderer.SpriteRenderer.DrawSprite(pipImages.Image, basePosition + new float2(-8, 1), "chrome");
pipImages.Image.DrawAt(wr, basePosition + new float2(-8, 1), "chrome");
}
void DrawPips(Actor self, float2 basePosition)
void DrawPips(WorldRenderer wr, Actor self, float2 basePosition)
{
if (self.Owner != self.World.LocalPlayer) return;
@@ -122,7 +121,7 @@ namespace OpenRA.Traits
}
var pipImages = new Animation("pips");
pipImages.PlayRepeating(pipStrings[(int)pip]);
Game.Renderer.SpriteRenderer.DrawSprite(pipImages.Image, pipxyBase + pipxyOffset, "chrome");
pipImages.Image.DrawAt(wr, pipxyBase + pipxyOffset, "chrome");
pipxyOffset += new float2(4, 0);
}
// Increment row
@@ -131,7 +130,7 @@ namespace OpenRA.Traits
}
}
void DrawTags(Actor self, float2 basePosition)
void DrawTags(WorldRenderer wr, Actor self, float2 basePosition)
{
if (self.Owner != self.World.LocalPlayer) return;
@@ -148,7 +147,7 @@ namespace OpenRA.Traits
var tagImages = new Animation("pips");
tagImages.PlayRepeating(tagStrings[(int)tag]);
Game.Renderer.SpriteRenderer.DrawSprite(tagImages.Image, tagxyBase + tagxyOffset, "chrome");
tagImages.Image.DrawAt(wr, tagxyBase + tagxyOffset, "chrome");
// Increment row
tagxyOffset.Y += 8;
@@ -158,13 +157,14 @@ namespace OpenRA.Traits
void DrawUnitPath(Actor self)
{
if (!Game.world.LocalPlayer.PlayerActor.Trait<DeveloperMode>().PathDebug) return;
if (self.World.LocalPlayer == null ||!self.World.LocalPlayer.PlayerActor.Trait<DeveloperMode>().PathDebug) return;
var activity = self.GetCurrentActivity();
var mobile = self.TraitOrDefault<IMove>();
if (mobile != null)
if (activity != null && mobile != null)
{
var alt = new float2(0, -mobile.Altitude);
var path = mobile.GetCurrentPath(self);
var path = activity.GetCurrentPath();
var start = self.CenterLocation + alt;
var c = Color.Green;

View File

@@ -1,36 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see LICENSE.
*/
#endregion
using System.Drawing;
using OpenRA.Graphics;
using System.Linq;
namespace OpenRA.Traits
{
public class TargetableInfo : ITraitInfo
{
public readonly string[] TargetTypes = {};
public object Create( ActorInitializer init ) { return new Targetable(this); }
}
public class Targetable : ITargetable
{
TargetableInfo Info;
public Targetable(TargetableInfo info)
{
Info = info;
}
public string[] TargetTypes
{
get { return Info.TargetTypes;}
}
}
}

117
OpenRA.Game/Traits/TraitsInterfaces.cs Normal file → Executable file
View File

@@ -10,9 +10,10 @@
using System.Collections.Generic;
using System.Drawing;
using OpenRA.FileFormats;
using OpenRA.GameRules;
using OpenRA.Graphics;
using OpenRA.FileFormats;
using OpenRA.Network;
namespace OpenRA.Traits
{
@@ -28,28 +29,37 @@ namespace OpenRA.Traits
public int Damage;
public DamageState DamageState;
public DamageState PreviousDamageState;
public bool DamageStateChanged;
public bool DamageStateChanged;
public int PreviousHealth;
public int Health;
}
public interface ITick { void Tick(Actor self); }
public interface IRender { IEnumerable<Renderable> Render(Actor self); }
public interface IIssueOrder
{
Order IssueOrder( Actor self, int2 xy, MouseInput mi, Actor underCursor );
int OrderPriority( Actor self, int2 xy, MouseInput mi, Actor underCursor );
IEnumerable<IOrderTargeter> Orders { get; }
Order IssueOrder( Actor self, IOrderTargeter order, Target target );
}
public interface IResolveOrder { void ResolveOrder(Actor self, Order order); }
public interface IOrderTargeter
{
string OrderID { get; }
int OrderPriority { get; }
bool CanTargetUnit( Actor self, Actor target, bool forceAttack, bool forceMove, ref string cursor );
bool CanTargetLocation( Actor self, int2 location, List<Actor> actorsAtLocation, bool forceAttack, bool forceMove, ref string cursor );
}
public interface IResolveOrder { void ResolveOrder(Actor self, Order order); }
public interface IValidateOrder { bool OrderValidation(OrderManager orderManager, World world, int clientId, Order order);
}
public interface IOrderCursor { string CursorForOrder(Actor self, Order order); }
public interface IOrderVoice { string VoicePhraseForOrder(Actor self, Order order); }
public interface ITargetable { string[] TargetTypes { get; } }
public interface ICustomUnitOrderGenerator : IOrderGenerator {};
public interface INotifySold { void Selling( Actor self ); void Sold( Actor self ); }
public interface INotifyDamage { void Damaged(Actor self, AttackInfo e); }
public interface INotifyBuildComplete { void BuildingComplete(Actor self); }
public interface INotifyProduction { void UnitProduced(Actor self, Actor other, int2 exit); }
public interface INotifyCapture { void OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner); }
public interface IAcceptSpy { void OnInfiltrate(Actor self, Actor spy); }
public interface INotifyEnterCell { void OnEnterCell(Actor self, int2 cell); }
public interface IStoreOre { int Capacity { get; }}
public interface IDisable { bool Disabled { get; } }
@@ -64,18 +74,17 @@ namespace OpenRA.Traits
public interface IVisibilityModifier { bool IsVisible(Actor self, Player byPlayer); }
public interface IRadarColorModifier { Color RadarColorOverride(Actor self); }
public interface IOccupySpace
public interface IHasLocation
{
int2 PxPosition { get; }
}
public interface IOccupySpace : IHasLocation
{
int2 TopLeft { get; }
IEnumerable<int2> OccupiedCells();
}
public interface IOccupyAir
{
int2 TopLeft { get; }
IEnumerable<int2> OccupiedAirCells();
}
public static class IOccupySpaceExts
{
public static int2 NearestCellTo( this IOccupySpace ios, int2 other )
@@ -97,24 +106,23 @@ namespace OpenRA.Traits
public interface INotifyAttack { void Attacking(Actor self); }
public interface IRenderModifier { IEnumerable<Renderable> ModifyRender(Actor self, IEnumerable<Renderable> r); }
public interface IDamageModifier { float GetDamageModifier( WarheadInfo warhead ); }
public interface ISpeedModifier { float GetSpeedModifier(); }
public interface IDamageModifier { float GetDamageModifier(Actor attacker, WarheadInfo warhead); }
public interface ISpeedModifier { decimal GetSpeedModifier(); }
public interface IFirepowerModifier { float GetFirepowerModifier(); }
public interface IPalette { void InitPalette( WorldRenderer wr ); }
public interface IPaletteModifier { void AdjustPalette(Dictionary<string,Palette> b); }
public interface IPips { IEnumerable<PipType> GetPips(Actor self); }
public interface ITags { IEnumerable<TagType> GetTags(); }
public interface ITeleportable /* crap name! */
public interface ITeleportable : IHasLocation /* crap name! */
{
bool CanEnterCell(int2 location);
void SetPosition(Actor self, int2 cell);
void SetPxPosition(Actor self, int2 px);
}
public interface IMove : ITeleportable
{
float MovementCostForCell(Actor self, int2 cell);
float MovementSpeedForCell(Actor self, int2 cell);
IEnumerable<float2> GetCurrentPath(Actor self);
int Altitude { get; set; }
}
@@ -139,22 +147,37 @@ namespace OpenRA.Traits
public readonly string Palette;
public readonly int Z;
public readonly int ZOffset;
public float Scale;
public Renderable(Sprite sprite, float2 pos, string palette, int z, int zOffset)
{
Sprite = sprite;
Pos = pos;
Palette = palette;
Z = z;
ZOffset = zOffset;
}
public Renderable(Sprite sprite, float2 pos, string palette, int z, int zOffset)
{
Sprite = sprite;
Pos = pos;
Palette = palette;
Z = z;
ZOffset = zOffset;
Scale = 1f; /* default */
}
public Renderable(Sprite sprite, float2 pos, string palette, int z, int zOffset, float scale)
{
Sprite = sprite;
Pos = pos;
Palette = palette;
Z = z;
ZOffset = zOffset;
Scale = scale; /* default */
}
public Renderable(Sprite sprite, float2 pos, string palette, int z)
: this(sprite, pos, palette, z, 0) { }
public Renderable(Sprite sprite, float2 pos, string palette, int z)
: this(sprite, pos, palette, z, 0) { }
public Renderable WithPalette(string newPalette) { return new Renderable(Sprite, Pos, newPalette, Z, ZOffset); }
public Renderable WithZOffset(int newOffset) { return new Renderable(Sprite, Pos, Palette, Z, newOffset); }
public Renderable WithPos(float2 newPos) { return new Renderable(Sprite, newPos, Palette, Z, ZOffset); }
public Renderable(Sprite sprite, float2 pos, string palette, int z, float scale)
: this(sprite, pos, palette, z, 0, scale) { }
public Renderable WithPalette(string newPalette) { return new Renderable(Sprite, Pos, newPalette, Z, ZOffset, Scale); }
public Renderable WithZOffset(int newOffset) { return new Renderable(Sprite, Pos, Palette, Z, newOffset, Scale); }
public Renderable WithPos(float2 newPos) { return new Renderable(Sprite, newPos, Palette, Z, ZOffset, Scale); }
}
public interface ITraitInfo { object Create(ActorInitializer init); }
@@ -171,18 +194,23 @@ namespace OpenRA.Traits
public interface IActivity
{
IActivity NextActivity { get; set; }
IActivity Tick(Actor self);
void Cancel(Actor self);
void Queue(IActivity activity);
IEnumerable<float2> GetCurrentPath();
}
public interface IRenderOverlay { void Render(); }
public interface IRenderOverlay { void Render( WorldRenderer wr ); }
public interface INotifyIdle { void Idle(Actor self); }
public interface IBlocksBullets { }
public interface IPostRenderSelection { void RenderAfterWorld(Actor self); }
public interface IPreRenderSelection { void RenderBeforeWorld(Actor self); }
public interface IPostRender { void RenderAfterWorld(WorldRenderer wr, Actor self); }
public interface IPostRenderSelection { void RenderAfterWorld(WorldRenderer wr, Actor self); }
public interface IPreRenderSelection { void RenderBeforeWorld(WorldRenderer wr, Actor self); }
public interface ITraitNotSynced{} // Traits marked with NotSynced arent sync-checked
public struct Target // a target: either an actor, or a fixed location.
{
@@ -203,9 +231,16 @@ namespace OpenRA.Traits
public static readonly Target None = new Target();
public bool IsValid { get { return valid && (actor == null || actor.IsInWorld); } }
public float2 CenterLocation { get { return actor != null ? actor.CenterLocation : pos.ToInt2(); } }
public int2 PxPosition { get { return IsActor ? actor.Trait<IHasLocation>().PxPosition : pos.ToInt2(); } }
public float2 CenterLocation { get { return PxPosition; } }
public Actor Actor { get { return actor; } }
public bool IsActor { get { return actor != null; } }
public Actor Actor { get { return IsActor ? actor : null; } }
public bool IsActor { get { return actor != null && !actor.Destroyed; } }
}
public interface ITargetable
{
string[] TargetTypes { get; }
IEnumerable<int2> TargetableCells( Actor self );
}
}

View File

@@ -34,7 +34,7 @@ namespace OpenRA.Traits
public static int GetFacing( float2 d, int currentFacing )
{
if( float2.WithinEpsilon( d, float2.Zero, 0.001f ) )
if (float2.WithinEpsilon(d, float2.Zero, 0.001f))
return currentFacing;
int highest = -1;
@@ -82,14 +82,14 @@ namespace OpenRA.Traits
ecc * (cosAngle * v.Y - sinAngle * v.X));
}
public static float2 CenterOfCell(int2 loc)
public static int2 CenterOfCell(int2 loc)
{
return new float2(12, 12) + Game.CellSize * (float2)loc;
return new int2( Game.CellSize / 2, Game.CellSize / 2 ) + Game.CellSize * loc;
}
public static float2 BetweenCells(int2 from, int2 to)
public static int2 BetweenCells(int2 from, int2 to)
{
return 0.5f * (CenterOfCell(from) + CenterOfCell(to));
return int2.Lerp( CenterOfCell( from ), CenterOfCell( to ), 1, 2 );
}
public static int2 AsInt2(this int[] xs) { return new int2(xs[0], xs[1]); }
@@ -99,14 +99,20 @@ namespace OpenRA.Traits
public static Renderable Centered(Actor self, Sprite s, float2 location)
{
var pal = self.Owner == null ? "player0" : self.Owner.Palette;
var loc = location - 0.5f * s.size;
var scale = self.TraitOrDefault<Scale>();
var scaleModifier = 1f;
if (scale != null)
scaleModifier = scale.Info.Value;
var loc = location - 0.5f * s.size * scaleModifier;
return new Renderable(s, loc.Round(), pal, (int)self.CenterLocation.Y);
}
public static IActivity SequenceActivities(params IActivity[] acts)
{
return acts.Reverse().Aggregate(
(next, a) => { a.NextActivity = next; return a; });
(next, a) => { a.Queue( next ); return a; });
}
public static Color ArrayToColor(int[] x) { return Color.FromArgb(x[0], x[1], x[2]); }

View File

@@ -0,0 +1,31 @@
#region Copyright & License Information
/*
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see LICENSE.
*/
#endregion
using OpenRA.Network;
namespace OpenRA.Traits
{
public class ValidateOrderInfo : TraitInfo<ValidateOrder> { }
public class ValidateOrder : IValidateOrder
{
public bool OrderValidation(OrderManager orderManager, World world, int clientId, Order order)
{
// Drop exploiting orders
if (order.Subject != null && order.Subject.Owner.ClientIndex != clientId)
{
Game.Debug("Detected exploit order from {0}: {1}".F(clientId, order.OrderString));
return false;
}
return true;
}
}
}

View File

@@ -1,23 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace OpenRA.Traits
{
public class ValuedInfo : TraitInfo<Valued>
{
public readonly int Cost = 0;
}
public class TooltipInfo : TraitInfo<Tooltip>
{
public readonly string Description = "";
public readonly string Name = "";
public readonly string Icon = null;
public readonly string[] AlternateName = { };
}
public class Valued { }
public class Tooltip { }
}

View File

@@ -9,13 +9,34 @@
#endregion
using OpenRA.FileFormats;
using OpenRA.Graphics;
namespace OpenRA.Traits
{
public class PlayerColorPaletteInfo : TraitInfo<PlayerColorPalette>
public class PlayerColorPaletteInfo : ITraitInfo
{
public readonly string BasePalette = null;
public readonly string BaseName = "player";
public readonly PaletteFormat PaletteFormat = PaletteFormat.ra;
public object Create( ActorInitializer init ) { return new PlayerColorPalette( init.self.Owner, this ); }
}
public class PlayerColorPalette {}
public class PlayerColorPalette : IPalette
{
readonly Player owner;
readonly PlayerColorPaletteInfo info;
public PlayerColorPalette( Player owner, PlayerColorPaletteInfo info )
{
this.owner = owner;
this.info = info;
}
public void InitPalette( WorldRenderer wr )
{
var paletteName = "{0}{1}".F( info.BaseName, owner.Index );
var newpal = new Palette(wr.GetPalette(info.BasePalette),
new PlayerColorRemap(owner.Color, owner.Color2, info.PaletteFormat));
wr.AddPalette(paletteName, newpal);
}
}
}

View File

@@ -18,17 +18,15 @@ namespace OpenRA.Traits
public class ResourceLayerInfo : TraitInfo<ResourceLayer> { }
public class ResourceLayer: IRenderOverlay, IWorldLoaded
{
{
World world;
public ResourceType[] resourceTypes;
CellContents[,] content;
public void Render()
public void Render( WorldRenderer wr )
{
var cliprect = Game.viewport.ShroudBounds().HasValue
? Rectangle.Intersect(Game.viewport.ShroudBounds().Value, world.Map.Bounds) : world.Map.Bounds;
var cliprect = Game.viewport.ShroudBounds( world );
cliprect = Rectangle.Intersect(Game.viewport.ViewBounds(), cliprect);
var minx = cliprect.Left;
@@ -37,18 +35,24 @@ namespace OpenRA.Traits
var miny = cliprect.Top;
var maxy = cliprect.Bottom;
foreach( var rt in world.WorldActor.TraitsImplementing<ResourceType>() )
rt.info.PaletteIndex = wr.GetPaletteIndex(rt.info.Palette);
ShroudRenderer shroud = null;
if( world.LocalPlayer != null )
shroud = world.LocalPlayer.Shroud;
for (int x = minx; x < maxx; x++)
for (int y = miny; y < maxy; y++)
{
if (world.LocalPlayer != null &&
!world.LocalPlayer.Shroud.IsExplored(new int2(x, y)))
if (shroud != null && !shroud.IsExplored(new int2(x, y)))
continue;
var c = content[x, y];
if (c.image != null)
Game.Renderer.SpriteRenderer.DrawSprite(c.image[c.density],
c.image[c.density].DrawAt(
Game.CellSize * new int2(x, y),
c.type.info.Palette);
c.type.info.PaletteIndex);
}
}
@@ -67,7 +71,7 @@ namespace OpenRA.Traits
for (int y = map.YOffset; y < map.YOffset + map.Height; y++)
{
// Todo: Valid terrain should be specified in the resource
if (!w.IsCellBuildable(new int2(x,y), false))
if (!AllowResourceAt(new int2(x,y)))
continue;
content[x, y].type = resourceTypes.FirstOrDefault(
@@ -85,6 +89,14 @@ namespace OpenRA.Traits
}
}
public bool AllowResourceAt( int2 a )
{
if( !world.Map.IsInMap( a.X, a.Y ) ) return false;
if( !world.GetTerrainInfo( a ).Buildable ) return false;
if( world.WorldActor.Trait<UnitInfluence>().AnyUnitsAt( a ) ) return false;
return true;
}
Sprite[] ChooseContent(ResourceType t)
{
return t.info.Sprites[world.SharedRandom.Next(t.info.Sprites.Length)];
@@ -146,6 +158,7 @@ namespace OpenRA.Traits
content[p.X, p.Y].type = null;
content[p.X, p.Y].image = null;
content[p.X, p.Y].density = 0;
world.Map.CustomTerrain[p.X, p.Y] = null;
}
public ResourceType GetResource(int2 p) { return content[p.X, p.Y].type; }

View File

@@ -23,6 +23,7 @@ namespace OpenRA.Traits
public readonly string TerrainType = "Ore";
public Sprite[][] Sprites;
public int PaletteIndex;
public object Create(ActorInitializer init) { return new ResourceType(this); }
}

View File

@@ -12,9 +12,6 @@ using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.GameRules;
using OpenRA.Traits;
namespace OpenRA.Traits
{
@@ -122,19 +119,14 @@ namespace OpenRA.Traits
public static IEnumerable<int2> GetVisOrigins(Actor a)
{
if (a.Info.Traits.Contains<BuildingInfo>())
var ios = a.TraitOrDefault<IOccupySpace>();
if (ios != null)
{
var bi = a.Info.Traits.Get<BuildingInfo>();
return Footprint.Tiles(a.Info.Name, bi, a.Location);
}
else
{
var mobile = a.TraitOrDefault<Mobile>();
if (mobile != null)
return new[] { mobile.fromCell, mobile.toCell };
else
return new[] { (1f / Game.CellSize * a.CenterLocation).ToInt2() };
var cells = ios.OccupiedCells();
if (cells.Any()) return cells;
}
return new[] { (1f / Game.CellSize * a.CenterLocation).ToInt2() };
}
void RemoveActor(Actor a)

View File

@@ -45,9 +45,9 @@ namespace OpenRA.Traits
for (var i = 0; i <= bins.GetUpperBound(0); i++)
bins[i, j].Clear();
foreach (var a in self.World.Actors)
foreach (var a in self.World.Queries.WithTrait<IHasLocation>())
{
var bounds = a.GetBounds(true);
var bounds = a.Actor.GetBounds(true);
if (bounds.Right <= Game.CellSize * self.World.Map.XOffset) continue;
if (bounds.Bottom <= Game.CellSize * self.World.Map.YOffset) continue;
@@ -61,7 +61,7 @@ namespace OpenRA.Traits
for (var j = j1; j <= j2; j++)
for (var i = i1; i <= i2; i++)
bins[i, j].Add(a);
bins[i, j].Add(a.Actor);
}
}

View File

@@ -37,6 +37,7 @@ namespace OpenRA.Traits
map = world.Map;
influence = new InfluenceNode[world.Map.MapSize.X, world.Map.MapSize.Y];
world.ActorAdded += a => Add( a, a.TraitOrDefault<IOccupySpace>() );
world.ActorRemoved += a => Remove( a, a.TraitOrDefault<IOccupySpace>() );
}
@@ -55,8 +56,9 @@ namespace OpenRA.Traits
public void Add( Actor self, IOccupySpace unit )
{
foreach( var c in unit.OccupiedCells() )
influence[ c.X, c.Y ] = new InfluenceNode { next = influence[ c.X, c.Y ], actor = self };
if (unit != null)
foreach( var c in unit.OccupiedCells() )
influence[ c.X, c.Y ] = new InfluenceNode { next = influence[ c.X, c.Y ], actor = self };
}
public void Remove( Actor self, IOccupySpace unit )
@@ -72,7 +74,8 @@ namespace OpenRA.Traits
return;
else if( influenceNode.actor == toRemove )
influenceNode = influenceNode.next;
else
if (influenceNode != null)
RemoveInner( ref influenceNode.next, toRemove );
}

View File

@@ -11,10 +11,8 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.GameRules;
using OpenRA.Graphics;
using OpenRA.Traits;
using OpenRA.Widgets;
namespace OpenRA
{
@@ -40,82 +38,24 @@ namespace OpenRA
return Game.modData.SheetBuilder.Add(data, new Size(Game.CellSize, Game.CellSize));
}
public void Draw( World world )
public void Draw( WorldRenderer wr, World world )
{
if (Game.Settings.Debug.ShowCollisions)
if( world.LocalPlayer == null ) return;
if (world.LocalPlayer.PlayerActor.Trait<DeveloperMode>().UnitInfluenceDebug)
{
var uim = world.WorldActor.Trait<UnitInfluence>();
for (var i = world.Map.Bounds.Left; i < world.Map.Bounds.Right; i++)
for (var j = world.Map.Bounds.Top; j < world.Map.Bounds.Bottom; j++)
if (uim.GetUnitsAt(new int2(i, j)).Any())
Game.Renderer.SpriteRenderer.DrawSprite(unitDebug, Game.CellSize * new float2(i, j), "terrain");
unitDebug.DrawAt(wr, Game.CellSize * new float2(i, j), "terrain");
}
}
public void DrawBuildingGrid( World world, string name, BuildingInfo bi )
public void DrawGrid( WorldRenderer wr, Dictionary<int2, bool> cells )
{
var position = Game.viewport.ViewToWorld(Viewport.LastMousePos).ToInt2();
var topLeft = position - Footprint.AdjustForBuildingSize( bi );
// Linebuild for walls.
// Assumes a 1x1 footprint; weird things will happen for other footprints
if (Rules.Info[name].Traits.Contains<LineBuildInfo>())
{
foreach (var t in LineBuildUtils.GetLineBuildCells(world, topLeft, name, bi))
Game.Renderer.SpriteRenderer.DrawSprite(world.IsCloseEnoughToBase(world.LocalPlayer, name, bi, t)
? buildOk : buildBlocked, Game.CellSize * t, "terrain");
}
else
{
var res = world.WorldActor.Trait<ResourceLayer>();
var isCloseEnough = world.IsCloseEnoughToBase(world.LocalPlayer, name, bi, topLeft);
foreach (var t in Footprint.Tiles(name, bi, topLeft))
Game.Renderer.SpriteRenderer.DrawSprite((isCloseEnough && world.IsCellBuildable(t, bi.WaterBound) && res.GetResource(t) == null)
? buildOk : buildBlocked, Game.CellSize * t, "terrain");
}
Game.Renderer.SpriteRenderer.Flush();
}
}
public static class LineBuildUtils
{
public static IEnumerable<int2> GetLineBuildCells(World world, int2 location, string name, BuildingInfo bi)
{
int range = Rules.Info[name].Traits.Get<LineBuildInfo>().Range;
var topLeft = location; // 1x1 assumption!
if (world.IsCellBuildable(topLeft, bi.WaterBound))
yield return topLeft;
// Start at place location, search outwards
// TODO: First make it work, then make it nice
var vecs = new[] { new int2(1, 0), new int2(0, 1), new int2(-1, 0), new int2(0, -1) };
int[] dirs = { 0, 0, 0, 0 };
for (int d = 0; d < 4; d++)
{
for (int i = 1; i < range; i++)
{
if (dirs[d] != 0)
continue;
int2 cell = topLeft + i * vecs[d];
if (world.IsCellBuildable(cell, bi.WaterBound))
continue; // Cell is empty; continue search
// Cell contains an actor. Is it the type we want?
if (world.Queries.WithTrait<LineBuild>().Any(a => (a.Actor.Info.Name == name && a.Actor.Location.X == cell.X && a.Actor.Location.Y == cell.Y)))
dirs[d] = i; // Cell contains actor of correct type
else
dirs[d] = -1; // Cell is blocked by another actor type
}
// Place intermediate-line sections
if (dirs[d] > 0)
for (int i = 1; i < dirs[d]; i++)
yield return topLeft + i * vecs[d];
}
foreach( var c in cells )
( c.Value ? buildOk : buildBlocked ).DrawAt( wr, Game.CellSize * c.Key, "terrain" );
}
}
}

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