Compare commits
238 Commits
playtest-2
...
playtest-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ed6faed62 | ||
|
|
1198ed9fe8 | ||
|
|
8b8d3edea2 | ||
|
|
d1a43a45f1 | ||
|
|
3db703a7ed | ||
|
|
20837c3776 | ||
|
|
807dc8b392 | ||
|
|
af0f8d09d6 | ||
|
|
5d2254aeee | ||
|
|
e54fd9f582 | ||
|
|
797f30446d | ||
|
|
9ecf678b04 | ||
|
|
431c956932 | ||
|
|
52ff778881 | ||
|
|
3181c46d27 | ||
|
|
fa2439f03e | ||
|
|
349ee8cfbf | ||
|
|
bb8f4494f5 | ||
|
|
88b8dd6c3e | ||
|
|
eff943b3eb | ||
|
|
70ba7fe5f6 | ||
|
|
8c8df3bda9 | ||
|
|
353393571f | ||
|
|
39076c98ac | ||
|
|
8b9f7a12fa | ||
|
|
f21f314bfe | ||
|
|
f51bd87332 | ||
|
|
39c7843654 | ||
|
|
4cacd074d7 | ||
|
|
d79b3d32d9 | ||
|
|
cc2bedc454 | ||
|
|
1ab4b9b72c | ||
|
|
59f5c8ed52 | ||
|
|
3ef6a8317a | ||
|
|
96d37df4b3 | ||
|
|
8aa76b07cb | ||
|
|
32673344a8 | ||
|
|
2c198e5d5c | ||
|
|
691602a5a1 | ||
|
|
a7b7b68a2e | ||
|
|
5a5bf8a8cc | ||
|
|
635f634b71 | ||
|
|
84bb78060f | ||
|
|
44dd801f16 | ||
|
|
483120ea20 | ||
|
|
839419635d | ||
|
|
d2d73a32a2 | ||
|
|
24b33891a7 | ||
|
|
623ef971bf | ||
|
|
aa7972755b | ||
|
|
a873f099ff | ||
|
|
7fb0c0cf8c | ||
|
|
c1af43da7f | ||
|
|
76c81b610a | ||
|
|
f4959853fa | ||
|
|
676db9753c | ||
|
|
d864989c19 | ||
|
|
68cd537c9b | ||
|
|
97a42e7d43 | ||
|
|
f3c7c0e957 | ||
|
|
d2eb42fbd2 | ||
|
|
5da2c319c1 | ||
|
|
9f1f6eae9d | ||
|
|
3769b845b6 | ||
|
|
42a4d30162 | ||
|
|
6598f0382a | ||
|
|
19eb685ecb | ||
|
|
7ff2e9d004 | ||
|
|
efa306a111 | ||
|
|
e3e7d0b38c | ||
|
|
e9652db486 | ||
|
|
61a26a636b | ||
|
|
209443d062 | ||
|
|
2062d26fea | ||
|
|
104a2d23cf | ||
|
|
24fdb489b9 | ||
|
|
d8c6acad45 | ||
|
|
d437b0949a | ||
|
|
d55ee5e037 | ||
|
|
0dea1d1471 | ||
|
|
1cca6f8fe5 | ||
|
|
e4666f3af3 | ||
|
|
6bbff98057 | ||
|
|
8df4f7b3dd | ||
|
|
f5d26bebb7 | ||
|
|
3af1e47744 | ||
|
|
b618fc7cc2 | ||
|
|
a6cdcea414 | ||
|
|
cdfc21af2c | ||
|
|
89a6d4f98e | ||
|
|
2d0028396a | ||
|
|
7604a60894 | ||
|
|
70b5cc8281 | ||
|
|
4a3847be65 | ||
|
|
c532a98333 | ||
|
|
fab6ada8b9 | ||
|
|
ebda91631e | ||
|
|
cec348c462 | ||
|
|
983697c70b | ||
|
|
f81982d607 | ||
|
|
14cf20aad5 | ||
|
|
19b649ba60 | ||
|
|
72a53808ed | ||
|
|
9ee9de01b1 | ||
|
|
86ad0e63e4 | ||
|
|
e09a7f4682 | ||
|
|
9b5b019a36 | ||
|
|
919181e04d | ||
|
|
936bf98496 | ||
|
|
fe8c80aca6 | ||
|
|
3ae75362bb | ||
|
|
1ca9c90565 | ||
|
|
0b560bfc6e | ||
|
|
f575c20d38 | ||
|
|
b3d608092c | ||
|
|
b7123cda7d | ||
|
|
65bbfbaef2 | ||
|
|
89da26a35a | ||
|
|
ecf9c260b6 | ||
|
|
98e1fe852a | ||
|
|
dee3a27328 | ||
|
|
dfc5afd1cd | ||
|
|
a503345c16 | ||
|
|
44503323f1 | ||
|
|
3ff71d5982 | ||
|
|
4f354e1474 | ||
|
|
85f854ccde | ||
|
|
faee82654f | ||
|
|
c6dc0e8c8b | ||
|
|
5bdd0705b2 | ||
|
|
3ee1628b13 | ||
|
|
47e036e1b2 | ||
|
|
8a23f63f1b | ||
|
|
79b989b560 | ||
|
|
d15a53d88b | ||
|
|
903e85bc38 | ||
|
|
da14de15e0 | ||
|
|
e897212a3d | ||
|
|
0aaf636273 | ||
|
|
37bd2f4e54 | ||
|
|
e100d525b9 | ||
|
|
782f17e218 | ||
|
|
58dd8b9026 | ||
|
|
55a792b929 | ||
|
|
62acd78b35 | ||
|
|
ac148afd6e | ||
|
|
4280f18c4c | ||
|
|
4ada54064d | ||
|
|
638c23098b | ||
|
|
d2128d8e2a | ||
|
|
5a1abe7692 | ||
|
|
bb5a6e0155 | ||
|
|
b2c05b5779 | ||
|
|
f4a8a99d87 | ||
|
|
5591154ae7 | ||
|
|
9ccdf629f6 | ||
|
|
20e8f6385b | ||
|
|
8241718d01 | ||
|
|
5f0bb4b6c2 | ||
|
|
f87796df21 | ||
|
|
263849789f | ||
|
|
749965acd1 | ||
|
|
f7d46b8ca1 | ||
|
|
0c75c8de18 | ||
|
|
a1ddbf78f8 | ||
|
|
70e22a89d5 | ||
|
|
ec80d82fbc | ||
|
|
0f9b2041ae | ||
|
|
3225899a20 | ||
|
|
be523e2a38 | ||
|
|
e9e8c3fe49 | ||
|
|
e5e7cdb427 | ||
|
|
19e0c2a83f | ||
|
|
b96c430f84 | ||
|
|
68a0070fa6 | ||
|
|
90ab2477b4 | ||
|
|
2303d8064a | ||
|
|
f94c7034bf | ||
|
|
3002c4b77d | ||
|
|
8c0da26ae1 | ||
|
|
1a1d5ede19 | ||
|
|
7320493a21 | ||
|
|
b100b4131c | ||
|
|
2e0e4b0bc5 | ||
|
|
27e4bbf1cb | ||
|
|
4df9fc1acc | ||
|
|
aed7f2ace6 | ||
|
|
0dc50c65f5 | ||
|
|
b335d67ce3 | ||
|
|
ad44610e5a | ||
|
|
3f8d75a1ac | ||
|
|
e03ec690ff | ||
|
|
b00cc6108d | ||
|
|
4a2a747556 | ||
|
|
dfd51c0caa | ||
|
|
cad46e43c5 | ||
|
|
f8316af454 | ||
|
|
96276514f3 | ||
|
|
e67ff1da7e | ||
|
|
bcb9acb14b | ||
|
|
8b89952d59 | ||
|
|
f3fa81b8f1 | ||
|
|
954b070736 | ||
|
|
c26a0cb222 | ||
|
|
29d93f7d9d | ||
|
|
94e0a30904 | ||
|
|
5ebde33256 | ||
|
|
a5b954a563 | ||
|
|
7a4f29afc6 | ||
|
|
5f27b05103 | ||
|
|
80179a19c2 | ||
|
|
fb91b81dd5 | ||
|
|
de4c224b5c | ||
|
|
83669dd50b | ||
|
|
9d3b93f717 | ||
|
|
38dcd857dd | ||
|
|
66688d05b0 | ||
|
|
f6ad5dd0b0 | ||
|
|
7d6b0310da | ||
|
|
b28c05d904 | ||
|
|
0f3212f65b | ||
|
|
92a09720b0 | ||
|
|
eff4e80877 | ||
|
|
3c497e638e | ||
|
|
b9c226bc18 | ||
|
|
c880e47add | ||
|
|
62616eba14 | ||
|
|
f09deda5ad | ||
|
|
5e2a278478 | ||
|
|
4496d77f63 | ||
|
|
5e15cb6f65 | ||
|
|
c69377f7b5 | ||
|
|
d731d1bfc9 | ||
|
|
3938611c32 | ||
|
|
b06a649a3a | ||
|
|
ebcd4a8b8c | ||
|
|
552accf675 | ||
|
|
cc5b7c5676 |
@@ -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.
|
||||
|
||||
6
AUTHORS
6
AUTHORS
@@ -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.
|
||||
|
||||
|
||||
82
CHANGELOG
82
CHANGELOG
@@ -1,10 +1,62 @@
|
||||
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.
|
||||
Overhauled weapon projectile physics and rendering.
|
||||
Overhauled weapon projectile physics, rendering, and sounds.
|
||||
Overhauled firing offsets and muzzle flashes for all units.
|
||||
Added starting units configuration to the lobby (MCV only, light support, heavy support).
|
||||
Added lobby options for disabling shroud and fog of war.
|
||||
Added "Build off Ally ConYards" lobby option.
|
||||
Added starting cash selector.
|
||||
Added an ingame credits menu.
|
||||
Added country name resolution to the server browser and lobby.
|
||||
Added descriptive error messages when connecting to a server fails.
|
||||
Added guard ability to combat units ('d').
|
||||
Added force-move ability (hold alt to prioritize move/crush over attack/harvest).
|
||||
@@ -12,6 +64,9 @@ NEW:
|
||||
Added kick-ban to the server lobby.
|
||||
Added a renderer geometry visualization to the debug/cheats menu.
|
||||
Overhauled lobby layout.
|
||||
Overhauled diplomacy menu.
|
||||
Fixed turreted buildings ignoring attack orders.
|
||||
Fixed units targeting cloaked actors.
|
||||
Fixed exploit allowing arbitrary building placement.
|
||||
Fixed exploit allowing a malicious client to steal admin rights.
|
||||
Fixed exploit related to support aircraft.
|
||||
@@ -37,15 +92,15 @@ NEW:
|
||||
Fixed artwork glitches in several units and buildings.
|
||||
Fixed incorrect player color on captured oil derricks on some maps.
|
||||
Fixed support powers being available after winning a game.
|
||||
Fixed sniper damage vs walls.
|
||||
Fixed destroyable ore mines.
|
||||
Fixed chronotank teleport sound.
|
||||
Fixed snipers and tanya shooting vehicles and buildings.
|
||||
Sniper health reduced.
|
||||
Improved chronoshift interaction with vehicle husks.
|
||||
Added build limit to iron curtain and chronosphere.
|
||||
Removed bibs from iron curtain and chronosphere.
|
||||
Improved dog attack behavior.
|
||||
Tweaked some weapon explosions.
|
||||
Sniper health reduced.
|
||||
Iron curtain duration increased to 20 seconds.
|
||||
Chronoshift duration decreased to 20 seconds.
|
||||
Tesla coil price reduced to $1200 and firing delay increased.
|
||||
@@ -63,11 +118,13 @@ NEW:
|
||||
Removed maps: Hotzone, Mjolnir-2, Seaside.
|
||||
Disabled bots on several maps where they don't work well.
|
||||
C&C:
|
||||
Fixed Nod01 mission.
|
||||
Fixed mouse cursor colors.
|
||||
Restored "classic" multi-engineer behavior. Buildings above 50% health will be damaged by 50%; buildings below this will be captured.
|
||||
Added some texture to the UI background.
|
||||
Improved snow color on snow maps.
|
||||
Fixed mouse cursor colors.
|
||||
Fixed Nod01 mission.
|
||||
Fixed veterancy chevrons on cloaked units.
|
||||
Added some texture to the UI background.
|
||||
Added EVA notifications when an enemy player launches an airstrike or nuclear missile.
|
||||
Increased aggressiveness of Viceroids.
|
||||
Reduced the pip-count on silos.
|
||||
Orca range increased, reload time decreased.
|
||||
@@ -79,15 +136,18 @@ NEW:
|
||||
Updated maps: Skull Valley, Slippery Slopes, The Hourglass.
|
||||
Renamed maps: Rock Canyon -> Manufacturing Consent, Bialystok -> Lessons from Kosovo.
|
||||
Dune 2000:
|
||||
Removed converted art and sound assets - now uses the original game files.
|
||||
Restored Deviator tank for Ordos.
|
||||
Added new title font.
|
||||
Added maps: Imperial Basin.
|
||||
Fixed harvester docking animation.
|
||||
Fixed statistics tracking.
|
||||
Added harvesting animation.
|
||||
Uses original (better quality) audio.
|
||||
Updated maps: Black Mesa, Brimstone, Death Depths, Dune Boogie.
|
||||
Engine:
|
||||
Fixed a crash with server hosting under Windows.
|
||||
Added a fatal error dialog with options to view logs and read the FAQ.
|
||||
Added sync checking for projectile effects.
|
||||
Removed support for mono versions < 2.10.
|
||||
Added support for portable installs (create a directory "Support" inside the game root).
|
||||
Significant improvements to pathfinder performance when ordering units to areas they cannot reach.
|
||||
Reduced order lag for singleplayer games.
|
||||
Added audio device configuration.
|
||||
@@ -106,9 +166,11 @@ NEW:
|
||||
Added additional logging for server errors.
|
||||
Significant additional work towards TS/RA2 terrain support.
|
||||
Build system and packages:
|
||||
Host windows OpenAL and SDL dependencies on our own servers to avoid excessive downtime.
|
||||
Fix compilation issues under .Net 3.5.
|
||||
Integrated windows dependencies in the installer.
|
||||
Map Editor and Tools:
|
||||
Added an ingame asset viewer / converter to D2K and RA (enable via the Debug tab in the settings menu).
|
||||
Added utility '--map-hash' command for retrieving the hash of a map.
|
||||
Added EditorConfig definitions for editors that support it.
|
||||
Added StyleCop definitions for editors that support it.
|
||||
|
||||
|
||||
82
HACKING
82
HACKING
@@ -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
28
INSTALL
@@ -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)
|
||||
|
||||
81
Makefile
81
Makefile
@@ -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
|
||||
|
||||
@@ -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,21 @@ 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(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 +193,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 +206,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 +274,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 +289,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 +322,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 +352,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 { }
|
||||
|
||||
|
||||
93
OpenRA.FileFormats/FieldSaver.cs
Normal file
93
OpenRA.FileFormats/FieldSaver.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
25
OpenRA.FileFormats/Primitives/IObservableCollection.cs
Normal file
25
OpenRA.FileFormats/Primitives/IObservableCollection.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
59
OpenRA.FileFormats/Primitives/ObservableCollection.cs
Normal file
59
OpenRA.FileFormats/Primitives/ObservableCollection.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
137
OpenRA.FileFormats/Primitives/ObservableDictionary.cs
Normal file
137
OpenRA.FileFormats/Primitives/ObservableDictionary.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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); } }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
@@ -162,6 +167,21 @@ namespace OpenRA.GameRules
|
||||
public string CycleTabsKey = "tab";
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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
19
OpenRA.Game/Network/UnitOrders.cs
Executable file → Normal 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)
|
||||
|
||||
@@ -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,8 @@
|
||||
<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" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
|
||||
|
||||
@@ -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.Location)
|
||||
.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.Location)
|
||||
.Where(a => a.Info.Traits.Contains<ITargetableInfo>())
|
||||
.OrderByDescending(a => a.Info.SelectionPriority())
|
||||
.FirstOrDefault();
|
||||
|
||||
@@ -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); }
|
||||
}
|
||||
}
|
||||
@@ -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); }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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; } }
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
210
OpenRA.Game/Traits/World/ActorMap.cs
Normal file
210
OpenRA.Game/Traits/World/ActorMap.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
|
||||
161
OpenRA.Game/Traits/World/ScreenMap.cs
Executable file
161
OpenRA.Game/Traits/World/ScreenMap.cs
Executable file
@@ -0,0 +1,161 @@
|
||||
#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 pxPos)
|
||||
{
|
||||
if (viewer == null)
|
||||
return NoFrozenActors;
|
||||
|
||||
var i = (pxPos.X / info.BinSize).Clamp(0, cols - 1);
|
||||
var j = (pxPos.Y / info.BinSize).Clamp(0, rows - 1);
|
||||
return frozen[viewer][j*cols + i]
|
||||
.Where(kv => kv.Key.IsValid && kv.Value.Contains(pxPos))
|
||||
.Select(kv => kv.Key);
|
||||
}
|
||||
|
||||
public IEnumerable<Actor> ActorsAt(int2 pxPos)
|
||||
{
|
||||
var i = (pxPos.X / info.BinSize).Clamp(0, cols - 1);
|
||||
var j = (pxPos.Y / info.BinSize).Clamp(0, rows - 1);
|
||||
return actors[j * cols + i]
|
||||
.Where(kv => kv.Key.IsInWorld && kv.Value.Contains(pxPos))
|
||||
.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ namespace OpenRA.Widgets
|
||||
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");
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
4
OpenRA.Game/Widgets/TooltipContainerWidget.cs
Executable file → Normal file
4
OpenRA.Game/Widgets/TooltipContainerWidget.cs
Executable file → Normal 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;
|
||||
|
||||
28
OpenRA.Game/Widgets/ViewportControllerWidget.cs
Executable file → Normal file
28
OpenRA.Game/Widgets/ViewportControllerWidget.cs
Executable file → Normal 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, 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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,33 +131,38 @@ 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)
|
||||
@@ -171,7 +171,7 @@ namespace OpenRA.Widgets
|
||||
{
|
||||
if (e.KeyName.Length == 1 && char.IsDigit(e.KeyName[0]))
|
||||
{
|
||||
world.Selection.DoControlGroup(world, e.KeyName[0] - '0', e.Modifiers, e.MultiTapCount);
|
||||
world.Selection.DoControlGroup(world, worldRenderer, e.KeyName[0] - '0', e.Modifiers, e.MultiTapCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -183,9 +183,9 @@ namespace OpenRA.Widgets
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@@ -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 )
|
||||
|
||||
@@ -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
27
OpenRA.Irc/Channel.cs
Normal 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
402
OpenRA.Irc/IrcClient.cs
Normal 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
112
OpenRA.Irc/IrcClientUser.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
83
OpenRA.Irc/IrcConnection.cs
Normal file
83
OpenRA.Irc/IrcConnection.cs
Normal 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
67
OpenRA.Irc/IrcUtils.cs
Normal 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
155
OpenRA.Irc/Line.cs
Normal 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
25
OpenRA.Irc/NumericCommand.cs
Normal file
25
OpenRA.Irc/NumericCommand.cs
Normal 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
|
||||
}
|
||||
}
|
||||
70
OpenRA.Irc/OpenRA.Irc.csproj
Normal file
70
OpenRA.Irc/OpenRA.Irc.csproj
Normal 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
30
OpenRA.Irc/Topic.cs
Normal 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
73
OpenRA.Irc/User.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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">
|
||||
@@ -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)
|
||||
@@ -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[]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = () =>
|
||||
{
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -1,52 +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 OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.Widgets.Logic
|
||||
{
|
||||
public class CncInstallLogic
|
||||
{
|
||||
[ObjectCreator.UseCtor]
|
||||
public CncInstallLogic(Widget widget, Dictionary<string, string> installData, Action continueLoading)
|
||||
{
|
||||
var panel = widget.Get("INSTALL_PANEL");
|
||||
var args = new WidgetArgs()
|
||||
{
|
||||
{ "afterInstall", () => { Ui.CloseWindow(); continueLoading(); } },
|
||||
{ "installData", installData }
|
||||
};
|
||||
|
||||
panel.Get<ButtonWidget>("DOWNLOAD_BUTTON").OnClick = () =>
|
||||
Ui.OpenWindow("INSTALL_DOWNLOAD_PANEL", args);
|
||||
|
||||
panel.Get<ButtonWidget>("INSTALL_BUTTON").OnClick = () =>
|
||||
Ui.OpenWindow("INSTALL_FROMCD_PANEL", new WidgetArgs(args)
|
||||
{
|
||||
{ "filesToCopy", new[] { "CONQUER.MIX", "DESERT.MIX", "SCORES.MIX",
|
||||
"SOUNDS.MIX", "TEMPERAT.MIX", "WINTER.MIX" } },
|
||||
{ "filesToExtract", new[] { "speech.mix", "tempicnh.mix", "transit.mix" } },
|
||||
});
|
||||
|
||||
panel.Get<ButtonWidget>("QUIT_BUTTON").OnClick = Game.Exit;
|
||||
|
||||
panel.Get<ButtonWidget>("MODS_BUTTON").OnClick = () =>
|
||||
{
|
||||
Ui.OpenWindow("MODS_PANEL", new WidgetArgs()
|
||||
{
|
||||
{ "onExit", () => { } },
|
||||
{ "onSwitch", Ui.CloseWindow },
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,6 +137,7 @@ namespace OpenRA.Mods.Cnc.Widgets.Logic
|
||||
|
||||
ConnectionLogic.Connect(IPAddress.Loopback.ToString(),
|
||||
Game.CreateLocalServer(map),
|
||||
"",
|
||||
() => OpenLobbyPanel(MenuType.Main, true),
|
||||
() => { Game.CloseServer(); menuType = MenuType.Main; });
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.FileFormats.Graphics;
|
||||
using OpenRA.GameRules;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.RA;
|
||||
using OpenRA.Mods.RA.Widgets;
|
||||
using OpenRA.Mods.RA.Widgets.Logic;
|
||||
@@ -32,7 +33,7 @@ namespace OpenRA.Mods.Cnc.Widgets.Logic
|
||||
World world;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public CncSettingsLogic(Widget widget, World world, Action onExit)
|
||||
public CncSettingsLogic(Widget widget, World world, Action onExit, WorldRenderer worldRenderer)
|
||||
{
|
||||
this.world = world;
|
||||
var panel = widget.Get("SETTINGS_PANEL");
|
||||
@@ -90,7 +91,7 @@ namespace OpenRA.Mods.Cnc.Widgets.Logic
|
||||
pixelDoubleCheckbox.OnClick = () =>
|
||||
{
|
||||
graphicsSettings.PixelDouble ^= true;
|
||||
Game.viewport.Zoom = graphicsSettings.PixelDouble ? 2 : 1;
|
||||
worldRenderer.Viewport.Zoom = graphicsSettings.PixelDouble ? 2 : 1;
|
||||
};
|
||||
|
||||
var showShellmapCheckbox = generalPane.Get<CheckboxWidget>("SHOW_SHELLMAP");
|
||||
@@ -104,6 +105,10 @@ namespace OpenRA.Mods.Cnc.Widgets.Logic
|
||||
var windowHeight = generalPane.Get<TextFieldWidget>("WINDOW_HEIGHT");
|
||||
windowHeight.Text = graphicsSettings.WindowedSize.Y.ToString();
|
||||
|
||||
var languageDropDownButton = generalPane.Get<DropDownButtonWidget>("LANGUAGE_DROPDOWNBUTTON");
|
||||
languageDropDownButton.OnMouseDown = _ => SettingsMenuLogic.ShowLanguageDropdown(languageDropDownButton);
|
||||
languageDropDownButton.GetText = () => FieldLoader.Translate(Game.Settings.Graphics.Language);
|
||||
|
||||
// Audio
|
||||
var soundSlider = generalPane.Get<SliderWidget>("SOUND_SLIDER");
|
||||
soundSlider.OnChange += x => { soundSettings.SoundVolume = x; Sound.SoundVolume = x; };
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace OpenRA.Mods.Cnc.Widgets.Logic
|
||||
nameLabel.GetText = () => tooltip.Name;
|
||||
|
||||
var prereqs = buildable.Prerequisites.Select(a => ActorName(a));
|
||||
var requiresString = prereqs.Any() ? "Requires {0}".F(prereqs.JoinWith(", ")) : "";
|
||||
var requiresString = prereqs.Any() ? requiresLabel.Text.F(prereqs.JoinWith(", ")) : "";
|
||||
requiresLabel.GetText = () => requiresString;
|
||||
|
||||
var power = bi != null ? bi.Power : 0;
|
||||
|
||||
@@ -37,8 +37,8 @@ namespace OpenRA.Mods.Cnc.Widgets
|
||||
public readonly string TooltipContainer;
|
||||
public readonly string TooltipTemplate = "PRODUCTION_TOOLTIP";
|
||||
|
||||
public readonly string ReadyText = "";
|
||||
public readonly string HoldText = "";
|
||||
[Translate] public readonly string ReadyText = "";
|
||||
[Translate] public readonly string HoldText = "";
|
||||
|
||||
public string TooltipActor { get; private set; }
|
||||
public readonly World World;
|
||||
|
||||
@@ -21,8 +21,8 @@ namespace OpenRA.Mods.Cnc.Widgets
|
||||
{
|
||||
public class SupportPowersWidget : Widget
|
||||
{
|
||||
public readonly string ReadyText = "";
|
||||
public readonly string HoldText = "";
|
||||
[Translate] public readonly string ReadyText = "";
|
||||
[Translate] public readonly string HoldText = "";
|
||||
|
||||
public readonly string TooltipContainer;
|
||||
public readonly string TooltipTemplate = "SUPPORT_POWER_TOOLTIP";
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
<CustomCommands>
|
||||
<CustomCommands>
|
||||
<Command type="AfterBuild" command="cp ${TargetFile} ../mods/d2k" workingdir="${ProjectDir}" />
|
||||
<Command type="AfterBuild" command="mono RALint.exe d2k" workingdir="${ProjectDir}/../" />
|
||||
</CustomCommands>
|
||||
</CustomCommands>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
@@ -77,10 +76,7 @@
|
||||
<Reference Include="System.Drawing" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="D2kLoadScreen.cs" />
|
||||
<Compile Include="Widgets\Logic\D2kInstallLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\D2kInstallFromCDLogic.cs" />
|
||||
<Compile Include="Widgets\Logic\D2kDownloadPackagesLogic.cs" />
|
||||
<Compile Include="BuildingCaptureNotification.cs" />
|
||||
<Compile Include="Render\WithCrumbleOverlay.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2012 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.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.D2k.Widgets.Logic
|
||||
{
|
||||
public class D2kDownloadPackagesLogic
|
||||
{
|
||||
Widget panel;
|
||||
Dictionary<string, string> installData;
|
||||
ProgressBarWidget progressBar;
|
||||
LabelWidget statusLabel;
|
||||
Action afterInstall;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
public D2kDownloadPackagesLogic(Widget widget, Dictionary<string, string> installData, Action afterInstall)
|
||||
{
|
||||
this.installData = installData;
|
||||
this.afterInstall = afterInstall;
|
||||
|
||||
panel = widget.Get("INSTALL_DOWNLOAD_PANEL");
|
||||
progressBar = panel.Get<ProgressBarWidget>("PROGRESS_BAR");
|
||||
statusLabel = panel.Get<LabelWidget>("STATUS_LABEL");
|
||||
|
||||
ShowDownloadDialog();
|
||||
}
|
||||
|
||||
void ShowDownloadDialog()
|
||||
{
|
||||
statusLabel.GetText = () => "Initializing...";
|
||||
progressBar.SetIndeterminate(true);
|
||||
var retryButton = panel.Get<ButtonWidget>("RETRY_BUTTON");
|
||||
retryButton.IsVisible = () => false;
|
||||
|
||||
var cancelButton = panel.Get<ButtonWidget>("CANCEL_BUTTON");
|
||||
|
||||
// Save the package to a temp file
|
||||
var file = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
var dest = new string[] { Platform.SupportDir, "Content", Game.modData.Manifest.Mods[0] }.Aggregate(Path.Combine);
|
||||
|
||||
Action<DownloadProgressChangedEventArgs> onDownloadProgress = i =>
|
||||
{
|
||||
if (progressBar.Indeterminate)
|
||||
progressBar.SetIndeterminate(false);
|
||||
|
||||
progressBar.Percentage = i.ProgressPercentage;
|
||||
statusLabel.GetText = () => "Downloading {1}/{2} kB ({0}%)".F(i.ProgressPercentage, i.BytesReceived / 1024, i.TotalBytesToReceive / 1024);
|
||||
};
|
||||
|
||||
Action<string> onExtractProgress = s =>
|
||||
{
|
||||
Game.RunAfterTick(() => statusLabel.GetText = () => s);
|
||||
};
|
||||
|
||||
Action<string> onError = s =>
|
||||
{
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
statusLabel.GetText = () => "Error: " + s;
|
||||
retryButton.IsVisible = () => true;
|
||||
});
|
||||
};
|
||||
|
||||
Action<AsyncCompletedEventArgs, bool> onDownloadComplete = (i, cancelled) =>
|
||||
{
|
||||
if (i.Error != null)
|
||||
{
|
||||
onError(Download.FormatErrorMessage(i.Error));
|
||||
return;
|
||||
}
|
||||
else if (cancelled)
|
||||
{
|
||||
onError("Download cancelled");
|
||||
return;
|
||||
}
|
||||
|
||||
// Automatically extract
|
||||
statusLabel.GetText = () => "Extracting...";
|
||||
progressBar.SetIndeterminate(true);
|
||||
if (InstallUtils.ExtractZip(file, dest, onExtractProgress, onError))
|
||||
{
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
Ui.CloseWindow();
|
||||
afterInstall();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var dl = new Download(installData["PackageURL"], file, onDownloadProgress, onDownloadComplete);
|
||||
|
||||
cancelButton.OnClick = () => { dl.Cancel(); Ui.CloseWindow(); };
|
||||
retryButton.OnClick = () => { dl.Cancel(); ShowDownloadDialog(); };
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user