Compare commits

...

243 Commits

Author SHA1 Message Date
Matthias Mailänder
c5c518dbce Merge pull request #3969 from pchote/hotkeys
Improved hotkey support
2013-10-21 13:53:43 -07:00
Paul Chote
7ffbfb9b7e Add a Hotkey class for user-configurable keys. Fixes #3779.
Users can now define and use hotkeys that include modifiers (ctrl/meta/shift/alt).
2013-10-22 09:14:05 +13:00
Matthias Mailänder
5803661d83 Merge pull request #3974 from pchote/frozen-actor-fix
Fix bogus screen-map queries.
2013-10-21 11:43:41 -07:00
Matthias Mailänder
b3cceb792d Merge pull request #3975 from pchote/contrail-fix
Extract contrail fading for dead projectiles into an IEffect.
2013-10-21 11:30:35 -07:00
Matthias Mailänder
03cf2a3072 Merge pull request #3976 from pchote/scale-fix
Use scale in RenderSimple.RenderPreview.
2013-10-21 11:19:23 -07:00
Matthias Mailänder
ab86b3308e Merge pull request #3977 from pchote/travis-fix
Re-enable travis notices without joining.
2013-10-21 10:48:06 -07:00
Paul Chote
aab6fec68b Remove VirtKey and KeyName. 2013-10-21 20:24:24 +13:00
Paul Chote
e5f93ec39e Introduce Keycode to simplify key checks. 2013-10-21 20:24:24 +13:00
Paul Chote
477d045e0a Re-enable travis notices without joining.
This reverts commit dbb38e3a42.
2013-10-21 18:54:05 +13:00
Paul Chote
03c4c476f1 Use scale in RenderSimple.RenderPreview. Fixes #3970. 2013-10-21 18:52:29 +13:00
Paul Chote
7ecf84dc61 Extract contrail fading for dead projectiles into an IEffect. 2013-10-21 18:27:37 +13:00
Paul Chote
33f514001e Fix viewport-px / world-px confusion in ScreenMap. Fixes #3964. 2013-10-21 17:57:53 +13:00
Matthias Mailänder
dbb38e3a42 Update .travis.yml
the channel does not allow notices from outside
2013-10-21 00:09:58 +02:00
Matthias Mailänder
31aa1bf421 Update README.md 2013-10-20 23:18:21 +02:00
Paul Chote
9a540e0536 Merge pull request #3971 from Mailaender/silent-travis
Made Travis CI bot IRC notifications less noisy
2013-10-20 13:11:05 -07:00
Matthias Mailänder
06f06ffbb1 a less noisy Travis CI bot IRC notification 2013-10-20 14:35:33 +02:00
Matthias Mailänder
3ed6faed62 Update OpenRA.nsi
fixed "no files found"
2013-10-19 13:46:20 +02:00
Matthias Mailänder
1198ed9fe8 Delete HACKING
unmaintained,
moved to https://github.com/OpenRA/OpenRA/wiki/Hacking
2013-10-19 13:37:38 +02:00
Matthias Mailänder
8b8d3edea2 Merge pull request #3958 from Mailaender/docs
Ship COPYING and CHANGELOG on all platforms
2013-10-19 04:30:31 -07:00
Matthias Mailänder
d1a43a45f1 Merge pull request #3960 from Mailaender/rpm-revert
Simplified the RPM build scripts again
2013-10-19 04:30:20 -07:00
Matthias Mailänder
3db703a7ed Merge pull request #3959 from Mailaender/makefile
Removed TilesetBuilder from core
2013-10-19 04:29:53 -07:00
Matthias Mailänder
20837c3776 cleaned packaging comments 2013-10-19 13:22:36 +02:00
Matthias Mailänder
807dc8b392 silenced curl 2013-10-19 13:22:36 +02:00
Matthias Mailänder
af0f8d09d6 simplify RPM buildpackage script for the build server
hard-code everything outside /usr/share/openra
2013-10-19 13:22:36 +02:00
Matthias Mailänder
5d2254aeee fixed md5sum file not found global mix database.dat 2013-10-19 13:22:32 +02:00
Matthias Mailänder
e54fd9f582 remove the tileset builder from core
not required anymore for d2k
it is also not shipped because it won't work standalone
2013-10-19 12:50:58 +02:00
Matthias Mailänder
797f30446d ship license and changelog on all platforms
keep INSTALL & HACKING in the source code only
2013-10-19 12:35:38 +02:00
Matthias Mailänder
9ecf678b04 Update OpenRA.nsi
fixed Error: unterminated string parsing line at OpenRA.nsi:144
2013-10-19 12:20:43 +02:00
Matthias Mailänder
431c956932 Update OpenRA.nsi
added languages and sorted alphabetically
2013-10-19 12:13:12 +02:00
Matthias Mailänder
52ff778881 Update buildpackage.sh
Fixed make: *** [install-shortcuts] Error 1
2013-10-19 11:24:19 +02:00
Matthias Mailänder
3181c46d27 Update Makefile
Fixed  make: *** No rule to make target `shortcuts', needed by `install-shortcuts'.
2013-10-19 11:12:54 +02:00
Paul Chote
fa2439f03e Merge pull request #3956 from ScottNZ/bleed
Update CHANGELOG
2013-10-19 00:44:18 -07:00
ScottNZ
349ee8cfbf Update CHANGELOG 2013-10-19 18:53:09 +13:00
Paul Chote
bb8f4494f5 Merge pull request #3938 from Mailaender/game-id
Added a Game ID and print it to syncreport.log
2013-10-17 22:51:27 -07:00
Paul Chote
88b8dd6c3e Merge pull request #3948 from Mailaender/replay-duration
Replay duration cleanup
2013-10-17 22:44:41 -07:00
Matthias Mailänder
eff943b3eb added OS and Mono/.NET runtime version to syncreport.log 2013-10-17 20:54:58 +02:00
Matthias Mailänder
70ba7fe5f6 added OpenRA, OS and .NET/Mono runtime version to exception.log 2013-10-17 20:39:41 +02:00
Matthias Mailänder
8c8df3bda9 expose Game ID and client player name to syncreport.log 2013-10-17 20:36:17 +02:00
Matthias Mailänder
353393571f added a Game ID
closes #2570
2013-10-17 20:36:17 +02:00
Matthias Mailänder
39076c98ac Merge pull request #3950 from xanax/bleed
Added -disabled in chrome.yaml & dialog.png files
2013-10-17 11:26:17 -07:00
Sébastien Kerguen
8b9f7a12fa Added -disabled in chrome.yaml & dialog.png files
Added -disabled in chrome.yaml & dialog.png files
2013-10-16 23:34:27 +02:00
Matthias Mailänder
f21f314bfe avoid magic number for replay duration calculation 2013-10-16 21:09:45 +02:00
Matthias Mailänder
f51bd87332 Merge pull request #3943 from pchote/wall-fix
Fix projectiles vs walls.
2013-10-16 08:03:47 -07:00
Matthias Mailänder
39c7843654 Merge pull request #3944 from pchote/sound-fix
Fix bogus sound attenuation.
2013-10-16 07:54:13 -07:00
Matthias Mailänder
4cacd074d7 Merge pull request #3945 from pchote/zoom-fix
Prevent pixel doubling of group / pip / tag / chevron decorations.
2013-10-16 07:47:23 -07:00
Matthias Mailänder
d79b3d32d9 Merge pull request #3946 from wuschel/bleed
fix/balance RA map Athena (koth)
2013-10-16 07:32:46 -07:00
wuschel
cc2bedc454 fix/balance RA map Athena
1. Reduced strategic victory timer from 10 to 3 minutes to increase
pressure on players.
2. Increased the size of the starting islands in order to give a player
more strategic options.

Removed *Options: OpenRA.MapOptions* as seen in
https://github.com/OpenRA/OpenRA/pull/3939. Checked all starting
positions in-Game.
2013-10-16 13:13:27 +02:00
Paul Chote
1ab4b9b72c Fix the rank chevron size when zooming. 2013-10-16 19:12:59 +13:00
Paul Chote
59f5c8ed52 Fix the pip/tag/group decoration size when zooming. 2013-10-16 19:12:59 +13:00
Paul Chote
3ef6a8317a Remove Game.Zoom. 2013-10-16 18:21:46 +13:00
Paul Chote
96d37df4b3 Restore original sound parameters. Fixes #3919. 2013-10-16 17:53:40 +13:00
Paul Chote
8aa76b07cb Use the current position for projectile explosions. Fixes #3795. 2013-10-16 17:31:28 +13:00
Matthias Mailänder
32673344a8 Merge pull request #3933 from pchote/zoom-lines
Don't zoom UI lines when pixel doubling.
2013-10-15 08:28:30 -07:00
Matthias Mailänder
2c198e5d5c Merge pull request #3939 from wuschel/bleed
fix/balance on RA map Apollo
2013-10-15 08:25:24 -07:00
Matthias Mailänder
691602a5a1 Merge pull request #3941 from ScottNZ/bleed
Add missing [Translate]s to support power widgets
2013-10-15 08:19:49 -07:00
ScottNZ
a7b7b68a2e Add missing [Translate]s to support power widgets 2013-10-15 14:40:50 +13:00
wuschel
5a5bf8a8cc RA map Apollo / update map.yaml / remove Options: ...
For some reason the editor in release-20130915 added *Options: OpenRA.MapOptions* into the map.yaml file. I removed that entry.
2013-10-15 02:11:12 +02:00
wuschel
635f634b71 fix/balance on RA map Apollo
Southern side of map lacked 1 oil derrick and 1 oil patch / added to
southern side of map.
2013-10-15 00:27:10 +02:00
Paul Chote
84bb78060f Don't zoom UI lines when pixel doubling. 2013-10-15 11:19:16 +13:00
Paul Chote
44dd801f16 Extract shared constants in Draw*Bar and DrawSelectionBox. 2013-10-15 10:45:51 +13:00
Paul Chote
483120ea20 Extract DrawTargetMarker to WorldRenderer. 2013-10-15 10:45:51 +13:00
Paul Chote
839419635d Improve contrasted range circle rendering. 2013-10-15 10:45:51 +13:00
Paul Chote
d2d73a32a2 Automatically flush line renderer when changing width. 2013-10-15 10:45:51 +13:00
Matthias Mailänder
24b33891a7 Merge pull request #3935 from xanax/bleed
Fixed spawnpoints in chrome.yaml (D2K, TS, RA)
2013-10-14 12:48:51 -07:00
Chris Forbes
623ef971bf Merge pull request #3936 from Mailaender/mini-base
Added BaseProvider to mini-games
2013-10-14 12:46:09 -07:00
Matthias Mailänder
aa7972755b Merge pull request #3937 from reaperrr/patch-1
Gives ant adequate health radius and fixes instant turn
2013-10-14 12:45:45 -07:00
reaperrr
a873f099ff Gives ant adequate health radius and fixes instant turn
Ant was using the same tiny health radius as other infantry. Additionally, I gave it a ROT value so it no longer insta-turns up to 180° without the slightest delay.
2013-10-14 21:35:11 +02:00
Sébastien Kerguen
7fb0c0cf8c Fixed colorpicker coordinates in chrome.yaml
Fixed colorpicker coordinates in chrome.yaml
2013-10-14 20:06:30 +02:00
Matthias Mailänder
c1af43da7f added BaseProvider to mini-games
fixes #3934
2013-10-14 19:45:28 +02:00
Sébastien Kerguen
76c81b610a Fixed spawnpoints in chrome.yaml (D2K, TS, RA)
Fixed spawnpoints in chrome.yaml (D2K, TS, RA)
Fixes #3931
2013-10-14 18:43:51 +02:00
Matthias Mailänder
f4959853fa Merge pull request #3836 from Mailaender/rpm-filesystem
List files again to avoid owning standard dirs in RPM
2013-10-14 08:44:11 -07:00
Matthias Mailänder
676db9753c Merge pull request #3932 from pchote/health-vis
Improve debug overlays
2013-10-14 08:42:33 -07:00
Paul Chote
d864989c19 Tidy PathfinderDebugOverlay. 2013-10-14 21:18:50 +13:00
Paul Chote
68cd537c9b Rename DebugOverlay -> PathfinderDebugOverlay. 2013-10-14 21:16:46 +13:00
Paul Chote
97a42e7d43 Fix A* debug overlay in D2K. 2013-10-14 21:14:21 +13:00
Paul Chote
f3c7c0e957 Update ingame label to match new behaviour. 2013-10-14 21:01:47 +13:00
Paul Chote
d2eb42fbd2 Add Health radius to CombatDebugOverlay. Fixes #3862. 2013-10-14 20:57:41 +13:00
Paul Chote
5da2c319c1 Fix inconsistent naming in DebugMuzzlePositions. 2013-10-14 20:57:20 +13:00
Paul Chote
9f1f6eae9d Merge pull request #3929 from ScottNZ/translation
Add translation support
2013-10-13 23:57:12 -07:00
ScottNZ
3769b845b6 Make tooltip names/descriptions translatable 2013-10-14 19:38:21 +13:00
ScottNZ
42a4d30162 Set up English translation for requires/on-hold/ready texts 2013-10-14 19:38:21 +13:00
ScottNZ
6598f0382a Add support for game translation 2013-10-14 19:38:18 +13:00
Paul Chote
19eb685ecb Merge pull request #3930 from xanax/bleed
chrome.yaml updated (scrollthumb-hover & -pressed)
2013-10-13 23:01:43 -07:00
Paul Chote
7ff2e9d004 Merge pull request #3926 from Phrohdoh/EjectOnDeath
EjectOnDeath - updated and 100% working
2013-10-13 22:51:36 -07:00
Paul Chote
efa306a111 Merge pull request #3928 from Mailaender/double-click-watch
Start Replay on double click
2013-10-13 22:29:53 -07:00
Taryn Hill
e3e7d0b38c Add ignoreActor and checkTransientActors to IPositionable.CanEnterCell
Improved 'return' checks
Removed unnecessary 'using'
Fixed defaults.yaml spelling error
2013-10-13 20:24:29 -04:00
Taryn Hill
e9652db486 EjectOnDeath works with air & ground vehicles, updated defaults.yaml 2013-10-13 20:13:55 -04:00
Taryn Hill
61a26a636b Moved EjectOnDeath to Mods.RA root 2013-10-13 20:13:55 -04:00
Sébastien Kerguen
209443d062 Small chrome.yaml update (D2K, RA, TS)
Small chrome.yaml update (D2K, RA, TS):
- new comments
- fixed scrollthumb-hover & -pressed
2013-10-13 21:55:00 +02:00
Sébastien Kerguen
2062d26fea chrome.yaml updated (scrollthumb-hover & -pressed)
scrollthumb-hover & scrollthumb-pressed updated in chrome.yaml (TS, D2K,
RA)
2013-10-13 10:54:54 +02:00
ScottNZ
104a2d23cf Split FieldSaver out into its own file 2013-10-13 16:49:19 +13:00
Matthias Mailänder
24fdb489b9 start replay on double click 2013-10-12 23:51:27 +02:00
Paul Chote
d8c6acad45 Merge pull request #3903 from Mailaender/gameinit-redundancy
Removed the now redundant per mod Install/DownloadPackagesLogic
2013-10-12 14:38:13 -07:00
Paul Chote
d437b0949a Merge pull request #3920 from Mailaender/tidy
Minor cleanup of the main folder
2013-10-12 14:20:32 -07:00
Matthias Mailänder
d55ee5e037 remove unused icon 2013-10-12 10:54:57 +02:00
Matthias Mailänder
0dea1d1471 renamed and relocated fonts 2013-10-12 10:41:18 +02:00
Paul Chote
1cca6f8fe5 Merge pull request #3649 from Mailaender/passwords
Completed support for password protected games
2013-10-09 22:13:20 -07:00
Matthias Mailänder
e4666f3af3 Merge pull request #3921 from xanax/bleed
Updated chrome.yaml and dialog.png (RA,D2K,TS)
2013-10-09 11:45:40 -07:00
Sébastien Kerguen
6bbff98057 Reuse previous D2K graphics for dialog.png
Reuse previous D2K graphics but with a re-arranged dialog.png file.
2013-10-09 20:39:05 +02:00
Sébastien Kerguen
8df4f7b3dd Updated chrome.yaml and dialog.png (RA,D2K,TS)
I have updated chrome.yaml and dialog.png files for RA, D2K, TS mods.
Featuring "button-hover" and "textfield-hover" states.
2013-10-08 23:19:00 +02:00
Paul Chote
f5d26bebb7 Don't crash if the server mods don't match.
This allows the ingame dialog to be displayed.
2013-10-07 19:37:15 +02:00
Matthias Mailänder
3af1e47744 separate connection and direct connect dialog from serverbrowser 2013-10-07 19:37:15 +02:00
Matthias Mailänder
b618fc7cc2 complete password protected servers
closes #2290
2013-10-07 19:37:12 +02:00
Matthias Mailänder
a6cdcea414 reorder checkboxes more intuitively
port forwarding under ports
2013-10-07 19:31:18 +02:00
Matthias Mailänder
cdfc21af2c remove redundant install and download logic 2013-10-06 10:52:55 +02:00
Chris Forbes
89a6d4f98e Merge pull request #3909 from pchote/project-fix
Fix Mods.RA project file
2013-10-05 15:23:27 -07:00
Paul Chote
2d0028396a Fix DefaultLoadScreen coding style. 2013-10-06 11:04:15 +13:00
Paul Chote
7604a60894 Fix Mods.RA csproj. 2013-10-06 10:58:05 +13:00
Chris Forbes
70b5cc8281 Merge pull request #3905 from Mailaender/splashscreens
Removed Loadscreen redundancy
2013-10-05 13:21:10 -07:00
Chris Forbes
4a3847be65 Merge pull request #3906 from Mailaender/d2k-idle-infantry
Fixed D2k infantry stopping during a firing animation with muzzle flash
2013-10-05 13:20:27 -07:00
Chris Forbes
c532a98333 Merge pull request #3907 from Mailaender/passable-cliffs
Fixed passable cliffs on Arrakis
2013-10-05 13:20:07 -07:00
Chris Forbes
fab6ada8b9 Merge pull request #3908 from Mailaender/explode-in-world
Fixed cargos exploding where they were loaded
2013-10-05 13:19:41 -07:00
Matthias Mailänder
ebda91631e also don't leave a husk somewhere when loaded as cargo 2013-10-05 21:52:33 +02:00
Matthias Mailänder
cec348c462 don't explode when not in world
fixes #3895
StyleCop cleanup
2013-10-05 21:47:23 +02:00
Matthias Mailänder
983697c70b made more cliffs unpassable to avoid glitches
fixes #3701
2013-10-05 21:22:57 +02:00
Matthias Mailänder
f81982d607 removed unused tileset builder templates 2013-10-05 21:22:34 +02:00
Matthias Mailänder
14cf20aad5 add fake idle animations to avoid endless muzzle sprites
fixes #3852
2013-10-05 21:09:55 +02:00
Matthias Mailänder
19b649ba60 unify D2k, RA, TS loadscreens to avoid redundancy
load text comments from mod.yaml (comma separated)
2013-10-05 18:37:22 +02:00
Matthias Mailänder
72a53808ed Merge pull request #3902 from pchote/helicopter-fix
Fix helicopter reloading
2013-10-05 03:58:15 -07:00
Paul Chote
9ee9de01b1 Use cell positioning to look up resupply actors. Fixes #3901.
FindActorsInBox only checks against the centre of
actors, so testing two points is rarely going to
work.
2013-10-05 23:28:46 +13:00
Matthias Mailänder
86ad0e63e4 Merge pull request #3887 from pchote/viewport-cleanup
Viewport cleanup
2013-10-05 02:23:18 -07:00
Paul Chote
e09a7f4682 Merge pull request #3888 from Mailaender/ts-asset-download
Added Auto-Download for Tiberian Sun assets
2013-10-05 01:33:57 -07:00
Matthias Mailänder
9b5b019a36 Merge pull request #3894 from ScottNZ/balance
More RA balance work
2013-10-05 01:16:59 -07:00
Paul Chote
919181e04d Clean up viewport internals. 2013-10-05 21:01:22 +13:00
Paul Chote
936bf98496 Move Viewport inside WorldRenderer. 2013-10-05 21:01:22 +13:00
Paul Chote
fe8c80aca6 Add Game.Zoom for remaining engine stuff. 2013-10-05 21:01:22 +13:00
Paul Chote
3ae75362bb Remove WorldUtils.FindFrozenActorsAtMouse. 2013-10-05 21:01:22 +13:00
Paul Chote
1ca9c90565 Add ScreenMap.ActorsAt(MouseInput) overload. 2013-10-05 21:01:21 +13:00
Paul Chote
0b560bfc6e Move more viewport lookups to WorldRenderer. 2013-10-05 21:01:21 +13:00
Paul Chote
f575c20d38 Route viewport centering via WorldRenderer. 2013-10-05 21:01:21 +13:00
Paul Chote
b3d608092c Tidy WorldCommandWidget. 2013-10-05 21:01:21 +13:00
Paul Chote
b7123cda7d Route screen size queries via Game.Renderer. 2013-10-05 21:01:21 +13:00
Matthias Mailänder
65bbfbaef2 Merge pull request #3859 from ScottNZ/irc
Add IRC client to the serverbrowser
2013-10-05 00:43:49 -07:00
Matthias Mailänder
89da26a35a package URL should be good to go now
adapt test file as tibsun.mix is just a container not included
2013-10-05 08:57:05 +02:00
Matthias Mailänder
ecf9c260b6 minor StyleCop cleanups 2013-10-05 08:57:04 +02:00
Matthias Mailänder
98e1fe852a isometry is now WIP ;) 2013-10-05 08:57:04 +02:00
Matthias Mailänder
dee3a27328 made Firestorm expansion optional 2013-10-05 08:57:04 +02:00
Matthias Mailänder
dfc5afd1cd Merge pull request #3899 from pchote/viewport-regression-fixes
Viewport regression fixes
2013-10-04 23:54:59 -07:00
Matthias Mailänder
a503345c16 Merge pull request #3900 from pchote/website-packaging-fix
Update packaging for recent website changes.
2013-10-04 23:26:32 -07:00
Paul Chote
44503323f1 Upload the source package to the /source/ directory on the server. 2013-10-05 13:49:09 +13:00
Paul Chote
3ff71d5982 Fix asset downloader NRE. Fixes #3898. 2013-10-05 13:29:39 +13:00
Paul Chote
4f354e1474 Batch ActorMap add/removes and filter invalid ActorsInBox. Fixes #3897. 2013-10-05 13:19:47 +13:00
Paul Chote
85f854ccde Clean up ActorMap. 2013-10-05 12:59:54 +13:00
ScottNZ
faee82654f Add OpenRA.Irc to packaging scripts 2013-10-05 12:05:28 +13:00
ScottNZ
c6dc0e8c8b Add IRC interface to ra/cnc server browsers 2013-10-05 12:05:28 +13:00
ScottNZ
5bdd0705b2 Add main IRC logic 2013-10-05 12:05:27 +13:00
ScottNZ
3ee1628b13 Add databinding support to ScrollPanelWidget 2013-10-05 12:05:21 +13:00
Matthias Mailänder
47e036e1b2 Merge pull request #3893 from reaperrr/ra-fixes
Fixes two cosmetical issues with RA mod
2013-10-03 09:43:41 -07:00
reaperrr
8a23f63f1b Corrects paradrop description. It actually drops two rocket soldiers as well. 2013-10-03 17:37:23 +02:00
reaperrr
79b989b560 Downsize ant selection box. Fixes #3885 2013-10-03 17:36:10 +02:00
ScottNZ
d15a53d88b Reduce prices of medi and mech 2013-10-03 19:13:48 +13:00
ScottNZ
903e85bc38 Double hellfire ROT 2013-10-01 14:59:15 +13:00
ScottNZ
da14de15e0 Increase hellfire aa/ag burst delay 2013-10-01 14:58:43 +13:00
ScottNZ
e897212a3d Increase projectile speed for grenadier 2013-10-01 01:07:52 +13:00
ScottNZ
0aaf636273 Increase light/heavy damage multipliers for v2rl 2013-09-30 23:01:28 +13:00
ScottNZ
37bd2f4e54 Increase longbow AG hellfire damage to 60 2013-09-30 23:01:21 +13:00
ScottNZ
e100d525b9 Increase longbow health to 150 2013-09-30 20:34:49 +13:00
ScottNZ
782f17e218 Make hind do more damage, especially against infantry 2013-09-30 20:17:01 +13:00
ScottNZ
58dd8b9026 Decrease v2rl damage and increase rof 2013-09-30 19:57:47 +13:00
ScottNZ
55a792b929 Increase arty hp to 100 2013-09-30 19:55:54 +13:00
ScottNZ
62acd78b35 Increase v2rl hp to 200 2013-09-30 19:55:43 +13:00
ScottNZ
ac148afd6e Reduce v2rl speed to 6 2013-09-30 19:34:43 +13:00
ScottNZ
4280f18c4c Increase price of v2rl to $900 2013-09-30 19:33:16 +13:00
ScottNZ
4ada54064d Increase price of arty to $800 2013-09-30 19:32:32 +13:00
Paul Chote
638c23098b Merge pull request #3890 from Mailaender/d2k-walls
Brought back concrete walls in D2k
2013-09-29 14:14:36 -07:00
Paul Chote
d2128d8e2a Merge pull request #3868 from Mailaender/nsis-shortcuts
Added an optional desktop shortcut for Windows
2013-09-29 14:06:52 -07:00
Paul Chote
5a1abe7692 Merge pull request #3889 from Mailaender/sync-report-available
Avoid misleading "No sync report available!" messages
2013-09-29 14:06:23 -07:00
Matthias Mailänder
bb5a6e0155 Hotkey: O for concrete walls in d2k 2013-09-29 22:35:33 +02:00
Matthias Mailänder
b2c05b5779 bring back player buildable walls in d2k 2013-09-29 22:35:24 +02:00
Matthias Mailänder
f4a8a99d87 avoid misleading no sync report available messages 2013-09-29 21:29:17 +02:00
Matthias Mailänder
5591154ae7 Merge pull request #3883 from dan9550/ts-install-logic
Tiberian Sun CD Install Logic
2013-09-29 07:45:23 -07:00
ScottNZ
9ccdf629f6 Reduce mcv build time to 32s 2013-09-30 00:01:19 +13:00
Matthias Mailänder
20e8f6385b Merge pull request #3886 from pchote/ppos-cleanup
Remove legacy PPos type.
2013-09-29 02:13:23 -07:00
Paul Chote
8241718d01 Fix double-rendering regression. 2013-09-29 21:29:10 +13:00
Paul Chote
5f0bb4b6c2 Fix some style nits in WorldRenderer. 2013-09-29 21:15:46 +13:00
Chris Forbes
f87796df21 Merge pull request #3878 from Mailaender/rotten-maps
Fixed map Training Camp
2013-09-28 19:53:58 -07:00
Chris Forbes
263849789f Merge pull request #3861 from pchote/spatialbins-cleanup
Overhaul spatial caching
2013-09-28 19:53:31 -07:00
ScottNZ
749965acd1 Use baseprovider in ra 2013-09-29 13:50:15 +13:00
ScottNZ
f7d46b8ca1 Reduce price of shok to $400 2013-09-29 12:59:41 +13:00
ScottNZ
0c75c8de18 Increase speeds of e3, e4 and shok to match e1's speed 2013-09-29 12:51:26 +13:00
ScottNZ
a1ddbf78f8 Reduce prices of gap to $800 and mgg to $1200 2013-09-29 12:44:08 +13:00
ScottNZ
70e22a89d5 Increase MGG shroud generation range from 4 to 6 2013-09-29 12:42:16 +13:00
ScottNZ
ec80d82fbc Uparmour MGG 2013-09-29 12:38:24 +13:00
ScottNZ
0f9b2041ae Make fact sellable again 2013-09-29 11:22:14 +13:00
ScottNZ
3225899a20 Reduce mcv crate no base selection shares to 100 (~70% chance of getting mcv with no mcv or fact) 2013-09-29 11:22:03 +13:00
ScottNZ
be523e2a38 Uparmour MRJ 2013-09-28 23:10:31 +12:00
Dan9550
e9e8c3fe49 Basic Tiberian Sun install logic 2013-09-28 19:38:33 +10:00
Dan9550
e5e7cdb427 TS Installer logic, tibsun.mix encrypted 2013-09-28 04:49:22 +10:00
Paul Chote
19e0c2a83f Remove PPos and PVecInt. 2013-09-27 15:58:56 +12:00
Paul Chote
b96c430f84 Remove PVecInt from Viewport. 2013-09-27 15:54:37 +12:00
Paul Chote
68a0070fa6 Remove PPos hack from ScreenShaker. 2013-09-27 15:50:45 +12:00
Paul Chote
90ab2477b4 Convert Sound to world coordinates. 2013-09-27 15:41:33 +12:00
Paul Chote
2303d8064a Remove Sprite.DrawAt. 2013-09-27 15:41:33 +12:00
Paul Chote
f94c7034bf Use SpriteRenderables for selection decorations. 2013-09-27 15:41:33 +12:00
Paul Chote
3002c4b77d Use SpriteRenderables for tile overlays. 2013-09-27 15:41:33 +12:00
Paul Chote
8c0da26ae1 Remove PPos from GainsExperience. 2013-09-27 15:41:33 +12:00
Paul Chote
1a1d5ede19 Remove PPos from TeslaZapRenderable. 2013-09-27 15:41:33 +12:00
Paul Chote
7320493a21 Fix radar cursor fake mouse event location. 2013-09-27 15:41:33 +12:00
Paul Chote
b100b4131c Remove PPos hacks from WorldInteractionController. 2013-09-27 15:41:32 +12:00
Paul Chote
2e0e4b0bc5 Add WorldRenderer.Position for screen -> world conversion. 2013-09-27 15:41:32 +12:00
Paul Chote
27e4bbf1cb Replace CenterLocationInit -> CenterPositionInit. 2013-09-27 15:41:32 +12:00
Paul Chote
4df9fc1acc Render minefield targeting using PBOG-style overlay. 2013-09-27 15:41:32 +12:00
Paul Chote
aed7f2ace6 Remove PPos and tidy DebugOverlay. 2013-09-27 15:41:32 +12:00
Paul Chote
0dc50c65f5 Remove unnecessary int2 -> PPos -> int2 conversions. 2013-09-27 15:41:32 +12:00
Paul Chote
b335d67ce3 Remove PPos hacks from RadarWidget. 2013-09-27 15:41:32 +12:00
Paul Chote
ad44610e5a Replace dynamic Actor.Bounds with the (unchanging) relative rect. 2013-09-27 15:39:57 +12:00
Paul Chote
3f8d75a1ac Remove SpatialBins. 2013-09-27 15:36:51 +12:00
Paul Chote
e03ec690ff Track actor positions in ActorMap. 2013-09-27 15:36:51 +12:00
Paul Chote
b00cc6108d Make ActorMap addition explicit. 2013-09-27 15:36:51 +12:00
Paul Chote
4a2a747556 Pull ActorMap back out into a trait. 2013-09-27 15:36:49 +12:00
Paul Chote
dfd51c0caa Introduce ScreenMap trait for caching screen-coord queries. 2013-09-27 15:36:25 +12:00
Paul Chote
cad46e43c5 Add WorldRenderer parameter to WorldLoaded. 2013-09-27 15:29:44 +12:00
Matthias Mailänder
f8316af454 Documented Gentoo build dependencies
closes #3869
2013-09-24 22:23:48 +02:00
Matthias Mailänder
96276514f3 removed bit-rotting in training-camp map
- crash because Tooltip: Icon: is used in legacy ways
- unbuildable sniper/demo-truck
- no need for custom demo truck with mine layer icon
2013-09-24 20:53:01 +02:00
Matthias Mailänder
e67ff1da7e Update INSTALL
copy-dependencies.bat did not make it
2013-09-23 19:55:24 +02:00
Matthias Mailänder
bcb9acb14b Add an optional desktop shortcut for Windows
closes #3853
2013-09-22 19:08:34 +02:00
ScottNZ
8b89952d59 Add observable collections 2013-09-22 19:22:07 +12:00
ScottNZ
f3fa81b8f1 Fix ActionQueue.PerformActions executing actions out of order 2013-09-22 19:22:06 +12:00
Sam Hegarty
954b070736 Use quotes around rpm file names to prevent issues with spaces. 2013-09-22 09:10:31 +02:00
Paul Chote
c26a0cb222 Merge pull request #3837 from Mailaender/makefile-fixes
Use the Makefile install rules for our packaging
2013-09-22 00:06:45 -07:00
Matthias Mailänder
29d93f7d9d separate Linux only shortcuts and icons from make install 2013-09-22 09:01:23 +02:00
Paul Chote
94e0a30904 Merge pull request #3858 from Mailaender/replay-folders
Save Replays in per Mod and Version folders
2013-09-21 21:50:30 -07:00
Paul Chote
5ebde33256 Merge pull request #3860 from Mailaender/heal-allies-only
Added IsAlliedWith check to Heal
2013-09-21 21:45:57 -07:00
Matthias Mailänder
a5b954a563 never heal enemies
fixes #3597
2013-09-21 07:37:20 +02:00
Matthias Mailänder
7a4f29afc6 apt-get update in before_install 2013-09-20 18:27:52 +02:00
Matthias Mailänder
5f27b05103 update documentation 2013-09-20 18:27:52 +02:00
Matthias Mailänder
80179a19c2 spaces to tabs 2013-09-20 18:27:52 +02:00
Matthias Mailänder
fb91b81dd5 rename RALint → OpenRA.Lint 2013-09-20 18:27:47 +02:00
Matthias Mailänder
de4c224b5c separate RALint tests from regular builds 2013-09-20 18:27:46 +02:00
Matthias Mailänder
83669dd50b install icons, desktop shortcuts and launchers in Makefile
avoid redundancy with package build scripts
add uninstall rules
2013-09-20 18:27:46 +02:00
Matthias Mailänder
9d3b93f717 save replays in per mod and version folders
StyleCop cleanups
2013-09-20 17:12:22 +02:00
Paul Chote
38dcd857dd Merge pull request #3825 from Mailaender/tm-removal
Renamed some project files for IP clearance
2013-09-19 22:08:57 -07:00
Chris Forbes
66688d05b0 Merge pull request #3840 from Mailaender/mtm-fix
Fixed Unresolved=InfiltrateForCashInfo Missing=InfiltratableInfo in Monster Tank Madness
2013-09-19 20:30:26 -07:00
Chris Forbes
f6ad5dd0b0 Merge pull request #3843 from Mailaender/chmod-x
Removed executable bits for some maps
2013-09-19 20:26:25 -07:00
Matthias Mailänder
7d6b0310da removed executable bits 2013-09-15 16:23:03 +02:00
Matthias Mailänder
b28c05d904 Merge pull request #3842 from ckorn/patch-1
Makefile: Add geoip so it gets installed, fixes #3834
2013-09-15 06:45:12 -07:00
Christoph Korn
0f3212f65b Makefile: Add geoip so it gets installed
Without adding it to CORE it does not get installed and the game crashes.
2013-09-15 15:34:09 +02:00
Matthias Mailänder
92a09720b0 Fixed Unresolved=InfiltrateForCashInfo Missing=InfiltratableInfo
closes #3835
2013-09-15 11:38:36 +02:00
Matthias Mailänder
eff4e80877 fix Bountysource url 2013-09-15 10:32:05 +02:00
Sam Hegarty
3c497e638e Use a combination of find xargs and read to prevent file/dir name problems. 2013-09-15 10:19:50 +02:00
Matthias Mailänder
b9c226bc18 cope with with data file names that contain spaces 2013-09-15 10:19:50 +02:00
Matthias Mailänder
c880e47add list files again to avoid owning standard dirs in RPM
reverts commit 9d256d93a5

fixes file /usr/bin from install of openra-release.20130915-1.noarch
conflicts with file from package filesystem-3.2-13.fc19.x86_64
2013-09-15 10:19:49 +02:00
Matthias Mailänder
62616eba14 rename C&C to TD 2013-09-14 23:08:08 +02:00
286 changed files with 6637 additions and 7071 deletions

View File

@@ -5,12 +5,15 @@
language: c
# Make sure build dependencies are installed.
before_install:
- sudo apt-get update
install:
- sudo apt-get update && sudo apt-get install mono-gmcs cli-common-dev libsdl1.2debian libgl1-mesa-glx libopenal1 libfreetype6
- sudo apt-get install mono-gmcs cli-common-dev libsdl1.2debian libgl1-mesa-glx libopenal1 libfreetype6
# Run the build script which will automatically call RALint and ensure that the IDE project files are still valid.
script:
- make all
- make test
- xbuild
# Only watch the development branch.
@@ -20,4 +23,10 @@ branches:
# Notify developers when build passed/failed.
notifications:
irc: "irc.freenode.net#openra"
irc:
template:
- "%{repository}#%{build_number} %{commit} %{author}: %{message} %{build_url}"
channels:
- "irc.freenode.net#openra"
use_notice: true
skip_join: true

View File

@@ -71,9 +71,5 @@ distributed under the CC BY-SA 3.0 license.
Finally, special thanks goes to the original teams
at Westwood Studios and EA for creating the classic
games that inspired the creation of OpenRA.
Red Alert, Command and Conquer, and related
trademarks belong to Electronic Arts Inc. and are
used without permission.
games which OpenRA aims to reimagine.

View File

@@ -1,3 +1,51 @@
NEW:
All mods:
Added global chat to the server browser.
Fixed dead units sometimes exploding or leaving husks when they weren't in the world.
Added hover and disabled button states [RA / D2K / TS]
Added double click start support to the replay browser.
Fixed low projectiles not colliding with walls.
Improved UI decorations (selection boxes, target lines, etc) in pixel double mode.
Red Alert:
Fixed a crash in Monster Tank Madness.
Fixed medics sometimes healing enemies.
Reduced Giant Ant selection box size.
Fixed paradrop description.
Various balance adjustments:
Uparmored Mobile Radar Jammer to heavy.
Uparmored Mobile Gap Generator to heavy, increased its shroud generation range from 4 to 6, and reduced its price from $1500 to $1200.
Reduced Gap Generator price from $1000 to $800.
Made Construction Yard sellable again.
Reduced MCV build time from 1 minute to 32 seconds.
No-base MCV crate chance reduced from ~100% to ~70%.
Introduced a maximum build radius of 16 cells from the nearest friendly Construction Yard.
Increased movement speeds of Rocket Soldier, Flamethrower, and Tesla Trooper from 3 to 4.
Reduced Tesla Trooper price from $500 to $400.
Increased Artillery price from $600 to $800, and increased its health from 75 to 100.
Increased V2 Rocket price from $700 to $900, reduced its movement speed from 7 to 6, increased its health from 150 to 200, reduced its warhead damage against primary base structures, and increased its rate of fire.
Increased Hind damage from 20 to 30, and introduced a small additional damage bonus against infantry.
Increased Longbow health from 120 to 150, increased its missile damage against ground targets from 40 to 60, increased its missile rate of turn from 5 to 10, and introduced a slight delay between missile launches.
Increased Grenadier projectile speed.
Reduced Medic price from $300 to $200.
Reduced Mechanic price from $800 to $500.
Added a 20% chance to eject a driver from destroyed vehicles.
Athena:
Reduced strategic victory timer to 3 minutes.
Increased the size of the starting islands.
Dune 2000:
Added buildable concrete walls.
Fixed some cliffs being passable.
Fixed infantry sometimes using the wrong animation when standing.
Fixed A* debug overlay.
Engine:
Replays are now saved in per-mod and per-version folders.
Added password protection support for servers.
Added language translation support.
Added game ID and version information to exception and sync reports.
Build system and packages:
Added GeoIP to Makefile so it is installed properly.
Added desktop shortcut creation support to the Makefile and Windows installer.
20130915:
All mods:
Overhauled Fog of War to freeze the state of resource, smudge, and structures.

82
HACKING
View File

@@ -1,82 +0,0 @@
HACKING
There are n main sections to OpenRA: UI, Rendering, unit behaviour, ...
All units/structures/most things in the map are Actors. Actors contain a collection of traits.
Traits consist of an info class and a class that does stuff. There is one instance of the infoclass shared across all actors of the same type. Each actor gets its own instance of the trait class itself. Infoclasses are responsible for instantiating their corresponding trait class -- see ITraitInfo, and TraitInfo<T> for the trivial implementation of this. In some cases the trait class's constructor needs some args, in which case TraitInfo<T> can't be used. This is a limitation of C# generics.
Actor assembly is done via the mod's yaml files. A section exists for each actor type,
and within that section we list the traits the actor should have.
These get looked up in the loaded mod DLLs. Each trait can contain properties,
which are automatically loaded into the corresponding fields on the trait's ITraitInfo.
- Traits: look at TraitInterfaces.cs
We've tried to make individual traits implement as self-contained a unit of functionality
as possible - all cross-trait references should be in terms of an interface from
TraitInterfaces.cs.
- Things an actor can be *doing* are represented as Activity subclasses.
Actor has a queue of these. The standard set of activities are in OpenRA.Mods.RA/Activities. Ground vehicle movement is more complex, and has bits in OpenRA.Mods.RA/Move. Aircraft use different bits again, in OpenRA.Mods.RA/Air. There are some bits elsewhere for custom infantry behaviors, etc. In some cases, a trait or activity will maintain an internal subqueue of activities. This works exactly the same way as the actor's main activity queue -- its state is evolved by Util.RunActivity().
- Units offer orders they can perform (given context) through traits that implement IIssueOrder. There are two parts to this -- an Orders collection, which exposes IOrderTargeter objects, which describe orders that the actor *could* generate, and the rules for choosing which to perform when the "default action" button is pressed (RMB). The second part is IssueOrder() itself, which resolves an IOrderTargeter into an actual order object to be sent.
- For more complex things that require modal UI (like special abilities,
RA-style sell/repair buttons, etc) we have IOrderGenerator implementations. This can
completely replace the normal actors-provide-orders model temporarily. IOGs wiring is
provided through OpenRa.Game/Controller.cs (ToggleInputMode<T>, CancelInputMode)
- Things that don't affect gameplay, or (increasingly) are just transient are implemented as
IEffect, rather than real Actors. This is similar to the temp ents mechanism in many other
game engines.
- Most player-level or global-level game behavior is implemented as traits on special Player
and World actors. These are accessible via Player.PlayerActor and World.WorldActor. This
includes production queue support, ore/tiberium growth, various palette manipulation magic.
- Many traits can be modified by adding an appropriate IFooModifier implementation to the unit.
This includes rendering, where IRenderModifier allows you to define an arbitrary transform on
the Renderables emitted by the actor's IRender implementation(s). Examples are things like
cloaking, invisibility to certain players, flying units with shadows, etc. Other modifiers
can affect movement speed, damage taken, weapon firepower, etc.
Game code is collected into "Mod" units. Mods can be added prior to starting the game.
Currently there is no dependancy mechanism, but provided you are doing additions or overrides
you can add multiple mods without problem.
Everything is a mod (including RA - which is loaded by default).
The contents of the mod is defined in a manifest file mod.yaml. This lists the packages
containing art assets (typically .mix files), yaml files defining actor defintions,
and ini files containing legacy information that have yet to be ported over to
the (relatively new) yaml system.
The unit artwork itself must be defined in one or more sequences files. These are specified in the manifest. RA puts everything in mods/ra/sequences.yaml.
check mod.yaml for a list of what the mod uses); the format is self explanatory.
Chrome artwork is similarly defined in Chrome.xml. Chrome is already mod dependent. Sortof ;)
mod-dependent *behavior* would be nice too, not just skinning. This is a property of the traits
however; once we port UI into traits this will become a non-issue.
UserSettings stores the data loaded from settings.ini (or defaults). Eventually we need to be
able to save values changed in game into settings.ini (not yet implemented)
Bugs: There is a list of known bugs and features at http://bugs.open-ra.org/ .
We also have a website at http://www.open-ra.org/ .
Our IRC channel is #openra on irc.freenode.net .
As far as using git, get your own repository on github. Push your changes into your git repository, and it will/might :P be merged into http://github.com/OpenRA/OpenRA .
See http://help.github.com/ for working with GitHub and see http://progit.org/ for working
with Git.
Other things we probably want to put in here:
- A guide on how to add a generic unit via yaml using existing traits
- and then introduce some element that requires a simple trait change.
- how to set up a new mod (TC-style or mutator-style)
- VFS (OpenRa.FileFormats.FileSystem, Package, Folder classes)
- Trait inheritance (and the magicness of ^ActorType)
- Removing inherited traits (prepend `-` to the trait name)
- Multiple instances of a trait (`@` and all subsequent characters are ignored for
the purposes of looking up the trait.

28
INSTALL
View File

@@ -14,16 +14,20 @@ Windows
(http://www.microsoft.com/downloads/details.aspx?FamilyID=AB99342F-5D1A-413D-8319-81DA479AB0D7&displaylang=en)
* Cg Toolkit >= 2.2 (optional, for Cg renderer)
(https://developer.nvidia.com/cg-toolkit-download)
* SDL 1.2 (included)
(http://libsdl.org/download-1.2.php)
* FreeType (included)
(http://gnuwin32.sourceforge.net/packages/freetype.htm)
* zlib (included)
(http://gnuwin32.sourceforge.net/packages/zlib.htm)
* OpenAL (included)
(http://kcat.strangesoft.net/openal.html)
To compile OpenRA, open the OpenRA.sln solution in the main folder,
or build it from the command-line with MSBuild.
Execute `copy-dependencies.bat' to place the DLLs we conveniently
bundle for you. They include binaries of:
* http://libsdl.org/download-1.2.php
* http://gnuwin32.sourceforge.net/packages/freetype.htm
* http://gnuwin32.sourceforge.net/packages/zlib.htm
* http://kcat.strangesoft.net/openal.html
Copy both the native DLLs from .\packaging\windows
and the CLI images from .\thirdparty to the main folder.
Run the game with `OpenRA.Game.exe Game.Mods=ra` for Red Alert
or `OpenRA.Game.exe Game.Mods=cnc` for Command & Conquer
@@ -48,7 +52,7 @@ can then run from the `openra` shortcut.
Slackware
---------
The following packages from slackbuilds.org are required, on 13.37 32bit:
The following packages from slackbuilds.org are required, on 13.37 32-bit:
* libgdiplus
* mono
* openAL
@@ -61,4 +65,12 @@ openSUSE
* openal
* freetype2
* SDL
* cg (optional, for Cg renderer)
* cg (optional, for Cg renderer)
Gentoo
------
* dev-lang/mono
* dev-dotnet/libgdiplus
* media-libs/openal
* media-gfx/nvidia-cg-toolkit (optional, for Cg renderer)

View File

@@ -6,12 +6,21 @@
# to compile with development tools, run:
# make all
#
# to check the official mods for erroneous yaml files, run:
# make test
#
# to generate documentation aimed at modders, run:
# make docs
#
# to install, run:
# make [prefix=/foo] [bindir=/bar/bin] install
#
# to install with development tools, run:
# make [prefix=/foo] [bindir=/bar/bin] install-all
#
# to install Linux desktop files and icons:
# make install-shortcuts
#
# to uninstall, run:
# make uninstall
#
@@ -54,7 +63,7 @@ INSTALL_PROGRAM = $(INSTALL) -m755
INSTALL_DATA = $(INSTALL) -m644
# program targets
CORE = fileformats rcg rgl rsdl rnull game utility
CORE = fileformats rcg rgl rsdl rnull game utility geoip irc
TOOLS = editor tsbuild ralint
VERSION = $(shell git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || echo git-`git rev-parse --short HEAD`)
@@ -88,6 +97,14 @@ game_FLAGS = -win32icon:OpenRA.Game/OpenRA.ico
PROGRAMS += game
game: $(game_TARGET)
irc_SRCS := $(shell find OpenRA.Irc/ -iname '*.cs')
irc_TARGET = OpenRA.Irc.dll
irc_KIND = library
irc_DEPS = $(fileformats_TARGET) $(game_TARGET)
irc_LIBS = $(COMMON_LIBS) $(irc_DEPS)
PROGRAMS += irc
irc: $(irc_TARGET)
# Renderer dlls
rsdl_SRCS := $(shell find OpenRA.Renderer.SdlCommon/ -iname '*.cs')
rsdl_TARGET = OpenRA.Renderer.SdlCommon.dll
@@ -132,9 +149,8 @@ STD_MOD_DEPS = $(STD_MOD_LIBS) $(ralint_TARGET)
mod_ra_SRCS := $(shell find OpenRA.Mods.RA/ -iname '*.cs')
mod_ra_TARGET = mods/ra/OpenRA.Mods.RA.dll
mod_ra_KIND = library
mod_ra_DEPS = $(STD_MOD_DEPS) $(utility_TARGET) $(geoip_TARGET)
mod_ra_LIBS = $(COMMON_LIBS) $(STD_MOD_LIBS) $(utility_TARGET) $(geoip_TARGET)
mod_ra_EXTRA_CMDS = mono --debug RALint.exe ra
mod_ra_DEPS = $(STD_MOD_DEPS) $(utility_TARGET) $(geoip_TARGET) $(irc_TARGET)
mod_ra_LIBS = $(COMMON_LIBS) $(STD_MOD_LIBS) $(utility_TARGET) $(geoip_TARGET) $(irc_TARGET)
PROGRAMS += mod_ra
mod_ra: $(mod_ra_TARGET)
@@ -144,7 +160,6 @@ mod_cnc_TARGET = mods/cnc/OpenRA.Mods.Cnc.dll
mod_cnc_KIND = library
mod_cnc_DEPS = $(STD_MOD_DEPS) $(mod_ra_TARGET)
mod_cnc_LIBS = $(COMMON_LIBS) $(STD_MOD_LIBS) $(mod_ra_TARGET)
mod_cnc_EXTRA_CMDS = mono --debug RALint.exe cnc
PROGRAMS += mod_cnc
mod_cnc: $(mod_cnc_TARGET)
@@ -154,7 +169,6 @@ mod_d2k_TARGET = mods/d2k/OpenRA.Mods.D2k.dll
mod_d2k_KIND = library
mod_d2k_DEPS = $(STD_MOD_DEPS) $(mod_ra_TARGET) $(mod_cnc_TARGET)
mod_d2k_LIBS = $(COMMON_LIBS) $(STD_MOD_LIBS) $(mod_ra_TARGET)
mod_d2k_EXTRA_CMDS = mono --debug RALint.exe d2k
PROGRAMS += mod_d2k
mod_d2k: $(mod_d2k_TARGET)
@@ -164,7 +178,6 @@ mod_ts_TARGET = mods/ts/OpenRA.Mods.TS.dll
mod_ts_KIND = library
mod_ts_DEPS = $(STD_MOD_DEPS) $(mod_ra_TARGET)
mod_ts_LIBS = $(COMMON_LIBS) $(STD_MOD_LIBS) $(mod_ra_TARGET)
mod_ts_EXTRA_CMDS = mono --debug RALint.exe ts
PROGRAMS += mod_ts
mod_ts: $(mod_ts_TARGET)
@@ -187,14 +200,24 @@ OpenRA.Editor.Form1.resources:
editor: OpenRA.Editor.MapSelect.resources OpenRA.Editor.Form1.resources $(editor_TARGET)
# Analyses mod yaml for easy to detect errors
ralint_SRCS := $(shell find RALint/ -iname '*.cs')
ralint_TARGET = RALint.exe
ralint_SRCS := $(shell find OpenRA.Lint/ -iname '*.cs')
ralint_TARGET = OpenRA.Lint.exe
ralint_KIND = exe
ralint_DEPS = $(fileformats_TARGET) $(game_TARGET)
ralint_LIBS = $(COMMON_LIBS) $(ralint_DEPS)
PROGRAMS += ralint
ralint: $(ralint_TARGET)
test:
@mono --debug OpenRA.Lint.exe ra
@echo "OpenRA.Lint: ra mod yaml checks passed."
@mono --debug OpenRA.Lint.exe cnc
@echo "OpenRA.Lint: cnc mod yaml checks passed."
@mono --debug OpenRA.Lint.exe d2k
@echo "OpenRA.Lint: d2k mod yaml checks passed."
@mono --debug OpenRA.Lint.exe ts
@echo "OpenRA.Lint: ts mod yaml checks passed."
# Builds and exports tilesets from a bitmap
tsbuild_SRCS := $(shell find OpenRA.TilesetBuilder/ -iname '*.cs')
tsbuild_TARGET = OpenRA.TilesetBuilder.exe
@@ -218,8 +241,8 @@ tsbuild: OpenRA.TilesetBuilder.FormBuilder.resources OpenRA.TilesetBuilder.FormN
utility_SRCS := $(shell find OpenRA.Utility/ -iname '*.cs')
utility_TARGET = OpenRA.Utility.exe
utility_KIND = exe
utility_DEPS = $(fileformats_TARGET) $(game_TARGET)
utility_LIBS = $(COMMON_LIBS) $(utility_DEPS) thirdparty/ICSharpCode.SharpZipLib.dll System.Windows.Forms.dll
utility_DEPS = $(fileformats_TARGET) $(game_TARGET)
utility_LIBS = $(COMMON_LIBS) $(utility_DEPS) thirdparty/ICSharpCode.SharpZipLib.dll System.Windows.Forms.dll
PROGRAMS += utility
utility: $(utility_TARGET)
@@ -253,7 +276,7 @@ $(foreach prog,$(PROGRAMS),$(eval $(call BUILD_ASSEMBLY,$(prog))))
#
default: dependencies core
core: game renderers mods utility tsbuild
core: game renderers mods utility
tools: editor tsbuild ralint
@@ -301,6 +324,8 @@ install-core: default
@$(INSTALL_DATA) "global mix database.dat" "$(DATA_INSTALL_DIR)/global mix database.dat"
@$(INSTALL_DATA) "GeoIP.dat" "$(DATA_INSTALL_DIR)/GeoIP.dat"
@$(INSTALL_DATA) AUTHORS "$(DATA_INSTALL_DIR)/AUTHORS"
@$(INSTALL_DATA) CHANGELOG "$(DATA_INSTALL_DIR)/CHANGELOG"
@$(INSTALL_DATA) COPYING "$(DATA_INSTALL_DIR)/COPYING"
@$(CP_R) glsl "$(DATA_INSTALL_DIR)"
@$(CP_R) cg "$(DATA_INSTALL_DIR)"
@@ -318,10 +343,8 @@ install-core: default
@echo 'DATADIR="$${ROOTDIR}/'"$(datadir)"'"' >> openra
@echo 'cd "$${DATADIR}/openra"' >> openra
@echo 'exec mono OpenRA.Game.exe "$$@"' >> openra
@$(INSTALL_DIR) "$(BIN_INSTALL_DIR)"
@$(INSTALL_PROGRAM) -m +rx openra "$(BIN_INSTALL_DIR)"
@-$(RM) openra
install-tools: tools
@@ -335,16 +358,33 @@ install-tools: tools
@echo 'DATADIR="$${ROOTDIR}/'"$(datadir)"'"' >> openra-editor
@echo 'cd "$${DATADIR}/openra"' >> openra-editor
@echo 'exec mono OpenRA.Editor.exe "$$@"' >> openra-editor
@$(INSTALL_DIR) "$(BIN_INSTALL_DIR)"
@$(INSTALL_PROGRAM) -m +rx openra-editor "$(BIN_INSTALL_DIR)"
@-$(RM) openra-editor
install-shortcuts:
@$(INSTALL_DIR) "$(DESTDIR)$(datadir)/icons/"
@$(CP_R) packaging/linux/hicolor/ "$(DESTDIR)$(datadir)/icons"
@$(INSTALL_DIR) "$(DESTDIR)$(datadir)/applications"
@$(INSTALL_DATA) packaging/linux/openra.desktop "$(DESTDIR)$(datadir)/applications"
@$(INSTALL_DIR) "$(DESTDIR)$(datadir)/applications"
@$(INSTALL_DATA) packaging/linux/openra-editor.desktop "$(DESTDIR)$(datadir)/applications"
uninstall:
@-$(RM_R) "$(DATA_INSTALL_DIR)"
@-$(RM_F) "$(BIN_INSTALL_DIR)/openra"
@-$(RM_F) "$(BIN_INSTALL_DIR)/openra-editor"
@-$(RM_F) "$(DESTDIR)$(datadir)/applications/openra.desktop"
@-$(RM_F) "$(DESTDIR)$(datadir)/applications/openra-editor.desktop"
@-$(RM_F) "$(DESTDIR)$(datadir)/icons/hicolor/16x16/apps/openra.png"
@-$(RM_F) "$(DESTDIR)$(datadir)/icons/hicolor/32x32/apps/openra.png"
@-$(RM_F) "$(DESTDIR)$(datadir)/icons/hicolor/32x32/apps/openra-editor.png"
@-$(RM_F) "$(DESTDIR)$(datadir)/icons/hicolor/48x48/apps/openra.png"
@-$(RM_F) "$(DESTDIR)$(datadir)/icons/hicolor/48x48/apps/openra-editor.png"
@-$(RM_F) "$(DESTDIR)$(datadir)/icons/hicolor/64x64/apps/openra.png"
@-$(RM_F) "$(DESTDIR)$(datadir)/icons/hicolor/128x128/apps/openra.png"
help:
@echo to compile, run:
@@ -353,12 +393,21 @@ help:
@echo to compile with development tools, run:
@echo \ \ make all
@echo
@echo to check the official mods for erroneous yaml files, run:
@echo \ \ make test
@echo
@echo to generate documentation aimed at modders, run:
@echo \ \ make docs
@echo
@echo to install, run:
@echo \ \ make \[prefix=/foo\] \[bindir=/bar/bin\] install
@echo
@echo to install with development tools, run:
@echo \ \ make \[prefix=/foo\] \[bindir=/bar/bin\] install-all
@echo
@echo to install Linux desktop files and icons
@echo \ \ make install-shortcuts
@echo
@echo to uninstall, run:
@echo \ \ make uninstall
@echo

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2013 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,
@@ -14,6 +14,7 @@ using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
namespace OpenRA.FileFormats
{
@@ -38,31 +39,31 @@ namespace OpenRA.FileFormats
object val;
if (kv.Value != null)
val = kv.Value(kv.Key.Name, kv.Key.FieldType, my);
else if (!TryGetValueFromYaml(kv.Key.Name, kv.Key.FieldType, my, out val))
else if (!TryGetValueFromYaml(kv.Key, my, out val))
continue;
kv.Key.SetValue(self, val);
}
}
static bool TryGetValueFromYaml(string fieldName, Type fieldType, MiniYaml yaml, out object ret)
static bool TryGetValueFromYaml(FieldInfo field, MiniYaml yaml, out object ret)
{
ret = null;
var n = yaml.Nodes.Where(x => x.Key == fieldName).ToList();
var n = yaml.Nodes.Where(x => x.Key == field.Name).ToList();
if (n.Count == 0)
return false;
if (n.Count == 1 && n[0].Value.Nodes.Count == 0)
{
ret = GetValue(fieldName, fieldType, n[0].Value.Value);
ret = GetValue(field.Name, field.FieldType, n[0].Value.Value, field);
return true;
}
else if (n.Count > 1)
{
throw new InvalidOperationException("The field {0} has multiple definitions:\n{1}"
.F(fieldName, n.Select(m => "\t- " + m.Location).JoinWith("\n")));
.F(field.Name, n.Select(m => "\t- " + m.Location).JoinWith("\n")));
}
throw new InvalidOperationException("TryGetValueFromYaml: unable to load field {0} (of type {1})".F(fieldName, fieldType));
throw new InvalidOperationException("TryGetValueFromYaml: unable to load field {0} (of type {1})".F(field.Name, field.FieldType));
}
public static T Load<T>(MiniYaml y) where T : new()
@@ -80,7 +81,7 @@ namespace OpenRA.FileFormats
if (field != null)
{
if (!field.HasAttribute<FieldFromYamlKeyAttribute>())
field.SetValue(self, GetValue(field.Name, field.FieldType, value));
field.SetValue(self, GetValue(field.Name, field.FieldType, value, field));
return;
}
@@ -89,7 +90,7 @@ namespace OpenRA.FileFormats
if (prop != null)
{
if (!prop.HasAttribute<FieldFromYamlKeyAttribute>())
prop.SetValue(self, GetValue(prop.Name, prop.PropertyType, value), NoIndexes);
prop.SetValue(self, GetValue(prop.Name, prop.PropertyType, value, prop), NoIndexes);
return;
}
@@ -98,61 +99,70 @@ namespace OpenRA.FileFormats
public static T GetValue<T>(string field, string value)
{
return (T)GetValue(field, typeof(T), value);
return (T)GetValue(field, typeof(T), value, null);
}
public static object GetValue(string field, Type fieldType, string x)
public static object GetValue(string fieldName, Type fieldType, string value)
{
if (x != null) x = x.Trim();
return GetValue(fieldName, fieldType, value, null);
}
public static object GetValue(string fieldName, Type fieldType, string value, MemberInfo field)
{
if (value != null) value = value.Trim();
if (fieldType == typeof(int))
{
int res;
if (int.TryParse(x, out res))
if (int.TryParse(value, out res))
return res;
return InvalidValueAction(x, fieldType, field);
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(ushort))
{
ushort res;
if (ushort.TryParse(x, out res))
if (ushort.TryParse(value, out res))
return res;
return InvalidValueAction(x, fieldType, field);
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(float))
{
float res;
if (float.TryParse(x.Replace("%", ""), NumberStyles.Any, NumberFormatInfo.InvariantInfo, out res))
return res * (x.Contains('%') ? 0.01f : 1f);
return InvalidValueAction(x, fieldType, field);
if (float.TryParse(value.Replace("%", ""), NumberStyles.Any, NumberFormatInfo.InvariantInfo, out res))
return res * (value.Contains('%') ? 0.01f : 1f);
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(decimal))
{
decimal res;
if (decimal.TryParse(x.Replace("%", ""), NumberStyles.Any, NumberFormatInfo.InvariantInfo, out res))
return res * (x.Contains('%') ? 0.01m : 1m);
return InvalidValueAction(x, fieldType, field);
if (decimal.TryParse(value.Replace("%", ""), NumberStyles.Any, NumberFormatInfo.InvariantInfo, out res))
return res * (value.Contains('%') ? 0.01m : 1m);
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(string))
return x;
{
if (field != null && field.HasAttribute<TranslateAttribute>())
return Regex.Replace(value, "@[^@]+@", m => Translate(m.Value.Substring(1, m.Value.Length - 2)), RegexOptions.Compiled);
return value;
}
else if (fieldType == typeof(Color))
{
var parts = x.Split(',');
var parts = value.Split(',');
if (parts.Length == 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));
if (parts.Length == 4)
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);
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(HSLColor))
{
var parts = x.Split(',');
var parts = value.Split(',');
// Allow old ColorRamp format to be parsed as HSLColor
if (parts.Length == 3 || parts.Length == 4)
@@ -161,21 +171,30 @@ namespace OpenRA.FileFormats
(byte)int.Parse(parts[1]).Clamp(0, 255),
(byte)int.Parse(parts[2]).Clamp(0, 255));
return InvalidValueAction(x, fieldType, field);
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(Hotkey))
{
Hotkey res;
if (Hotkey.TryParse(value, out res))
return res;
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(WRange))
{
WRange res;
if (WRange.TryParse(x, out res))
if (WRange.TryParse(value, out res))
return res;
return InvalidValueAction(x, fieldType, field);
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(WVec))
{
var parts = x.Split(',');
var parts = value.Split(',');
if (parts.Length == 3)
{
WRange rx, ry, rz;
@@ -183,12 +202,12 @@ namespace OpenRA.FileFormats
return new WVec(rx, ry, rz);
}
return InvalidValueAction(x, fieldType, field);
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(WPos))
{
var parts = x.Split(',');
var parts = value.Split(',');
if (parts.Length == 3)
{
WRange rx, ry, rz;
@@ -196,62 +215,62 @@ namespace OpenRA.FileFormats
return new WPos(rx, ry, rz);
}
return InvalidValueAction(x, fieldType, field);
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(WAngle))
{
int res;
if (int.TryParse(x, out res))
if (int.TryParse(value, out res))
return new WAngle(res);
return InvalidValueAction(x, fieldType, field);
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(WRot))
{
var parts = x.Split(',');
var parts = value.Split(',');
if (parts.Length == 3)
{
int rr, rp, ry;
if (int.TryParse(x, out rr) && int.TryParse(x, out rp) && int.TryParse(x, out ry))
if (int.TryParse(value, out rr) && int.TryParse(value, out rp) && int.TryParse(value, out ry))
return new WRot(new WAngle(rr), new WAngle(rp), new WAngle(ry));
}
return InvalidValueAction(x, fieldType, field);
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType.IsEnum)
{
if (!Enum.GetNames(fieldType).Select(a => a.ToLower()).Contains(x.ToLower()))
return InvalidValueAction(x, fieldType, field);
return Enum.Parse(fieldType, x, true);
if (!Enum.GetNames(fieldType).Select(a => a.ToLower()).Contains(value.ToLower()))
return InvalidValueAction(value, fieldType, fieldName);
return Enum.Parse(fieldType, value, true);
}
else if (fieldType == typeof(bool))
return ParseYesNo(x, fieldType, field);
return ParseYesNo(value, fieldType, fieldName);
else if (fieldType.IsArray)
{
if (x == null)
if (value == null)
return Array.CreateInstance(fieldType.GetElementType(), 0);
var parts = x.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var ret = Array.CreateInstance(fieldType.GetElementType(), parts.Length);
for (int i = 0; i < parts.Length; i++)
ret.SetValue(GetValue(field, fieldType.GetElementType(), parts[i].Trim()), i);
ret.SetValue(GetValue(fieldName, fieldType.GetElementType(), parts[i].Trim(), field), i);
return ret;
}
else if (fieldType == typeof(int2))
{
var parts = x.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
return new int2(int.Parse(parts[0]), int.Parse(parts[1]));
}
else if (fieldType == typeof(float2))
{
var parts = x.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
float xx = 0;
float yy = 0;
float res;
@@ -264,13 +283,13 @@ namespace OpenRA.FileFormats
else if (fieldType == typeof(Rectangle))
{
var parts = x.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
return new Rectangle(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), int.Parse(parts[3]));
}
else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Bits<>))
{
var parts = x.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var argTypes = new Type[] { typeof(string[]) };
var argValues = new object[] { parts };
return fieldType.GetConstructor(argTypes).Invoke(argValues);
@@ -279,11 +298,11 @@ namespace OpenRA.FileFormats
else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
var innerType = fieldType.GetGenericArguments().First();
var innerValue = GetValue("Nullable<T>", innerType, x);
return fieldType.GetConstructor(new []{ innerType }).Invoke(new []{ innerValue });
var innerValue = GetValue("Nullable<T>", innerType, value, field);
return fieldType.GetConstructor(new[] { innerType }).Invoke(new[] { innerValue });
}
UnknownFieldAction("[Type] {0}".F(x), fieldType);
UnknownFieldAction("[Type] {0}".F(value), fieldType);
return null;
}
@@ -312,7 +331,7 @@ namespace OpenRA.FileFormats
if (loadUsing.Length != 0)
ret[field] = (_1, fieldType, yaml) => loadUsing[0].LoaderFunc(field)(yaml);
else if (fromYamlKey.Length != 0)
ret[field] = (f, ft, yaml) => GetValue(f, ft, yaml.Value);
ret[field] = (f, ft, yaml) => GetValue(f, ft, yaml.Value, field);
else if (ignore.Length == 0)
ret[field] = null;
}
@@ -342,81 +361,24 @@ namespace OpenRA.FileFormats
return loaderFuncCache;
}
}
public static string Translate(string key)
{
if (Translations == null || string.IsNullOrEmpty(key))
return key;
string value;
if (!Translations.TryGetValue(key, out value))
return key;
return value;
}
public static Dictionary<string, string> Translations = new Dictionary<string, string>();
}
public static class FieldSaver
{
public static MiniYaml Save(object o)
{
var nodes = new List<MiniYamlNode>();
string root = null;
foreach (var f in o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance))
{
if (f.HasAttribute<FieldFromYamlKeyAttribute>())
root = FormatValue(o, f);
else
nodes.Add(new MiniYamlNode(f.Name, FormatValue(o, f)));
}
return new MiniYaml(root, nodes);
}
public static MiniYaml SaveDifferences(object o, object from)
{
if (o.GetType() != from.GetType())
throw new InvalidOperationException("FieldLoader: can't diff objects of different types");
var fields = o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance)
.Where(f => FormatValue(o, f) != FormatValue(from, f));
return new MiniYaml(null, fields.Select(f => new MiniYamlNode(f.Name, FormatValue(o, f))).ToList());
}
public static MiniYamlNode SaveField(object o, string field)
{
return new MiniYamlNode(field, FieldSaver.FormatValue(o, o.GetType().GetField(field)));
}
public static string FormatValue(object v, Type t)
{
if (v == null)
return "";
// Color.ToString() does the wrong thing; force it to format as an array
if (t == typeof(Color))
{
var c = (Color)v;
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));
}
// Don't save floats in settings.yaml using country-specific decimal separators which can be misunderstood as group seperators.
if (t == typeof(float))
return ((float)v).ToString(CultureInfo.InvariantCulture);
if (t == typeof(Rectangle))
{
var r = (Rectangle)v;
return "{0},{1},{2},{3}".F(r.X, r.Y, r.Width, r.Height);
}
if (t.IsArray)
{
var elems = ((Array)v).OfType<object>();
return elems.JoinWith(",");
}
return v.ToString();
}
public static string FormatValue(object o, FieldInfo f)
{
return FormatValue(f.GetValue(o), f.FieldType);
}
}
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class TranslateAttribute : Attribute { }
public class FieldFromYamlKeyAttribute : Attribute { }

View File

@@ -0,0 +1,93 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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 COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Reflection;
namespace OpenRA.FileFormats
{
public static class FieldSaver
{
public static MiniYaml Save(object o)
{
var nodes = new List<MiniYamlNode>();
string root = null;
foreach (var f in o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance))
{
if (f.HasAttribute<FieldFromYamlKeyAttribute>())
root = FormatValue(o, f);
else
nodes.Add(new MiniYamlNode(f.Name, FormatValue(o, f)));
}
return new MiniYaml(root, nodes);
}
public static MiniYaml SaveDifferences(object o, object from)
{
if (o.GetType() != from.GetType())
throw new InvalidOperationException("FieldLoader: can't diff objects of different types");
var fields = o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance)
.Where(f => FormatValue(o, f) != FormatValue(from, f));
return new MiniYaml(null, fields.Select(f => new MiniYamlNode(f.Name, FormatValue(o, f))).ToList());
}
public static MiniYamlNode SaveField(object o, string field)
{
return new MiniYamlNode(field, FieldSaver.FormatValue(o, o.GetType().GetField(field)));
}
public static string FormatValue(object v, Type t)
{
if (v == null)
return "";
// Color.ToString() does the wrong thing; force it to format as an array
if (t == typeof(Color))
{
var c = (Color)v;
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));
}
// Don't save floats in settings.yaml using country-specific decimal separators which can be misunderstood as group seperators.
if (t == typeof(float))
return ((float)v).ToString(CultureInfo.InvariantCulture);
if (t == typeof(Rectangle))
{
var r = (Rectangle)v;
return "{0},{1},{2},{3}".F(r.X, r.Y, r.Width, r.Height);
}
if (t.IsArray)
{
var elems = ((Array)v).OfType<object>();
return elems.JoinWith(",");
}
return v.ToString();
}
public static string FormatValue(object o, FieldInfo f)
{
return FormatValue(f.GetValue(o), f.FieldType);
}
}
}

View File

@@ -66,10 +66,9 @@ namespace OpenRA
public struct KeyInput
{
public KeyInputEvent Event;
public char UnicodeChar;
public string KeyName;
public Keycode Key;
public Modifiers Modifiers;
public int VirtKey;
public int MultiTapCount;
public char UnicodeChar;
}
}

119
OpenRA.FileFormats/Hotkey.cs Executable file
View File

@@ -0,0 +1,119 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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 COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
namespace OpenRA
{
public struct Hotkey
{
public static Hotkey Invalid = new Hotkey(Keycode.UNKNOWN, Modifiers.None);
public readonly Keycode Key;
public readonly Modifiers Modifiers;
public static bool TryParse(string s, out Hotkey result)
{
result = Invalid;
var parts = s.Split(' ');
if (parts.Length >= 2)
{
if (!Enum.GetNames(typeof(Keycode)).Contains(parts[0]))
return false;
var modString = s.Substring(s.IndexOf(' '));
var modParts = modString.Split(',').Select(x => x.Trim());
if (modParts.Any(p => !Enum.GetNames(typeof(Modifiers)).Contains(p)))
return false;
var key = (Keycode)Enum.Parse(typeof(Keycode), parts[0]);
var mods = (Modifiers)Enum.Parse(typeof(Modifiers), modString);
result = new Hotkey(key, mods);
return true;
}
if (parts.Length == 1)
{
// HACK: Try parsing as a legacy key name
// This is a stop-gap solution to keep backwards
// compatibility while outside code is converted
var i = 0;
for (; i < (int)Keycode.LAST; i++)
if (KeycodeExts.DisplayString((Keycode)i) == parts[0])
break;
if (i < (int)Keycode.LAST)
{
result = new Hotkey((Keycode)i, Modifiers.None);
return true;
}
}
return false;
}
public static Hotkey FromKeyInput(KeyInput ki)
{
return new Hotkey(ki.Key, ki.Modifiers);
}
public Hotkey(Keycode virtKey, Modifiers mod)
{
Key = virtKey;
Modifiers = mod;
}
public static bool operator !=(Hotkey a, Hotkey b) { return !(a == b); }
public static bool operator ==(Hotkey a, Hotkey b)
{
// Unknown keys are never equal
if (a.Key == Keycode.UNKNOWN)
return false;
return a.Key == b.Key && a.Modifiers == b.Modifiers;
}
public override int GetHashCode() { return Key.GetHashCode() ^ Modifiers.GetHashCode(); }
public override bool Equals(object obj)
{
if (obj == null)
return false;
return (Hotkey)obj == this;
}
public override string ToString() { return "{0} {1}".F(Key, Modifiers.ToString("F")); }
public string DisplayString()
{
var ret = KeycodeExts.DisplayString(Key).ToUpper();
if (Modifiers.HasModifier(Modifiers.Shift))
ret = "Shift + " + ret;
if (Modifiers.HasModifier(Modifiers.Alt))
ret = "Alt + " + ret;
if (Modifiers.HasModifier(Modifiers.Ctrl))
ret = "Ctrl + " + ret;
if (Modifiers.HasModifier(Modifiers.Meta))
ret = (Platform.CurrentPlatform == PlatformType.OSX ? "Cmd + " : "Meta + ") + ret;
return ret;
}
}
}

View File

@@ -21,7 +21,7 @@ namespace OpenRA.FileFormats
{
static IEnumerable<ZipEntry> GetEntries(this ZipInputStream z)
{
for (; ; )
for (;;)
{
var e = z.GetNextEntry();
if (e != null) yield return e; else break;
@@ -43,9 +43,9 @@ namespace OpenRA.FileFormats
if (!Directory.Exists(destPath))
Directory.CreateDirectory(destPath);
if (!Directory.Exists(srcPath)) { onError("Cannot find "+package); return false; }
if (!Directory.Exists(srcPath)) { onError("Cannot find " + package); return false; }
FileSystem.Mount(srcPath);
if (!FileSystem.Exists(package)) { onError("Cannot find "+package); return false; }
if (!FileSystem.Exists(package)) { onError("Cannot find " + package); return false; }
FileSystem.Mount(package);
foreach (string s in files)
@@ -54,10 +54,11 @@ namespace OpenRA.FileFormats
using (var sourceStream = FileSystem.Open(s))
using (var destStream = File.Create(destFile))
{
onProgress("Extracting "+s);
onProgress("Extracting " + s);
destStream.Write(sourceStream.ReadAllBytes());
}
}
return true;
}
@@ -68,13 +69,15 @@ namespace OpenRA.FileFormats
var fromPath = Path.Combine(srcPath, file);
if (!File.Exists(fromPath))
{
onError("Cannot find "+file);
onError("Cannot find " + file);
return false;
}
var destFile = Path.GetFileName(file).ToLowerInvariant();
onProgress("Extracting "+destFile);
onProgress("Extracting " + destFile);
File.Copy(fromPath, Path.Combine(destPath, destFile), true);
}
return true;
}
@@ -82,7 +85,7 @@ namespace OpenRA.FileFormats
{
if (!File.Exists(zipFile))
{
onError("Invalid path: "+zipFile);
onError("Invalid path: " + zipFile);
return false;
}
@@ -90,16 +93,17 @@ namespace OpenRA.FileFormats
try
{
var z = new ZipInputStream(File.OpenRead(zipFile));
z.ExtractZip(dest, extracted, s => onProgress("Extracting "+s));
z.ExtractZip(dest, extracted, s => onProgress("Extracting " + s));
}
catch (SharpZipBaseException)
{
foreach(var f in extracted)
foreach (var f in extracted)
File.Delete(f);
onError("Invalid archive");
return false;
}
return true;
}

500
OpenRA.FileFormats/Keycode.cs Executable file
View File

@@ -0,0 +1,500 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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 COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
namespace OpenRA
{
public enum Keycode
{
UNKNOWN = 0,
FIRST = 0,
BACKSPACE = 8,
TAB = 9,
CLEAR = 12,
RETURN = 13,
PAUSE = 19,
ESCAPE = 27,
SPACE = 32,
EXCLAIM = 33,
QUOTEDBL = 34,
HASH = 35,
DOLLAR = 36,
AMPERSAND = 38,
QUOTE = 39,
LEFTPAREN = 40,
RIGHTPAREN = 41,
ASTERISK = 42,
PLUS = 43,
COMMA = 44,
MINUS = 45,
PERIOD = 46,
SLASH = 47,
NUMBER_0 = 48,
NUMBER_1 = 49,
NUMBER_2 = 50,
NUMBER_3 = 51,
NUMBER_4 = 52,
NUMBER_5 = 53,
NUMBER_6 = 54,
NUMBER_7 = 55,
NUMBER_8 = 56,
NUMBER_9 = 57,
COLON = 58,
SEMICOLON = 59,
LESS = 60,
EQUALS = 61,
GREATER = 62,
QUESTION = 63,
AT = 64,
LEFTBRACKET = 91,
BACKSLASH = 92,
RIGHTBRACKET = 93,
CARET = 94,
UNDERSCORE = 95,
BACKQUOTE = 96,
A = 97,
B = 98,
C = 99,
D = 100,
E = 101,
F = 102,
G = 103,
H = 104,
I = 105,
J = 106,
K = 107,
L = 108,
M = 109,
N = 110,
O = 111,
P = 112,
Q = 113,
R = 114,
S = 115,
T = 116,
U = 117,
V = 118,
W = 119,
X = 120,
Y = 121,
Z = 122,
DELETE = 127,
WORLD_0 = 160,
WORLD_1 = 161,
WORLD_2 = 162,
WORLD_3 = 163,
WORLD_4 = 164,
WORLD_5 = 165,
WORLD_6 = 166,
WORLD_7 = 167,
WORLD_8 = 168,
WORLD_9 = 169,
WORLD_10 = 170,
WORLD_11 = 171,
WORLD_12 = 172,
WORLD_13 = 173,
WORLD_14 = 174,
WORLD_15 = 175,
WORLD_16 = 176,
WORLD_17 = 177,
WORLD_18 = 178,
WORLD_19 = 179,
WORLD_20 = 180,
WORLD_21 = 181,
WORLD_22 = 182,
WORLD_23 = 183,
WORLD_24 = 184,
WORLD_25 = 185,
WORLD_26 = 186,
WORLD_27 = 187,
WORLD_28 = 188,
WORLD_29 = 189,
WORLD_30 = 190,
WORLD_31 = 191,
WORLD_32 = 192,
WORLD_33 = 193,
WORLD_34 = 194,
WORLD_35 = 195,
WORLD_36 = 196,
WORLD_37 = 197,
WORLD_38 = 198,
WORLD_39 = 199,
WORLD_40 = 200,
WORLD_41 = 201,
WORLD_42 = 202,
WORLD_43 = 203,
WORLD_44 = 204,
WORLD_45 = 205,
WORLD_46 = 206,
WORLD_47 = 207,
WORLD_48 = 208,
WORLD_49 = 209,
WORLD_50 = 210,
WORLD_51 = 211,
WORLD_52 = 212,
WORLD_53 = 213,
WORLD_54 = 214,
WORLD_55 = 215,
WORLD_56 = 216,
WORLD_57 = 217,
WORLD_58 = 218,
WORLD_59 = 219,
WORLD_60 = 220,
WORLD_61 = 221,
WORLD_62 = 222,
WORLD_63 = 223,
WORLD_64 = 224,
WORLD_65 = 225,
WORLD_66 = 226,
WORLD_67 = 227,
WORLD_68 = 228,
WORLD_69 = 229,
WORLD_70 = 230,
WORLD_71 = 231,
WORLD_72 = 232,
WORLD_73 = 233,
WORLD_74 = 234,
WORLD_75 = 235,
WORLD_76 = 236,
WORLD_77 = 237,
WORLD_78 = 238,
WORLD_79 = 239,
WORLD_80 = 240,
WORLD_81 = 241,
WORLD_82 = 242,
WORLD_83 = 243,
WORLD_84 = 244,
WORLD_85 = 245,
WORLD_86 = 246,
WORLD_87 = 247,
WORLD_88 = 248,
WORLD_89 = 249,
WORLD_90 = 250,
WORLD_91 = 251,
WORLD_92 = 252,
WORLD_93 = 253,
WORLD_94 = 254,
WORLD_95 = 255,
KP0 = 256,
KP1 = 257,
KP2 = 258,
KP3 = 259,
KP4 = 260,
KP5 = 261,
KP6 = 262,
KP7 = 263,
KP8 = 264,
KP9 = 265,
KP_PERIOD = 266,
KP_DIVIDE = 267,
KP_MULTIPLY = 268,
KP_MINUS = 269,
KP_PLUS = 270,
KP_ENTER = 271,
KP_EQUALS = 272,
UP = 273,
DOWN = 274,
RIGHT = 275,
LEFT = 276,
INSERT = 277,
HOME = 278,
END = 279,
PAGEUP = 280,
PAGEDOWN = 281,
F1 = 282,
F2 = 283,
F3 = 284,
F4 = 285,
F5 = 286,
F6 = 287,
F7 = 288,
F8 = 289,
F9 = 290,
F10 = 291,
F11 = 292,
F12 = 293,
F13 = 294,
F14 = 295,
F15 = 296,
NUMLOCK = 300,
CAPSLOCK = 301,
SCROLLOCK = 302,
RSHIFT = 303,
LSHIFT = 304,
RCTRL = 305,
LCTRL = 306,
RALT = 307,
LALT = 308,
RMETA = 309,
LMETA = 310,
LSUPER = 311,
RSUPER = 312,
MODE = 313,
COMPOSE = 314,
HELP = 315,
PRINT = 316,
SYSREQ = 317,
BREAK = 318,
MENU = 319,
POWER = 320,
EURO = 321,
UNDO = 322,
LAST
}
public static class KeycodeExts
{
static readonly Dictionary<Keycode, string> KeyNames = new Dictionary<Keycode, string>
{
{ Keycode.UNKNOWN, "unknown" },
{ Keycode.BACKSPACE, "backspace" },
{ Keycode.TAB, "tab" },
{ Keycode.CLEAR, "clear" },
{ Keycode.RETURN, "return" },
{ Keycode.PAUSE, "pause" },
{ Keycode.ESCAPE, "escape" },
{ Keycode.SPACE, "space" },
{ Keycode.EXCLAIM, "!" },
{ Keycode.QUOTEDBL, "\"" },
{ Keycode.HASH, "#" },
{ Keycode.DOLLAR, "$" },
{ Keycode.AMPERSAND, "&" },
{ Keycode.QUOTE, "'" },
{ Keycode.LEFTPAREN, "(" },
{ Keycode.RIGHTPAREN, ")" },
{ Keycode.ASTERISK, "*" },
{ Keycode.PLUS, "+" },
{ Keycode.COMMA, "," },
{ Keycode.MINUS, "-" },
{ Keycode.PERIOD, "." },
{ Keycode.SLASH, "/" },
{ Keycode.NUMBER_0, "0" },
{ Keycode.NUMBER_1, "1" },
{ Keycode.NUMBER_2, "2" },
{ Keycode.NUMBER_3, "3" },
{ Keycode.NUMBER_4, "4" },
{ Keycode.NUMBER_5, "5" },
{ Keycode.NUMBER_6, "6" },
{ Keycode.NUMBER_7, "7" },
{ Keycode.NUMBER_8, "8" },
{ Keycode.NUMBER_9, "9" },
{ Keycode.COLON, ":" },
{ Keycode.SEMICOLON, " }," },
{ Keycode.LESS, "<" },
{ Keycode.EQUALS, "=" },
{ Keycode.GREATER, ">" },
{ Keycode.QUESTION, "?" },
{ Keycode.AT, "@" },
{ Keycode.LEFTBRACKET, "[" },
{ Keycode.BACKSLASH, "\\" },
{ Keycode.RIGHTBRACKET, "]" },
{ Keycode.CARET, "^" },
{ Keycode.UNDERSCORE, "_" },
{ Keycode.BACKQUOTE, "`" },
{ Keycode.A, "a" },
{ Keycode.B, "b" },
{ Keycode.C, "c" },
{ Keycode.D, "d" },
{ Keycode.E, "e" },
{ Keycode.F, "f" },
{ Keycode.G, "g" },
{ Keycode.H, "h" },
{ Keycode.I, "i" },
{ Keycode.J, "j" },
{ Keycode.K, "k" },
{ Keycode.L, "l" },
{ Keycode.M, "m" },
{ Keycode.N, "n" },
{ Keycode.O, "o" },
{ Keycode.P, "p" },
{ Keycode.Q, "q" },
{ Keycode.R, "r" },
{ Keycode.S, "s" },
{ Keycode.T, "t" },
{ Keycode.U, "u" },
{ Keycode.V, "v" },
{ Keycode.W, "w" },
{ Keycode.X, "x" },
{ Keycode.Y, "y" },
{ Keycode.Z, "z" },
{ Keycode.DELETE, "delete" },
{ Keycode.WORLD_0, "world 0" },
{ Keycode.WORLD_1, "world 1" },
{ Keycode.WORLD_2, "world 2" },
{ Keycode.WORLD_3, "world 3" },
{ Keycode.WORLD_4, "world 4" },
{ Keycode.WORLD_5, "world 5" },
{ Keycode.WORLD_6, "world 6" },
{ Keycode.WORLD_7, "world 7" },
{ Keycode.WORLD_8, "world 8" },
{ Keycode.WORLD_9, "world 9" },
{ Keycode.WORLD_10, "world 10" },
{ Keycode.WORLD_11, "world 11" },
{ Keycode.WORLD_12, "world 12" },
{ Keycode.WORLD_13, "world 13" },
{ Keycode.WORLD_14, "world 14" },
{ Keycode.WORLD_15, "world 15" },
{ Keycode.WORLD_16, "world 16" },
{ Keycode.WORLD_17, "world 17" },
{ Keycode.WORLD_18, "world 18" },
{ Keycode.WORLD_19, "world 19" },
{ Keycode.WORLD_20, "world 20" },
{ Keycode.WORLD_21, "world 21" },
{ Keycode.WORLD_22, "world 22" },
{ Keycode.WORLD_23, "world 23" },
{ Keycode.WORLD_24, "world 24" },
{ Keycode.WORLD_25, "world 25" },
{ Keycode.WORLD_26, "world 26" },
{ Keycode.WORLD_27, "world 27" },
{ Keycode.WORLD_28, "world 28" },
{ Keycode.WORLD_29, "world 29" },
{ Keycode.WORLD_30, "world 30" },
{ Keycode.WORLD_31, "world 31" },
{ Keycode.WORLD_32, "world 32" },
{ Keycode.WORLD_33, "world 33" },
{ Keycode.WORLD_34, "world 34" },
{ Keycode.WORLD_35, "world 35" },
{ Keycode.WORLD_36, "world 36" },
{ Keycode.WORLD_37, "world 37" },
{ Keycode.WORLD_38, "world 38" },
{ Keycode.WORLD_39, "world 39" },
{ Keycode.WORLD_40, "world 40" },
{ Keycode.WORLD_41, "world 41" },
{ Keycode.WORLD_42, "world 42" },
{ Keycode.WORLD_43, "world 43" },
{ Keycode.WORLD_44, "world 44" },
{ Keycode.WORLD_45, "world 45" },
{ Keycode.WORLD_46, "world 46" },
{ Keycode.WORLD_47, "world 47" },
{ Keycode.WORLD_48, "world 48" },
{ Keycode.WORLD_49, "world 49" },
{ Keycode.WORLD_50, "world 50" },
{ Keycode.WORLD_51, "world 51" },
{ Keycode.WORLD_52, "world 52" },
{ Keycode.WORLD_53, "world 53" },
{ Keycode.WORLD_54, "world 54" },
{ Keycode.WORLD_55, "world 55" },
{ Keycode.WORLD_56, "world 56" },
{ Keycode.WORLD_57, "world 57" },
{ Keycode.WORLD_58, "world 58" },
{ Keycode.WORLD_59, "world 59" },
{ Keycode.WORLD_60, "world 60" },
{ Keycode.WORLD_61, "world 61" },
{ Keycode.WORLD_62, "world 62" },
{ Keycode.WORLD_63, "world 63" },
{ Keycode.WORLD_64, "world 64" },
{ Keycode.WORLD_65, "world 65" },
{ Keycode.WORLD_66, "world 66" },
{ Keycode.WORLD_67, "world 67" },
{ Keycode.WORLD_68, "world 68" },
{ Keycode.WORLD_69, "world 69" },
{ Keycode.WORLD_70, "world 70" },
{ Keycode.WORLD_71, "world 71" },
{ Keycode.WORLD_72, "world 72" },
{ Keycode.WORLD_73, "world 73" },
{ Keycode.WORLD_74, "world 74" },
{ Keycode.WORLD_75, "world 75" },
{ Keycode.WORLD_76, "world 76" },
{ Keycode.WORLD_77, "world 77" },
{ Keycode.WORLD_78, "world 78" },
{ Keycode.WORLD_79, "world 79" },
{ Keycode.WORLD_80, "world 80" },
{ Keycode.WORLD_81, "world 81" },
{ Keycode.WORLD_82, "world 82" },
{ Keycode.WORLD_83, "world 83" },
{ Keycode.WORLD_84, "world 84" },
{ Keycode.WORLD_85, "world 85" },
{ Keycode.WORLD_86, "world 86" },
{ Keycode.WORLD_87, "world 87" },
{ Keycode.WORLD_88, "world 88" },
{ Keycode.WORLD_89, "world 89" },
{ Keycode.WORLD_90, "world 90" },
{ Keycode.WORLD_91, "world 91" },
{ Keycode.WORLD_92, "world 92" },
{ Keycode.WORLD_93, "world 93" },
{ Keycode.WORLD_94, "world 94" },
{ Keycode.WORLD_95, "world 95" },
{ Keycode.KP0, "[0]" },
{ Keycode.KP1, "[1]" },
{ Keycode.KP2, "[2]" },
{ Keycode.KP3, "[3]" },
{ Keycode.KP4, "[4]" },
{ Keycode.KP5, "[5]" },
{ Keycode.KP6, "[6]" },
{ Keycode.KP7, "[7]" },
{ Keycode.KP8, "[8]" },
{ Keycode.KP9, "[9]" },
{ Keycode.KP_PERIOD, "[.]" },
{ Keycode.KP_DIVIDE, "[/]" },
{ Keycode.KP_MULTIPLY, "[*]" },
{ Keycode.KP_MINUS, "[-]" },
{ Keycode.KP_PLUS, "[+]" },
{ Keycode.KP_ENTER, "enter" },
{ Keycode.KP_EQUALS, "equals" },
{ Keycode.UP, "up" },
{ Keycode.DOWN, "down" },
{ Keycode.RIGHT, "right" },
{ Keycode.LEFT, "left" },
{ Keycode.INSERT, "insert" },
{ Keycode.HOME, "home" },
{ Keycode.END, "end" },
{ Keycode.PAGEUP, "page up" },
{ Keycode.PAGEDOWN, "page down" },
{ Keycode.F1, "f1" },
{ Keycode.F2, "f2" },
{ Keycode.F3, "f3" },
{ Keycode.F4, "f4" },
{ Keycode.F5, "f5" },
{ Keycode.F6, "f6" },
{ Keycode.F7, "f7" },
{ Keycode.F8, "f8" },
{ Keycode.F9, "f9" },
{ Keycode.F10, "f10" },
{ Keycode.F11, "f11" },
{ Keycode.F12, "f12" },
{ Keycode.F13, "f13" },
{ Keycode.F14, "f14" },
{ Keycode.F15, "f15" },
{ Keycode.NUMLOCK, "numlock" },
{ Keycode.CAPSLOCK, "caps lock" },
{ Keycode.SCROLLOCK, "scroll lock" },
{ Keycode.RSHIFT, "right shift" },
{ Keycode.LSHIFT, "left shift" },
{ Keycode.RCTRL, "right ctrl" },
{ Keycode.LCTRL, "left ctrl" },
{ Keycode.RALT, "right alt" },
{ Keycode.LALT, "left alt" },
{ Keycode.RMETA, "right meta" },
{ Keycode.LMETA, "left meta" },
{ Keycode.LSUPER, "left super" },
{ Keycode.RSUPER, "right super" },
{ Keycode.MODE, "alt gr" },
{ Keycode.COMPOSE, "compose" },
{ Keycode.HELP, "help" },
{ Keycode.PRINT, "print screen" },
{ Keycode.SYSREQ, "sys req" },
{ Keycode.BREAK, "break" },
{ Keycode.MENU, "menu" },
{ Keycode.POWER, "power" },
{ Keycode.EURO, "euro" },
{ Keycode.UNDO, "undo" }
};
public static string DisplayString(Keycode k)
{
var ret = "unknown";
KeyNames.TryGetValue(k, out ret);
return ret;
}
}
}

View File

@@ -21,7 +21,7 @@ namespace OpenRA.FileFormats
public readonly string[]
Mods, Folders, Rules, ServerTraits,
Sequences, VoxelSequences, Cursors, Chrome, Assemblies, ChromeLayout,
Weapons, Voices, Notifications, Music, Movies, TileSets,
Weapons, Voices, Notifications, Music, Movies, Translations, TileSets,
ChromeMetrics, PackageContents;
public readonly Dictionary<string, string> Packages;
@@ -53,6 +53,7 @@ namespace OpenRA.FileFormats
Notifications = YamlList(yaml, "Notifications");
Music = YamlList(yaml, "Music");
Movies = YamlList(yaml, "Movies");
Translations = YamlList(yaml, "Translations");
TileSets = YamlList(yaml, "TileSets");
ChromeMetrics = YamlList(yaml, "ChromeMetrics");
PackageContents = YamlList(yaml, "PackageContents");

View File

@@ -80,6 +80,7 @@
<Compile Include="Evaluator.cs" />
<Compile Include="Exts.cs" />
<Compile Include="FieldLoader.cs" />
<Compile Include="FieldSaver.cs" />
<Compile Include="FileFormats\AudLoader.cs" />
<Compile Include="FileFormats\Blast.cs" />
<Compile Include="FileFormats\Blowfish.cs" />
@@ -120,7 +121,10 @@
<Compile Include="Primitives\Cache.cs" />
<Compile Include="Primitives\Cached.cs" />
<Compile Include="Primitives\DisposableAction.cs" />
<Compile Include="Primitives\IObservableCollection.cs" />
<Compile Include="Primitives\Lazy.cs" />
<Compile Include="Primitives\ObservableCollection.cs" />
<Compile Include="Primitives\ObservableDictionary.cs" />
<Compile Include="Primitives\Pair.cs" />
<Compile Include="Primitives\PriorityQueue.cs" />
<Compile Include="Primitives\Set.cs" />
@@ -147,6 +151,8 @@
<Compile Include="Filesystem\D2kSoundResources.cs" />
<Compile Include="Graphics\R8Reader.cs" />
<Compile Include="Graphics\TileSetRenderer.cs" />
<Compile Include="Keycode.cs" />
<Compile Include="Hotkey.cs" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">

View File

@@ -11,6 +11,7 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using OpenRA.FileFormats;
namespace OpenRA
@@ -45,6 +46,22 @@ namespace OpenRA
return PlatformType.Unknown;
}
public static string RuntimeVersion
{
get
{
var mono = Type.GetType("Mono.Runtime");
if (mono == null)
return ".NET CLR {0}".F(Environment.Version);
var version = mono.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static);
if (version == null)
return "Mono (unknown version) CLR {0}".F(Environment.Version);
return "Mono {0} CLR {1}".F(version.Invoke(null, null), Environment.Version);
}
}
public static string SupportDir
{
get

View File

@@ -36,7 +36,7 @@ namespace OpenRA.FileFormats
while (!actions.Empty && actions.Peek().Time <= t)
{
var da = actions.Pop();
a += da.Action;
a = da.Action + a;
}
}
a();

View File

@@ -0,0 +1,25 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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 COPYING.
*/
#endregion
using System;
using System.Collections;
namespace OpenRA.FileFormats.Primitives
{
public interface IObservableCollection
{
event Action<object> OnAdd;
event Action<object> OnRemove;
event Action<int> OnRemoveAt;
event Action<object, object> OnSet;
event Action OnRefresh;
IEnumerable ObservedItems { get; }
}
}

View File

@@ -0,0 +1,59 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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 COPYING.
*/
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace OpenRA.FileFormats.Primitives
{
public class ObservableCollection<T> : Collection<T>, IObservableCollection
{
public event Action<object> OnAdd = k => { };
public event Action<object> OnRemove = k => { };
public event Action<int> OnRemoveAt = i => { };
public event Action<object, object> OnSet = (o, n) => { };
public event Action OnRefresh = () => { };
public ObservableCollection() : base() { }
public ObservableCollection(IList<T> list) : base(list) { }
protected override void SetItem(int index, T item)
{
var old = this[index];
base.SetItem(index, item);
OnSet(old, item);
}
protected override void InsertItem(int index, T item)
{
base.InsertItem(index, item);
OnAdd(item);
}
protected override void ClearItems()
{
base.ClearItems();
OnRefresh();
}
protected override void RemoveItem(int index)
{
base.RemoveItem(index);
OnRemoveAt(index);
}
public IEnumerable ObservedItems
{
get { return base.Items; }
}
}
}

View File

@@ -0,0 +1,137 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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 COPYING.
*/
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
namespace OpenRA.FileFormats.Primitives
{
public class ObservableSortedDictionary<TKey, TValue> : ObservableDictionary<TKey, TValue>
{
public ObservableSortedDictionary(IComparer<TKey> comparer)
{
InnerDict = new SortedDictionary<TKey, TValue>(comparer);
}
public override void Add(TKey key, TValue value)
{
InnerDict.Add(key, value);
FireOnRefresh();
}
}
public class ObservableDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IObservableCollection
{
protected IDictionary<TKey, TValue> InnerDict;
public event Action<object> OnAdd = k => { };
public event Action<object> OnRemove = k => { };
public event Action<int> OnRemoveAt = i => { };
public event Action<object, object> OnSet = (o, n) => { };
public event Action OnRefresh = () => { };
protected void FireOnRefresh()
{
OnRefresh();
}
protected ObservableDictionary() { }
public ObservableDictionary(IEqualityComparer<TKey> comparer)
{
InnerDict = new Dictionary<TKey, TValue>(comparer);
}
public virtual void Add(TKey key, TValue value)
{
InnerDict.Add(key, value);
OnAdd(key);
}
public bool Remove(TKey key)
{
var found = InnerDict.Remove(key);
if (found)
OnRemove(key);
return found;
}
public bool ContainsKey(TKey key)
{
return InnerDict.ContainsKey(key);
}
public ICollection<TKey> Keys { get { return InnerDict.Keys; } }
public ICollection<TValue> Values { get { return InnerDict.Values; } }
public bool TryGetValue(TKey key, out TValue value)
{
return InnerDict.TryGetValue(key, out value);
}
public TValue this[TKey key]
{
get { return InnerDict[key]; }
set { InnerDict[key] = value; }
}
public void Clear()
{
InnerDict.Clear();
OnRefresh();
}
public int Count
{
get { return InnerDict.Count; }
}
public void Add(KeyValuePair<TKey, TValue> item)
{
Add(item.Key, item.Value);
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
return InnerDict.Contains(item);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
InnerDict.CopyTo(array, arrayIndex);
}
public bool IsReadOnly
{
get { return InnerDict.IsReadOnly; }
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
return Remove(item.Key);
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return InnerDict.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return InnerDict.GetEnumerator();
}
public IEnumerable ObservedItems
{
get { return InnerDict.Keys; }
}
}
}

View File

@@ -24,17 +24,14 @@ namespace OpenRA
public readonly World World;
public readonly uint ActorID;
public Lazy<Rectangle> Bounds;
Lazy<IOccupySpace> occupySpace;
Lazy<IFacing> facing;
public Cached<Rectangle> Bounds;
public Cached<Rectangle> ExtendedBounds;
public IOccupySpace OccupiesSpace { get { return occupySpace.Value; } }
public CPos Location { get { return occupySpace.Value.TopLeft; } }
public PPos CenterLocation { get { return PPos.FromWPos(occupySpace.Value.CenterPosition); } }
public WPos CenterPosition { get { return occupySpace.Value.CenterPosition; } }
public WRot Orientation
@@ -76,27 +73,25 @@ namespace OpenRA
facing = Lazy.New(() => TraitOrDefault<IFacing>());
size = Lazy.New(() =>
{
var si = Info.Traits.GetOrDefault<SelectableInfo>();
if (si != null && si.Bounds != null)
return new int2(si.Bounds[0], si.Bounds[1]);
return TraitsImplementing<IAutoSelectionSize>().Select(x => x.SelectionSize(this)).FirstOrDefault();
});
applyIRender = (x, wr) => x.Render(this, wr);
applyRenderModifier = (m, p, wr) => p.ModifyRender(this, wr, m);
Bounds = Cached.New(() => CalculateBounds(false));
ExtendedBounds = Cached.New(() => CalculateBounds(true));
Bounds = Lazy.New(() =>
{
var si = Info.Traits.GetOrDefault<SelectableInfo>();
var size = (si != null && si.Bounds != null) ? new int2(si.Bounds[0], si.Bounds[1]) :
TraitsImplementing<IAutoSelectionSize>().Select(x => x.SelectionSize(this)).FirstOrDefault();
var offset = -size / 2;
if (si != null && si.Bounds != null && si.Bounds.Length > 2)
offset += new int2(si.Bounds[2], si.Bounds[3]);
return new Rectangle(offset.X, offset.Y, size.X, size.Y);
});
}
public void Tick()
{
Bounds.Invalidate();
ExtendedBounds.Invalidate();
currentActivity = Traits.Util.RunActivity(this, currentActivity);
}
@@ -105,8 +100,6 @@ namespace OpenRA
get { return currentActivity == null; }
}
OpenRA.FileFormats.Lazy<int2> size;
// note: these delegates are cached to avoid massive allocation.
Func<IRender, WorldRenderer, IEnumerable<IRenderable>> applyIRender;
Func<IEnumerable<IRenderable>, IRenderModifier, WorldRenderer, IEnumerable<IRenderable>> applyRenderModifier;
@@ -117,34 +110,6 @@ namespace OpenRA
return mods.Aggregate(sprites, (m, p) => applyRenderModifier(m, p, wr));
}
// When useAltitude = true, the bounding box is extended
// vertically to altitude = 0 to support FindUnitsInCircle queries
// When false, the bounding box is given for the actor
// at its current altitude
Rectangle CalculateBounds(bool useAltitude)
{
var sizeVector = (PVecInt)size.Value;
var loc = CenterLocation - sizeVector / 2;
var si = Info.Traits.GetOrDefault<SelectableInfo>();
if (si != null && si.Bounds != null && si.Bounds.Length > 2)
{
loc += new PVecInt(si.Bounds[2], si.Bounds[3]);
}
var ios = occupySpace.Value;
if (ios != null)
{
var altitude = ios.CenterPosition.Z * Game.CellSize / 1024;
loc -= new PVecInt(0, altitude);
if (useAltitude)
sizeVector = new PVecInt(sizeVector.X, sizeVector.Y + altitude);
}
return new Rectangle(loc.X, loc.Y, sizeVector.X, sizeVector.Y);
}
public bool IsInWorld { get; internal set; }
public void QueueActivity(bool queued, Activity nextActivity)

View File

@@ -10,6 +10,7 @@
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Traits;
namespace OpenRA
{
@@ -79,12 +80,12 @@ namespace OpenRA
public SubCell Value(World world) { return (SubCell)value; }
}
public class CenterLocationInit : IActorInit<PPos>
public class CenterPositionInit : IActorInit<WPos>
{
[FieldFromYamlKey] public readonly int2 value = int2.Zero;
public CenterLocationInit() { }
public CenterLocationInit(PPos init) { value = init.ToInt2(); }
public PPos Value(World world) { return (PPos)value; }
[FieldFromYamlKey] public readonly WPos value = WPos.Zero;
public CenterPositionInit() { }
public CenterPositionInit(WPos init) { value = init; }
public WPos Value(World world) { return value; }
}
public class OwnerInit : IActorInit<Player>

View File

@@ -1,122 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 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 COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA
{
public enum SubCell { FullCell, TopLeft, TopRight, Center, BottomLeft, BottomRight }
public class ActorMap
{
class InfluenceNode
{
public InfluenceNode next;
public SubCell subCell;
public Actor actor;
}
InfluenceNode[,] influence;
Map map;
public ActorMap( World world )
{
map = world.Map;
influence = new InfluenceNode[world.Map.MapSize.X, world.Map.MapSize.Y];
world.ActorAdded += a => Add( a, a.OccupiesSpace );
world.ActorRemoved += a => Remove( a, a.OccupiesSpace );
}
public IEnumerable<Actor> GetUnitsAt(CPos a)
{
if (!map.IsInMap(a)) yield break;
for( var i = influence[ a.X, a.Y ] ; i != null ; i = i.next )
if (!i.actor.Destroyed)
yield return i.actor;
}
public IEnumerable<Actor> GetUnitsAt(CPos a, SubCell sub)
{
if (!map.IsInMap(a)) yield break;
for( var i = influence[ a.X, a.Y ] ; i != null ; i = i.next )
if (!i.actor.Destroyed && (i.subCell == sub || i.subCell == SubCell.FullCell))
yield return i.actor;
}
public bool HasFreeSubCell(CPos a)
{
if (!AnyUnitsAt(a))
return true;
return new[]{ SubCell.TopLeft, SubCell.TopRight, SubCell.Center,
SubCell.BottomLeft, SubCell.BottomRight }.Any(b => !AnyUnitsAt(a,b));
}
public SubCell? FreeSubCell(CPos a)
{
if (!HasFreeSubCell(a))
return null;
return new[]{ SubCell.TopLeft, SubCell.TopRight, SubCell.Center,
SubCell.BottomLeft, SubCell.BottomRight }.First(b => !AnyUnitsAt(a,b));
}
public bool AnyUnitsAt(CPos a)
{
return influence[ a.X, a.Y ] != null;
}
public bool AnyUnitsAt(CPos a, SubCell sub)
{
for( var i = influence[ a.X, a.Y ] ; i != null ; i = i.next )
if (i.subCell == sub || i.subCell == SubCell.FullCell)
return true;
return false;
}
public void Add( Actor self, IOccupySpace unit )
{
if (unit != null)
foreach( var c in unit.OccupiedCells() )
influence[ c.First.X, c.First.Y ] = new InfluenceNode { next = influence[ c.First.X, c.First.Y ], subCell = c.Second, actor = self };
}
public void Remove( Actor self, IOccupySpace unit )
{
if (unit != null)
foreach (var c in unit.OccupiedCells())
RemoveInner( ref influence[ c.First.X, c.First.Y ], self );
}
void RemoveInner( ref InfluenceNode influenceNode, Actor toRemove )
{
if( influenceNode == null )
return;
else if( influenceNode.actor == toRemove )
influenceNode = influenceNode.next;
if (influenceNode != null)
RemoveInner( ref influenceNode.next, toRemove );
}
public void Update(Actor self, IOccupySpace unit)
{
Remove(self, unit);
if (!self.IsDead()) Add(self, unit);
}
}
}

View File

@@ -39,7 +39,6 @@ namespace OpenRA
public float2 ToFloat2() { return new float2(X, Y); }
public int2 ToInt2() { return new int2(X, Y); }
public PPos ToPPos() { return new PPos(Game.CellSize * X, Game.CellSize * Y); }
public WPos CenterPosition { get { return new WPos(1024 * X + 512, 1024 * Y + 512, 0); } }
public WPos TopLeft { get { return new WPos(1024 * X, 1024 * Y, 0); } }

View File

@@ -19,33 +19,13 @@ namespace OpenRA
WebClient wc;
bool cancelled;
public Download(string url, string path, Action<DownloadProgressChangedEventArgs> onProgress, Action<AsyncCompletedEventArgs, bool> onComplete)
{
wc = new WebClient();
wc.Proxy = null;
wc.DownloadProgressChanged += (_,a) => onProgress(a);
wc.DownloadFileCompleted += (_,a) => onComplete(a, cancelled);
Game.OnQuit += () => Cancel();
wc.DownloadFileCompleted += (_,a) => {Game.OnQuit -= () => Cancel();};
wc.DownloadFileAsync(new Uri(url), path);
}
public void Cancel()
{
Game.OnQuit -= () => Cancel();
wc.CancelAsync();
cancelled = true;
}
public static string FormatErrorMessage(Exception e)
{
var ex = e as System.Net.WebException;
if (ex == null) return e.Message;
if (ex == null)
return e.Message;
switch(ex.Status)
switch (ex.Status)
{
case WebExceptionStatus.NameResolutionFailure:
case WebExceptionStatus.Timeout:
@@ -57,5 +37,26 @@ namespace OpenRA
return ex.Message;
}
}
public Download(string url, string path, Action<DownloadProgressChangedEventArgs> onProgress, Action<AsyncCompletedEventArgs, bool> onComplete)
{
wc = new WebClient();
wc.Proxy = null;
wc.DownloadProgressChanged += (_, a) => onProgress(a);
wc.DownloadFileCompleted += (_, a) => onComplete(a, cancelled);
Game.OnQuit += () => Cancel();
wc.DownloadFileCompleted += (_, a) => { Game.OnQuit -= () => Cancel(); };
wc.DownloadFileAsync(new Uri(url), path);
}
public void Cancel()
{
Game.OnQuit -= () => Cancel();
wc.CancelAsync();
cancelled = true;
}
}
}

View File

@@ -32,10 +32,8 @@ namespace OpenRA
public static MouseButtonPreference mouseButtonPreference = new MouseButtonPreference();
public static ModData modData;
static WorldRenderer worldRenderer;
public static Viewport viewport;
public static Settings Settings;
static WorldRenderer worldRenderer;
internal static OrderManager orderManager;
static Server.Server server;
@@ -45,14 +43,9 @@ namespace OpenRA
public static Renderer Renderer;
public static bool HasInputFocus = false;
public static void MoveViewport(float2 loc)
public static void JoinServer(string host, int port, string password)
{
viewport.Center(loc);
}
public static void JoinServer(string host, int port)
{
JoinInner(new OrderManager(host, port,
JoinInner(new OrderManager(host, port, password,
new ReplayRecorderConnection(new NetworkConnection(host, port), ChooseReplayFilename)));
}
@@ -71,18 +64,18 @@ namespace OpenRA
public static void JoinReplay(string replayFile)
{
JoinInner(new OrderManager("<no server>", -1, new ReplayConnection(replayFile)));
JoinInner(new OrderManager("<no server>", -1, "", new ReplayConnection(replayFile)));
}
static void JoinLocal()
{
JoinInner(new OrderManager("<no server>", -1, new EchoConnection()));
JoinInner(new OrderManager("<no server>", -1, "", new EchoConnection()));
}
public static int RenderFrame = 0;
public static int NetFrameNumber { get { return orderManager.NetFrameNumber; } }
public static int LocalTick { get { return orderManager.LocalFrameNumber; } }
const int NetTickScale = 3; // 120ms net tick for 40ms local tick
public const int NetTickScale = 3; // 120ms net tick for 40ms local tick
public static event Action<OrderManager> ConnectionStateChanged = _ => { };
static ConnectionState lastConnectionState = ConnectionState.PreConnecting;
@@ -122,7 +115,8 @@ namespace OpenRA
public static void RunAfterTick(Action a) { delayedActions.Add(a); }
public static void RunAfterDelay(int delay, Action a) { delayedActions.Add(a, delay); }
static void Tick(OrderManager orderManager, Viewport viewPort)
static float cursorFrame = 0f;
static void Tick(OrderManager orderManager)
{
if (orderManager.Connection.ConnectionState != lastConnectionState)
{
@@ -130,15 +124,35 @@ namespace OpenRA
ConnectionStateChanged(orderManager);
}
Tick(orderManager);
TickInner(orderManager);
if (worldRenderer != null && orderManager.world != worldRenderer.world)
Tick(worldRenderer.world.orderManager);
TickInner(worldRenderer.world.orderManager);
using (new PerfSample("render"))
{
++RenderFrame;
viewport.DrawRegions(worldRenderer, new DefaultInputHandler( orderManager.world ));
Sound.SetListenerPosition(viewport.CenterLocation);
// worldRenderer is null during the initial install/download screen
if (worldRenderer != null)
{
Game.Renderer.BeginFrame(worldRenderer.Viewport.TopLeft.ToFloat2(), worldRenderer.Viewport.Zoom);
Sound.SetListenerPosition(worldRenderer.Position(worldRenderer.Viewport.CenterLocation));
worldRenderer.Draw();
}
else
Game.Renderer.BeginFrame(float2.Zero, 1f);
using (new PerfSample("render_widgets"))
{
Ui.Draw();
var cursorName = Ui.Root.GetCursorOuter(Viewport.LastMousePos) ?? "default";
CursorProvider.DrawCursor(Game.Renderer, cursorName, Viewport.LastMousePos, (int)cursorFrame);
}
using (new PerfSample("render_flip"))
{
Game.Renderer.EndFrame(new DefaultInputHandler(orderManager.world));
}
}
PerfHistory.items["render"].Tick();
@@ -149,7 +163,7 @@ namespace OpenRA
delayedActions.PerformActions();
}
static void Tick(OrderManager orderManager)
static void TickInner(OrderManager orderManager)
{
int t = Environment.TickCount;
int dt = t - orderManager.LastTickTime;
@@ -161,6 +175,7 @@ namespace OpenRA
var world = orderManager.world;
if (orderManager.GameStarted)
++Viewport.TicksSinceLastMove;
Sound.Tick();
Sync.CheckSyncUnchanged(world, orderManager.TickImmediate);
@@ -192,7 +207,8 @@ namespace OpenRA
orderManager.LastTickTime = Environment.TickCount;
Sync.CheckSyncUnchanged(world, () => world.TickRender(worldRenderer));
viewport.Tick();
cursorFrame += 0.5f;
}
}
}
@@ -210,11 +226,13 @@ namespace OpenRA
BeforeGameStart();
var map = modData.PrepareMap(mapUID);
viewport = new Viewport(new int2(Renderer.Resolution), map.Bounds, Renderer);
orderManager.world = new World(modData.Manifest, map, orderManager, isShellmap);
worldRenderer = new WorldRenderer(orderManager.world);
orderManager.world.LoadComplete(worldRenderer);
if (orderManager.GameStarted)
return;
if (orderManager.GameStarted) return;
Ui.MouseFocusWidget = null;
Ui.KeyboardFocusWidget = null;
@@ -323,7 +341,6 @@ namespace OpenRA
PerfHistory.items["render_flip"].hasNormalTick = false;
JoinLocal();
viewport = new Viewport(new int2(Renderer.Resolution), Rectangle.Empty, Renderer);
if (Game.Settings.Server.Dedicated)
{
@@ -387,7 +404,7 @@ namespace OpenRA
var idealFrameTime = 1.0 / Settings.Graphics.MaxFramerate;
var sw = new Stopwatch();
Tick( orderManager, viewport );
Tick(orderManager);
if (Settings.Graphics.CapFramerate)
{

View File

@@ -28,6 +28,7 @@ namespace OpenRA.GameRules
public int ListenPort = 1234;
public int ExternalPort = 1234;
public bool AdvertiseOnline = true;
public string Password = "";
public string MasterServer = "http://master.open-ra.org/";
public bool DiscoverNatDevices = false; // Allow users to disable NAT discovery if problems occur
public bool AllowPortForward = true; // let the user disable it even if compatible devices are found
@@ -51,6 +52,7 @@ namespace OpenRA.GameRules
ListenPort = other.ListenPort;
ExternalPort = other.ExternalPort;
AdvertiseOnline = other.AdvertiseOnline;
Password = other.Password;
MasterServer = other.MasterServer;
DiscoverNatDevices = other.DiscoverNatDevices;
AllowPortForward = other.AllowPortForward;
@@ -96,6 +98,9 @@ namespace OpenRA.GameRules
public int BatchSize = 8192;
public int NumTempBuffers = 8;
public int SheetSize = 2048;
public string Language = "english";
public string DefaultLanguage = "english";
}
public class SoundSettings
@@ -143,25 +148,40 @@ namespace OpenRA.GameRules
public class KeySettings
{
public string CycleBaseKey = "backspace";
public string ToLastEventKey = "space";
public string ToSelectionKey = "home";
public Hotkey CycleBaseKey = new Hotkey(Keycode.BACKSPACE, Modifiers.None);
public Hotkey ToLastEventKey = new Hotkey(Keycode.SPACE, Modifiers.None);
public Hotkey ToSelectionKey = new Hotkey(Keycode.HOME, Modifiers.None);
public string PauseKey = "f9";
public string SellKey = "f10";
public string PowerDownKey = "f11";
public string RepairKey = "f12";
public Hotkey PauseKey = new Hotkey(Keycode.F9, Modifiers.None);
public Hotkey SellKey = new Hotkey(Keycode.F10, Modifiers.None);
public Hotkey PowerDownKey = new Hotkey(Keycode.F11, Modifiers.None);
public Hotkey RepairKey = new Hotkey(Keycode.F12, Modifiers.None);
public string AttackMoveKey = "a";
public string StopKey = "s";
public string ScatterKey = "x";
public string DeployKey = "f";
public string StanceCycleKey = "z";
public string GuardKey = "d";
public Hotkey AttackMoveKey = new Hotkey(Keycode.A, Modifiers.None);
public Hotkey StopKey = new Hotkey(Keycode.S, Modifiers.None);
public Hotkey ScatterKey = new Hotkey(Keycode.X, Modifiers.None);
public Hotkey DeployKey = new Hotkey(Keycode.F, Modifiers.None);
public Hotkey StanceCycleKey = new Hotkey(Keycode.Z, Modifiers.None);
public Hotkey GuardKey = new Hotkey(Keycode.D, Modifiers.None);
public string CycleTabsKey = "tab";
public Hotkey CycleTabsKey = new Hotkey(Keycode.TAB, Modifiers.None);
}
public class IrcSettings
{
public string Hostname = "irc.open-ra.org";
public int Port = 6667;
public string Nickname = null;
public string Username = "openra";
public string Realname = null;
public string DefaultNickname = "Newbie";
public string Channel = "global";
public string TimestampFormat = "HH:mm:ss";
public int ReconnectDelay = 10000;
public int ConnectionTimeout = 300000;
public bool Debug = false;
public bool ConnectAutomatically = false;
}
public class Settings
{
@@ -174,6 +194,7 @@ namespace OpenRA.GameRules
public ServerSettings Server = new ServerSettings();
public DebugSettings Debug = new DebugSettings();
public KeySettings Keys = new KeySettings();
public IrcSettings Irc = new IrcSettings();
public Dictionary<string, object> Sections;
@@ -189,6 +210,7 @@ namespace OpenRA.GameRules
{"Server", Server},
{"Debug", Debug},
{"Keys", Keys},
{"Irc", Irc}
};
// Override fieldloader to ignore invalid entries

View File

@@ -49,20 +49,10 @@ namespace OpenRA.Graphics
var src = wr.ScreenPosition(pos);
var dest = wr.ScreenPosition(pos + length);
var lineWidth = wlr.LineWidth;
if (lineWidth != width)
{
wlr.Flush();
wlr.LineWidth = width;
}
var oldWidth = wlr.LineWidth;
wlr.LineWidth = wr.Viewport.Zoom * width;
wlr.DrawLine(src, dest, color, color);
if (lineWidth != width)
{
wlr.Flush();
wlr.LineWidth = lineWidth;
}
wlr.LineWidth = oldWidth;
}
public void RenderDebugGeometry(WorldRenderer wr) {}

View File

@@ -58,9 +58,13 @@ namespace OpenRA.Graphics
public void Render(WorldRenderer wr)
{
// Need at least 4 points to smooth the contrail over
if (length - skip < 4 )
if (length - skip < 4)
return;
var wlr = Game.Renderer.WorldLineRenderer;
var oldWidth = wlr.LineWidth;
wlr.LineWidth = wr.Viewport.Zoom;
// Start of the first line segment is the tail of the list - don't smooth it.
var curPos = trail[idx(next - skip - 1)];
var curCell = curPos.ToCPos();
@@ -73,12 +77,14 @@ namespace OpenRA.Graphics
var nextColor = Exts.ColorLerp(i * 1f / (length - 4), color, Color.Transparent);
if (!world.FogObscures(curCell) && !world.FogObscures(nextCell))
Game.Renderer.WorldLineRenderer.DrawLine(wr.ScreenPosition(curPos), wr.ScreenPosition(nextPos), curColor, nextColor);
wlr.DrawLine(wr.ScreenPosition(curPos), wr.ScreenPosition(nextPos), curColor, nextColor);
curPos = nextPos;
curCell = nextCell;
curColor = nextColor;
}
wlr.LineWidth = oldWidth;
}
public void RenderDebugGeometry(WorldRenderer wr) {}

View File

@@ -15,9 +15,8 @@ namespace OpenRA.Graphics
{
public class LineRenderer : Renderer.IBatchRenderer
{
public float LineWidth = 1f;
static float2 offset = new float2(0.5f,0.5f);
static float2 offset = new float2(0.5f, 0.5f);
float lineWidth = 1f;
Renderer renderer;
IShader shader;
@@ -30,6 +29,19 @@ namespace OpenRA.Graphics
this.shader = shader;
}
public float LineWidth
{
get { return lineWidth; }
set
{
if (LineWidth != value)
Flush();
lineWidth = value;
}
}
public void Flush()
{
if (nv > 0)
@@ -39,7 +51,7 @@ namespace OpenRA.Graphics
{
var vb = renderer.GetTempVertexBuffer();
vb.SetData(vertices, nv);
renderer.SetLineWidth(LineWidth * Game.viewport.Zoom);
renderer.SetLineWidth(LineWidth);
renderer.DrawBatch(vb, 0, nv, PrimitiveType.LineList);
});
renderer.Device.SetBlendMode(BlendMode.None);

View File

@@ -93,7 +93,7 @@ namespace OpenRA.Graphics
public void BeforeRender(WorldRenderer wr) {}
public void Render(WorldRenderer wr)
{
sprite.DrawAt(ScreenPosition(wr), palette, scale);
Game.Renderer.WorldSpriteRenderer.DrawSprite(sprite, ScreenPosition(wr), palette, sprite.size*scale);
}
public void RenderDebugGeometry(WorldRenderer wr)

View File

@@ -123,7 +123,7 @@ namespace OpenRA.Graphics
static IGraphicsDevice device;
public static Size Resolution { get { return device.WindowSize; } }
public Size Resolution { get { return device.WindowSize; } }
// Work around a bug in OSX 10.6.8 / mono 2.10.2 / SDL 1.2.14
// which makes the window non-interactive in Windowed/Pseudofullscreen mode.

View File

@@ -161,9 +161,8 @@ namespace OpenRA.Graphics
GenerateSprites(shroud);
var clipRect = Game.viewport.WorldBounds(wr.world);
// We draw the shroud when disabled to hide the sharp map edges
var clipRect = wr.Viewport.CellBounds;
DrawShroud(wr, clipRect, sprites, shroudPalette);
if (world.LobbyInfo.GlobalSettings.Fog)
@@ -184,24 +183,26 @@ namespace OpenRA.Graphics
if (starti != i)
{
s[starti, j].DrawAt(
// Stretch a solid black sprite over the rows above
// TODO: This doesn't make sense for isometric terrain
Game.Renderer.WorldSpriteRenderer.DrawSprite(
s[starti, j],
Game.CellSize * new float2(starti, j),
pal,
new float2(Game.CellSize * (i - starti), Game.CellSize));
starti = i + 1;
}
s[i, j].DrawAt(
Game.CellSize * new float2(i, j),
pal);
Game.Renderer.WorldSpriteRenderer.DrawSprite(s[i, j], Game.CellSize * new float2(i, j), pal);
starti = i + 1;
last = s[i, j];
}
// Stretch a solid black sprite over the rows to the left
// TODO: This doesn't make sense for isometric terrain
if (starti < clip.Right)
s[starti, j].DrawAt(
Game.CellSize * new float2(starti, j),
pal,
Game.Renderer.WorldSpriteRenderer.DrawSprite(s[starti, j],
Game.CellSize * new float2(starti, j), pal,
new float2(Game.CellSize * (clip.Right - starti), Game.CellSize));
}
}

View File

@@ -55,21 +55,6 @@ namespace OpenRA.Graphics
{
return textureCoords[k];
}
public void DrawAt(float2 location, PaletteReference pal)
{
Game.Renderer.WorldSpriteRenderer.DrawSprite(this, location, pal, size);
}
public void DrawAt(float2 location, PaletteReference pal, float scale)
{
Game.Renderer.WorldSpriteRenderer.DrawSprite(this, location, pal, size*scale);
}
public void DrawAt(float2 location, PaletteReference pal, float2 size)
{
Game.Renderer.WorldSpriteRenderer.DrawSprite(this, location, pal, size);
}
}
public enum TextureChannel

View File

@@ -46,36 +46,14 @@ namespace OpenRA.Graphics
public void Draw(WorldRenderer wr, Viewport viewport)
{
int verticesPerRow = 4*map.Bounds.Width;
int visibleRows = (int)(viewport.Height * 1f / Game.CellSize / viewport.Zoom + 2);
int firstRow = (int)(viewport.Location.Y * 1f / Game.CellSize - map.Bounds.Top);
int lastRow = firstRow + visibleRows;
var verticesPerRow = 4*map.Bounds.Width;
var bounds = viewport.CellBounds;
var firstRow = bounds.Top - map.Bounds.Top;
var lastRow = bounds.Bottom - map.Bounds.Top;
if (lastRow < 0 || firstRow > map.Bounds.Height)
return;
if (world.VisibleBounds.HasValue)
{
var r = world.VisibleBounds.Value;
if (firstRow < r.Top - map.Bounds.Top)
firstRow = r.Top - map.Bounds.Top;
if (firstRow > r.Bottom - map.Bounds.Top)
firstRow = r.Bottom - map.Bounds.Top;
}
// Sanity checking
if (firstRow < 0)
firstRow = 0;
if (lastRow > map.Bounds.Height)
lastRow = map.Bounds.Height;
if (lastRow < firstRow)
lastRow = firstRow;
Game.Renderer.WorldSpriteRenderer.DrawVertexBuffer(
vertexBuffer, verticesPerRow * firstRow, verticesPerRow * (lastRow - firstRow),
PrimitiveType.QuadList, wr.Theater.Sheet);

View File

@@ -46,7 +46,7 @@ namespace OpenRA.Graphics
public void BeforeRender(WorldRenderer wr) {}
public void Render(WorldRenderer wr)
{
var screenPos = Game.viewport.Zoom*(wr.ScreenPosition(pos) - Game.viewport.Location) - 0.5f*font.Measure(text).ToFloat2();
var screenPos = wr.Viewport.Zoom*(wr.ScreenPosition(pos) - wr.Viewport.TopLeft.ToFloat2()) - 0.5f*font.Measure(text).ToFloat2();
var screenPxPos = new float2((float)Math.Round(screenPos.X), (float)Math.Round(screenPos.Y));
font.DrawTextWithContrast(text, screenPxPos, color, Color.Black, 1);
}

View File

@@ -12,7 +12,6 @@ using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Widgets;
using OpenRA.Support;
namespace OpenRA.Graphics
@@ -20,187 +19,6 @@ namespace OpenRA.Graphics
[Flags]
public enum ScrollDirection { None = 0, Up = 1, Left = 2, Down = 4, Right = 8 }
public class Viewport
{
readonly int2 screenSize;
readonly Renderer renderer;
readonly Rectangle mapBounds;
Rectangle scrollLimits;
int2 scrollPosition;
// Top-left of the viewport, in world-px units
public float2 Location { get { return scrollPosition; } }
public float2 CenterLocation { get { return scrollPosition + 0.5f/Zoom*screenSize.ToFloat2(); } }
public Rectangle WorldRect
{
get
{
return new Rectangle(scrollPosition.X / Game.CellSize,
scrollPosition.Y / Game.CellSize,
(int)(screenSize.X / Zoom / Game.CellSize),
(int)(screenSize.Y / Zoom / Game.CellSize));
}
}
public int Width { get { return screenSize.X; } }
public int Height { get { return screenSize.Y; } }
float zoom = 1f;
public float Zoom
{
get
{
return zoom;
}
set
{
var oldCenter = CenterLocation;
zoom = value;
// Update scroll limits
var viewTL = (Game.CellSize*new float2(mapBounds.Left, mapBounds.Top)).ToInt2();
var viewBR = (Game.CellSize*new float2(mapBounds.Right, mapBounds.Bottom)).ToInt2();
var border = (.5f/Zoom * screenSize.ToFloat2()).ToInt2();
scrollLimits = Rectangle.FromLTRB(viewTL.X - border.X,
viewTL.Y - border.Y,
viewBR.X - border.X,
viewBR.Y - border.Y);
// Re-center viewport
scrollPosition = NormalizeScrollPosition((oldCenter - 0.5f / Zoom * screenSize.ToFloat2()).ToInt2());
}
}
float cursorFrame = 0f;
public static int TicksSinceLastMove = 0;
public static int2 LastMousePos;
public void Scroll(float2 delta)
{
Scroll(delta, false);
}
public void Scroll(float2 delta, bool ignoreBorders)
{
// Convert from world-px to viewport-px
var d = (1f/Zoom*delta).ToInt2();
var newScrollPosition = scrollPosition + d;
if(!ignoreBorders)
newScrollPosition = NormalizeScrollPosition(newScrollPosition);
scrollPosition = newScrollPosition;
}
int2 NormalizeScrollPosition(int2 newScrollPosition)
{
return newScrollPosition.Clamp(scrollLimits);
}
public ScrollDirection GetBlockedDirections()
{
var ret = ScrollDirection.None;
if(scrollPosition.Y <= scrollLimits.Top) ret |= ScrollDirection.Up;
if(scrollPosition.X <= scrollLimits.Left) ret |= ScrollDirection.Left;
if(scrollPosition.Y >= scrollLimits.Bottom) ret |= ScrollDirection.Down;
if(scrollPosition.X >= scrollLimits.Right) ret |= ScrollDirection.Right;
return ret;
}
public Viewport(int2 screenSize, Rectangle mapBounds, Renderer renderer)
{
this.screenSize = screenSize;
this.renderer = renderer;
this.mapBounds = mapBounds;
Zoom = Game.Settings.Graphics.PixelDouble ? 2 : 1;
scrollPosition = new int2(scrollLimits.Location) + new int2(scrollLimits.Size)/2;
}
public void DrawRegions( WorldRenderer wr, IInputHandler inputHandler )
{
renderer.BeginFrame(scrollPosition, Zoom);
if (wr != null)
wr.Draw();
using( new PerfSample("render_widgets") )
{
Ui.Draw();
var cursorName = Ui.Root.GetCursorOuter(Viewport.LastMousePos) ?? "default";
CursorProvider.DrawCursor(renderer, cursorName, Viewport.LastMousePos, (int)cursorFrame);
}
using( new PerfSample("render_flip") )
{
renderer.EndFrame( inputHandler );
}
}
public void Tick()
{
cursorFrame += 0.5f;
}
// Convert from viewport coords to cell coords (not px)
public CPos ViewToWorld(MouseInput mi) { return ViewToWorld(mi.Location); }
public CPos ViewToWorld(int2 loc)
{
return (CPos)( (1f / Game.CellSize) * (1f/Zoom*loc.ToFloat2() + Location) ).ToInt2();
}
public PPos ViewToWorldPx(int2 loc) { return (PPos)(1f/Zoom*loc.ToFloat2() + Location).ToInt2(); }
public PPos ViewToWorldPx(MouseInput mi) { return ViewToWorldPx(mi.Location); }
public void Center(float2 loc)
{
scrollPosition = NormalizeScrollPosition((Game.CellSize * loc - 1f/(2*Zoom)*screenSize.ToFloat2()).ToInt2());
}
public void Center(IEnumerable<Actor> actors)
{
if (!actors.Any()) return;
var avgPos = actors
.Select(a => (PVecInt)a.CenterLocation)
.Aggregate((a, b) => a + b) / actors.Count();
scrollPosition = NormalizeScrollPosition((avgPos.ToFloat2() - (1f / (2 * Zoom) * screenSize.ToFloat2())).ToInt2());
}
// Rectangle (in viewport coords) that contains things to be drawn
public Rectangle ViewBounds(World world)
{
var r = WorldBounds(world);
var origin = Location.ToInt2();
var left = Math.Max(0, Game.CellSize * r.Left - origin.X)*Zoom;
var top = Math.Max(0, Game.CellSize * r.Top - origin.Y)*Zoom;
var right = Math.Min((Game.CellSize * r.Right - origin.X) * Zoom, Width);
var bottom = Math.Min((Game.CellSize * r.Bottom - origin.Y) * Zoom, Height);
return Rectangle.FromLTRB((int)left, (int)top, (int)right, (int)bottom);
}
int2 cachedScroll = new int2(int.MaxValue, int.MaxValue);
Rectangle cachedRect;
// Rectangle (in cell coords) of cells that are currently visible on the screen
public Rectangle WorldBounds(World world)
{
if (cachedScroll != scrollPosition)
{
var boundary = new int2(1,1); // Add a curtain of cells around the viewport to account for rounding errors
var tl = ViewToWorld(int2.Zero).ToInt2() - boundary;
var br = ViewToWorld(new int2(Width, Height)).ToInt2() + boundary;
cachedRect = Rectangle.Intersect(Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y), world.Map.Bounds);
cachedScroll = scrollPosition;
}
var b = world.VisibleBounds;
return (b.HasValue) ? Rectangle.Intersect(cachedRect, b.Value) : cachedRect;
}
}
public static class ViewportExts
{
public static bool Includes(this ScrollDirection d, ScrollDirection s)
@@ -213,4 +31,135 @@ namespace OpenRA.Graphics
return (d.Includes(s) != val) ? d ^ s : d;
}
}
public class Viewport
{
readonly WorldRenderer worldRenderer;
// Map bounds (world-px)
readonly Rectangle mapBounds;
// Viewport geometry (world-px)
public int2 CenterLocation { get; private set; }
public int2 TopLeft { get { return CenterLocation - viewportSize / 2; } }
public int2 BottomRight { get { return CenterLocation + viewportSize / 2; } }
int2 viewportSize;
bool cellBoundsDirty = true;
float zoom = 1f;
public float Zoom
{
get
{
return zoom;
}
set
{
zoom = value;
viewportSize = (1f / zoom * new float2(Game.Renderer.Resolution)).ToInt2();
cellBoundsDirty = true;
}
}
public static int TicksSinceLastMove = 0;
public static int2 LastMousePos;
public ScrollDirection GetBlockedDirections()
{
var ret = ScrollDirection.None;
if (CenterLocation.Y <= mapBounds.Top)
ret |= ScrollDirection.Up;
if (CenterLocation.X <= mapBounds.Left)
ret |= ScrollDirection.Left;
if (CenterLocation.Y >= mapBounds.Bottom)
ret |= ScrollDirection.Down;
if (CenterLocation.X >= mapBounds.Right)
ret |= ScrollDirection.Right;
return ret;
}
public Viewport(WorldRenderer wr, Map map)
{
worldRenderer = wr;
// Calculate map bounds in world-px
var b = map.Bounds;
var tl = wr.ScreenPxPosition(new CPos(b.Left, b.Top).TopLeft);
var br = wr.ScreenPxPosition(new CPos(b.Right, b.Bottom).BottomRight);
mapBounds = Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y);
CenterLocation = (tl + br) / 2;
Zoom = Game.Settings.Graphics.PixelDouble ? 2 : 1;
}
public CPos ViewToWorld(int2 view)
{
return worldRenderer.Position(ViewToWorldPx(view)).ToCPos();
}
public int2 ViewToWorldPx(int2 view) { return (1f / Zoom * view.ToFloat2()).ToInt2() + TopLeft; }
public int2 WorldToViewPx(int2 world) { return (Zoom * (world - TopLeft).ToFloat2()).ToInt2(); }
public void Center(IEnumerable<Actor> actors)
{
if (!actors.Any())
return;
Center(actors.Select(a => a.CenterPosition).Average());
}
public void Center(WPos pos)
{
CenterLocation = worldRenderer.ScreenPxPosition(pos).Clamp(mapBounds);
cellBoundsDirty = true;
}
public void Scroll(float2 delta, bool ignoreBorders)
{
// Convert scroll delta from world-px to viewport-px
CenterLocation += (1f / Zoom * delta).ToInt2();
cellBoundsDirty = true;
if (!ignoreBorders)
CenterLocation = CenterLocation.Clamp(mapBounds);
}
// Rectangle (in viewport coords) that contains things to be drawn
static readonly Rectangle ScreenClip = Rectangle.FromLTRB(0, 0, Game.Renderer.Resolution.Width, Game.Renderer.Resolution.Height);
public Rectangle ScissorBounds
{
get
{
var r = CellBounds;
var ctl = new CPos(r.Left, r.Top).TopLeft;
var cbr = new CPos(r.Right, r.Bottom).TopLeft;
var tl = WorldToViewPx(worldRenderer.ScreenPxPosition(ctl)).Clamp(ScreenClip);
var br = WorldToViewPx(worldRenderer.ScreenPxPosition(cbr)).Clamp(ScreenClip);
return Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y);
}
}
// Rectangle (in cell coords) of cells that are currently visible on the screen
Rectangle cachedRect;
public Rectangle CellBounds
{
get
{
if (cellBoundsDirty)
{
var boundary = new CVec(1, 1);
var tl = worldRenderer.Position(TopLeft).ToCPos() - boundary;
var br = worldRenderer.Position(BottomRight).ToCPos() + boundary;
cachedRect = Rectangle.Intersect(Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y), worldRenderer.world.Map.Bounds);
cellBoundsDirty = false;
}
var b = worldRenderer.world.VisibleBounds;
return b.HasValue ? Rectangle.Intersect(cachedRect, b.Value) : cachedRect;
}
}
}
}

View File

@@ -34,6 +34,7 @@ namespace OpenRA.Graphics
{
public readonly World world;
public readonly Theater Theater;
public Viewport Viewport { get; private set; }
internal readonly TerrainRenderer terrainRenderer;
internal readonly ShroudRenderer shroudRenderer;
@@ -44,6 +45,7 @@ namespace OpenRA.Graphics
internal WorldRenderer(World world)
{
this.world = world;
Viewport = new Viewport(this, world.Map);
palette = new HardwarePalette();
palettes = new Cache<string, PaletteReference>(CreatePaletteReference);
@@ -73,12 +75,8 @@ namespace OpenRA.Graphics
List<IRenderable> GenerateRenderables()
{
var bounds = Game.viewport.WorldBounds(world);
var comparer = new RenderableComparer(this);
var tl = bounds.TopLeftAsCPos();
var br = bounds.BottomRightAsCPos();
var actors = world.FindActorsInBox(tl, br)
var actors = world.ScreenMap.ActorsInBox(Viewport.TopLeft, Viewport.BottomRight)
.Append(world.WorldActor)
.ToList();
@@ -116,10 +114,10 @@ namespace OpenRA.Graphics
return;
var renderables = GenerateRenderables();
var bounds = Game.viewport.ViewBounds(world);
var bounds = Viewport.ScissorBounds;
Game.Renderer.EnableScissor(bounds.Left, bounds.Top, bounds.Width, bounds.Height);
terrainRenderer.Draw(this, Game.viewport);
terrainRenderer.Draw(this, Viewport);
Game.Renderer.Flush();
for (var i = 0; i < renderables.Count; i++)
@@ -151,25 +149,28 @@ namespace OpenRA.Graphics
Game.Renderer.Flush();
}
public void DrawSelectionBox(Actor selectedUnit, Color c)
public void DrawSelectionBox(Actor a, Color c)
{
var bounds = selectedUnit.Bounds.Value;
var pos = ScreenPxPosition(a.CenterPosition);
var bounds = a.Bounds.Value;
var xy = new float2(bounds.Left, bounds.Top);
var Xy = new float2(bounds.Right, bounds.Top);
var xY = new float2(bounds.Left, bounds.Bottom);
var XY = new float2(bounds.Right, bounds.Bottom);
var tl = pos + new float2(bounds.Left, bounds.Top);
var br = pos + new float2(bounds.Right, bounds.Bottom);
var tr = new float2(br.X, tl.Y);
var bl = new float2(tl.X, br.Y);
var u = new float2(4f / Viewport.Zoom, 0);
var v = new float2(0, 4f / Viewport.Zoom);
var wlr = Game.Renderer.WorldLineRenderer;
wlr.DrawLine(xy, xy + new float2(4, 0), c, c);
wlr.DrawLine(xy, xy + new float2(0, 4), c, c);
wlr.DrawLine(Xy, Xy + new float2(-4, 0), c, c);
wlr.DrawLine(Xy, Xy + new float2(0, 4), c, c);
wlr.DrawLine(tl + u, tl, c, c);
wlr.DrawLine(tl, tl + v, c, c);
wlr.DrawLine(tr, tr - u, c, c);
wlr.DrawLine(tr, tr + v, c, c);
wlr.DrawLine(xY, xY + new float2(4, 0), c, c);
wlr.DrawLine(xY, xY + new float2(0, -4), c, c);
wlr.DrawLine(XY, XY + new float2(-4, 0), c, c);
wlr.DrawLine(XY, XY + new float2(0, -4), c, c);
wlr.DrawLine(bl, bl + u, c, c);
wlr.DrawLine(bl, bl - v, c, c);
wlr.DrawLine(br, br - u, c, c);
wlr.DrawLine(br, br - v, c, c);
}
public void DrawRollover(Actor unit)
@@ -179,24 +180,6 @@ namespace OpenRA.Graphics
selectable.DrawRollover(this, unit);
}
public void DrawLocus(Color c, CPos[] cells)
{
var dict = cells.ToDictionary(a => a, a => 0);
var wlr = Game.Renderer.WorldLineRenderer;
foreach (var t in dict.Keys)
{
if (!dict.ContainsKey(t + new CVec(-1, 0)))
wlr.DrawLine(t.ToPPos().ToFloat2(), (t + new CVec(0, 1)).ToPPos().ToFloat2(), c, c);
if (!dict.ContainsKey(t + new CVec(1, 0)))
wlr.DrawLine((t + new CVec(1, 0)).ToPPos().ToFloat2(), (t + new CVec(1, 1)).ToPPos().ToFloat2(), c, c);
if (!dict.ContainsKey(t + new CVec(0, -1)))
wlr.DrawLine(t.ToPPos().ToFloat2(), (t + new CVec(1, 0)).ToPPos().ToFloat2(), c, c);
if (!dict.ContainsKey(t + new CVec(0, 1)))
wlr.DrawLine((t + new CVec(0, 1)).ToPPos().ToFloat2(), (t + new CVec(1, 1)).ToPPos().ToFloat2(), c, c);
}
}
public void DrawRangeCircle(Color c, float2 location, float range)
{
for (var i = 0; i < 32; i++)
@@ -208,14 +191,29 @@ namespace OpenRA.Graphics
}
}
public void DrawRangeCircleWithContrast(Color fg, float2 location, float range, Color bg, int offset)
public void DrawRangeCircleWithContrast(Color fg, float2 location, float range, Color bg)
{
if (offset > 0) {
DrawRangeCircle(bg, location, range + (float) offset/Game.CellSize);
DrawRangeCircle(bg, location, range - (float) offset/Game.CellSize);
}
var wlr = Game.Renderer.WorldLineRenderer;
var oldWidth = wlr.LineWidth;
wlr.LineWidth = 3;
DrawRangeCircle(bg, location, range);
wlr.LineWidth = 1;
DrawRangeCircle(fg, location, range);
wlr.LineWidth = oldWidth;
}
public void DrawTargetMarker(Color c, float2 location)
{
var tl = new float2(-1 / Viewport.Zoom, -1 / Viewport.Zoom);
var br = new float2(1 / Viewport.Zoom, 1 / Viewport.Zoom);
var bl = new float2(tl.X, br.Y);
var tr = new float2(br.X, tl.Y);
var wlr = Game.Renderer.WorldLineRenderer;
wlr.DrawLine(location + tl, location + tr, c, c);
wlr.DrawLine(location + tr, location + br, c, c);
wlr.DrawLine(location + br, location + bl, c, c);
wlr.DrawLine(location + bl, location + tl, c, c);
}
public void RefreshPalette()
@@ -227,8 +225,8 @@ namespace OpenRA.Graphics
// Conversion between world and screen coordinates
public float2 ScreenPosition(WPos pos)
{
var c = Game.CellSize/1024f;
return new float2(c*pos.X, c*(pos.Y - pos.Z));
var c = Game.CellSize / 1024f;
return new float2(c * pos.X, c * (pos.Y - pos.Z));
}
public int2 ScreenPxPosition(WPos pos)
@@ -241,8 +239,8 @@ namespace OpenRA.Graphics
// For scaling vectors to pixel sizes in the voxel renderer
public float[] ScreenVector(WVec vec)
{
var c = Game.CellSize/1024f;
return new float[] {c*vec.X, c*vec.Y, c*vec.Z, 1};
var c = Game.CellSize / 1024f;
return new float[] { c * vec.X, c * vec.Y, c * vec.Z, 1 };
}
public int2 ScreenPxOffset(WVec vec)
@@ -252,6 +250,14 @@ namespace OpenRA.Graphics
return new int2((int)Math.Round(px[0]), (int)Math.Round(px[1] - px[2]));
}
public float ScreenZPosition(WPos pos, int zOffset) { return (pos.Y + pos.Z + zOffset)*Game.CellSize/1024f; }
public float ScreenZPosition(WPos pos, int offset)
{
return (pos.Y + pos.Z + offset) * Game.CellSize / 1024f;
}
public WPos Position(int2 screenPx)
{
return new WPos(1024 * screenPx.X / Game.CellSize, 1024 * screenPx.Y / Game.CellSize, 0);
}
}
}

View File

@@ -99,6 +99,7 @@ namespace OpenRA
[FieldLoader.Ignore] public List<MiniYamlNode> Weapons = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> Voices = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> Notifications = new List<MiniYamlNode>();
[FieldLoader.Ignore] public List<MiniYamlNode> Translations = new List<MiniYamlNode>();
// Binary map data
[FieldLoader.Ignore] public byte TileFormat = 1;
@@ -191,6 +192,7 @@ namespace OpenRA
Weapons = MiniYaml.NodesOrEmpty(yaml, "Weapons");
Voices = MiniYaml.NodesOrEmpty(yaml, "Voices");
Notifications = MiniYaml.NodesOrEmpty(yaml, "Notifications");
Translations = MiniYaml.NodesOrEmpty(yaml, "Translations");
CustomTerrain = new string[MapSize.X, MapSize.Y];
@@ -247,6 +249,7 @@ namespace OpenRA
root.Add(new MiniYamlNode("Weapons", null, Weapons));
root.Add(new MiniYamlNode("Voices", null, Voices));
root.Add(new MiniYamlNode("Notifications", null, Notifications));
root.Add(new MiniYamlNode("Translations", null, Translations));
var entries = new Dictionary<string, byte[]>();
entries.Add("map.bin", SaveBinaryData());

View File

@@ -44,6 +44,7 @@ namespace OpenRA
public ModData(params string[] mods)
{
Languages = new string[0];
Manifest = new Manifest(mods);
ObjectCreator = new ObjectCreator(Manifest);
LoadScreen = ObjectCreator.CreateObject<ILoadScreen>(Manifest.LoadScreen.Value);
@@ -71,6 +72,47 @@ namespace OpenRA
CursorProvider.Initialize(Manifest.Cursors);
}
public IEnumerable<string> Languages { get; private set; }
void LoadTranslations(Map map)
{
var selectedTranslations = new Dictionary<string, string>();
var defaultTranslations = new Dictionary<string, string>();
if (!Manifest.Translations.Any())
{
Languages = new string[0];
FieldLoader.Translations = new Dictionary<string, string>();
return;
}
var yaml = Manifest.Translations.Select(MiniYaml.FromFile).Aggregate(MiniYaml.MergeLiberal);
Languages = yaml.Select(t => t.Key).ToArray();
yaml = MiniYaml.MergeLiberal(map.Translations, yaml);
foreach (var y in yaml)
{
if (y.Key == Game.Settings.Graphics.Language)
selectedTranslations = y.Value.NodesDict.ToDictionary(x => x.Key, x => x.Value.Value ?? "");
if (y.Key == Game.Settings.Graphics.DefaultLanguage)
defaultTranslations = y.Value.NodesDict.ToDictionary(x => x.Key, x => x.Value.Value ?? "");
}
var translations = new Dictionary<string, string>();
foreach (var tkv in defaultTranslations.Concat(selectedTranslations))
{
if (translations.ContainsKey(tkv.Key))
continue;
if (selectedTranslations.ContainsKey(tkv.Key))
translations.Add(tkv.Key, selectedTranslations[tkv.Key]);
else
translations.Add(tkv.Key, tkv.Value);
}
FieldLoader.Translations = translations;
}
public Map PrepareMap(string uid)
{
LoadScreen.Display();
@@ -78,6 +120,8 @@ namespace OpenRA
throw new InvalidDataException("Invalid map uid: {0}".F(uid));
var map = new Map(AvailableMaps[uid].Path);
LoadTranslations(map);
// Reinit all our assets
InitializeLoaders();
FileSystem.LoadFromManifest(Manifest);

View File

@@ -135,7 +135,7 @@ namespace OpenRA.Network
{
var len = reader.ReadInt32();
var client = reader.ReadInt32();
var buf = reader.ReadBytes( len );
var buf = reader.ReadBytes(len);
if (len == 0)
throw new NotImplementedException();
lock (this)

View File

@@ -27,8 +27,10 @@ namespace OpenRA.Network
public readonly string Host;
public readonly int Port;
public readonly string Password = "";
public string ServerError = "Server is not responding.";
public string ServerError = "Server is not responding";
public bool AuthenticationFailed = false;
public int NetFrameNumber { get; private set; }
public int LocalFrameNumber;
@@ -52,10 +54,11 @@ namespace OpenRA.Network
Connection.Send(i, new List<byte[]>());
}
public OrderManager(string host, int port, IConnection conn)
public OrderManager(string host, int port, string password, IConnection conn)
{
this.Host = host;
this.Port = port;
Host = host;
Port = port;
Password = password;
Connection = conn;
syncReport = new SyncReport(this);
}

View File

@@ -13,7 +13,7 @@ using OpenRA.Network;
namespace OpenRA.Network
{
/* a maze of twisty little hacks,... */
/* HACK: a maze of twisty little hacks... */
public class Replay
{
public readonly string Filename;
@@ -49,7 +49,7 @@ namespace OpenRA.Network
lastFrame = Math.Max(lastFrame, frame);
});
Duration = lastFrame;
Duration = lastFrame * Game.NetTickScale;
LobbyInfo = lobbyInfo;
}
@@ -66,4 +66,3 @@ namespace OpenRA.Network
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2013 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,
@@ -13,6 +13,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using OpenRA.Widgets;
namespace OpenRA.Network
{
@@ -23,7 +24,7 @@ namespace OpenRA.Network
Func<string> chooseFilename;
MemoryStream preStartBuffer = new MemoryStream();
public ReplayRecorderConnection( IConnection inner, Func<string> chooseFilename )
public ReplayRecorderConnection(IConnection inner, Func<string> chooseFilename)
{
this.chooseFilename = chooseFilename;
this.inner = inner;
@@ -34,18 +35,16 @@ namespace OpenRA.Network
void StartSavingReplay(byte[] initialContent)
{
var filename = chooseFilename();
var replaysDirectory = Path.Combine(Platform.SupportDir, "Replays");
var dir = new[] { Platform.SupportDir, "Replays", WidgetUtils.ActiveModId(), WidgetUtils.ActiveModVersion() }.Aggregate(Path.Combine);
if (!Directory.Exists(replaysDirectory))
Directory.CreateDirectory(replaysDirectory);
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
FileStream file = null;
var id = -1;
while (file == null)
{
var fullFilename = Path.Combine(replaysDirectory, id < 0
? "{0}.rep".F(filename)
: "{0}-{1}.rep".F(filename, id));
var fullFilename = Path.Combine(dir, id < 0 ? "{0}.rep".F(filename) : "{0}-{1}.rep".F(filename, id));
id++;
try
{
@@ -61,11 +60,11 @@ namespace OpenRA.Network
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 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 )
public void Receive(Action<int, byte[]> packetFn)
{
inner.Receive((client, data) =>
{
@@ -81,7 +80,7 @@ namespace OpenRA.Network
writer.Write(data.Length);
writer.Write(data);
packetFn(client, data);
} );
});
}
bool IsGameStart(byte[] data)
@@ -92,15 +91,14 @@ namespace OpenRA.Network
return false;
var frame = BitConverter.ToInt32(data, 0);
return frame == 0 && data.ToOrderList(null).Any(
o => o.OrderString == "StartGame");
return frame == 0 && data.ToOrderList(null).Any(o => o.OrderString == "StartGame");
}
bool disposed;
public void Dispose()
{
if( disposed )
if (disposed)
return;
writer.Close();
@@ -113,5 +111,4 @@ namespace OpenRA.Network
Dispose();
}
}
}
}

View File

@@ -138,11 +138,13 @@ namespace OpenRA.Network
public int StartingCash = 5000;
public string StartingUnitsClass = "none";
public bool AllowVersionMismatch;
public string GameUid;
}
public Session(string[] mods)
{
this.GlobalSettings.Mods = mods.ToArray();
this.GlobalSettings.GameUid = System.Guid.NewGuid().ToString();
}
public string Serialize()

View File

@@ -178,6 +178,9 @@ namespace OpenRA.Network
{
if (r.Frame == frame)
{
Log.Write("sync", "Player: {0} ({1} {2} {3})", Game.Settings.Player.Name, Platform.CurrentPlatform, Environment.OSVersion, Platform.RuntimeVersion);
var mod = Game.CurrentMods.First().Value;
Log.Write("sync", "Game ID: {0} (Mod: {1} at Version {2})", orderManager.LobbyInfo.GlobalSettings.GameUid, mod.Title, mod.Version);
Log.Write("sync", "Sync for net frame {0} -------------", r.Frame);
Log.Write("sync", "SharedRandom: {0} (#{1})", r.SyncedRandom, r.TotalCount);
Log.Write("sync", "Synced Traits:");
@@ -196,11 +199,12 @@ namespace OpenRA.Network
foreach (var f in e.Fields)
Log.Write("sync", "\t\t {0}: {1}".F(f.Key, f.Value));
}
return;
}
Log.Write("sync", "No sync report available!");
}
Log.Write("sync", "No sync report available!");
}
class Report

19
OpenRA.Game/Network/UnitOrders.cs Executable file → Normal file
View File

@@ -51,9 +51,11 @@ namespace OpenRA.Network
Game.AddChatLine(Color.White, "(player {0})".F(clientId), order.TargetString);
break;
}
case "Message": // Server message
Game.AddChatLine(Color.White, "Server", order.TargetString);
break;
case "Disconnected": /* reports that the target player disconnected */
{
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
@@ -117,11 +119,8 @@ namespace OpenRA.Network
case "HandshakeRequest":
{
var request = HandshakeRequest.Deserialize(order.TargetString);
var localMods = orderManager.LobbyInfo.GlobalSettings.Mods.Select(m => "{0}@{1}".F(m,Mod.AllMods[m].Version)).ToArray();
var localMods = orderManager.LobbyInfo.GlobalSettings.Mods.Select(m => "{0}@{1}".F(m, Mod.AllMods[m].Version)).ToArray();
// Check if mods match
if (localMods.FirstOrDefault().ToString().Split('@')[0] != request.Mods.FirstOrDefault().ToString().Split('@')[0])
throw new InvalidOperationException("Server's mod ({0}) and yours ({1}) don't match".F(localMods.FirstOrDefault().ToString().Split('@')[0], request.Mods.FirstOrDefault().ToString().Split('@')[0]));
// Check that the map exists on the client
if (!Game.modData.AvailableMaps.ContainsKey(request.Map))
{
@@ -146,7 +145,7 @@ namespace OpenRA.Network
{
Client = info,
Mods = localMods,
Password = "Foo"
Password = orderManager.Password
};
orderManager.IssueOrder(Order.HandshakeResponse(response.Serialize()));
@@ -156,6 +155,14 @@ namespace OpenRA.Network
case "ServerError":
{
orderManager.ServerError = order.TargetString;
orderManager.AuthenticationFailed = false;
break;
}
case "AuthenticationError":
{
orderManager.ServerError = order.TargetString;
orderManager.AuthenticationFailed = true;
break;
}
@@ -195,11 +202,13 @@ namespace OpenRA.Network
break;
}
case "Ping":
{
orderManager.IssueOrder(Order.Pong(order.TargetString));
break;
}
default:
{
if (!order.IsImmediate)

View File

@@ -83,11 +83,8 @@
<ItemGroup>
<Compile Include="Actor.cs" />
<Compile Include="ActorInitializer.cs" />
<Compile Include="ActorMap.cs" />
<Compile Include="ActorReference.cs" />
<Compile Include="Graphics\QuadRenderer.cs" />
<Compile Include="PVecInt.cs" />
<Compile Include="PPos.cs" />
<Compile Include="Download.cs" />
<Compile Include="Effects\DelayedAction.cs" />
<Compile Include="Effects\FlashTarget.cs" />
@@ -185,7 +182,6 @@
<Compile Include="Traits\World\ResourceType.cs" />
<Compile Include="Traits\World\ScreenShaker.cs" />
<Compile Include="Traits\World\Shroud.cs" />
<Compile Include="Traits\World\SpatialBins.cs" />
<Compile Include="Widgets\BackgroundWidget.cs" />
<Compile Include="Widgets\ButtonWidget.cs" />
<Compile Include="Widgets\ChatDisplayWidget.cs" />
@@ -238,6 +234,9 @@
<Compile Include="Graphics\Theater.cs" />
<Compile Include="Traits\Player\PlayerColorPalette.cs" />
<Compile Include="Traits\Player\PlayerHighlightPalette.cs" />
<Compile Include="Traits\World\ScreenMap.cs" />
<Compile Include="Traits\World\ActorMap.cs" />
<Compile Include="Widgets\HotkeyEntryWidget.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">

View File

@@ -19,8 +19,8 @@ namespace OpenRA.Orders
{
public IEnumerable<Order> Order(World world, CPos xy, MouseInput mi)
{
var underCursor = world.FindUnitsAtMouse(mi.Location)
.Where(a => a.HasTrait<ITargetable>())
var underCursor = world.ScreenMap.ActorsAt(mi)
.Where(a => !world.FogObscures(a) && a.HasTrait<ITargetable>())
.OrderByDescending(a => a.Info.SelectionPriority())
.FirstOrDefault();
@@ -29,7 +29,7 @@ namespace OpenRA.Orders
target = Target.FromActor(underCursor);
else
{
var frozen = world.FindFrozenActorsAtMouse(mi.Location)
var frozen = world.ScreenMap.FrozenActorsAt(world.RenderPlayer, mi)
.Where(a => a.Info.Traits.Contains<ITargetableInfo>())
.OrderByDescending(a => a.Info.SelectionPriority())
.FirstOrDefault();
@@ -59,10 +59,9 @@ namespace OpenRA.Orders
public string GetCursor(World world, CPos xy, MouseInput mi)
{
bool useSelect = false;
var underCursor = world.FindUnitsAtMouse(mi.Location)
.Where(a => a.HasTrait<ITargetable>())
var useSelect = false;
var underCursor = world.ScreenMap.ActorsAt(mi)
.Where(a => !world.FogObscures(a) && a.HasTrait<ITargetable>())
.OrderByDescending(a => a.Info.SelectionPriority())
.FirstOrDefault();
@@ -78,7 +77,7 @@ namespace OpenRA.Orders
target = Target.FromActor(underCursor);
else
{
var frozen = world.FindFrozenActorsAtMouse(mi.Location)
var frozen = world.ScreenMap.FrozenActorsAt(world.RenderPlayer, mi)
.Where(a => a.Info.Traits.Contains<ITargetableInfo>())
.OrderByDescending(a => a.Info.SelectionPriority())
.FirstOrDefault();

View File

@@ -1,107 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 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 COPYING.
*/
#endregion
using System;
using System.Drawing;
namespace OpenRA
{
/// <summary>
/// Pixel coordinate position in the world (fine).
/// </summary>
public struct PPos
{
public readonly int X, Y;
public PPos(int x, int y) { X = x; Y = y; }
public static readonly PPos Zero = new PPos(0, 0);
public static PPos FromWPos(WPos pos)
{
return new PPos(Game.CellSize*pos.X/1024, Game.CellSize*pos.Y/1024);
}
// Temporary hack for things that throw away altitude and
// cache screen positions directly. This can go once all
// the callers understand world coordinates
public static PPos FromWPosHackZ(WPos pos)
{
return new PPos(Game.CellSize*pos.X/1024, Game.CellSize*(pos.Y - pos.Z)/1024);
}
public static explicit operator PPos(int2 a) { return new PPos(a.X, a.Y); }
public static explicit operator PVecInt(PPos a) { return new PVecInt(a.X, a.Y); }
public static PPos operator +(PPos a, PVecInt b) { return new PPos(a.X + b.X, a.Y + b.Y); }
public static PVecInt operator -(PPos a, PPos b) { return new PVecInt(a.X - b.X, a.Y - b.Y); }
public static PPos operator -(PPos a, PVecInt b) { return new PPos(a.X - b.X, a.Y - b.Y); }
public static bool operator ==(PPos me, PPos other) { return (me.X == other.X && me.Y == other.Y); }
public static bool operator !=(PPos me, PPos other) { return !(me == other); }
public static PPos Max(PPos a, PPos b) { return new PPos(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y)); }
public static PPos Min(PPos a, PPos b) { return new PPos(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y)); }
public static PPos Lerp(PPos a, PPos b, int mul, int div)
{
return a + ((PVecInt)(b - a) * mul / div);
}
public static PPos Average(params PPos[] list)
{
if (list == null || list.Length == 0)
throw new ArgumentException("PPos: Cannot calculate average of empty list.");
var x = 0;
var y = 0;
foreach(var pos in list)
{
x += pos.X;
y += pos.Y;
}
x /= list.Length;
y /= list.Length;
return new PPos(x,y);
}
public float2 ToFloat2() { return new float2(X, Y); }
public int2 ToInt2() { return new int2(X, Y); }
public CPos ToCPos() { return new CPos((int)(1f / Game.CellSize * X), (int)(1f / Game.CellSize * Y)); }
public PPos Clamp(Rectangle r)
{
return new PPos(Math.Min(r.Right, Math.Max(X, r.Left)),
Math.Min(r.Bottom, Math.Max(Y, r.Top)));
}
public WPos ToWPos(int z)
{
return new WPos(1024*X/Game.CellSize,
1024*Y/Game.CellSize,
1024*z/Game.CellSize);
}
public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode(); }
public override bool Equals(object obj)
{
if (obj == null)
return false;
PPos o = (PPos)obj;
return o == this;
}
public override string ToString() { return "{0},{1}".F(X, Y); }
}
}

View File

@@ -1,79 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 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 COPYING.
*/
#endregion
using System;
using System.Drawing;
namespace OpenRA
{
/// <summary>
/// Pixel coordinate vector (fine; integer)
/// </summary>
public struct PVecInt
{
public readonly int X, Y;
public PVecInt(int x, int y) { X = x; Y = y; }
public PVecInt(Size p) { X = p.Width; Y = p.Height; }
public static readonly PVecInt Zero = new PVecInt(0, 0);
public static PVecInt OneCell { get { return new PVecInt(Game.CellSize, Game.CellSize); } }
public static explicit operator PVecInt(int2 a) { return new PVecInt(a.X, a.Y); }
public static PVecInt FromRadius(int r) { return new PVecInt(r, r); }
public static PVecInt operator +(PVecInt a, PVecInt b) { return new PVecInt(a.X + b.X, a.Y + b.Y); }
public static PVecInt operator -(PVecInt a, PVecInt b) { return new PVecInt(a.X - b.X, a.Y - b.Y); }
public static PVecInt operator *(int a, PVecInt b) { return new PVecInt(a * b.X, a * b.Y); }
public static PVecInt operator *(PVecInt b, int a) { return new PVecInt(a * b.X, a * b.Y); }
public static PVecInt operator /(PVecInt a, int b) { return new PVecInt(a.X / b, a.Y / b); }
public static PVecInt operator -(PVecInt a) { return new PVecInt(-a.X, -a.Y); }
public static bool operator ==(PVecInt me, PVecInt other) { return (me.X == other.X && me.Y == other.Y); }
public static bool operator !=(PVecInt me, PVecInt other) { return !(me == other); }
public static PVecInt Max(PVecInt a, PVecInt b) { return new PVecInt(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y)); }
public static PVecInt Min(PVecInt a, PVecInt b) { return new PVecInt(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y)); }
public static int Dot(PVecInt a, PVecInt b) { return a.X * b.X + a.Y * b.Y; }
public PVecInt Sign() { return new PVecInt(Math.Sign(X), Math.Sign(Y)); }
public PVecInt Abs() { return new PVecInt(Math.Abs(X), Math.Abs(Y)); }
public int LengthSquared { get { return X * X + Y * Y; } }
public int Length { get { return (int)Math.Sqrt(LengthSquared); } }
public float2 ToFloat2() { return new float2(X, Y); }
public int2 ToInt2() { return new int2(X, Y); }
public CVec ToCVec() { return new CVec(X / Game.CellSize, Y / Game.CellSize); }
public PVecInt Clamp(Rectangle r)
{
return new PVecInt(
Math.Min(r.Right, Math.Max(X, r.Left)),
Math.Min(r.Bottom, Math.Max(Y, r.Top))
);
}
public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode(); }
public override bool Equals(object obj)
{
if (obj == null)
return false;
PVecInt o = (PVecInt)obj;
return o == this;
}
public override string ToString() { return "{0},{1}".F(X, Y); }
}
}

View File

@@ -11,6 +11,7 @@
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA
@@ -64,7 +65,7 @@ namespace OpenRA
Cache<int, List<Actor>> controlGroups = new Cache<int, List<Actor>>(_ => new List<Actor>());
public void DoControlGroup(World world, int group, Modifiers mods, int MultiTapCount)
public void DoControlGroup(World world, WorldRenderer worldRenderer, int group, Modifiers mods, int MultiTapCount)
{
var addModifier = Platform.CurrentPlatform == PlatformType.OSX ? Modifiers.Meta : Modifiers.Ctrl;
if (mods.HasModifier(addModifier))
@@ -84,7 +85,7 @@ namespace OpenRA
if (mods.HasModifier(Modifiers.Alt) || MultiTapCount >= 2)
{
Game.viewport.Center(controlGroups[group]);
worldRenderer.Viewport.Center(controlGroups[group]);
return;
}

View File

@@ -239,6 +239,14 @@ namespace OpenRA.Server
var handshake = HandshakeResponse.Deserialize(data);
if (!string.IsNullOrEmpty(Settings.Password) && handshake.Password != Settings.Password)
{
var message = string.IsNullOrEmpty(handshake.Password) ? "Server requires a password" : "Incorrect password";
SendOrderTo(newConn, "AuthenticationError", message);
DropClient(newConn);
return;
}
var client = new Session.Client()
{
Name = handshake.Client.Name,
@@ -270,7 +278,7 @@ namespace OpenRA.Server
Log.Write("server", "Rejected connection from {0}; mods do not match.",
newConn.socket.RemoteEndPoint);
SendOrderTo(newConn, "ServerError", "Your mods don't match the server");
SendOrderTo(newConn, "ServerError", "Server is running an incompatible mod");
DropClient(newConn);
return;
}
@@ -283,7 +291,7 @@ namespace OpenRA.Server
Log.Write("server", "Rejected connection from {0}; Not running the same version.",
newConn.socket.RemoteEndPoint);
SendOrderTo(newConn, "ServerError", "Not running the same version.");
SendOrderTo(newConn, "ServerError", "Server is running an incompatible version");
DropClient(newConn);
return;
}
@@ -293,7 +301,7 @@ namespace OpenRA.Server
if (bans.Contains(client.IpAddress))
{
Log.Write("server", "Rejected connection from {0}; Banned.", newConn.socket.RemoteEndPoint);
SendOrderTo(newConn, "ServerError", "You are {0} from the server.".F(Settings.Ban.Contains(client.IpAddress) ? "banned" : "temporarily banned"));
SendOrderTo(newConn, "ServerError", "You have been {0} from the server".F(Settings.Ban.Contains(client.IpAddress) ? "banned" : "temporarily banned"));
DropClient(newConn);
return;
}

View File

@@ -91,7 +91,7 @@ namespace OpenRA
return defaultDevices.Concat(alDevices).ToArray();
}
public static void SetListenerPosition(float2 position) { soundEngine.SetListenerPosition(position); }
public static void SetListenerPosition(WPos position) { soundEngine.SetListenerPosition(position); }
static ISound Play(Player player, string name, bool headRelative, WPos pos, float volumeModifier)
{
@@ -101,7 +101,7 @@ namespace OpenRA
return null;
return soundEngine.Play2D(sounds[name],
false, headRelative, PPos.FromWPosHackZ(pos).ToFloat2(),
false, headRelative, pos,
InternalSoundVolume * volumeModifier, true);
}
@@ -115,7 +115,7 @@ namespace OpenRA
public static void PlayVideo(byte[] raw)
{
rawSource = LoadSoundRaw(raw);
video = soundEngine.Play2D(rawSource, false, true, float2.Zero, InternalSoundVolume, false);
video = soundEngine.Play2D(rawSource, false, true, WPos.Zero, InternalSoundVolume, false);
}
public static void PlayVideo()
@@ -174,7 +174,7 @@ namespace OpenRA
if (sound == null)
return;
music = soundEngine.Play2D(sound, false, true, float2.Zero, MusicVolume, false);
music = soundEngine.Play2D(sound, false, true, WPos.Zero, MusicVolume, false);
currentMusic = m;
MusicPlaying = true;
}
@@ -322,7 +322,7 @@ namespace OpenRA
if (!String.IsNullOrEmpty(name) && (p == null || p == p.World.LocalPlayer))
soundEngine.Play2D(sounds[name],
false, true, float2.Zero,
false, true, WPos.Zero,
InternalSoundVolume, attentuateVolume);
return true;
@@ -354,13 +354,13 @@ namespace OpenRA
interface ISoundEngine
{
ISoundSource AddSoundSourceFromMemory(byte[] data, int channels, int sampleBits, int sampleRate);
ISound Play2D(ISoundSource sound, bool loop, bool relative, float2 pos, float volume, bool attenuateVolume);
ISound Play2D(ISoundSource sound, bool loop, bool relative, WPos pos, float volume, bool attenuateVolume);
float Volume { get; set; }
void PauseSound(ISound sound, bool paused);
void StopSound(ISound sound);
void SetAllSoundsPaused(bool paused);
void StopAllSounds();
void SetListenerPosition(float2 position);
void SetListenerPosition(WPos position);
void SetSoundVolume(float volume, ISound music, ISound video);
}
@@ -397,7 +397,7 @@ namespace OpenRA
{
public bool isActive;
public int frameStarted;
public float2 pos;
public WPos pos;
public bool isRelative;
public ISoundSource sound;
}
@@ -507,10 +507,10 @@ namespace OpenRA
}
const int maxInstancesPerFrame = 3;
const int groupDistance = 64;
const int groupDistance = 2730;
const int groupDistanceSqr = groupDistance * groupDistance;
public ISound Play2D(ISoundSource sound, bool loop, bool relative, float2 pos, float volume, bool attenuateVolume)
public ISound Play2D(ISoundSource sound, bool loop, bool relative, WPos pos, float volume, bool attenuateVolume)
{
if (sound == null)
{
@@ -635,11 +635,12 @@ namespace OpenRA
}
}
public void SetListenerPosition(float2 position)
public void SetListenerPosition(WPos position)
{
var orientation = new[] { 0f, 0f, 1f, 0f, -1f, 0f };
// Move the listener out of the plane so that sounds near the middle of the screen aren't too positional
Al.alListener3f(Al.AL_POSITION, position.X, position.Y, position.Z + 2133);
Al.alListener3f(Al.AL_POSITION, position.X, position.Y, 50);
var orientation = new[] { 0f, 0f, 1f, 0f, -1f, 0f };
Al.alListenerfv(Al.AL_ORIENTATION, ref orientation[0]);
Al.alListenerf(Al.AL_METERS_PER_UNIT, .01f);
}
@@ -669,19 +670,20 @@ namespace OpenRA
public readonly int source = -1;
float volume = 1f;
public OpenAlSound(int source, int buffer, bool looping, bool relative, float2 pos, float volume)
public OpenAlSound(int source, int buffer, bool looping, bool relative, WPos pos, float volume)
{
if (source == -1) return;
this.source = source;
Al.alSourcef(source, Al.AL_PITCH, 1f);
Volume = volume;
Al.alSource3f(source, Al.AL_POSITION, pos.X, pos.Y, 0f);
Al.alSource3f(source, Al.AL_POSITION, pos.X, pos.Y, pos.Z);
Al.alSource3f(source, Al.AL_VELOCITY, 0f, 0f, 0f);
Al.alSourcei(source, Al.AL_BUFFER, buffer);
Al.alSourcei(source, Al.AL_LOOPING, looping ? Al.AL_TRUE : Al.AL_FALSE);
Al.alSourcei(source, Al.AL_SOURCE_RELATIVE, relative ? 1 : 0);
Al.alSourcef(source, Al.AL_REFERENCE_DISTANCE, 160);
Al.alSourcef(source, Al.AL_MAX_DISTANCE, 3200 / Game.viewport.Zoom);
Al.alSourcef(source, Al.AL_REFERENCE_DISTANCE, 6826);
Al.alSourcef(source, Al.AL_MAX_DISTANCE, 136533);
Al.alSourcePlay(source);
}
@@ -728,7 +730,7 @@ namespace OpenRA
return new NullSoundSource();
}
public ISound Play2D(ISoundSource sound, bool loop, bool relative, float2 pos, float volume, bool attenuateVolume)
public ISound Play2D(ISoundSource sound, bool loop, bool relative, WPos pos, float volume, bool attenuateVolume)
{
return new NullSound();
}
@@ -737,7 +739,7 @@ namespace OpenRA
public void StopSound(ISound sound) {}
public void SetAllSoundsPaused(bool paused) {}
public void StopAllSounds() {}
public void SetListenerPosition(float2 position) {}
public void SetListenerPosition(WPos position) {}
public void SetSoundVolume(float volume, ISound music, ISound video) {}
public float Volume { get; set; }

View File

@@ -46,6 +46,10 @@ namespace OpenRA
static void FatalError(Exception e)
{
Log.AddChannel("exception", "exception.log");
var mod = Game.CurrentMods.First().Value;
Log.Write("exception", "{0} Mod at Version {1}", mod.Title, mod.Version);
Log.Write("exception", "Operating System: {0} ({1})", Platform.CurrentPlatform, Environment.OSVersion);
Log.Write("exception", "Runtime Version: {0}", Platform.RuntimeVersion);
var rpt = BuildExceptionReport(e).ToString();
Log.Write("exception", "{0}", rpt);
Console.Error.WriteLine(rpt);
@@ -69,7 +73,8 @@ namespace OpenRA
static StringBuilder BuildExceptionReport(Exception e, StringBuilder sb, int d)
{
if (e == null) return sb;
if (e == null)
return sb;
sb.AppendFormat("Exception of type `{0}`: {1}", e.GetType().FullName, e.Message);

View File

@@ -36,8 +36,6 @@ namespace OpenRA
{typeof(int2), ((Func<int2, int>)hash_int2).Method},
{typeof(CPos), ((Func<CPos, int>)hash_CPos).Method},
{typeof(CVec), ((Func<CVec, int>)hash_CVec).Method},
{typeof(PPos), ((Func<PPos, int>)hash_PPos).Method},
{typeof(PVecInt), ((Func<PVecInt, int>)hash_PVecInt).Method},
{typeof(WRange), ((Func<WRange, int>)hash<WRange>).Method},
{typeof(WPos), ((Func<WPos, int>)hash<WPos>).Method},
{typeof(WVec), ((Func<WVec, int>)hash<WVec>).Method},
@@ -115,16 +113,6 @@ namespace OpenRA
{
return ((i2.X * 5) ^ (i2.Y * 3)) / 4;
}
public static int hash_PPos(PPos i2)
{
return ((i2.X * 5) ^ (i2.Y * 3)) / 4;
}
public static int hash_PVecInt(PVecInt i2)
{
return ((i2.X * 5) ^ (i2.Y * 3)) / 4;
}
public static int hash_tdict(TypeDictionary d)
{

View File

@@ -60,27 +60,17 @@ namespace OpenRA.Traits
return;
var from = wr.ScreenPxPosition(self.CenterPosition);
var wlr = Game.Renderer.WorldLineRenderer;
foreach (var target in targets)
{
if (target.Type == TargetType.Invalid)
continue;
var to = wr.ScreenPxPosition(target.CenterPosition);
wlr.DrawLine(from, to, c, c);
DrawTargetMarker(wlr, from);
DrawTargetMarker(wlr, to);
Game.Renderer.WorldLineRenderer.DrawLine(from, to, c, c);
wr.DrawTargetMarker(c, from);
wr.DrawTargetMarker(c, to);
}
}
void DrawTargetMarker(LineRenderer wlr, float2 p)
{
wlr.DrawLine(p + new float2(-1, -1), p + new float2(-1, 1), c, c);
wlr.DrawLine(p + new float2(-1, 1), p + new float2(1, 1), c, c);
wlr.DrawLine(p + new float2(1, 1), p + new float2(1, -1), c, c);
wlr.DrawLine(p + new float2(1, -1), p + new float2(-1, -1), c, c);
}
}
public static class LineTargetExts
@@ -89,12 +79,7 @@ namespace OpenRA.Traits
{
var line = self.TraitOrDefault<DrawLineToTarget>();
if (line != null)
{
self.World.AddFrameEndTask(w =>
{
line.SetTargets(self, targets, color, false);
});
}
self.World.AddFrameEndTask(w => line.SetTargets(self, targets, color, false));
}
public static void SetTargetLine(this Actor self, Target target, Color color)

View File

@@ -20,10 +20,7 @@ namespace OpenRA.Traits
{
public class FrozenActorLayerInfo : ITraitInfo
{
[Desc("Size of partition bins (screen pixels)")]
public readonly int BinSize = 250;
public object Create(ActorInitializer init) { return new FrozenActorLayer(init.world, this); }
public object Create(ActorInitializer init) { return new FrozenActorLayer(init.self); }
}
public class FrozenActor
@@ -98,34 +95,21 @@ namespace OpenRA.Traits
[Sync] public int VisibilityHash;
[Sync] public int FrozenHash;
readonly FrozenActorLayerInfo info;
readonly World world;
readonly Player owner;
Dictionary<uint, FrozenActor> frozen;
List<FrozenActor>[,] bins;
public FrozenActorLayer(World world, FrozenActorLayerInfo info)
public FrozenActorLayer(Actor self)
{
this.info = info;
world = self.World;
owner = self.Owner;
frozen = new Dictionary<uint, FrozenActor>();
bins = new List<FrozenActor>[
world.Map.MapSize.X * Game.CellSize / info.BinSize,
world.Map.MapSize.Y * Game.CellSize / info.BinSize];
for (var j = 0; j <= bins.GetUpperBound(1); j++)
for (var i = 0; i <= bins.GetUpperBound(0); i++)
bins[i, j] = new List<FrozenActor>();
}
public void Add(FrozenActor fa)
{
frozen.Add(fa.ID, fa);
var top = (int)Math.Max(0, fa.Bounds.Top / info.BinSize);
var left = (int)Math.Max(0, fa.Bounds.Left / info.BinSize);
var bottom = (int)Math.Min(bins.GetUpperBound(1), fa.Bounds.Bottom / info.BinSize);
var right = (int)Math.Min(bins.GetUpperBound(0), fa.Bounds.Right / info.BinSize);
for (var j = top; j <= bottom; j++)
for (var i = left; i <= right; i++)
bins[i, j].Add(fa);
world.ScreenMap.Add(owner, fa);
}
public void Tick(Actor self)
@@ -148,8 +132,7 @@ namespace OpenRA.Traits
foreach (var r in remove)
{
foreach (var bin in bins)
bin.Remove(frozen[r]);
world.ScreenMap.Remove(owner, frozen[r]);
frozen.Remove(r);
}
}
@@ -161,13 +144,6 @@ namespace OpenRA.Traits
.SelectMany(ff => ff.Render(wr));
}
public IEnumerable<FrozenActor> FrozenActorsAt(int2 pxPos)
{
var x = (pxPos.X / info.BinSize).Clamp(0, bins.GetUpperBound(0));
var y = (pxPos.Y / info.BinSize).Clamp(0, bins.GetUpperBound(1));
return bins[x, y].Where(p => p.Bounds.Contains(pxPos) && p.IsValid);
}
public FrozenActor FromID(uint id)
{
FrozenActor ret;

View File

@@ -25,7 +25,7 @@ namespace OpenRA.Traits
var anim = new Animation(RenderSimple.GetImage(ai), () => 0);
anim.PlayRepeating("idle");
return anim.Render(WPos.Zero, pr);
return anim.Render(WPos.Zero, WVec.Zero, 0, pr, Scale);
}
}

View File

@@ -40,7 +40,9 @@ namespace OpenRA.Traits
if (!Info.Selectable)
return;
var pos = wr.ScreenPxPosition(self.CenterPosition);
var bounds = self.Bounds.Value;
bounds.Offset(pos.X, pos.Y);
var xy = new float2(bounds.Left, bounds.Top);
var Xy = new float2(bounds.Right, bounds.Top);
@@ -56,7 +58,9 @@ namespace OpenRA.Traits
if (!Info.Selectable)
return;
var pos = wr.ScreenPxPosition(self.CenterPosition);
var bounds = self.Bounds.Value;
bounds.Offset(pos.X, pos.Y);
var xy = new float2(bounds.Left, bounds.Top);
var Xy = new float2(bounds.Right, bounds.Top);
@@ -72,8 +76,8 @@ namespace OpenRA.Traits
var value = extraBar.GetValue();
if (value != 0)
{
xy.Y += 4;
Xy.Y += 4;
xy.Y += (int)(4 / wr.Viewport.Zoom);
Xy.Y += (int)(4 / wr.Viewport.Zoom);
DrawSelectionBar(wr, self, xy, Xy, extraBar.GetValue(), extraBar.GetColor());
}
}
@@ -81,25 +85,29 @@ namespace OpenRA.Traits
void DrawSelectionBar(WorldRenderer wr, Actor self, float2 xy, float2 Xy, float value, Color barColor)
{
if (!self.IsInWorld) return;
if (!self.IsInWorld)
return;
var health = self.TraitOrDefault<Health>();
if (health == null || health.IsDead) return;
var c = Color.FromArgb(128, 30, 30, 30);
var c2 = Color.FromArgb(128, 10, 10, 10);
var p = new float2(0, -4 / wr.Viewport.Zoom);
var q = new float2(0, -3 / wr.Viewport.Zoom);
var r = new float2(0, -2 / wr.Viewport.Zoom);
var barColor2 = Color.FromArgb(255, barColor.R / 2, barColor.G / 2, barColor.B / 2);
var z = float2.Lerp(xy, Xy, value);
var wlr = Game.Renderer.WorldLineRenderer;
wlr.DrawLine(xy + new float2(0, -4), Xy + new float2(0, -4), c, c);
wlr.DrawLine(xy + new float2(0, -3), Xy + new float2(0, -3), c2, c2);
wlr.DrawLine(xy + new float2(0, -2), Xy + new float2(0, -2), c, c);
wlr.DrawLine(xy + p, Xy + p, c, c);
wlr.DrawLine(xy + q, Xy + q, c2, c2);
wlr.DrawLine(xy + r, Xy + r, c, c);
wlr.DrawLine(xy + new float2(0, -3), z + new float2(0, -3), barColor, barColor);
wlr.DrawLine(xy + new float2(0, -2), z + new float2(0, -2), barColor2, barColor2);
wlr.DrawLine(xy + new float2(0, -4), z + new float2(0, -4), barColor2, barColor2);
wlr.DrawLine(xy + p, z + p, barColor2, barColor2);
wlr.DrawLine(xy + q, z + q, barColor, barColor);
wlr.DrawLine(xy + r, z + r, barColor2, barColor2);
}
void DrawHealthBar(WorldRenderer wr, Actor self, float2 xy, float2 Xy)
@@ -111,6 +119,9 @@ namespace OpenRA.Traits
var c = Color.FromArgb(128, 30, 30, 30);
var c2 = Color.FromArgb(128, 10, 10, 10);
var p = new float2(0, -4 / wr.Viewport.Zoom);
var q = new float2(0, -3 / wr.Viewport.Zoom);
var r = new float2(0, -2 / wr.Viewport.Zoom);
var healthColor = (health.DamageState == DamageState.Critical) ? Color.Red :
(health.DamageState == DamageState.Heavy) ? Color.Yellow : Color.LimeGreen;
@@ -124,13 +135,13 @@ namespace OpenRA.Traits
var z = float2.Lerp(xy, Xy, (float)health.HP / health.MaxHP);
var wlr = Game.Renderer.WorldLineRenderer;
wlr.DrawLine(xy + new float2(0, -4), Xy + new float2(0, -4), c, c);
wlr.DrawLine(xy + new float2(0, -3), Xy + new float2(0, -3), c2, c2);
wlr.DrawLine(xy + new float2(0, -2), Xy + new float2(0, -2), c, c);
wlr.DrawLine(xy + p, Xy + p, c, c);
wlr.DrawLine(xy + q, Xy + q, c2, c2);
wlr.DrawLine(xy + r, Xy + r, c, c);
wlr.DrawLine(xy + new float2(0, -3), z + new float2(0, -3), healthColor, healthColor);
wlr.DrawLine(xy + new float2(0, -2), z + new float2(0, -2), healthColor2, healthColor2);
wlr.DrawLine(xy + new float2(0, -4), z + new float2(0, -4), healthColor2, healthColor2);
wlr.DrawLine(xy + p, z + p, healthColor2, healthColor2);
wlr.DrawLine(xy + q, z + q, healthColor, healthColor);
wlr.DrawLine(xy + r, z + r, healthColor2, healthColor2);
if (health.DisplayHp != health.HP)
{
@@ -142,15 +153,16 @@ namespace OpenRA.Traits
deltaColor.B / 2);
var zz = float2.Lerp(xy, Xy, (float)health.DisplayHp / health.MaxHP);
wlr.DrawLine(z + new float2(0, -3), zz + new float2(0, -3), deltaColor, deltaColor);
wlr.DrawLine(z + new float2(0, -2), zz + new float2(0, -2), deltaColor2, deltaColor2);
wlr.DrawLine(z + new float2(0, -4), zz + new float2(0, -4), deltaColor2, deltaColor2);
wlr.DrawLine(z + p, zz + p, deltaColor2, deltaColor2);
wlr.DrawLine(z + q, zz + q, deltaColor, deltaColor);
wlr.DrawLine(z + r, zz + r, deltaColor2, deltaColor2);
}
}
void DrawUnitPath(WorldRenderer wr, Actor self)
{
if (self.World.LocalPlayer == null ||!self.World.LocalPlayer.PlayerActor.Trait<DeveloperMode>().PathDebug) return;
if (self.World.LocalPlayer == null || !self.World.LocalPlayer.PlayerActor.Trait<DeveloperMode>().PathDebug)
return;
var activity = self.GetCurrentActivity();
if (activity != null)
@@ -159,15 +171,10 @@ namespace OpenRA.Traits
var start = wr.ScreenPxPosition(self.CenterPosition);
var c = Color.Green;
var wlr = Game.Renderer.WorldLineRenderer;
foreach (var stp in targets.Where(t => t.Type != TargetType.Invalid).Select(p => wr.ScreenPxPosition(p.CenterPosition)))
foreach (var stp in targets.Where(t => t.Type != TargetType.Invalid).Select(pos => wr.ScreenPxPosition(pos.CenterPosition)))
{
wlr.DrawLine(stp + new float2(-1, -1), stp + new float2(-1, 1), c, c);
wlr.DrawLine(stp + new float2(-1, 1), stp + new float2(1, 1), c, c);
wlr.DrawLine(stp + new float2(1, 1), stp + new float2(1, -1), c, c);
wlr.DrawLine(stp + new float2(1, -1), stp + new float2(-1, -1), c, c);
wlr.DrawLine(start, stp, c, c);
Game.Renderer.WorldLineRenderer.DrawLine(start, stp, c, c);
wr.DrawTargetMarker(c, stp);
start = stp;
}
}

View File

@@ -34,16 +34,19 @@ namespace OpenRA.Traits
if (self.World.FogObscures(self))
return;
var pos = wr.ScreenPxPosition(self.CenterPosition);
var bounds = self.Bounds.Value;
var xy = new float2(bounds.Left, bounds.Top);
var xY = new float2(bounds.Left, bounds.Bottom);
bounds.Offset(pos.X, pos.Y);
var xy = new int2(bounds.Left, bounds.Top);
var xY = new int2(bounds.Left, bounds.Bottom);
DrawControlGroup(wr, self, xy);
DrawPips(wr, self, xY);
DrawTags(wr, self, new float2(.5f * (bounds.Left + bounds.Right), bounds.Top));
DrawTags(wr, self, new int2((bounds.Left + bounds.Right) / 2, bounds.Top));
}
void DrawControlGroup(WorldRenderer wr, Actor self, float2 basePosition)
void DrawControlGroup(WorldRenderer wr, Actor self, int2 basePosition)
{
var group = self.World.Selection.GetControlGroupForActor(self);
if (group == null) return;
@@ -51,10 +54,12 @@ namespace OpenRA.Traits
var pipImages = new Animation("pips");
pipImages.PlayFetchIndex("groups", () => (int)group);
pipImages.Tick();
pipImages.Image.DrawAt(basePosition + new float2(-8, 1), wr.Palette("chrome"));
var pos = wr.Viewport.WorldToViewPx(basePosition) - (0.5f * pipImages.Image.size).ToInt2() + new int2(9, 5);
Game.Renderer.SpriteRenderer.DrawSprite(pipImages.Image, pos, wr.Palette("chrome"));
}
void DrawPips(WorldRenderer wr, Actor self, float2 basePosition)
void DrawPips(WorldRenderer wr, Actor self, int2 basePosition)
{
if (!self.Owner.IsAlliedWith(self.World.RenderPlayer))
return;
@@ -66,10 +71,11 @@ namespace OpenRA.Traits
var pipImages = new Animation("pips");
pipImages.PlayRepeating(pipStrings[0]);
var pipSize = pipImages.Image.size;
var pipxyBase = basePosition + new float2(1, -pipSize.Y);
var pipxyOffset = new float2(0, 0); // Correct for offset due to multiple columns/rows
var pipSize = pipImages.Image.size.ToInt2();
var pipxyBase = wr.Viewport.WorldToViewPx(basePosition) + new int2(1 - pipSize.X / 2, - (3 + pipSize.Y / 2));
var pipxyOffset = new int2(0, 0);
var pal = wr.Palette("chrome");
var width = self.Bounds.Value.Width;
foreach (var pips in pipSources)
{
@@ -77,8 +83,6 @@ namespace OpenRA.Traits
if (thisRow == null)
continue;
var width = self.Bounds.Value.Width;
foreach (var pip in thisRow)
{
if (pipxyOffset.X + pipSize.X >= width)
@@ -86,9 +90,11 @@ namespace OpenRA.Traits
pipxyOffset.X = 0;
pipxyOffset.Y -= pipSize.Y;
}
pipImages.PlayRepeating(pipStrings[(int)pip]);
pipImages.Image.DrawAt(pipxyBase + pipxyOffset, pal);
pipxyOffset += new float2(pipSize.X, 0);
pipxyOffset += new int2(pipSize.X, 0);
Game.Renderer.SpriteRenderer.DrawSprite(pipImages.Image, pipxyBase + pipxyOffset, pal);
}
// Increment row
@@ -97,15 +103,16 @@ namespace OpenRA.Traits
}
}
void DrawTags(WorldRenderer wr, Actor self, float2 basePosition)
void DrawTags(WorldRenderer wr, Actor self, int2 basePosition)
{
if (!self.Owner.IsAlliedWith(self.World.RenderPlayer))
return;
// If a mod wants to implement a unit with multiple tags, then they are placed on multiple rows
var tagxyBase = basePosition + new float2(-16, 2); // Correct for the offset in the shp file
var tagxyOffset = new float2(0, 0); // Correct for offset due to multiple rows
var tagImages = new Animation("pips");
var pal = wr.Palette("chrome");
var tagxyOffset = new int2(0, 6);
var tagBase = wr.Viewport.WorldToViewPx(basePosition);
foreach (var tags in self.TraitsImplementing<ITags>())
{
foreach (var tag in tags.GetTags())
@@ -113,9 +120,9 @@ namespace OpenRA.Traits
if (tag == TagType.None)
continue;
var tagImages = new Animation("pips");
tagImages.PlayRepeating(tagStrings[(int)tag]);
tagImages.Image.DrawAt(tagxyBase + tagxyOffset, pal);
var pos = tagBase + tagxyOffset - (0.5f * tagImages.Image.size).ToInt2();
Game.Renderer.SpriteRenderer.DrawSprite(tagImages.Image, pos, pal);
// Increment row
tagxyOffset.Y += 8;

View File

@@ -137,6 +137,7 @@ namespace OpenRA.Traits
public interface IPositionable : IOccupySpace
{
bool CanEnterCell(CPos location);
bool CanEnterCell(CPos location, Actor ignoreActor, bool checkTransientActors);
void SetPosition(Actor self, CPos cell);
void SetPosition(Actor self, WPos pos);
void SetVisualPosition(Actor self, WPos pos);
@@ -174,7 +175,7 @@ namespace OpenRA.Traits
public interface UsesInit<T> where T : IActorInit { }
public interface INotifySelection { void SelectionChanged(); }
public interface IWorldLoaded { void WorldLoaded(World w); }
public interface IWorldLoaded { void WorldLoaded(World w, WorldRenderer wr); }
public interface ICreatePlayers { void CreatePlayers(World w); }
public interface IBotInfo { string Name { get; } }

View File

@@ -18,9 +18,9 @@ namespace OpenRA.Traits
public object Create( ActorInitializer init ) { return new Waypoint( init ); }
}
class Waypoint : IOccupySpace, ISync
class Waypoint : IOccupySpace, ISync, INotifyAddedToWorld, INotifyRemovedFromWorld
{
[Sync] CPos location;
[Sync] readonly CPos location;
public Waypoint(ActorInitializer init)
{
@@ -30,5 +30,19 @@ namespace OpenRA.Traits
public CPos TopLeft { get { return location; } }
public IEnumerable<Pair<CPos, SubCell>> OccupiedCells() { yield break; }
public WPos CenterPosition { get { return location.CenterPosition; } }
public void AddedToWorld(Actor self)
{
self.World.ActorMap.AddInfluence(self, this);
self.World.ActorMap.AddPosition(self, this);
self.World.ScreenMap.Add(self);
}
public void RemovedFromWorld(Actor self)
{
self.World.ActorMap.RemoveInfluence(self, this);
self.World.ActorMap.RemovePosition(self, this);
self.World.ScreenMap.Remove(self);
}
}
}

View File

@@ -0,0 +1,210 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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 COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Traits;
namespace OpenRA.Traits
{
public enum SubCell { FullCell, TopLeft, TopRight, Center, BottomLeft, BottomRight }
public class ActorMapInfo : ITraitInfo
{
[Desc("Size of partition bins (cells)")]
public readonly int BinSize = 10;
public object Create(ActorInitializer init) { return new ActorMap(init.world, this); }
}
public class ActorMap : ITick
{
class InfluenceNode
{
public InfluenceNode Next;
public SubCell SubCell;
public Actor Actor;
}
static readonly SubCell[] SubCells =
{
SubCell.TopLeft, SubCell.TopRight, SubCell.Center,
SubCell.BottomLeft, SubCell.BottomRight
};
readonly ActorMapInfo info;
readonly Map map;
InfluenceNode[,] influence;
List<Actor>[] actors;
int rows, cols;
// Position updates are done in one pass
// to ensure consistency during a tick
List<Actor> addActorPosition;
List<Actor> removeActorPosition;
public ActorMap(World world, ActorMapInfo info)
{
this.info = info;
map = world.Map;
influence = new InfluenceNode[world.Map.MapSize.X, world.Map.MapSize.Y];
cols = world.Map.MapSize.X / info.BinSize + 1;
rows = world.Map.MapSize.Y / info.BinSize + 1;
actors = new List<Actor>[rows * cols];
for (var j = 0; j < rows; j++)
for (var i = 0; i < cols; i++)
actors[j * cols + i] = new List<Actor>();
addActorPosition = new List<Actor>();
removeActorPosition = new List<Actor>();
}
public IEnumerable<Actor> GetUnitsAt(CPos a)
{
if (!map.IsInMap(a))
yield break;
for (var i = influence[a.X, a.Y]; i != null; i = i.Next)
if (!i.Actor.Destroyed)
yield return i.Actor;
}
public IEnumerable<Actor> GetUnitsAt(CPos a, SubCell sub)
{
if (!map.IsInMap(a))
yield break;
for (var i = influence[a.X, a.Y]; i != null; i = i.Next)
if (!i.Actor.Destroyed && (i.SubCell == sub || i.SubCell == SubCell.FullCell))
yield return i.Actor;
}
public bool HasFreeSubCell(CPos a)
{
if (!AnyUnitsAt(a))
return true;
return SubCells.Any(b => !AnyUnitsAt(a, b));
}
public SubCell? FreeSubCell(CPos a)
{
if (!HasFreeSubCell(a))
return null;
return SubCells.First(b => !AnyUnitsAt(a, b));
}
public bool AnyUnitsAt(CPos a)
{
return influence[a.X, a.Y] != null;
}
public bool AnyUnitsAt(CPos a, SubCell sub)
{
for (var i = influence[a.X, a.Y]; i != null; i = i.Next)
if (i.SubCell == sub || i.SubCell == SubCell.FullCell)
return true;
return false;
}
public void AddInfluence(Actor self, IOccupySpace ios)
{
foreach (var c in ios.OccupiedCells())
influence[c.First.X, c.First.Y] = new InfluenceNode { Next = influence[c.First.X, c.First.Y], SubCell = c.Second, Actor = self };
}
public void RemoveInfluence(Actor self, IOccupySpace ios)
{
foreach (var c in ios.OccupiedCells())
RemoveInfluenceInner(ref influence[c.First.X, c.First.Y], self);
}
void RemoveInfluenceInner(ref InfluenceNode influenceNode, Actor toRemove)
{
if (influenceNode == null)
return;
else if (influenceNode.Actor == toRemove)
influenceNode = influenceNode.Next;
if (influenceNode != null)
RemoveInfluenceInner(ref influenceNode.Next, toRemove);
}
public void Tick(Actor self)
{
// Position updates are done in one pass
// to ensure consistency during a tick
foreach (var bin in actors)
bin.RemoveAll(removeActorPosition.Contains);
removeActorPosition.Clear();
foreach (var a in addActorPosition)
{
var pos = a.OccupiesSpace.CenterPosition;
var i = (pos.X / info.BinSize).Clamp(0, cols - 1);
var j = (pos.Y / info.BinSize).Clamp(0, rows - 1);
actors[j * cols + i].Add(a);
}
addActorPosition.Clear();
}
public void AddPosition(Actor a, IOccupySpace ios)
{
addActorPosition.Add(a);
}
public void RemovePosition(Actor a, IOccupySpace ios)
{
removeActorPosition.Add(a);
}
public void UpdatePosition(Actor a, IOccupySpace ios)
{
RemovePosition(a, ios);
AddPosition(a, ios);
}
public IEnumerable<Actor> ActorsInBox(WPos a, WPos b)
{
var left = Math.Min(a.X, b.X);
var top = Math.Min(a.Y, b.Y);
var right = Math.Max(a.X, b.X);
var bottom = Math.Max(a.Y, b.Y);
var i1 = (left / info.BinSize).Clamp(0, cols - 1);
var i2 = (right / info.BinSize).Clamp(0, cols - 1);
var j1 = (top / info.BinSize).Clamp(0, rows - 1);
var j2 = (bottom / info.BinSize).Clamp(0, rows - 1);
var actorsInBox = new List<Actor>();
for (var j = j1; j <= j2; j++)
{
for (var i = i1; i <= i2; i++)
{
foreach (var actor in actors[j * cols + i])
{
var c = actor.CenterPosition;
if (actor.IsInWorld && left <= c.X && c.X <= right && top <= c.Y && c.Y <= bottom)
actorsInBox.Add(actor);
}
}
}
return actorsInBox.Distinct();
}
}
}

View File

@@ -37,7 +37,7 @@ namespace OpenRA.Traits
rt.info.PaletteRef = wr.Palette(rt.info.Palette);
}
var clip = Game.viewport.WorldBounds(world);
var clip = wr.Viewport.CellBounds;
for (var x = clip.Left; x < clip.Right; x++)
{
for (var y = clip.Top; y < clip.Bottom; y++)
@@ -48,16 +48,13 @@ namespace OpenRA.Traits
var c = render[x, y];
if (c.Image != null)
{
var tile = c.Image[c.Density];
var px = wr.ScreenPxPosition(pos.CenterPosition) - 0.5f * tile.size;
tile.DrawAt(px, c.Type.info.PaletteRef);
}
new SpriteRenderable(c.Image[c.Density], pos.CenterPosition,
WVec.Zero, -511, c.Type.info.PaletteRef, 1f, true).Render(wr);
}
}
}
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
this.world = w;
content = new CellContents[w.Map.MapSize.X, w.Map.MapSize.Y];

View File

@@ -0,0 +1,166 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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 COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Traits
{
public class ScreenMapInfo : ITraitInfo
{
[Desc("Size of partition bins (world pixels)")]
public readonly int BinSize = 250;
public object Create(ActorInitializer init) { return new ScreenMap(init.world, this); }
}
public class ScreenMap : IWorldLoaded
{
ScreenMapInfo info;
WorldRenderer worldRenderer;
Cache<Player, Dictionary<FrozenActor, Rectangle>[]> frozen;
Dictionary<Actor, Rectangle>[] actors;
int rows, cols;
public ScreenMap(World world, ScreenMapInfo info)
{
this.info = info;
cols = world.Map.MapSize.X * Game.CellSize / info.BinSize + 1;
rows = world.Map.MapSize.Y * Game.CellSize / info.BinSize + 1;
frozen = new Cache<Player, Dictionary<FrozenActor, Rectangle>[]>(InitializeFrozenActors);
actors = new Dictionary<Actor, Rectangle>[rows * cols];
for (var j = 0; j < rows; j++)
for (var i = 0; i < cols; i++)
actors[j * cols + i] = new Dictionary<Actor, Rectangle>();
}
public void WorldLoaded(World w, WorldRenderer wr) { worldRenderer = wr; }
Dictionary<FrozenActor, Rectangle>[] InitializeFrozenActors(Player p)
{
var f = new Dictionary<FrozenActor, Rectangle>[rows * cols];
for (var j = 0; j < rows; j++)
for (var i = 0; i < cols; i++)
f[j * cols + i] = new Dictionary<FrozenActor, Rectangle>();
return f;
}
public void Add(Player viewer, FrozenActor fa)
{
var pos = worldRenderer.ScreenPxPosition(fa.CenterPosition);
var bounds = fa.Bounds;
bounds.Offset(pos.X, pos.Y);
var top = Math.Max(0, bounds.Top / info.BinSize);
var left = Math.Max(0, bounds.Left / info.BinSize);
var bottom = Math.Min(rows - 1, bounds.Bottom / info.BinSize);
var right = Math.Min(cols - 1, bounds.Right / info.BinSize);
for (var j = top; j <= bottom; j++)
for (var i = left; i <= right; i++)
frozen[viewer][j*cols + i].Add(fa, bounds);
}
public void Remove(Player viewer, FrozenActor fa)
{
foreach (var bin in frozen[viewer])
bin.Remove(fa);
}
public void Add(Actor a)
{
var pos = worldRenderer.ScreenPxPosition(a.CenterPosition);
var bounds = a.Bounds.Value;
bounds.Offset(pos.X, pos.Y);
var top = Math.Max(0, bounds.Top / info.BinSize);
var left = Math.Max(0, bounds.Left / info.BinSize);
var bottom = Math.Min(rows - 1, bounds.Bottom / info.BinSize);
var right = Math.Min(cols - 1, bounds.Right / info.BinSize);
for (var j = top; j <= bottom; j++)
for (var i = left; i <= right; i++)
actors[j * cols + i].Add(a, bounds);
}
public void Remove(Actor a)
{
foreach (var bin in actors)
bin.Remove(a);
}
public void Update(Actor a)
{
Remove(a);
Add(a);
}
public static readonly IEnumerable<FrozenActor> NoFrozenActors = new FrozenActor[0].AsEnumerable();
public IEnumerable<FrozenActor> FrozenActorsAt(Player viewer, int2 worldPx)
{
if (viewer == null)
return NoFrozenActors;
var i = (worldPx.X / info.BinSize).Clamp(0, cols - 1);
var j = (worldPx.Y / info.BinSize).Clamp(0, rows - 1);
return frozen[viewer][j*cols + i]
.Where(kv => kv.Key.IsValid && kv.Value.Contains(worldPx))
.Select(kv => kv.Key);
}
public IEnumerable<FrozenActor> FrozenActorsAt(Player viewer, MouseInput mi)
{
return FrozenActorsAt(viewer, worldRenderer.Viewport.ViewToWorldPx(mi.Location));
}
public IEnumerable<Actor> ActorsAt(int2 worldPx)
{
var i = (worldPx.X / info.BinSize).Clamp(0, cols - 1);
var j = (worldPx.Y / info.BinSize).Clamp(0, rows - 1);
return actors[j * cols + i]
.Where(kv => kv.Key.IsInWorld && kv.Value.Contains(worldPx))
.Select(kv => kv.Key);
}
public IEnumerable<Actor> ActorsAt(MouseInput mi)
{
return ActorsAt(worldRenderer.Viewport.ViewToWorldPx(mi.Location));
}
public IEnumerable<Actor> ActorsInBox(int2 a, int2 b)
{
return ActorsInBox(Rectangle.FromLTRB(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Max(a.X, b.X), Math.Max(a.Y, b.Y)));
}
public IEnumerable<Actor> ActorsInBox(Rectangle r)
{
var left = (r.Left / info.BinSize).Clamp(0, cols - 1);
var right = (r.Right / info.BinSize).Clamp(0, cols - 1);
var top = (r.Top / info.BinSize).Clamp(0, rows - 1);
var bottom = (r.Bottom / info.BinSize).Clamp(0, rows - 1);
var actorsInBox = new List<Actor>();
for (var j = top; j <= bottom; j++)
for (var i = left; i <= right; i++)
actorsInBox.AddRange(actors[j * cols + i]
.Where(kv => kv.Key.IsInWorld && kv.Value.IntersectsWith(r))
.Select(kv => kv.Key));
return actorsInBox.Distinct();
}
}
}

View File

@@ -11,21 +11,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
namespace OpenRA.Traits
{
public class ScreenShakerInfo : TraitInfo<ScreenShaker> { }
public class ScreenShaker : ITick
public class ScreenShaker : ITick, IWorldLoaded
{
WorldRenderer worldRenderer;
List<ShakeEffect> shakeEffects = new List<ShakeEffect>();
int ticks = 0;
public void WorldLoaded(World w, WorldRenderer wr) { worldRenderer = wr; }
public void Tick(Actor self)
{
if (shakeEffects.Any())
{
Game.viewport.Scroll(GetScrollOffset(), true);
worldRenderer.Viewport.Scroll(GetScrollOffset(), true);
shakeEffects.RemoveAll(t => t.ExpiryTime == ticks);
}
@@ -46,7 +50,7 @@ namespace OpenRA.Traits
float GetIntensity()
{
var cp = ((PPos)Game.viewport.CenterLocation.ToInt2()).ToWPos(0);
var cp = worldRenderer.Position(worldRenderer.Viewport.CenterLocation);
var intensity = 100 * 1024 * 1024 * shakeEffects.Sum(
e => (float)e.Intensity / (e.Position - cp).LengthSquared);

View File

@@ -1,87 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 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 COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace OpenRA.Traits
{
class SpatialBinsInfo : ITraitInfo
{
public readonly int BinSize = 8;
public object Create(ActorInitializer init) { return new SpatialBins( init.self, this ); }
}
class SpatialBins : ITick
{
List<Actor>[,] bins;
int scale;
public SpatialBins(Actor self, SpatialBinsInfo info)
{
bins = new List<Actor>[
self.World.Map.MapSize.X / info.BinSize,
self.World.Map.MapSize.Y / info.BinSize];
scale = Game.CellSize * info.BinSize;
for (var j = 0; j <= bins.GetUpperBound(1); j++)
for (var i = 0; i <= bins.GetUpperBound(0); i++)
bins[i, j] = new List<Actor>();
}
public void Tick(Actor self)
{
for (var j = 0; j <= bins.GetUpperBound(1); j++)
for (var i = 0; i <= bins.GetUpperBound(0); i++)
bins[i, j].Clear();
foreach (var a in self.World.ActorsWithTrait<IOccupySpace>())
{
var bounds = a.Actor.ExtendedBounds.Value;
if (bounds.Right <= Game.CellSize * self.World.Map.Bounds.Left) continue;
if (bounds.Bottom <= Game.CellSize * self.World.Map.Bounds.Top) continue;
if (bounds.Left >= Game.CellSize * self.World.Map.Bounds.Right) continue;
if (bounds.Top >= Game.CellSize * self.World.Map.Bounds.Bottom) continue;
var i1 = Math.Max(0, bounds.Left / scale);
var i2 = Math.Min(bins.GetUpperBound(0), bounds.Right / scale);
var j1 = Math.Max(0, bounds.Top / scale);
var j2 = Math.Min(bins.GetUpperBound(1), bounds.Bottom / scale);
for (var j = j1; j <= j2; j++)
for (var i = i1; i <= i2; i++)
bins[i, j].Add(a.Actor);
}
}
IEnumerable<Actor> ActorsInBins(int i1, int i2, int j1, int j2)
{
j1 = Math.Max(0, j1); j2 = Math.Min(j2, bins.GetUpperBound(1));
i1 = Math.Max(0, i1); i2 = Math.Min(i2, bins.GetUpperBound(0));
for (var j = j1; j <= j2; j++)
for (var i = i1; i <= i2; i++)
foreach (var a in bins[i, j])
yield return a;
}
public IEnumerable<Actor> ActorsInBox(PPos a, PPos b)
{
var r = Rectangle.FromLTRB(a.X, a.Y, b.X, b.Y);
return ActorsInBins(a.X / scale, b.X / scale, a.Y / scale, b.Y / scale)
.Distinct()
.Where(u => u.IsInWorld && u.ExtendedBounds.Value.IntersectsWith(r));
}
}
}

View File

@@ -18,14 +18,14 @@ namespace OpenRA.Widgets
{
public class ButtonWidget : Widget
{
public Func<ButtonWidget, string> GetKey = _ => null;
public string Key
public Func<ButtonWidget, Hotkey> GetKey = _ => Hotkey.Invalid;
public Hotkey Key
{
get { return GetKey(this); }
set { GetKey = _ => value; }
}
public string Text = "";
[Translate] public string Text = "";
public bool Depressed = false;
public int VisualHeight = ChromeMetrics.Get<int>("ButtonDepth");
public string Font = ChromeMetrics.Get<string>("ButtonFont");
@@ -91,7 +91,7 @@ namespace OpenRA.Widgets
public override bool HandleKeyPress(KeyInput e)
{
if (e.KeyName != Key || e.Event != KeyInputEvent.Down)
if (Hotkey.FromKeyInput(e) != Key || e.Event != KeyInputEvent.Down)
return false;
if (!IsDisabled())

View File

@@ -49,7 +49,7 @@ namespace OpenRA.Widgets
{
if (e.Event == KeyInputEvent.Up) return false;
if (e.KeyName == "return" || e.KeyName == "enter" )
if (e.Key == Keycode.RETURN || e.Key == Keycode.KP_ENTER)
{
if (composing)
{
@@ -79,14 +79,14 @@ namespace OpenRA.Widgets
if (composing)
{
if (e.KeyName == "escape")
if (e.Key == Keycode.ESCAPE)
{
composing = false;
content = "";
YieldKeyboardFocus();
return true;
}
else if (e.KeyName == "backspace")
else if (e.Key == Keycode.BACKSPACE)
{
if (content.Length > 0)
content = content.Remove(content.Length - 1);

View File

@@ -71,7 +71,7 @@ namespace OpenRA.Widgets
// Mask to prevent any clicks from being sent to other widgets
fullscreenMask = new MaskWidget();
fullscreenMask.Bounds = new Rectangle(0, 0, Game.viewport.Width, Game.viewport.Height);
fullscreenMask.Bounds = new Rectangle(0, 0, Game.Renderer.Resolution.Width, Game.Renderer.Resolution.Height);
fullscreenMask.OnMouseDown += mi => RemovePanel();
if (onCancel != null)
fullscreenMask.OnMouseDown += _ => onCancel();

View File

@@ -0,0 +1,122 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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 COPYING.
*/
#endregion
using System;
using System.Drawing;
using System.Linq;
using OpenRA.Traits;
using OpenRA.Graphics;
namespace OpenRA.Widgets
{
public class HotkeyEntryWidget : Widget
{
public Hotkey Key;
public int VisualHeight = 1;
public int LeftMargin = 5;
public int RightMargin = 5;
public Action OnLoseFocus = () => { };
public Func<bool> IsDisabled = () => false;
public Color TextColor = Color.White;
public Color DisabledColor = Color.Gray;
public string Font = "Regular";
public HotkeyEntryWidget() : base() {}
protected HotkeyEntryWidget(HotkeyEntryWidget widget)
: base(widget)
{
Font = widget.Font;
TextColor = widget.TextColor;
DisabledColor = widget.DisabledColor;
VisualHeight = widget.VisualHeight;
}
public override bool YieldKeyboardFocus()
{
OnLoseFocus();
return base.YieldKeyboardFocus();
}
public override bool HandleMouseInput(MouseInput mi)
{
if (IsDisabled())
return false;
if (mi.Event != MouseInputEvent.Down)
return false;
// Attempt to take keyboard focus
if (!RenderBounds.Contains(mi.Location) || !TakeKeyboardFocus())
return false;
return true;
}
static readonly Keycode[] IgnoreKeys = new Keycode[]
{
Keycode.RSHIFT, Keycode.LSHIFT,
Keycode.RCTRL, Keycode.LCTRL,
Keycode.RALT, Keycode.LALT,
Keycode.RMETA, Keycode.LMETA
};
public override bool HandleKeyPress(KeyInput e)
{
if (IsDisabled() || e.Event == KeyInputEvent.Up)
return false;
if (!HasKeyboardFocus || IgnoreKeys.Contains(e.Key))
return false;
Key = Hotkey.FromKeyInput(e);
return true;
}
public override void Draw()
{
var apparentText = Key.DisplayString();
var font = Game.Renderer.Fonts[Font];
var pos = RenderOrigin;
var textSize = font.Measure(apparentText);
var disabled = IsDisabled();
var state = disabled ? "textfield-disabled" :
HasKeyboardFocus ? "textfield-focused" :
Ui.MouseOverWidget == this ? "textfield-hover" :
"textfield";
WidgetUtils.DrawPanel(state, RenderBounds);
// Inset text by the margin and center vertically
var textPos = pos + new int2(LeftMargin, (Bounds.Height - textSize.Y) / 2 - VisualHeight);
// Scissor when the text overflows
if (textSize.X > Bounds.Width - LeftMargin - RightMargin)
{
Game.Renderer.EnableScissor(pos.X + LeftMargin, pos.Y,
Bounds.Width - LeftMargin - RightMargin, Bounds.Bottom);
}
var color = disabled ? DisabledColor : TextColor;
font.DrawText(apparentText, textPos, color);
if (textSize.X > Bounds.Width - LeftMargin - RightMargin)
Game.Renderer.DisableScissor();
}
public override Widget Clone() { return new HotkeyEntryWidget(this); }
}
}

View File

@@ -10,6 +10,7 @@
using System;
using System.Drawing;
using OpenRA.FileFormats;
using OpenRA.Graphics;
namespace OpenRA.Widgets
@@ -19,7 +20,7 @@ namespace OpenRA.Widgets
public class LabelWidget : Widget
{
public string Text = null;
[Translate] public string Text = null;
public TextAlign Align = TextAlign.Left;
public TextVAlign VAlign = TextVAlign.Middle;
public string Font = "Regular";

View File

@@ -11,6 +11,7 @@
using System;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats.Primitives;
using OpenRA.Graphics;
namespace OpenRA.Widgets
@@ -61,7 +62,6 @@ namespace OpenRA.Widgets
public void ReplaceChild(Widget oldChild, Widget newChild)
{
oldChild.Removed();
newChild.Parent = this;
Children[Children.IndexOf(oldChild)] = newChild;
@@ -69,8 +69,6 @@ namespace OpenRA.Widgets
Scroll(0);
}
public override void DrawOuter()
{
if (!IsVisible())
@@ -85,7 +83,7 @@ namespace OpenRA.Widgets
if (thumbHeight == ScrollbarHeight)
thumbHeight = 0;
backgroundRect = new Rectangle(rb.X, rb.Y, rb.Width - ScrollbarWidth+1, rb.Height);
backgroundRect = new Rectangle(rb.X, rb.Y, rb.Width - ScrollbarWidth + 1, rb.Height);
upButtonRect = new Rectangle(rb.Right - ScrollbarWidth, rb.Y, ScrollbarWidth, ScrollbarWidth);
downButtonRect = new Rectangle(rb.Right - ScrollbarWidth, rb.Bottom - ScrollbarWidth, ScrollbarWidth, ScrollbarWidth);
scrollbarRect = new Rectangle(rb.Right - ScrollbarWidth, rb.Y + ScrollbarWidth - 1, ScrollbarWidth, ScrollbarHeight + 2);
@@ -145,6 +143,11 @@ namespace OpenRA.Widgets
ListOffset = 0;
}
public bool ScrolledToBottom
{
get { return ListOffset == Math.Min(0, Bounds.Height - ContentHeight); }
}
public void ScrollToItem(string itemKey)
{
var item = Children.FirstOrDefault(c =>
@@ -226,5 +229,116 @@ namespace OpenRA.Widgets
return UpPressed || DownPressed || ThumbPressed;
}
IObservableCollection collection;
Func<object, Widget> makeWidget;
Func<Widget, object, bool> widgetItemEquals;
bool autoScroll;
public void Unbind()
{
Bind(null, null, null, false);
}
public void Bind(IObservableCollection c, Func<object, Widget> makeWidget, Func<Widget, object, bool> widgetItemEquals, bool autoScroll)
{
this.autoScroll = autoScroll;
Game.RunAfterTick(() =>
{
if (collection != null)
{
collection.OnAdd -= BindingAdd;
collection.OnRemove -= BindingRemove;
collection.OnRemoveAt -= BindingRemoveAt;
collection.OnSet -= BindingSet;
collection.OnRefresh -= BindingRefresh;
}
this.makeWidget = makeWidget;
this.widgetItemEquals = widgetItemEquals;
RemoveChildren();
collection = c;
if (c != null)
{
foreach (var item in c.ObservedItems)
BindingAddImpl(item);
c.OnAdd += BindingAdd;
c.OnRemove += BindingRemove;
c.OnRemoveAt += BindingRemoveAt;
c.OnSet += BindingSet;
c.OnRefresh += BindingRefresh;
}
});
}
void BindingAdd(object item)
{
Game.RunAfterTick(() => BindingAddImpl(item));
}
void BindingAddImpl(object item)
{
var widget = makeWidget(item);
var scrollToBottom = autoScroll && ScrolledToBottom;
AddChild(widget);
if (scrollToBottom)
ScrollToBottom();
}
void BindingRemove(object item)
{
Game.RunAfterTick(() =>
{
var widget = Children.FirstOrDefault(w => widgetItemEquals(w, item));
if (widget != null)
RemoveChild(widget);
});
}
void BindingRemoveAt(int index)
{
Game.RunAfterTick(() =>
{
if (index < 0 || index >= Children.Count)
return;
RemoveChild(Children[index]);
});
}
void BindingSet(object oldItem, object newItem)
{
Game.RunAfterTick(() =>
{
var newWidget = makeWidget(newItem);
newWidget.Parent = this;
var i = Children.FindIndex(w => widgetItemEquals(w, oldItem));
if (i >= 0)
{
var oldWidget = Children[i];
oldWidget.Removed();
Children[i] = newWidget;
Layout.AdjustChildren();
}
else
AddChild(newWidget);
});
}
void BindingRefresh()
{
Game.RunAfterTick(() =>
{
RemoveChildren();
foreach (var item in collection.ObservedItems)
BindingAddImpl(item);
});
}
}
}

View File

@@ -33,7 +33,7 @@ namespace OpenRA.Widgets
public Func<bool> OnTabKey = () => false;
public Func<bool> OnEscKey = () => false;
public Action OnLoseFocus = () => { };
public int CursorPosition { get; protected set; }
public int CursorPosition { get; set; }
public Func<bool> IsDisabled = () => false;
public Color TextColor = Color.White;
@@ -110,16 +110,16 @@ namespace OpenRA.Widgets
if (!HasKeyboardFocus)
return false;
if ((e.KeyName == "return" || e.KeyName == "enter") && OnEnterKey())
if ((e.Key == Keycode.RETURN || e.Key == Keycode.KP_ENTER) && OnEnterKey())
return true;
if (e.KeyName == "tab" && OnTabKey())
if (e.Key == Keycode.TAB && OnTabKey())
return true;
if (e.KeyName == "escape" && OnEscKey())
if (e.Key == Keycode.ESCAPE && OnEscKey())
return true;
if (e.KeyName == "left")
if (e.Key == Keycode.LEFT)
{
if (CursorPosition > 0)
CursorPosition--;
@@ -127,7 +127,7 @@ namespace OpenRA.Widgets
return true;
}
if (e.KeyName == "right")
if (e.Key == Keycode.RIGHT)
{
if (CursorPosition <= Text.Length-1)
CursorPosition++;
@@ -135,19 +135,19 @@ namespace OpenRA.Widgets
return true;
}
if (e.KeyName == "home")
if (e.Key == Keycode.HOME)
{
CursorPosition = 0;
return true;
}
if (e.KeyName == "end")
if (e.Key == Keycode.END)
{
CursorPosition = Text.Length;
return true;
}
if (e.KeyName == "delete")
if (e.Key == Keycode.DELETE)
{
if (CursorPosition < Text.Length)
Text = Text.Remove(CursorPosition, 1);
@@ -160,7 +160,7 @@ namespace OpenRA.Widgets
public void TypeChar(KeyInput key)
{
if (key.KeyName == "backspace" && CursorPosition > 0)
if (key.Key == Keycode.BACKSPACE && CursorPosition > 0)
{
CursorPosition--;
Text = Text.Remove(CursorPosition, 1);

4
OpenRA.Game/Widgets/TooltipContainerWidget.cs Executable file → Normal file
View File

@@ -54,8 +54,8 @@ namespace OpenRA.Widgets
var pos = Viewport.LastMousePos + CursorOffset;
if (tooltip != null)
{
if (pos.X + tooltip.Bounds.Right > Game.viewport.Width)
pos.X = Game.viewport.Width - tooltip.Bounds.Right;
if (pos.X + tooltip.Bounds.Right > Game.Renderer.Resolution.Width)
pos.X = Game.Renderer.Resolution.Width - tooltip.Bounds.Right;
}
return pos;

38
OpenRA.Game/Widgets/ViewportControllerWidget.cs Executable file → Normal file
View File

@@ -57,12 +57,14 @@ namespace OpenRA.Widgets
ScrollDirection keyboardDirections;
ScrollDirection edgeDirections;
World world;
WorldRenderer worldRenderer;
[ObjectCreator.UseCtor]
public ViewportControllerWidget(World world, WorldRenderer worldRenderer)
: base()
{
this.world = world;
this.worldRenderer = worldRenderer;
tooltipContainer = Lazy.New(() =>
Ui.Root.Get<TooltipContainerWidget>(TooltipContainer));
}
@@ -93,7 +95,7 @@ namespace OpenRA.Widgets
public void UpdateMouseover()
{
TooltipType = WorldTooltipType.None;
var cell = Game.viewport.ViewToWorld(Viewport.LastMousePos);
var cell = worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos);
if (!world.Map.IsInMap(cell))
return;
@@ -103,8 +105,8 @@ namespace OpenRA.Widgets
return;
}
var underCursor = world.FindUnitsAtMouse(Viewport.LastMousePos)
.Where(a => a.HasTrait<IToolTip>())
var underCursor = world.ScreenMap.ActorsAt(worldRenderer.Viewport.ViewToWorldPx(Viewport.LastMousePos))
.Where(a => !world.FogObscures(a) && a.HasTrait<IToolTip>())
.OrderByDescending(a => a.Info.SelectionPriority())
.FirstOrDefault();
@@ -115,7 +117,7 @@ namespace OpenRA.Widgets
return;
}
var frozen = world.FindFrozenActorsAtMouse(Viewport.LastMousePos)
var frozen = world.ScreenMap.FrozenActorsAt(world.RenderPlayer, worldRenderer.Viewport.ViewToWorldPx(Viewport.LastMousePos))
.Where(a => a.TooltipName != null)
.OrderByDescending(a => a.Info.SelectionPriority())
.FirstOrDefault();
@@ -127,14 +129,14 @@ namespace OpenRA.Widgets
}
}
public static string GetScrollCursor(Widget w, ScrollDirection edge, int2 pos)
public override string GetCursor(int2 pos)
{
if (!Game.Settings.Game.ViewportEdgeScroll || Ui.MouseOverWidget != w)
if (!Game.Settings.Game.ViewportEdgeScroll || Ui.MouseOverWidget != this)
return null;
var blockedDirections = Game.viewport.GetBlockedDirections();
var blockedDirections = worldRenderer.Viewport.GetBlockedDirections();
foreach (var dir in ScrollCursors)
if (edge.Includes(dir.Key))
if (edgeDirections.Includes(dir.Key))
return dir.Value + (blockedDirections.Includes(dir.Key) ? "-blocked" : "");
return null;
@@ -150,15 +152,13 @@ namespace OpenRA.Widgets
(mi.Button == MouseButton.Middle || mi.Button == (MouseButton.Left | MouseButton.Right)))
{
var d = scrolltype == MouseScrollType.Inverted ? -1 : 1;
Game.viewport.Scroll((Viewport.LastMousePos - mi.Location) * d);
worldRenderer.Viewport.Scroll((Viewport.LastMousePos - mi.Location) * d, false);
return true;
}
return false;
}
public override string GetCursor(int2 pos) { return GetScrollCursor(this, edgeDirections, pos); }
public override bool YieldKeyboardFocus()
{
keyboardDirections = ScrollDirection.None;
@@ -167,12 +167,12 @@ namespace OpenRA.Widgets
public override bool HandleKeyPress(KeyInput e)
{
switch (e.KeyName)
switch (e.Key)
{
case "up": keyboardDirections = keyboardDirections.Set(ScrollDirection.Up, e.Event == KeyInputEvent.Down); return true;
case "down": keyboardDirections = keyboardDirections.Set(ScrollDirection.Down, e.Event == KeyInputEvent.Down); return true;
case "left": keyboardDirections = keyboardDirections.Set(ScrollDirection.Left, e.Event == KeyInputEvent.Down); return true;
case "right": keyboardDirections = keyboardDirections.Set(ScrollDirection.Right, e.Event == KeyInputEvent.Down); return true;
case Keycode.UP: keyboardDirections = keyboardDirections.Set(ScrollDirection.Up, e.Event == KeyInputEvent.Down); return true;
case Keycode.DOWN: keyboardDirections = keyboardDirections.Set(ScrollDirection.Down, e.Event == KeyInputEvent.Down); return true;
case Keycode.LEFT: keyboardDirections = keyboardDirections.Set(ScrollDirection.Left, e.Event == KeyInputEvent.Down); return true;
case Keycode.RIGHT: keyboardDirections = keyboardDirections.Set(ScrollDirection.Right, e.Event == KeyInputEvent.Down); return true;
}
return false;
@@ -195,7 +195,7 @@ namespace OpenRA.Widgets
var length = Math.Max(1, scroll.Length);
scroll *= (1f / length) * Game.Settings.Game.ViewportEdgeScrollStep;
Game.viewport.Scroll(scroll);
worldRenderer.Viewport.Scroll(scroll, false);
}
}
@@ -206,9 +206,9 @@ namespace OpenRA.Widgets
directions |= ScrollDirection.Left;
if (Viewport.LastMousePos.Y < EdgeScrollThreshold)
directions |= ScrollDirection.Up;
if (Viewport.LastMousePos.X >= Game.viewport.Width - EdgeScrollThreshold)
if (Viewport.LastMousePos.X >= Game.Renderer.Resolution.Width - EdgeScrollThreshold)
directions |= ScrollDirection.Right;
if (Viewport.LastMousePos.Y >= Game.viewport.Height - EdgeScrollThreshold)
if (Viewport.LastMousePos.Y >= Game.Renderer.Resolution.Height - EdgeScrollThreshold)
directions |= ScrollDirection.Down;
return directions;

View File

@@ -108,7 +108,7 @@ namespace OpenRA.Widgets
{
if (e.Event == KeyInputEvent.Down)
{
if (e.KeyName == "escape")
if (e.Key == Keycode.ESCAPE)
{
Stop();
return true;

View File

@@ -188,15 +188,15 @@ namespace OpenRA.Widgets
{
// Parse the YAML equations to find the widget bounds
var parentBounds = (Parent == null)
? new Rectangle(0, 0, Game.viewport.Width, Game.viewport.Height)
? new Rectangle(0, 0, Game.Renderer.Resolution.Width, Game.Renderer.Resolution.Height)
: Parent.Bounds;
var substitutions = args.ContainsKey("substitutions") ?
new Dictionary<string, int>((Dictionary<string, int>)args["substitutions"]) :
new Dictionary<string, int>();
substitutions.Add("WINDOW_RIGHT", Game.viewport.Width);
substitutions.Add("WINDOW_BOTTOM", Game.viewport.Height);
substitutions.Add("WINDOW_RIGHT", Game.Renderer.Resolution.Width);
substitutions.Add("WINDOW_BOTTOM", Game.Renderer.Resolution.Height);
substitutions.Add("PARENT_RIGHT", parentBounds.Width);
substitutions.Add("PARENT_LEFT", parentBounds.Left);
substitutions.Add("PARENT_TOP", parentBounds.Top);

View File

@@ -23,6 +23,7 @@ namespace OpenRA.Widgets
{
protected readonly World world;
readonly WorldRenderer worldRenderer;
int2 dragStart, dragEnd;
[ObjectCreator.UseCtor]
public WorldInteractionControllerWidget(World world, WorldRenderer worldRenderer)
@@ -47,11 +48,9 @@ namespace OpenRA.Widgets
worldRenderer.DrawRollover(u);
}
PPos dragStart, dragEnd;
public override bool HandleMouseInput(MouseInput mi)
{
var xy = Game.viewport.ViewToWorldPx(mi);
var xy = worldRenderer.Viewport.ViewToWorldPx(mi.Location);
var UseClassicMouseStyle = Game.Settings.Game.UseClassicMouseStyle;
@@ -90,13 +89,9 @@ namespace OpenRA.Widgets
{
if (MultiClick)
{
var unit = SelectActorsInBox(world, xy, xy, _ => true).FirstOrDefault();
var visibleWorld = Game.viewport.ViewBounds(world);
var topLeft = Game.viewport.ViewToWorldPx(new int2(visibleWorld.Left, visibleWorld.Top));
var bottomRight = Game.viewport.ViewToWorldPx(new int2(visibleWorld.Right, visibleWorld.Bottom));
var newSelection2= SelectActorsInBox(world, topLeft, bottomRight,
a => unit != null && a.Info.Name == unit.Info.Name && a.Owner == unit.Owner);
var unit = world.ScreenMap.ActorsAt(xy).FirstOrDefault();
var newSelection2 = SelectActorsInBox(world, worldRenderer.Viewport.TopLeft, worldRenderer.Viewport.BottomRight,
a => unit != null && a.Info.Name == unit.Info.Name && a.Owner == unit.Owner);
world.Selection.Combine(world, newSelection2, true, false);
}
@@ -127,7 +122,7 @@ namespace OpenRA.Widgets
}
public Pair<PPos, PPos>? SelectionBox
public Pair<int2, int2>? SelectionBox
{
get
{
@@ -136,56 +131,62 @@ namespace OpenRA.Widgets
}
}
public void ApplyOrders(World world, PPos xy, MouseInput mi)
void ApplyOrders(World world, int2 xy, MouseInput mi)
{
if (world.OrderGenerator == null) return;
var orders = world.OrderGenerator.Order(world, xy.ToCPos(), mi).ToArray();
var pos = worldRenderer.Position(xy);
var orders = world.OrderGenerator.Order(world, pos.ToCPos(), mi).ToArray();
orders.Do(o => world.IssueOrder(o));
world.PlayVoiceForOrders(orders);
}
public override string GetCursor(int2 pos)
public override string GetCursor(int2 screenPos)
{
return Sync.CheckSyncUnchanged(world, () =>
{
// Always show an arrow while selecting
if (SelectionBox != null)
return null; /* always show an arrow while selecting */
return null;
var xy = worldRenderer.Viewport.ViewToWorldPx(screenPos);
var pos = worldRenderer.Position(xy);
var cell = pos.ToCPos();
var mi = new MouseInput
{
Location = pos,
Location = screenPos,
Button = Game.mouseButtonPreference.Action,
Modifiers = Game.GetModifierKeys()
};
// TODO: fix this up.
return world.OrderGenerator.GetCursor(world, Game.viewport.ViewToWorld(mi), mi);
} );
return world.OrderGenerator.GetCursor(world, cell, mi);
});
}
public override bool HandleKeyPress(KeyInput e)
{
if (e.Event == KeyInputEvent.Down)
{
if (e.KeyName.Length == 1 && char.IsDigit(e.KeyName[0]))
if (e.Key >= Keycode.NUMBER_0 && e.Key <= Keycode.NUMBER_9)
{
world.Selection.DoControlGroup(world, e.KeyName[0] - '0', e.Modifiers, e.MultiTapCount);
var group = (int)e.Key - (int)Keycode.NUMBER_0;
world.Selection.DoControlGroup(world, worldRenderer, group, e.Modifiers, e.MultiTapCount);
return true;
}
// Disable pausing for spectators
else if (e.KeyName == Game.Settings.Keys.PauseKey && world.LocalPlayer != null)
else if (Hotkey.FromKeyInput(e) == Game.Settings.Keys.PauseKey && world.LocalPlayer != null)
world.SetPauseState(!world.Paused);
}
return false;
}
static readonly Actor[] NoActors = {};
IEnumerable<Actor> SelectActorsInBox(World world, PPos a, PPos b, Func<Actor, bool> cond)
IEnumerable<Actor> SelectActorsInBox(World world, int2 a, int2 b, Func<Actor, bool> cond)
{
return world.FindActorsInBox(a.ToWPos(0), b.ToWPos(0))
return world.ScreenMap.ActorsInBox(a, b)
.Where(x => x.HasTrait<Selectable>() && x.Trait<Selectable>().Info.Selectable && !world.FogObscures(x) && cond(x))
.GroupBy(x => x.GetSelectionPriority())
.OrderByDescending(g => g.Key)

View File

@@ -79,6 +79,7 @@ namespace OpenRA
public readonly Map Map;
public readonly TileSet TileSet;
public readonly ActorMap ActorMap;
public readonly ScreenMap ScreenMap;
public void IssueOrder( Order o ) { orderManager.IssueOrder( o ); } /* avoid exposing the OM to mod code */
@@ -121,8 +122,9 @@ namespace OpenRA
TileSet = Rules.TileSets[Map.Tileset];
SharedRandom = new XRandom(orderManager.LobbyInfo.GlobalSettings.RandomSeed);
WorldActor = CreateActor( "World", new TypeDictionary() );
ActorMap = new ActorMap(this);
WorldActor = CreateActor("World", new TypeDictionary());
ActorMap = WorldActor.Trait<ActorMap>();
ScreenMap = WorldActor.Trait<ScreenMap>();
// Add players
foreach (var cmp in WorldActor.TraitsImplementing<ICreatePlayers>())
@@ -135,8 +137,12 @@ namespace OpenRA
p.Stances[q] = Stance.Neutral;
Sound.SoundVolumeModifier = 1.0f;
}
public void LoadComplete(WorldRenderer wr)
{
foreach (var wlh in WorldActor.TraitsImplementing<IWorldLoaded>())
wlh.WorldLoaded(this);
wlh.WorldLoaded(this, wr);
}
public Actor CreateActor( string name, TypeDictionary initDict )

View File

@@ -21,38 +21,15 @@ namespace OpenRA
{
public static class WorldUtils
{
public static IEnumerable<Actor> FindUnitsAtMouse(this World world, int2 mouseLocation)
{
var loc = Game.viewport.ViewToWorldPx(mouseLocation).ToWPos(0);
return FindActorsInBox(world, loc, loc).Where(a => !world.FogObscures(a));
}
public static readonly IEnumerable<FrozenActor> NoFrozenActors = new FrozenActor[0].AsEnumerable();
public static IEnumerable<FrozenActor> FindFrozenActorsAtMouse(this World world, int2 mouseLocation)
{
if (world.RenderPlayer == null)
return NoFrozenActors;
var frozenLayer = world.RenderPlayer.PlayerActor.TraitOrDefault<FrozenActorLayer>();
if (frozenLayer == null)
return NoFrozenActors;
var loc = Game.viewport.ViewToWorldPx(mouseLocation).ToInt2();
return frozenLayer.FrozenActorsAt(loc);
}
public static IEnumerable<Actor> FindActorsInBox(this World world, CPos tl, CPos br)
{
// TODO: Support diamond boxes for isometric maps?
return world.FindActorsInBox(tl.TopLeft, br.BottomRight);
}
public static IEnumerable<Actor> FindActorsInBox(this World world, WPos tl, WPos br)
{
var a = PPos.FromWPos(tl);
var b = PPos.FromWPos(br);
var u = PPos.Min(a, b);
var v = PPos.Max(a, b);
return world.WorldActor.Trait<SpatialBins>().ActorsInBox(u,v);
return world.ActorMap.ActorsInBox(tl, br);
}
public static Actor ClosestTo(this IEnumerable<Actor> actors, Actor a)
@@ -73,13 +50,8 @@ namespace OpenRA
// Target ranges are calculated in 2D, so ignore height differences
var vec = new WVec(r, r, WRange.Zero);
var rSq = r.Range*r.Range;
return world.FindActorsInBox(origin - vec, origin + vec).Where(a =>
{
var pos = a.CenterPosition;
var dx = (long)(pos.X - origin.X);
var dy = (long)(pos.Y - origin.Y);
return dx*dx + dy*dy <= rSq;
});
return world.FindActorsInBox(origin - vec, origin + vec).Where(
a => (a.CenterPosition - origin).HorizontalLengthSquared <= rSq);
}
}

27
OpenRA.Irc/Channel.cs Normal file
View File

@@ -0,0 +1,27 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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 COPYING.
*/
#endregion
using System;
using OpenRA.FileFormats.Primitives;
namespace OpenRA.Irc
{
public class Channel
{
public readonly string Name;
public readonly ObservableSortedDictionary<string, User> Users = new ObservableSortedDictionary<string, User>(StringComparer.OrdinalIgnoreCase);
public Topic Topic = new Topic();
public Channel(string name)
{
Name = name;
}
}
}

402
OpenRA.Irc/IrcClient.cs Normal file
View File

@@ -0,0 +1,402 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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 COPYING.
*/
#endregion
using System;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Threading;
using OpenRA.FileFormats.Primitives;
namespace OpenRA.Irc
{
public class IrcClient : IDisposable
{
public static readonly IrcClient Instance = new IrcClient();
public static string MainChannel { get { return '#' + Game.Settings.Irc.Channel; } }
public static void AddHistory(string line)
{
Instance.History.Add("{0} {1}".F(DateTime.Now.ToString(Game.Settings.Irc.TimestampFormat), line));
}
public static void AddMessage(string nickname, string message)
{
AddHistory("{0}: {1}".F(nickname, message));
}
public static void AddNotice(string nickname, string message)
{
AddHistory("-{0}- {1}".F(nickname, message));
}
public static void AddSelfNotice(string nickname, string message)
{
AddHistory("-> -{0}- {1}".F(nickname, message));
}
public static void AddAction(string nickname, string message)
{
AddHistory("* {0} {1}".F(nickname, message));
}
static void InstanceInitialize()
{
var s = Game.Settings.Irc;
Instance.OnPublicMessage += l =>
{
var action = IrcUtils.FromAction(l.Message);
if (action != null)
AddAction(l.Prefix.Nickname, action);
else
AddMessage(l.Prefix.Nickname, l.Message);
};
Instance.OnPrivateMessage += l =>
{
var ctcp = IrcUtils.FromCtcp(l.Message);
if (ctcp == null)
return;
var split = ctcp.Split(new[] { ' ' }, 2);
var command = split[0];
if (command.EqualsIC("VERSION"))
{
var mod = Game.CurrentMods.Values.FirstOrDefault();
if (mod == null)
return;
Instance.CtcpRespond(l.Prefix.Nickname, command, "{0}: {1}".F(mod.Title, mod.Version));
}
};
Instance.OnPrivateNotice += l =>
{
if (l.Target == "*") // Drop pre-register notices
return;
AddNotice(l.Prefix.Nickname, l.Message);
};
Instance.OnRegister += l =>
{
Instance.Join(MainChannel);
Game.Settings.Irc.Nickname = Instance.LocalUser.Nickname;
Game.Settings.Save();
};
Instance.OnConnecting += () => AddHistory("Connecting");
Instance.OnConnect += () => AddHistory("Connected");
Instance.OnPart += l => AddHistory("{0} left{1}".F(l.Prefix.Nickname, l.Message != null ? ": " + l.Message : ""));
Instance.OnJoin += l => AddHistory("{0} joined".F(l.Prefix.Nickname));
Instance.OnQuit += l => AddHistory("{0} quit{1}".F(l.Prefix.Nickname, l.Message != null ? ": " + l.Message : ""));
Instance.OnKick += l => AddHistory("{0} kicked {1}{2}".F(l.Prefix.Nickname, l.KickeeNickname, l.Message != null ? ": " + l.Message : ""));
Instance.OnNicknameSet += l =>
{
AddHistory("{0} set their nickname to {1}".F(l.Prefix.Nickname, l.NewNickname));
if (l.NewNickname == Instance.LocalUser.Nickname)
{
Instance.Nickname = l.NewNickname;
Game.Settings.Irc.Nickname = l.NewNickname;
Game.Settings.Save();
}
};
Instance.OnTopicSet += l => AddHistory("{0} set the topic to {1}".F(l.Prefix.Nickname, l.Message));
Instance.OnNumeric += l =>
{
if (l.Numeric == NumericCommand.RPL_TOPIC)
{
var topic = Instance.GetChannel(MainChannel).Topic;
AddHistory("Topic is {0}".F(topic.Message));
}
else if (l.Numeric == NumericCommand.RPL_TOPICWHOTIME)
{
var topic = Instance.GetChannel(MainChannel).Topic;
AddHistory("Topic set by {0} at {1}".F(topic.Author.Nickname, topic.Time.ToLocalTime()));
}
else if (l.Numeric == NumericCommand.RPL_NOTOPIC)
AddHistory("No topic is set");
else if (l.Numeric == NumericCommand.ERR_NICKNAMEINUSE)
AddHistory("Nickname {0} is already in use".F(l.AltTarget));
else if (l.Numeric == NumericCommand.ERR_ERRONEUSNICKNAME)
AddHistory("Nickname {0} is erroneus".F(l.AltTarget));
};
Instance.OnDisconnect += () =>
{
if (Instance.ConnectionFailure != null)
{
AddHistory("Disconnected: {0}".F(Instance.ConnectionFailure.Message));
if (s.ReconnectDelay >= 0)
{
AddHistory("Reconnecting in {0} seconds".F(s.ReconnectDelay / 1000));
Instance.ConnectionState = IrcConnectionState.Reconnecting;
Game.RunAfterDelay(s.ReconnectDelay, () =>
{
if (Instance.IsReconnecting)
Instance.Connect(Instance.Hostname, Instance.Port, Instance.ConnectionTimeout, Instance.Nickname, Instance.Username, Instance.Realname);
});
}
}
else
AddHistory("Disconnected");
};
Instance.OnLineRead += l =>
{
if (s.Debug)
AddHistory(l.RawString);
};
Game.OnQuit += Instance.Disconnect;
}
static IrcClient()
{
Log.AddChannel("irc", "irc.log");
InstanceInitialize();
}
public readonly ObservableCollection<string> History = new ObservableCollection<string>();
IrcConnection connection;
Thread thread;
public IrcConnectionState ConnectionState { get; private set; }
public IrcClientUser LocalUser { get; private set; }
public Exception ConnectionFailure { get; private set; }
public string Hostname { get; private set; }
public int Port { get; private set; }
public int ConnectionTimeout { get; private set; }
public string Nickname { get; private set; }
public string Username { get; private set; }
public string Realname { get; private set; }
public bool IsConnected
{
get { return ConnectionState == IrcConnectionState.Connected; }
}
public bool IsReconnecting
{
get { return ConnectionState == IrcConnectionState.Reconnecting; }
}
public IrcClient()
{
ConnectionState = IrcConnectionState.Disconnected;
}
public void Connect(string hostname, int port, int connectionTimeout, string nickname, string username, string realname)
{
ConnectionFailure = null;
if (IsConnected)
Disconnect();
Hostname = hostname;
Port = port;
ConnectionTimeout = connectionTimeout;
Nickname = nickname;
Username = username;
Realname = realname;
thread = new Thread(() =>
{
try
{
ConnectionState = IrcConnectionState.Connecting;
LocalUser = new IrcClientUser(this);
connection = new IrcConnection();
OnConnecting();
connection.Connect(hostname, port, connectionTimeout);
ConnectionState = IrcConnectionState.Connected;
OnConnect();
SetNickname(nickname);
SetUser(username, realname);
ProcessLines();
}
catch (Exception e)
{
Log.Write("irc", e.ToString());
if (e is SocketException || e is IOException)
ConnectionFailure = e;
}
finally
{
Disconnect();
}
}) { IsBackground = true };
thread.Start();
}
public void WriteLine(string format, params object[] args)
{
try
{
connection.WriteLine(format, args);
}
catch (Exception e)
{
Log.Write("irc", e.ToString());
if (e is SocketException || e is IOException)
ConnectionFailure = e;
Disconnect();
}
}
public void Disconnect()
{
if (!IsConnected || IsReconnecting)
{
ConnectionState = IrcConnectionState.Disconnected;
return;
}
ConnectionState = IrcConnectionState.Disconnecting;
OnDisconnecting();
connection.Close();
ConnectionState = IrcConnectionState.Disconnected;
OnDisconnect();
LocalUser = null;
connection = null;
}
void IDisposable.Dispose()
{
Disconnect();
}
void ProcessLines()
{
string line;
while (IsConnected && (line = connection.ReadLine()) != null)
ProcessLine(line);
}
void ProcessLine(string line)
{
if (string.IsNullOrEmpty(line))
return;
var l = new Line(this, line);
OnLineRead(l);
int numeric;
if (int.TryParse(l.Command, out numeric))
{
var nl = new NumericLine(l, numeric);
LocalUser.OnNumeric(nl);
OnNumeric(nl);
switch (nl.Numeric)
{
case NumericCommand.RPL_WELCOME:
OnRegister(nl);
break;
case NumericCommand.RPL_ENDOFNAMES:
OnSync(nl);
break;
}
}
else
{
switch (l.Command)
{
case "PING":
Pong(l.Message);
OnPing(l);
break;
case "PRIVMSG":
if (IrcUtils.IsChannel(l.Target))
OnPublicMessage(l);
else
OnPrivateMessage(l);
break;
case "NOTICE":
if (IrcUtils.IsChannel(l.Target))
OnPublicNotice(l);
else
OnPrivateNotice(l);
break;
case "JOIN":
var jl = new JoinLine(l);
LocalUser.OnJoin(jl);
OnJoin(jl);
break;
case "PART":
LocalUser.OnPart(l);
OnPart(l);
break;
case "NICK":
var nsl = new NicknameSetLine(l);
LocalUser.OnNicknameSet(nsl);
OnNicknameSet(nsl);
break;
case "QUIT":
OnQuit(l);
LocalUser.OnQuit(l);
break;
case "KICK":
var kl = new KickLine(l);
LocalUser.OnKick(kl);
OnKick(kl);
break;
case "TOPIC":
LocalUser.OnTopicSet(l);
OnTopicSet(l);
break;
}
}
}
public event Action<NumericLine> OnRegister = l => { };
public event Action<NumericLine> OnSync = l => { };
public event Action<Line> OnLineRead = _ => { };
public event Action OnConnect = () => { };
public event Action OnConnecting = () => { };
public event Action OnDisconnect = () => { };
public event Action OnDisconnecting = () => { };
public event Action<Line> OnPublicMessage = _ => { };
public event Action<Line> OnPublicNotice = _ => { };
public event Action<Line> OnPrivateMessage = _ => { };
public event Action<Line> OnPrivateNotice = _ => { };
public event Action<JoinLine> OnJoin = _ => { };
public event Action<Line> OnPart = _ => { };
public event Action<NicknameSetLine> OnNicknameSet = _ => { };
public event Action<Line> OnQuit = _ => { };
public event Action<Line> OnPing = _ => { };
public event Action<NumericLine> OnNumeric = _ => { };
public event Action<KickLine> OnKick = _ => { };
public event Action<Line> OnTopicSet = _ => { };
public void SetNickname(string nickname) { WriteLine("NICK {0}", nickname); }
public void SetUser(string username, string realname) { WriteLine("USER {0} 0 * :{1}", username, realname); }
public void Join(string channel) { WriteLine("JOIN {0}", channel); }
public void Part(string channel) { WriteLine("PART {0}", channel); }
public void Message(string target, string message) { WriteLine("PRIVMSG {0} :{1}", target, message); }
public void Notice(string target, string message) { WriteLine("NOTICE {0} :{1}", target, message); }
public void Pong(string reply) { WriteLine("PONG :{0}", reply); }
public void CtcpRequest(string target, string command, string request) { Message(target, IrcUtils.ToCtcp("{0} {1}".F(command, request))); }
public void CtcpRespond(string target, string command, string response) { Notice(target, IrcUtils.ToCtcp("{0} {1}".F(command, response))); }
public void Act(string target, string message) { Message(target, IrcUtils.ToAction(message)); }
public void GetTopic(string channel) { WriteLine("TOPIC {0}", channel); }
public void Quit(string message) { WriteLine("QUIT :{0}", message); }
public Channel GetChannel(string channel)
{
if (!IsConnected)
return null;
Channel c;
LocalUser.Channels.TryGetValue(channel, out c);
return c;
}
}
public enum IrcConnectionState
{
Disconnected,
Connected,
Disconnecting,
Connecting,
Reconnecting
}
}

112
OpenRA.Irc/IrcClientUser.cs Normal file
View File

@@ -0,0 +1,112 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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 COPYING.
*/
#endregion
using System;
using System.Linq;
using OpenRA.FileFormats.Primitives;
namespace OpenRA.Irc
{
public class IrcClientUser : User
{
public readonly ObservableDictionary<string, Channel> Channels = new ObservableDictionary<string, Channel>(StringComparer.OrdinalIgnoreCase);
public readonly IrcClient Client;
public IrcClientUser(IrcClient client)
{
Client = client;
}
public void OnNumeric(NumericLine line)
{
switch (line.Numeric)
{
case NumericCommand.RPL_WELCOME:
new User(line.Message.Substring(line.Message.LastIndexOf(' ') + 1)).CopyTo(this);
break;
case NumericCommand.RPL_NAMREPLY:
{
var channel = line.GetChannel();
var nicknames = line.Message.Replace("~", "").Replace("&", "").Replace("@", "").Replace("%", "").Replace("+", "").Split(' ');
foreach (var nickname in nicknames.Where(n => !channel.Users.ContainsKey(n)))
channel.Users.Add(nickname, new User { Nickname = nickname });
}
break;
case NumericCommand.RPL_TOPIC:
line.GetChannel().Topic.Message = line.Message;
break;
case NumericCommand.RPL_TOPICWHOTIME:
{
var topic = line.GetChannel().Topic;
topic.Author = new User(line[4]);
topic.Time = IrcUtils.DateTimeFromUnixTime(int.Parse(line[5]));
}
break;
case NumericCommand.ERR_NICKNAMEINUSE:
if (line.Target == "*") // no nickname set yet
Client.SetNickname(Client.Nickname + new Random().Next(10000, 99999));
break;
}
}
public void OnJoin(Line line)
{
if (line.PrefixIsSelf())
Channels.Add(line.Target, new Channel(line.Target));
line.GetChannel().Users.Add(line.Prefix.Nickname, new User(line.Prefix));
}
public void OnPart(Line line)
{
line.GetChannel().Users.Remove(line.Prefix.Nickname);
if (line.PrefixIsSelf())
Channels.Remove(line.Target);
}
public void OnNicknameSet(NicknameSetLine line)
{
if (line.PrefixIsSelf())
Nickname = line.NewNickname;
foreach (var channel in Channels.Values.Where(c => c.Users.ContainsKey(line.Prefix.Nickname)))
{
var user = channel.Users[line.Prefix.Nickname];
channel.Users.Remove(line.Prefix.Nickname);
user.Nickname = line.NewNickname;
channel.Users.Add(line.NewNickname, user);
}
}
public void OnQuit(Line line)
{
foreach (var channel in Channels)
channel.Value.Users.Remove(line.Prefix.Nickname);
}
public void OnKick(KickLine line)
{
line.GetChannel().Users.Remove(line.KickeeNickname);
if (line.KickeeNickname.EqualsIC(Nickname))
Channels.Remove(line.Target);
}
public void OnTopicSet(Line line)
{
var topic = line.GetChannel().Topic;
topic.Message = line.Message;
topic.Author = line.Prefix;
topic.Time = DateTime.UtcNow;
}
}
}

View File

@@ -0,0 +1,83 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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 COPYING.
*/
#endregion
using System;
using System.IO;
using System.Net.Sockets;
namespace OpenRA.Irc
{
public class IrcConnection : IDisposable
{
TcpClient socket;
Stream stream;
StreamWriter writer;
StreamReader reader;
bool disposed;
public void Connect(string hostname, int port, int connectionTimeout)
{
CheckDisposed();
if (socket != null && socket.Connected)
throw new InvalidOperationException("Socket already connected");
socket = new TcpClient(hostname, port);
socket.ReceiveTimeout = socket.SendTimeout = connectionTimeout;
stream = socket.GetStream();
writer = new StreamWriter(stream) { AutoFlush = true };
reader = new StreamReader(stream);
}
public void WriteLine(string format, params object[] args)
{
CheckDisposed();
writer.WriteLine(format, args);
}
public string ReadLine()
{
CheckDisposed();
return reader.ReadLine();
}
public void Close()
{
CloseImpl();
GC.SuppressFinalize(this);
}
void CloseImpl()
{
if (disposed) return;
disposed = true;
if (socket != null) socket.Close();
if (stream != null) stream.Close();
if (writer != null) writer.Close();
if (reader != null) reader.Close();
}
void IDisposable.Dispose()
{
Close();
}
~IrcConnection()
{
CloseImpl();
}
void CheckDisposed()
{
if (disposed)
throw new ObjectDisposedException(GetType().FullName);
}
}
}

67
OpenRA.Irc/IrcUtils.cs Normal file
View File

@@ -0,0 +1,67 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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 COPYING.
*/
#endregion
using System;
using System.Linq;
namespace OpenRA.Irc
{
public static class IrcUtils
{
public static bool IsChannel(string s)
{
return !string.IsNullOrEmpty(s) && s[0] == '#';
}
public static bool IsNickname(string s)
{
return !string.IsNullOrEmpty(s) && (char.IsLetter(s[0]) || NicknameSpecialChars.Contains(s[0]))
&& s.Substring(1).All(c => char.IsLetterOrDigit(c) || NicknameSpecialChars.Contains(c) || c == '-');
}
const string NicknameSpecialChars = @"[]\`_^{|}";
public static DateTime DateTimeFromUnixTime(int seconds)
{
return new DateTime(1970, 1, 1).AddSeconds(seconds);
}
public static bool EqualsIC(this string a, string b)
{
return a.Equals(b, StringComparison.OrdinalIgnoreCase);
}
public static string FromCtcp(string message)
{
if (message.Length < 2 || !message.StartsWith("\x0001") || !message.EndsWith("\x0001"))
return null;
return message.Substring(1, message.Length - 2);
}
public static string ToCtcp(string message)
{
return "\x0001{0}\x0001".F(message);
}
public static string FromAction(string message)
{
if (!message.StartsWith("\x0001ACTION ") || !message.EndsWith("\x0001"))
return null;
return message.Substring(8, message.Length - 8 - 1);
}
public static string ToAction(string message)
{
return "\x0001ACTION {0}\x0001".F(message);
}
}
}

155
OpenRA.Irc/Line.cs Normal file
View File

@@ -0,0 +1,155 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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 COPYING.
*/
#endregion
using System.Linq;
namespace OpenRA.Irc
{
public class Line
{
public readonly IrcClient Client;
public readonly string RawString;
public readonly string[] RawStringParts;
public readonly User Prefix;
public readonly string Command;
public string Target { get; protected set; }
public string Message { get; protected set; }
public Line(Line line)
{
Client = line.Client;
RawString = line.RawString;
RawStringParts = line.RawStringParts;
Prefix = line.Prefix;
Command = line.Command;
Target = line.Target;
Message = line.Message;
}
public Line(IrcClient client, string line)
{
RawString = line;
RawStringParts = line.Split(' ');
Client = client;
if (line[0] == ':')
{
line = line.Substring(1);
var prefixDelim = line.Split(new[] { ' ' }, 2);
Prefix = new User(prefixDelim[0]);
if (prefixDelim.Length > 1)
{
var messageDelim = prefixDelim[1].Split(new[] { ':' }, 2);
var args = messageDelim[0].Trim().Split(' ');
Command = args[0];
if (args.Length > 1)
Target = args[1];
if (messageDelim.Length > 1)
Message = messageDelim[1];
}
}
else
{
var messageDelim = line.Split(new[] { ':' }, 2);
var args = messageDelim[0].Trim().Split(' ');
Command = args[0];
if (args.Length > 1)
Target = args[1];
if (messageDelim.Length > 1)
Message = messageDelim[1];
}
}
public virtual Channel GetChannel()
{
return Client.GetChannel(Target);
}
public string this[int index]
{
get { return RawStringParts[index]; }
}
public bool PrefixIsSelf()
{
return Client.LocalUser != null && Prefix.Nickname.EqualsIC(Client.LocalUser.Nickname);
}
public bool TargetIsSelf()
{
return Target != null && Target.EqualsIC(Client.LocalUser.Nickname);
}
}
public class NicknameSetLine : Line
{
public readonly string NewNickname;
public NicknameSetLine(Line line)
: base(line)
{
NewNickname = Message;
}
}
public class NumericLine : Line
{
public readonly NumericCommand Numeric;
public readonly string AltTarget;
public override Channel GetChannel()
{
if (IrcUtils.IsChannel(AltTarget))
return Client.GetChannel(AltTarget);
return Client.GetChannel(Target);
}
public NumericLine(Line line, int numeric)
: base(line)
{
if (!IrcUtils.IsChannel(Target))
{
var numericParts = line.RawStringParts.Skip(1).TakeWhile(p => !p.StartsWith(":"));
AltTarget = numericParts.LastOrDefault(IrcUtils.IsChannel);
if (AltTarget == null)
AltTarget = numericParts.LastOrDefault();
}
Numeric = (NumericCommand)numeric;
}
}
public class JoinLine : Line // for compatibility with certain IRCds
{
public JoinLine(Line line)
: base(line)
{
if (Message != null) // don't overwrite the target if it was already set properly by the IRCd
Target = Message;
}
}
public class KickLine : Line
{
public readonly string KickeeNickname;
public KickLine(Line line)
: base(line)
{
KickeeNickname = this[3];
}
}
}

View File

@@ -0,0 +1,25 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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 COPYING.
*/
#endregion
namespace OpenRA.Irc
{
public enum NumericCommand
{
Undefined = 0,
RPL_WELCOME = 001,
RPL_NOTOPIC = 331,
RPL_TOPIC = 332,
RPL_TOPICWHOTIME = 333,
RPL_NAMREPLY = 353,
RPL_ENDOFNAMES = 366,
ERR_ERRONEUSNICKNAME = 432,
ERR_NICKNAMEINUSE = 433
}
}

View File

@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{85B48234-8B31-4BE6-AF9C-665CC6866841}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>OpenRA.Irc</RootNamespace>
<AssemblyName>OpenRA.Irc</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Compile Include="Channel.cs" />
<Compile Include="IrcClient.cs" />
<Compile Include="IrcClientUser.cs" />
<Compile Include="IrcConnection.cs" />
<Compile Include="IrcUtils.cs" />
<Compile Include="Line.cs" />
<Compile Include="NumericCommand.cs" />
<Compile Include="Topic.cs" />
<Compile Include="User.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
<Project>{bdaeab25-991e-46a7-af1e-4f0e03358daa}</Project>
<Name>OpenRA.FileFormats</Name>
</ProjectReference>
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj">
<Project>{0dfb103f-2962-400f-8c6d-e2c28ccba633}</Project>
<Name>OpenRA.Game</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Drawing" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

30
OpenRA.Irc/Topic.cs Normal file
View File

@@ -0,0 +1,30 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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 COPYING.
*/
#endregion
using System;
namespace OpenRA.Irc
{
public class Topic
{
public string Message;
public User Author;
public DateTime Time;
public Topic() { }
public Topic(string message, User author, DateTime time)
{
Message = message;
Author = author;
Time = time;
}
}
}

73
OpenRA.Irc/User.cs Normal file
View File

@@ -0,0 +1,73 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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 COPYING.
*/
#endregion
using System;
namespace OpenRA.Irc
{
public class User
{
public string Nickname;
public string Username;
public string Hostname;
public User() { }
public User(User user)
{
Nickname = user.Nickname;
Username = user.Username;
Hostname = user.Hostname;
}
public void CopyTo(User user)
{
user.Nickname = Nickname;
user.Username = Username;
user.Hostname = Hostname;
}
public User(string prefix)
{
if (string.IsNullOrEmpty(prefix))
throw new ArgumentException();
var ex = prefix.IndexOf('!');
var at = prefix.IndexOf('@');
if (ex >= 0 && at >= 0 && at < ex)
throw new ArgumentException("Bogus input string: @ before !");
if (ex >= 0)
{
Nickname = prefix.Substring(0, ex);
if (at >= 0)
{
Username = prefix.Substring(ex + 1, at - ex - 1);
Hostname = prefix.Substring(at + 1);
}
else
Username = prefix.Substring(ex + 1);
}
else
Nickname = prefix;
}
public override string ToString()
{
var ret = "" + Nickname;
if (Username != null)
ret += "!" + Username;
if (Hostname != null)
ret += "@" + Hostname;
return ret;
}
}
}

View File

@@ -8,8 +8,8 @@
<ProjectGuid>{F9FA4D9F-2302-470A-8A07-6E37F488C124}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>RALint</RootNamespace>
<AssemblyName>RALint</AssemblyName>
<RootNamespace>OpenRA</RootNamespace>
<AssemblyName>OpenRA.Lint</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<FileUpgradeFlags>
@@ -36,7 +36,7 @@
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\</OutputPath>
<OutputPath>..</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
@@ -70,7 +70,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="RALint.cs" />
<Compile Include="YamlChecker.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">

View File

@@ -14,21 +14,21 @@ using OpenRA;
using OpenRA.FileFormats;
using OpenRA.Traits;
namespace RALint
namespace OpenRA.Lint
{
static class RALint
static class YamlChecker
{
static int errors = 0;
static void EmitError(string e)
{
Console.WriteLine("RALint(1,1): Error: {0}", e);
Console.WriteLine("OpenRA.Lint(1,1): Error: {0}", e);
++errors;
}
static void EmitWarning(string e)
{
Console.WriteLine("RALint(1,1): Warning: {0}", e);
Console.WriteLine("OpenRA.Lint(1,1): Warning: {0}", e);
}
static int Main(string[] args)

View File

@@ -39,7 +39,7 @@ namespace OpenRA.Mods.Cnc
if (r == null) return;
var s = new Sheet("mods/cnc/uibits/chrome.png");
var res = Renderer.Resolution;
var res = r.Resolution;
bounds = new Rectangle(0, 0, res.Width, res.Height);
ss = new[]

View File

@@ -11,6 +11,7 @@
using System;
using System.Collections.Generic;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Move;
using OpenRA.Traits;
@@ -21,14 +22,16 @@ namespace OpenRA.Mods.RA
class CncShellmapScript : IWorldLoaded, ITick
{
static CPos viewportOrigin;
WPos viewportOrigin;
Dictionary<string, Actor> actors;
WorldRenderer worldRenderer;
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
worldRenderer = wr;
var b = w.Map.Bounds;
viewportOrigin = new CPos(b.Left + b.Width / 2, b.Top + b.Height / 2);
Game.MoveViewport(viewportOrigin.ToFloat2());
viewportOrigin = new CPos(b.Left + b.Width / 2, b.Top + b.Height / 2).CenterPosition;
worldRenderer.Viewport.Center(viewportOrigin);
actors = w.WorldActor.Trait<SpawnMapActors>().Actors;
@@ -38,8 +41,8 @@ namespace OpenRA.Mods.RA
void SetViewport()
{
var t = (ticks + 45) % (360f * speed) * (Math.PI / 180) * 1f / speed;
var loc = viewportOrigin.ToFloat2() + (new float2(-15, 4) * float2.FromAngle((float)t));
Game.viewport.Center(loc);
var offset = new float2(-15360, 4096) * float2.FromAngle((float)t);
worldRenderer.Viewport.Center(viewportOrigin + new WVec((int)offset.X, (int)offset.Y, 0));
}
int ticks = 0;

View File

@@ -12,6 +12,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.RA;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Move;
@@ -28,12 +29,12 @@ namespace OpenRA.Mods.Cnc.Missions
Dictionary<string, Actor> actors;
Dictionary<string, Player> players;
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
players = w.Players.ToDictionary(p => p.InternalName);
actors = w.WorldActor.Trait<SpawnMapActors>().Actors;
var b = w.Map.Bounds;
Game.MoveViewport(new CPos(b.Left + b.Width / 2, b.Top + b.Height / 2).ToFloat2());
wr.Viewport.Center(new CPos(b.Left + b.Width / 2, b.Top + b.Height / 2).CenterPosition);
Action afterFMV = () =>
{

View File

@@ -12,6 +12,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.Cnc;
using OpenRA.Mods.RA;
using OpenRA.Mods.RA.Activities;
@@ -162,7 +163,7 @@ namespace OpenRA.Mods.Cnc.Missions
nr3.QueueActivity(nr3.Trait<Mobile>().ScriptedMove(nr3.Location - new CVec(0, -5)));
}
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
world = w;
nod = w.Players.Single(p => p.InternalName == "NOD");
@@ -192,13 +193,10 @@ namespace OpenRA.Mods.Cnc.Missions
nr1 = actors["NODReinforceNthA"];
nr2 = actors["NODReinforceNthB"];
nr3 = actors["NODReinforceNWstA"];
Game.MoveViewport(nr1.Location.ToFloat2());
Action afterFMV = () =>
{
MissionUtils.PlayMissionMusic();
};
wr.Viewport.Center(nr1.Location.CenterPosition);
Game.RunAfterDelay(0, () => Media.PlayFMVFullscreen(w, "nod1pre.vqa", () =>
Media.PlayFMVFullscreen(w, "nod1.vqa", afterFMV)));
Media.PlayFMVFullscreen(w, "nod1.vqa", MissionUtils.PlayMissionMusic)));
}
}
}

View File

@@ -43,7 +43,6 @@
<CustomCommands>
<CustomCommands>
<Command type="AfterBuild" command="cp ${TargetFile} ../mods/cnc" workingdir="${ProjectDir}" />
<Command type="AfterBuild" command="mono RALint.exe cnc" workingdir="${ProjectDir}/../" />
</CustomCommands>
</CustomCommands>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
@@ -94,7 +93,6 @@
<Compile Include="Widgets\Logic\CncIngameChromeLogic.cs" />
<Compile Include="Widgets\Logic\CncIngameMenuLogic.cs" />
<Compile Include="Widgets\Logic\CncInstallFromCDLogic.cs" />
<Compile Include="Widgets\Logic\CncInstallLogic.cs" />
<Compile Include="Widgets\Logic\CncMenuLogic.cs" />
<Compile Include="Widgets\Logic\CncSettingsLogic.cs" />
<Compile Include="Widgets\Logic\ProductionTooltipLogic.cs" />

View File

@@ -24,7 +24,7 @@ namespace OpenRA.Mods.Cnc.Widgets.Logic
var labelWidth = Game.Renderer.Fonts[label.Font].Measure(button.TooltipText).X;
label.Bounds.Width = labelWidth;
var hotkeyLabel = "({0})".F(button.Key.ToUpperInvariant());
var hotkeyLabel = "({0})".F(button.Key.DisplayString());
hotkey.GetText = () => hotkeyLabel;
hotkey.Bounds.X = labelWidth + 2 * label.Bounds.X;

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