Compare commits
223 Commits
playtest-2
...
playtest-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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.
|
||||
|
||||
|
||||
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)
|
||||
|
||||
77
Makefile
77
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)
|
||||
|
||||
@@ -318,10 +341,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 +356,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: 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 +391,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(); };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +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 OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.D2k.Widgets.Logic
|
||||
{
|
||||
public class D2kInstallLogic
|
||||
{
|
||||
[ObjectCreator.UseCtor]
|
||||
public D2kInstallLogic(Widget widget, Dictionary<string, string> installData, Action continueLoading)
|
||||
{
|
||||
var panel = widget.Get("INSTALL_PANEL");
|
||||
var args = new WidgetArgs()
|
||||
{
|
||||
{ "afterInstall", () => { Ui.CloseWindow(); continueLoading(); } },
|
||||
{ "installData", installData },
|
||||
{ "continueLoading", continueLoading }
|
||||
};
|
||||
|
||||
panel.Get<ButtonWidget>("DOWNLOAD_BUTTON").OnClick = () =>
|
||||
Ui.OpenWindow("INSTALL_DOWNLOAD_PANEL", args);
|
||||
|
||||
panel.Get<ButtonWidget>("COPY_BUTTON").OnClick = () =>
|
||||
Ui.OpenWindow("INSTALL_FROMCD_PANEL", args);
|
||||
|
||||
panel.Get<ButtonWidget>("QUIT_BUTTON").OnClick = Game.Exit;
|
||||
|
||||
panel.Get<ButtonWidget>("MODS_BUTTON").OnClick = () =>
|
||||
{
|
||||
Ui.OpenWindow("MODS_PANEL", new WidgetArgs()
|
||||
{
|
||||
{ "onExit", () => { } },
|
||||
{ "onSwitch", Ui.CloseWindow },
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user