Compare commits

...

763 Commits

Author SHA1 Message Date
Paul Chote
36c3e07535 Remove CRLF from GameServer.cs. 2018-01-01 21:12:58 +01:00
Paul Chote
58547fda89 Guess mod titles based on other versions, if they exist. 2018-01-01 21:12:58 +01:00
Paul Chote
73da5e7a20 Fix d2k ingame-client tooltip background. 2018-01-01 22:12:09 +02:00
Paul Chote
7f20d1474d Fix RA aircraft prerequisites. 2018-01-01 19:40:38 +01:00
Paul Chote
ee728be537 Fix WithCargo crash. 2018-01-01 19:25:50 +01:00
Paul Chote
7b056509ec Don't try to remove tooltips that haven't been added. 2018-01-01 18:23:16 +02:00
Paul Chote
2eb090f153 Fix actors disappearing for a tick when swapping to the frozen actor. 2018-01-01 16:01:44 +01:00
Paul Chote
f8abd5d319 Serialize the correct player in FrozenActor-targeting orders. 2018-01-01 16:01:44 +01:00
Paul Chote
b3cde077fc Don't break the order stream if a frozen actor refers to a bogus player. 2018-01-01 16:01:44 +01:00
Paul Chote
47fa9e496d Fix tooltips not showing for frozen actors with dead backing actors.
This also documents some old bugs.
2018-01-01 16:01:44 +01:00
abcdefg30
19a9c70d5c Fix the communications center being on low power 2018-01-01 15:49:56 +01:00
abcdefg30
94a062b3e7 Properly unload passengers in nod05
Also lets the transport fly out of the map
2018-01-01 15:49:56 +01:00
Andrii Yukhymchak
11db40a2b3 Fixed issue with unit ready when capturing 2018-01-01 12:52:47 +00:00
Paul Chote
dec11f4fa0 Force spectator color to white to avoid spoofing by the server. 2018-01-01 11:25:08 +01:00
Paul Chote
ae394f937b Fix the color validator overriding spectator colors. 2018-01-01 11:25:08 +01:00
Paul Chote
3353215b66 Add a total player count to the MP browser. 2018-01-01 11:12:27 +01:00
Paul Chote
cd6dfd2185 Work around the "BUG: in order targeter" game chat warning. 2018-01-01 10:48:03 +01:00
Paul Chote
e70b61c4b0 Fix Chrono Tanks telesporting towards (0,0) when force-moving to an actor. 2018-01-01 10:48:03 +01:00
Mustafa Alperen Seki
2f6bbd906b Ignore Silos for SW Targeting on Harkonnen Missions
Also Defences for Saboteur
2017-12-31 12:02:13 +01:00
reaperrr
8fe45cb894 Fix surface ships except transport being unable to exit ship yard 2017-12-31 08:36:37 +02:00
FrameLimiter
61df53ea82 Fixes civilian panic from civ building explosion
The Civilian building explosions were not making the civilians panic.
2017-12-31 03:26:17 +01:00
abc013
38c4c10c73 Added Allies-06b to the Red Alert mod 2017-12-31 02:51:03 +01:00
Mustafa Alperen Seki
1aa57cd5b1 Fix an owner change for Smugglers on Har6s. 2017-12-30 21:30:10 +01:00
Mustafa Alperen Seki
b7830d97f2 Add Harkonnen9b 2017-12-30 21:30:10 +01:00
Mustafa Alperen Seki
a80924ebb3 Add Harkonnen 8 2017-12-30 20:18:37 +01:00
Mustafa Alperen Seki
69e9b94c5d Add Campaign Tooltip to D2K 2017-12-30 17:40:09 +01:00
Paul Chote
45f346c004 Fix label offsets. 2017-12-30 17:03:58 +01:00
Paul Chote
7fba6a2155 Add a view-only server list tab to the multiplayer lobby. 2017-12-30 17:03:58 +01:00
Paul Chote
235b16d4b3 Split server list logic into its own LogicObject. 2017-12-30 17:03:58 +01:00
reaperrr
4ef11f2763 Fix TD Orca missiles alternating between offsets
This is now properly fixed by using Burst: 1.
2017-12-29 15:28:34 +00:00
reaperrr
b3c3c7b414 Make RA heli weapons alternate between offsets
Instead of using one for AG-only and one for AA-only.
2017-12-29 15:28:34 +00:00
reaperrr
9b629a8f39 Remove dysfunctional TS yaml hacks
In addition to now being redundant due to Armament-side cycling through LocalOffsets, these didn't work because Burst is reset once ReloadDelay is reached, so that equal BurstDelay never really had the intended effect.
2017-12-29 15:28:34 +00:00
reaperrr
cefe3d2c8f When Armament has multiple LocalOffsets but weapon has Burst: 1, cycle through offsets 2017-12-29 15:28:34 +00:00
Mustafa Alperen Seki
9871abe562 Make crushable conditional 2017-12-29 15:25:38 +00:00
Paul Chote
14f6601f2b Tweak wording of playtest notice. 2017-12-29 02:56:50 +01:00
Paul Chote
1d8b2a9a08 Replace contrast with shadow on main menu version label. 2017-12-29 02:56:50 +01:00
Paul Chote
5c53172ab0 Add an update warning to the main menu. 2017-12-29 02:56:50 +01:00
Paul Chote
3effa5cec4 Add a setting to disable version checks. 2017-12-29 02:56:50 +01:00
Paul Chote
0208d0cc10 Move the version check code to WebServices and run only on first launch. 2017-12-29 02:56:50 +01:00
FrameLimiter
759bd044ed Moved Chinook entry/exit points to edge 2017-12-29 01:29:32 +01:00
FrameLimiter
4cdab30c48 Added an entry delay to insertion transport 2017-12-29 01:29:32 +01:00
FrameLimiter
98ec28e850 Fixes the chinooks not being removed in Nod04b
∙The entry/exit points for the Chinooks were out bounds.
∙The rally/unload points for the Chinooks were very close together and causing unloaded cargo to overlap.
2017-12-29 01:29:32 +01:00
Paul Chote
ef680dbbfe Add tooltips to the password icon and player count. 2017-12-28 22:42:50 +01:00
Paul Chote
0eaec5d861 Add an animated reload glyph to the MP server refresh. 2017-12-28 22:42:50 +01:00
Mustafa Alperen Seki
7014393211 Move Generic Prefixes to yaml and add prefix for Neutral 2017-12-28 12:00:53 +00:00
Mustafa Alperen Seki
841c873276 Fix Crash when Mcv: UnitsCommonName is empty. 2017-12-28 11:36:44 +00:00
TheChosenEvilOne
15354f52c1 Added FACT unpack checkbox to ra/cnc/ts. 2017-12-28 11:22:29 +00:00
TheChosenEvilOne
1a947907d3 Make Transforms a PausableConditionalTrait. 2017-12-28 11:22:29 +00:00
reaperrr
3d4095cffd Make sure AttackOrFlee returns Attack when it makes sense
Not listing enemy "NearDead" and own relative atk power "Strong" here looked wrong, and might've resulted in no decision being made.
2017-12-28 11:00:18 +00:00
reaperrr
104148378f Remove some redundancy in AttackOrFleeFuzzy 2017-12-28 11:00:18 +00:00
reaperrr
6b567722b8 Factor number of bursts plus ReloadDelay into AttackOrFleeFuzzy.RelativePower 2017-12-28 11:00:18 +00:00
reaperrr
a4595af1e3 Make AttackOrFleeFuzzy consider all DamageWarheads instead of only the first 2017-12-28 11:00:18 +00:00
reaperrr
382c0b5f1d Make AI StateBase.CanAttackTarget only return true on valid armaments that are also enabled 2017-12-28 10:40:31 +00:00
reaperrr
e6835cef6e Replace some .Any checks with explicit .Count checks in AI 2017-12-28 10:40:31 +00:00
reaperrr
3fe808e0fb Make Ground and Navy AI squads look for enabled (instead of any) targetables 2017-12-28 10:40:31 +00:00
reaperrr
6de90b02d0 Unhardcode various AI squad radii 2017-12-28 10:40:31 +00:00
Arular101
eefaf23885 Fix global-bounty warnings 2017-12-28 10:23:23 +00:00
reaperrr
1bac76fdd1 Remove redundant custom Duration from Bomber John Chronoshift power
The MNLYR has ReturnToOrigin: false anyway, so this was redundant and made no difference whatsoever.
2017-12-28 10:04:43 +00:00
reaperrr
f0ee87f078 Upgrade TS rules to SupportPower seconds -> ticks 2017-12-28 10:04:43 +00:00
reaperrr
7fb595a437 Upgrade D2k rules to SupportPower seconds -> ticks 2017-12-28 10:04:43 +00:00
reaperrr
88704ebac6 Upgrade TD rules to SupportPower seconds -> ticks 2017-12-28 10:04:43 +00:00
reaperrr
aa6ddc6a6e Upgrade RA rules to SupportPower seconds -> ticks 2017-12-28 10:04:43 +00:00
reaperrr
b70e4de5ee Refactor support powers to use ticks instead of seconds for all delays - code changes
Also renamed ChargeTime to ChargeInterval.
2017-12-28 10:04:43 +00:00
Arular101
3d62b1f9b3 Adjusted Minigun damage vs wood to match current damage after multiplying by 100. From 8 * 0.6 = 4.8 (actual value is 4) to 800 * 0.5 = 400 2017-12-28 10:12:52 +01:00
Arular101
4ea6a3c18c Small adjustments 2017-12-28 10:12:52 +01:00
Arular101
e734668f56 Upgrade rules for increasing HP and damage by 100x for the TS mod 2017-12-28 10:12:52 +01:00
Arular101
be89ce9efd Add myself to authors 2017-12-28 10:12:52 +01:00
Arular101
ede72410ff Increase damage and HP by 100x 2017-12-28 10:12:52 +01:00
C. Helmig
3a9ee8d51a Fixes mines not being auto-targeted even if revealed. 2017-12-28 08:06:14 +01:00
Paul Chote
27beed402f Display information banners in the MP server browser.
Notices are provided for outdated / unknown mod versions
and latest but playtest available.
2017-12-28 03:40:41 +01:00
Paul Chote
e7ce9aa263 Unhardcode server ping/query URLs. 2017-12-28 03:40:41 +01:00
Michael Silber
86a17e9260 Added TS map They All Float (Westwood). Part of Issue #12518
Added me (frühstück) to AUTHORS
2017-12-27 22:09:09 +01:00
Paul Chote
c6ad768a76 Remove redundant powerdown conditions. 2017-12-27 20:18:27 +01:00
Paul Chote
5425a5a28e Replace CanPowerDown with ToggleConditionOnOrder. 2017-12-27 20:18:27 +01:00
Michael Silber
b8ec20a010 Carryall pickup indicator set to top, left corner 2017-12-27 19:07:44 +00:00
Michael Silber
c445a20acf D2K carryall pickup indicator offset fix 2017-12-27 19:07:44 +00:00
Mustafa Alperen Seki
7f77e4cf40 Remove ProductionDoorOverlay while transforming 2017-12-27 13:50:58 +00:00
Mustafa Alperen Seki
acf25354b0 Consider BuildAtProductionType for exit types too for normal ProductionQueue 2017-12-27 13:46:20 +00:00
Mustafa Alperen Seki
5ac0625f97 Add /kill and /dispose chat commands 2017-12-27 13:26:38 +00:00
Mustafa Alperen Seki
fc07391c8c Add Type Support for InfiltrateFor* traits 2017-12-27 11:13:09 +00:00
Mustafa Alperen Seki
f7de5d46be Allow GrantConditionOnPrerequisite to work on Player: 2017-12-27 02:31:07 +01:00
Matthias
7b6b79493c Replace @ with new lines in legacy map briefing sections 2017-12-27 02:12:58 +01:00
Michael Silber
a3b186ccbc Burnt Trees using RadarColor from trees; issue #14582 2017-12-26 23:45:37 +01:00
Paul Chote
c73ce50a09 Include bots in the player count for consistency with the sidebar. 2017-12-26 20:05:38 +01:00
Paul Chote
8fc2caa01a Change the "Empty" filter to not discard servers that have only spectators. 2017-12-26 20:05:38 +01:00
Paul Chote
bccc0f8f17 Add a player list to the MP server browser. 2017-12-26 20:05:38 +01:00
Paul Chote
3ba610b535 Implement new master server ping protocol. 2017-12-26 20:05:38 +01:00
Paul Chote
a3882501b1 Ignore malformed LAN advertisements instead of crashing.
This matches the behaviour for online game handling.
2017-12-26 20:05:38 +01:00
Paul Chote
ec97214c16 Fix ProductionOverlay crash. 2017-12-25 21:34:31 +01:00
Unknown
4be593123d add me (jongleur1983) to AUTHORS
#14558: MovingToRefinery takes actor instead of CPos

DeliveryOffset (previously added by the harvester) is now taken into account by the AutoCarryable
fix whitespaces
2017-12-25 03:52:44 +01:00
Peter Amrehn
73bd80ebc5 #14542: order CarrryAll to the target's location, not to 0,0
(which is in order.TargetLocation
#14542: don't use deprecated TargetActor property, replace by

Target.Actor.Location
2017-12-25 03:52:44 +01:00
Matthias
ebd8a2b193 Adhere to the ZeroBranePackage conventions. 2017-12-24 10:35:13 +00:00
Paul Chote
0b446167b6 Unify and tweak layout of MP browser window. 2017-12-24 01:47:44 +01:00
Paul Chote
959804b167 Restore separated server creation dialog.
This also adds details about advertising and configuration.
2017-12-24 01:47:44 +01:00
Paul Chote
5256fb7bbd Restore separated direct connect dialog. 2017-12-24 01:47:44 +01:00
Paul Chote
205c45198c Remove secondary AllowPortForward setting.
The global setting is fine, and this simplifies
both the code and the UI.
2017-12-24 01:47:44 +01:00
abcdefg30
38cb3dea05 Make vehicles spawn husks with a neutral owner 2017-12-24 00:41:15 +01:00
abcdefg30
a6258485c9 Add a "EffectiveOwnerFromOwner" property to change the effective owner of a spawned actor 2017-12-24 00:41:15 +01:00
abcdefg30
b99d21b818 Add support for carrying effective owners over to husk actors 2017-12-24 00:41:15 +01:00
abcdefg30
b9987dcbda Add a fallback to InternalOwner if the victim was defeated 2017-12-24 00:41:15 +01:00
abcdefg30
d4d9959159 Let Husk implement IEffectiveOwner 2017-12-24 00:41:15 +01:00
abcdefg30
eab93ccf97 Add an EffectiveOwnerInit 2017-12-24 00:41:15 +01:00
abcdefg30
503b91d391 Remove an unused using 2017-12-24 00:41:15 +01:00
abcdefg30
bd52e56aaf Add the possibility to deny spawning actors for dead players 2017-12-24 00:41:15 +01:00
RoosterDragon
e42e766bb6 Prevent incorrect usage of ModelRenderer.
Force BeginFrame/EndFrame to be called correctly, and prevent calls to RenderAsync when outside of a frame.
2017-12-23 22:58:17 +01:00
Paul Chote
4c64a37e1d Reject invalid orders from unvalidated clients. 2017-12-23 21:48:59 +01:00
Mustafa Alperen Seki
e2f3989f46 Fix that conditions for InitialUnits are not given 2017-12-23 21:22:31 +01:00
Mustafa Alperen Seki
65e4019424 Update some prerequisites to include subfactions too 2017-12-23 18:35:01 +00:00
Mustafa Alperen Seki
4490eb3a2a Change Smuggler Starport and Merc HFac Cameo to use Ordos ones 2017-12-23 18:28:00 +00:00
Mustafa Alperen Seki
d762411b95 Update D2K voice and notifications to include subfactions 2017-12-23 18:28:00 +00:00
Mustafa Alperen Seki
395eb36cd1 Update D2K FactionImages
Default is now Ordos, as there are 3 ordos subfactions and 2 for others.
Fremen now uses Atreides Structures as in original, rather than
Harkonnen
2017-12-23 18:28:00 +00:00
reaperrr
fbc18dfb18 Use HashSet instead of IEnumerable to pass actors to renderable lists 2017-12-23 17:56:11 +00:00
reaperrr
04fb0f209d Spatially partition some actor overlays 2017-12-23 17:56:11 +00:00
reaperrr
6ef95405bf Split overlay renderable generation to separate list
To make the code a bit more readable and consistent with normal renderables.
2017-12-23 17:56:11 +00:00
Paul Chote
4993e74748 Fix SelectionBox line depths. 2017-12-23 17:32:07 +00:00
Paul Chote
2794b14f52 Calculate actor preview bounds directly. 2017-12-23 17:16:13 +00:00
Mustafa Alperen Seki
7bf3d5d7f6 Fix Cruiser's Turret 2017-12-23 13:16:41 +01:00
Mustafa Alperen Seki
27cc487575 Add Harkonnen 9a 2017-12-22 12:22:08 +01:00
abcdefg30
3af3299921 Fix a crash in PlaceBuilding.cs 2017-12-22 09:06:27 +00:00
reaperrr
41dd7ca428 Fix NukeLaunch being displayed too early
Adding it to ScreenMap from the ctor unconditionally didn't mesh well with potentially being added to world with a delay.
2017-12-22 04:17:18 +02:00
Paul Chote
028f7c6546 Adjust TD airstrike targeting to account for limited visibility. 2017-12-22 02:33:25 +01:00
Paul Chote
2ad42b6a7e Account for visibility when selecting AI superweapon targets. 2017-12-22 02:33:25 +01:00
Paul Chote
9dc330b300 Update FrozenActor TargetTypes alongside other variable state. 2017-12-22 02:33:25 +01:00
Paul Chote
b9e03ac009 Add FrozenActorLayer.FrozenActorsInRegion. 2017-12-22 02:33:25 +01:00
Paul Chote
b221787350 Allow CellRegions to be created with MPos. 2017-12-22 02:33:25 +01:00
Paul Chote
d24d80f483 Update minigames for bounty checkbox. 2017-12-21 23:52:04 +01:00
Paul Chote
cd4f54ade3 Add Kill Bounties checkbox to RA. 2017-12-21 23:52:04 +01:00
Paul Chote
1a73a2578b Make GivesBounty conditional. 2017-12-21 23:52:04 +01:00
Paul Chote
e79680e22c Add LobbyPrerequisiteCheckbox trait. 2017-12-21 23:52:04 +01:00
Pavel Penev
f8b75bbed4 Fixed HarvesterInsurance assuming all refineries get free harvesters via FreeActorWithDelivery 2017-12-21 21:55:55 +00:00
Mustafa Alperen Seki
956dbb762c Add Harkonnen 7 2017-12-21 22:54:57 +01:00
reaperrr
14c608786d Fix ReloadAmmoPool upgrade rule 2017-12-21 21:29:51 +02:00
Paul Chote
cfb293a53a Add a pull request template with important contributor information. 2017-12-21 21:04:37 +02:00
Paul Chote
ec4cf0646f Rewrite Serialize to improve clarity. 2017-12-21 20:01:14 +01:00
Paul Chote
5b51261dd3 Serialize the correct target type. 2017-12-21 20:01:14 +01:00
Paul Chote
9c88fc154f Remove redundant switch from order serialization. 2017-12-21 20:01:14 +01:00
RoosterDragon
987d0b77ac When dumping syncable traits, use a struct for the synced values.
Since most traits have few syncable members, this allows us to avoid allocating an array whose lifetime is only a few ticks long. For traits with more members, we fall back to allocating the array.
2017-12-19 22:01:08 +02:00
RoosterDragon
ca01a1f186 Presize MemoryStream when possible.
Also use GetBuffer when we know we have presized the stream to the exact required size to prevent a needless copy.
2017-12-19 00:59:11 +01:00
RoosterDragon
5d8c9a560a Release audio resource for video when StopVideo is called. 2017-12-19 00:57:58 +01:00
RoosterDragon
3f8c1ad5df Don't allocate a full-sized array when creating a VertexBuffer.
We can allocate a much smaller zeroed buffer and copy that multiple times in order to zero the memory. When creating large VertexBuffers, this caps our temporary allocation size significantly.
2017-12-19 00:57:02 +01:00
Paul Chote
c269525397 Fix support power tooltip time coloring. 2017-12-19 00:55:09 +01:00
Mustafa Alperen Seki
77ef13816f Add 4PLAY10 2017-12-18 23:19:44 +00:00
Mustafa Alperen Seki
09f4c6339e Add 4PLAY8 2017-12-18 23:15:45 +00:00
Mustafa Alperen Seki
2251078dde Add 4PLAY5 2017-12-18 23:11:04 +00:00
Mustafa Alperen Seki
365251f845 Add 4PLAY1 2017-12-18 23:09:15 +00:00
Mustafa Alperen Seki
c6f0b3e1a0 Add 2PLAY14 2017-12-18 23:07:28 +00:00
Mustafa Alperen Seki
31206de855 Add 2PLAY11 2017-12-18 23:04:43 +00:00
Mustafa Alperen Seki
fe3534ebcd Add 2PLAY9 2017-12-18 23:02:29 +00:00
Mustafa Alperen Seki
0dd52acd98 Add 2PLAY4 2017-12-18 23:00:04 +00:00
Mustafa Alperen Seki
a36242d524 Add 2PLAY3 2017-12-18 22:57:32 +00:00
Mustafa Alperen Seki
ecc09256d8 Add 2PLAY5 2017-12-18 22:54:38 +00:00
Mustafa Alperen Seki
fe01afe794 Move Update Queue to bottom on Player.yaml and ingame-player.yaml 2017-12-18 19:22:07 +00:00
Mustafa Alperen Seki
6f41be209c Allow all upgradable structures to build upgrades. 2017-12-18 19:22:07 +00:00
Paul Chote
e35e93557c Remove obsolete NearestCellTo helper. 2017-12-18 07:47:15 +00:00
Paul Chote
30cafcbc25 Use Util.AdjacentCells to determine ExternalCapture validity.
This matches the set of cells targeted by MoveAdjacentTo.
2017-12-18 07:47:15 +00:00
reaperrr
85c54e04d9 Replace LINQ in AirStates.CountAntiAirUnits 2017-12-17 21:18:44 +00:00
reaperrr
01a88862b3 Remove IDisable for good 2017-12-17 21:18:44 +00:00
reaperrr
c1cba4ecc1 Make Gate more independent from Building and pausable-conditional
Replace Gate IsDisabled checks with IsTraitDisabled/Paused checks
2017-12-17 21:18:44 +00:00
reaperrr
383840135f Replace IsDisabled checks in production with IsTraitPaused/Disabled checks
Note: We might want to separate IsTraitDisabled checks later (possibly make the latter cancel the currently produced item), but that can be done in a follow-up.
2017-12-17 21:18:44 +00:00
reaperrr
3aa8b3ae29 Remove unit.IsDisabled check from AirStates 2017-12-17 21:18:44 +00:00
RoosterDragon
be761de768 Compute map UIDs without copying all data to a MemoryStream.
We can use MergedStream to create a single combined stream with all the input and pass this to the hash function. This saves copying all the data into a MemoryStream to achieve the same goal, which requires more memory and allocations.
2017-12-17 19:20:12 +01:00
Paul Chote
49f0e4ebcf Fix incorrectly named lobby option properties. 2017-12-17 14:51:10 +02:00
Paul Chote
1a405b17ba Register the current mod even if LaunchPath is bogus. 2017-12-17 04:21:17 +01:00
RoosterDragon
a9d1d374b8 Remove draw line 2D helper method.
Callers can provide 3D points easily, and this avoids the need to allocate and slow down enumeration via the points.Select(p => new float3(p, 0)) wrapper.
2017-12-17 01:51:51 +01:00
RoosterDragon
de38313579 Cache VariableExpressions on load.
Compiling these expressions is sadly expensive, and we needed new ones for every trait on every actor each time one was generated. The expressions thankfully can be shared as they are pure functions, which removes this overhead.
2017-12-17 01:42:21 +01:00
RoosterDragon
bf21fc5213 Use StringComparison.Ordinal for StartsWith checks in TechTree.cs. 2017-12-17 01:28:25 +01:00
RoosterDragon
13edaefcac Avoid LINQ when building map domains. 2017-12-17 01:28:25 +01:00
RoosterDragon
dd2ae9fe5e Avoid LINQ in some Tick methods.
As Tick is called often, avoiding allocation overhead in these methods is useful.
2017-12-17 01:28:25 +01:00
Kevin Hinton
86f9b8807e Add EffectiveOwner to Lua Scripting API. 2017-12-16 18:00:41 +00:00
FrameLimiter
72a11ec2a5 Removed duplicate values in defaults.yaml 2017-12-16 17:45:33 +00:00
FrameLimiter
60633dfdf2 Removed duplicate values in civilian.yaml 2017-12-16 17:45:33 +00:00
FrameLimiter
a6dc9c3b21 Removed duplicate value in allies-01 rules.yaml 2017-12-16 17:45:33 +00:00
RoosterDragon
5b51f2a0fa Avoid format strings in some places.
Where it is possible to directly concat strings, prefer this in some often-called methods.
2017-12-16 17:26:29 +01:00
RoosterDragon
f78c3bef33 Allow VS 2017 to format solution file. 2017-12-16 17:25:55 +01:00
RoosterDragon
c8c7629bce Convert some stray spaces to tabs. 2017-12-16 15:53:03 +00:00
Paul Chote
502c3e2bf5 Remove global chat integration. 2017-12-13 21:04:16 +01:00
GSonderling
488cec64b8 Added support for filtering exits by production type. 2017-12-13 04:08:52 +01:00
Arular101
1bf59e885d Upgrade rules for increasing HP and damage by 10x for the D2K mod 2017-12-12 23:56:25 +01:00
Arular101
50185d3ccd Small adjustments 2017-12-12 23:56:25 +01:00
Arular101
64a3187fd1 Increase damage and HP by 10x 2017-12-12 23:56:25 +01:00
Arular101
a1b3fe2bda Parabombs tooltip for airfield
with selection grouping
2017-12-12 23:14:30 +01:00
Arular101
56634564f2 Parabomb tooltip fixes 2017-12-12 23:14:30 +01:00
Mustafa Alperen Seki
fd1aa07f83 Adjust D2K Building Selection Boxes 2017-12-12 23:02:55 +01:00
Paul Chote
3ad6a87920 Center the vertical alignment of lobby options if less than the panel height.
This also enables an extra row to be shown before triggering the scroll bar.
2017-12-12 22:43:11 +01:00
Paul Chote
3f67feab0e Rename boolean LobbyOption fields. 2017-12-12 22:43:11 +01:00
Paul Chote
36fccbc453 Add Checkbox/Dropdown to lobby options yaml fields. 2017-12-12 22:43:11 +01:00
Paul Chote
ea32c758eb Expose default UI labels and tooltips to yaml. 2017-12-12 22:43:11 +01:00
Paul Chote
99908c4d80 Move hardcoded list of lobby options into yaml. 2017-12-12 22:43:11 +01:00
Paul Chote
2e24c14503 Make SpawnMPUnitsInfo fields readonly. 2017-12-12 22:43:11 +01:00
Paul Chote
2c17780b94 Make ShroudInfo fields readonly. 2017-12-12 22:43:11 +01:00
Paul Chote
763630f547 Make DeveloperInfo fields readonly. 2017-12-12 22:43:11 +01:00
Paul Chote
97cdce7448 Add additional metadata to lobby options. 2017-12-12 22:43:11 +01:00
Arular101
b71b2ad523 Upgrade rules for increasing HP and damage by 100x for the TD mod 2017-12-12 22:18:41 +01:00
Arular101
7ad989fc43 Adjust values in custom maps and missions 2017-12-12 22:18:41 +01:00
Arular101
4e62531b11 Adjust values to match current damage 2017-12-12 22:18:41 +01:00
Arular101
2e681ba674 Increase damage and HP by 100x 2017-12-12 22:18:41 +01:00
Arular101
917d6b7627 Upgrade rules for increasing HP and damage by 100x for the RA mod 2017-12-12 20:50:27 +01:00
Arular101
261126194c Balance changes from #14471 2017-12-12 20:50:27 +01:00
Arular101
8069247726 Adjust values in custom maps and missions 2017-12-12 20:50:27 +01:00
Arular101
a429a62bf9 Adjust values to match current damage 2017-12-12 20:50:27 +01:00
Arular101
e049452f22 Increase damage and HP by 100x 2017-12-12 20:50:27 +01:00
RoosterDragon
7760c41bd9 Avoid array resizing in OpenAlAsyncLoadSound.
- Where it is accessible, use the length of the stream to presize the MemoryStream to the correct size.
- Instead of copying out the result via ToArray, grab the underlying buffer via GetBuffer and use that to create the sound source.

This avoids extraneous copying of the array containing the audio.
2017-12-12 00:01:04 +01:00
RoosterDragon
0899d02377 Avoid allocations when generating RadarSignatureCells.
The RadarWidget can supply a reusable buffer to each trait to avoid individual traits having to return new enumerables. Additionally, this allows the two traits to avoid LINQ and further allocations as they can manually enumerate and populate the buffer themselves.
2017-12-12 00:00:51 +01:00
Mustafa Alperen Seki
c69df4eedf Make subfactions use UI for their main faction
So it won't crash if a human player uses them.
2017-12-11 23:43:10 +01:00
Paul Chote
6633483d12 Increase infantry mouse bounds to improve usability. 2017-12-11 19:45:07 +01:00
Paul Chote
55b6084b60 Add a lint test for conflicting Interactable and Selectable. 2017-12-11 19:45:07 +01:00
Paul Chote
c87409ed1a Remove legacy bounds code. 2017-12-11 19:45:07 +01:00
Paul Chote
bf57eceeec Select highest priority actor when not drag selecting. 2017-12-11 19:45:07 +01:00
Paul Chote
ff5b4b15b3 Introduce IDecorationBounds to replace Actor.SelectionOverlayBounds. 2017-12-11 19:45:07 +01:00
Paul Chote
6f5d035e79 Introduce IMouseBounds and split/rework mouse rectangles.
The render bounds for an actor now include the area covered
by bibs, shadows, and any other widgets. In many cases this
area is much larger than we really want to consider for
tooltips and mouse selection.

An optional Margin is added to Selectable to support cases
like infantry, where we want the mouse area of the actor
to be larger than the drawn selection box.
2017-12-11 19:45:07 +01:00
Paul Chote
8fcc80b05a Use IRender.ScreenBounds in ScreenMap.
Traits are now required to trigger a ScreenMap update whenever they
believe that their ScreenBounds have changed.
2017-12-11 19:45:07 +01:00
Paul Chote
fa65fef4d1 Add IRenderModifier.ModifyScreenBounds to support WithShadow. 2017-12-11 19:45:07 +01:00
Paul Chote
86bfe28ade Add ScreenBounds method to IRender interface.
This method is expected to return Rectangles that cover
the region where Renderables returned by Render may exist.
2017-12-11 19:45:07 +01:00
Paul Chote
46f6263061 Update ScreenMap state in a single pass at the end of the tick. 2017-12-11 19:45:07 +01:00
Paul Chote
373aaee004 Use World.(AddTo|RemoveFrom)Maps in Immobile/Building. 2017-12-11 19:45:07 +01:00
Paul Chote
9e18ec7314 Simplify ScreenMap bounds checking. 2017-12-11 19:45:07 +01:00
Paul Chote
cdc426d162 Add IActorPreview.ScreenBounds. 2017-12-11 19:45:07 +01:00
Paul Chote
29255c8e01 Add IModel.AggregateBounds and ModelAnimation.ScreenBounds.
The bounds define the smallest Rectangle that covers all
rotations of all frames within a model sequence.
2017-12-11 19:45:07 +01:00
Paul Chote
506b677527 Add ModelAnimation.IsVisible property. 2017-12-11 19:45:07 +01:00
Paul Chote
041431d966 Add ISpriteSequence.Bounds and Animation(WithOffset).ScreenBounds.
The bounds define the smallest Rectangle that covers all frames within a sequence.
2017-12-11 19:45:07 +01:00
reaperrr
85c5259361 Fix AI crashing when trying to make parachuted harvester search map for resources while mid-air 2017-12-11 01:55:18 +01:00
reaperrr
b5ffe17d60 Make AI skip harvester orders if map has no resources 2017-12-11 01:55:18 +01:00
reaperrr
9ad0d78cdd Make the AI only consider harvesters 'idle' if last resource search failed
Otherwise the AI would consider the harvester 'idle' in too many situations.
This way, the AI now only uses its own resource search algorithm if the next resource patch is too far away for the FindResources activity to find it.
2017-12-11 01:55:18 +01:00
reaperrr
4d553900cf Move up harv.IsEmpty check in HackyAI
This is a fairly cheap check, so we should perform it before the activity checks
2017-12-11 01:55:18 +01:00
RoosterDragon
70ffa99203 Fix some uses of Exts.IsTraitEnabled. 2017-12-11 01:46:41 +01:00
RoosterDragon
9241c0f8b7 Provide only one overload of IsTraitEnabled. 2017-12-11 01:46:41 +01:00
RoosterDragon
c75a866f0d Lazily allocate sheet builder in ModelRenderer.
Avoid allocating the sheet builder each frame until it is needed. For mods that do not need to render models, this avoids allocating a large buffer and backing sheet as it will never render to the sheet. For mods that do render models, but don't need any this frame, this avoids allocating a new SheetBuilder that will not be used.
2017-12-11 01:44:21 +01:00
RoosterDragon
ec84b61316 Reduce memory required in MovementClassDomainIndex
The number of distinct domains on a map is often dozens, or at most hundreds. We can use a ushort to represent this easily, and reduce the size of the backing storage required to track domain indicies over the whole map.
2017-12-11 01:41:07 +01:00
RoosterDragon
2dd1bd2d39 Initialize map inverse cell projections with capacity 1.
This prevents the capacity being set to 4 when the first item is added. For flat maps, the inverse projection will only ever be of size 1, thus this is sufficient capacity. For isometric maps, 1 is often sufficient, we only need more near height changes where the discontinuity means multiple cells may project back. We can pay for some reallocations to expand the size in these cases.

On flat maps, this reduces the memory required by the backing array 4x.
2017-12-11 01:38:17 +01:00
RoosterDragon
8ef8c60a1a Remove allocations when enumerating sync hashes on an actor.
Exposing Actor.SyncHashes as an array allows allocation free enumeration, we just need to adjust the SyncHash type to run the hash function.
2017-12-10 13:49:52 +00:00
RoosterDragon
5338784e45 Fix bad uses of FirstEnabledTraitOrDefault on TraitInfos.
These are not traits so this method does not work. We can use EnabledByDefault on the ConditionalTraitInfo instead.
2017-12-10 13:39:24 +00:00
RoosterDragon
7a7eed4fb7 Add FirstEnabledTraitOrDefault helper method.
This avoids the allocations caused by LINQ when using traits.FirstOrDefault(Exts.IsTraitEnabled). This is important in FrozenActorLayer.RefreshState which is called very often. We apply the new helper method to all areas using the old pattern. An overload that takes an array allows arrays to be enumerated without causing allocations.
2017-12-10 13:39:24 +00:00
Paul Chote
cb670d83b3 Set PredictedStance when changing stance from Lua. 2017-12-08 01:54:55 +01:00
reaperrr
1a20dd15cb Fix D2k hightech factory sprite offsets
To match original.
2017-12-08 02:41:11 +02:00
reaperrr
2218b1a078 Fix terrain type of d2k tile #401 2017-12-08 02:41:11 +02:00
reaperrr
ce88d50d9c Fix offset and animation speed of D2k refinery smoke overlay 2017-12-08 02:41:11 +02:00
Paul Chote
7ebea32be9 Remove hardcoded settings backend. 2017-12-08 01:29:15 +01:00
Paul Chote
eb3ab682e8 Remove unused hotkeys from each mod. 2017-12-08 01:29:15 +01:00
Paul Chote
b4c5346346 Migrate hotkey linter to HotkeyManager. 2017-12-08 01:29:15 +01:00
Paul Chote
74c390d1d0 Migrate input settings to HotkeyManager. 2017-12-08 01:29:15 +01:00
Paul Chote
2f79173044 Migrate hotkey consumers to HotkeyManager. 2017-12-08 01:29:15 +01:00
Paul Chote
35a3df3736 Duplicate default hotkeys in yaml. 2017-12-08 01:29:15 +01:00
Paul Chote
f98907f42e Add HotkeyManager class. 2017-12-08 01:29:15 +01:00
Paul Chote
811427adc4 Add HotkeyReference.IsActivatedBy method. 2017-12-08 01:29:15 +01:00
Paul Chote
c4237d6a1a Rename NamedHotkey to HotkeyReference. 2017-12-08 01:29:15 +01:00
abcdefg30
6dd42f7ab1 Replace bogus ROF values in missions 2017-12-07 20:44:41 +02:00
RoosterDragon
62ab6ae6f1 OccupiedCells and TargetableCells must return arrays, not just enumerables.
This allows callers to efficiently enumerate these returned collections without the allocation and overhead imposed by the IEnumerable interface. All implementations were already returning arrays, so this only required a signature change.
2017-12-07 20:39:30 +02:00
FrameLimiter
da036c5728 Fixes EditorTilesetFilter for T04 cactus 2017-12-05 21:32:28 +01:00
Smittytron
3d8ab75faf RA Balance changes (December 2017) 2017-12-05 12:38:13 +01:00
Paul Chote
9d2935935c Add a debug visualization for screen map rectangles. 2017-12-04 23:10:23 +01:00
Paul Chote
0f512088d2 Unify ingame-debug.yaml. 2017-12-04 23:10:23 +01:00
Paul Chote
1a1c6368fc Fix ScreenMap handling of flipped sprites. 2017-12-04 03:54:23 +02:00
Arular101
1b94378f71 Disable the new build radius lobby option for minigames 2017-12-02 20:26:23 +01:00
Mustafa Alperen Seki
f4086e4fe0 Update one of the dead worm tiles 2017-12-02 05:18:19 +01:00
Pavel Penev
77ae802b9c Added D2k asset installer d2k-e 2017-12-02 00:08:50 +01:00
Pavel Penev
8f5787a57f Added D2k asset installer d2k-d 2017-12-02 00:08:50 +01:00
Pavel Penev
45a2f5e6c1 Added D2k asset installer d2k-c 2017-12-02 00:08:50 +01:00
Pavel Penev
649971d517 Renamed D2k asset installers to systematize namings 2017-12-02 00:08:50 +01:00
Pavel Penev
450cbeea96 Added an extra bit of installer data to Install.log 2017-12-02 00:08:50 +01:00
Pavel Penev
e09d502ce1 Make mod launchers run the game inside a child launcher instead of via OpenRA.Game.exe
This also removes the need to manually set application icons on Windows.
2017-12-02 00:05:57 +01:00
Pavel Penev
bd67bd24c0 Pull out game loop initialization from OpenRA.Program into OpenRA.Game and exception handling into its own class under OpenRA.Game/Support 2017-12-02 00:05:57 +01:00
abcdefg30
f249554b4f Fix swapped waypoint naming in Survival 01 2017-11-30 02:00:43 +01:00
abcdefg30
53dabfeb49 Move all waypoints of Survival 01 inside the map bounds 2017-11-30 02:00:43 +01:00
abcdefg30
3a94312fcd Move all waypoints of Evacuation inside the map bounds 2017-11-30 02:00:43 +01:00
abcdefg30
616ee6cce4 Remove a misplaced camera actor 2017-11-30 02:00:43 +01:00
RoosterDragon
6a97502e09 Reuse buffers when loading Xcc databases.
We can reuse the list as a buffer when reading strings to avoid throwaway allocations, which will reduce GC pressure during loading.
2017-11-30 02:16:51 +02:00
reaperrr
c48ddbdfa5 Fix Mammoth Mk2 render bounds
And polish trait internals a little.
2017-11-29 11:39:24 +02:00
reaperrr
d21992130a Fix TS harvester render bounds 2017-11-29 11:39:24 +02:00
Paul Chote
4994716cf7 Ignore empty children when aggregating EventBounds. 2017-11-27 16:09:34 +02:00
dsimmons87
65a2410738 Fixed bug in map Retry Install
If the download failed, the map status would be MapStatus.DownloadError,
which would cause the Install method to return immediately. I've updated
the Install method now to account for MapStatus.DownloadError.
2017-11-27 15:18:33 +02:00
Paul Chote
8fea476a0d Fix NRE when ProvidesPrerequisite is defined on the player actor. 2017-11-26 19:51:46 +01:00
abcdefg30
ed67cea852 Fix a crash in Disguise.cs when order.TargetActor is null 2017-11-26 18:41:53 +01:00
abcdefg30
c83dda4ce7 Initialize AsActor as self.Info 2017-11-26 18:41:53 +01:00
abcdefg30
4c707ad6a6 Fix WithDisguisingInfantryBody crashing when losing the disguise 2017-11-26 18:41:53 +01:00
Arular101
02d57c5fee Soviet05 Radar Reveal
Added beacon and message
2017-11-26 17:26:57 +01:00
Mustafa Alperen Seki
2043576857 Add Modified Outpost tooltip to Ordos4. 2017-11-26 16:53:17 +01:00
Mustafa Alperen Seki
15b77f8d91 Add Harkonnen 5 2017-11-26 16:53:17 +01:00
Paul Chote
b898b5001a Rename AttackBase.IsAttacking to IsAiming to make its uses and assumptions clear. 2017-11-26 15:58:00 +01:00
Paul Chote
ea05221180 Count AttackStatus.NeedsToTurn as valid for AttackBase.IsAttacking. 2017-11-26 15:58:00 +01:00
Marwan
8a38c6ef23 WithTextDecoration Fix 2017-11-26 15:35:50 +01:00
Mustafa Alperen Seki
43d100246f Remove ^TowerHusk: 2017-11-26 15:26:44 +01:00
Mustafa Alperen Seki
73134b0417 Rename D2K vehicle husks 2017-11-26 15:26:44 +01:00
Smittytron
d5622ae1ca Fix TD cliff tiles 29-31 2017-11-26 15:17:38 +01:00
Arular101
3ed18f51e3 Fix V2 Rocket Launcher facings
Now with AmmoPool

V2 changes for Fort Lonestar
2017-11-26 15:01:43 +01:00
Paul Chote
855042894c Fix bogus cached PowerState for map placed actors. 2017-11-26 03:37:33 +01:00
reaperrr
5ec3ad0957 Fix turrets following target even if AttackTurreted is disabled or paused 2017-11-25 03:55:56 +02:00
reaperrr
e215c5019e Fix regressions introduced by #13998 2017-11-25 03:55:56 +02:00
reaperrr
6e59fe3bc2 Adapt TS to removal of IsDisabled checks from Attack* traits 2017-11-25 03:55:56 +02:00
reaperrr
456d32f3eb Make Attack* traits PausableConditional and replace IsDisabled with IsTraitDisabled checks 2017-11-25 03:55:56 +02:00
reaperrr
68487d1197 Replace turret IsDisabled check with AT.IsTraitDisabled check 2017-11-25 03:55:56 +02:00
reaperrr
cea2658f31 Remove IsDisabled from AffectsShroud traits
Makes them only disableable via conditions.
2017-11-25 03:55:56 +02:00
reaperrr
00cd7a8da5 Adapt TS to removal of actor IsDisabled check in Cloak 2017-11-25 03:55:56 +02:00
reaperrr
ede6d5a57d Remove Actor.IsDisabled check from Cloak
Makes cloaking only disableable via conditions.
2017-11-25 03:55:56 +02:00
Smittytron
9c729c77bc Add Real tough guy difficulty to Allies04 2017-11-24 00:35:39 +01:00
Paul Chote
7c901c27f1 Generate a versioned tarball during package creation. 2017-11-22 22:39:08 +01:00
Mustafa Alperen Seki
8ce460ad8c Fix badgers showing owner row on RA mod. 2017-11-22 18:24:50 +01:00
reaperrr
56b6aabbb8 Use new IAutoRenderSize for determining actor render bounds 2017-11-21 01:00:09 +02:00
reaperrr
2501a93b87 Remove superflous AutoSelectionSize from TS high bridge placeholder
It defines CustomSelectionSize, so AutoSelectionSize is redundant/wrong here.
2017-11-21 01:00:09 +02:00
reaperrr
5a596d27c9 Switch RenderNameTag to use SelectionOverlayBounds 2017-11-21 01:00:09 +02:00
reaperrr
6711af63eb Move SelectionBarsRenderable to Mods.Common 2017-11-21 01:00:09 +02:00
reaperrr
be290cfabd Split Actor.Bounds into RenderBounds and SelectableBounds
Additionally, internally renamed VisualBounds to SelectionOverlayBounds to avoid confusion with RenderBounds.

This step was necessary to prevent actors with selectable area smaller than their graphics to be removed too early from ScreenMap even though part of the graphics should still be visible.
RA cruisers were a prime example, but to a lesser extent several other actors were affected as well.

This separation also serves as preparation to determine the final RenderBounds from multiple source bounds later, to fix the remaining ScreenMap issues (building 'bibs', aircraft shadows).
2017-11-21 01:00:09 +02:00
Mustafa Alperen Seki
ca1448c7ba Add Support of Types for GivesBuildableArea 2017-11-20 16:17:13 +02:00
Paul Chote
4cbc2ee6f3 Add support for custom sell cursors. 2017-11-19 23:40:33 +01:00
Paul Chote
5030a2257e Delegate cursor selection to GlobalButtonOrderGenerator subclasses. 2017-11-19 23:40:33 +01:00
Arular101
586dd6541b More Changes 2017-11-19 23:23:24 +01:00
Arular101
9c61f524f0 Tooltip fixes 2017-11-19 23:23:24 +01:00
Paul Chote
10fb487abf Update macOS launcher/dependencies to latest tag.
This supports/forces the 64 bit mono runtime, and
is the first version that compiles all native code
using travis-ci.
2017-11-19 16:55:44 +00:00
Andre Mohren
25968ee66f Allow WithIdleOverlayInfo to render while the actor is being build. 2017-11-19 16:52:59 +00:00
Mustafa Alperen Seki
7b12cf887d Add Harkonnen 6b 2017-11-19 16:21:42 +01:00
Mustafa Alperen Seki
01c1e08bd8 Add Original Remap Palette for RA Mod Campaign
and Shellmap
2017-11-19 15:08:42 +01:00
Arular101
3e5829355d Small override fix mission Intervention 2017-11-19 13:48:42 +00:00
abcdefg30
48df21c4a5 Fix the insertion transport not being removed in allies03b
The exit was outside of the map's border
2017-11-19 14:42:37 +01:00
abcdefg30
8654bcc244 Fix the FreePrisoners objective being completed without freeing prisoners 2017-11-19 14:42:37 +01:00
abcdefg30
dddc5dc80f Capturing also fires OnRemovedFromWorld 2017-11-19 14:42:37 +01:00
abcdefg30
3e3d98c735 Resolve a TODO in allies03a.lua 2017-11-19 14:42:37 +01:00
Arular101
f16f9b8ae3 Allies06a Radar Reveal
Added beacon and message
2017-11-19 14:32:33 +01:00
Mustafa Alperen Seki
29ec16f15d Some changes to crate unit bonuses 2017-11-19 12:58:34 +00:00
FrameLimiter
1e94ded9dd Changed Warhead to Warhead@1Dam 2017-11-19 12:55:28 +00:00
FrameLimiter
edd9e39dcd Removed redundent LaunchAngle override on 8Inch 2017-11-19 12:55:28 +00:00
FrameLimiter
cfe1a8cd02 Removed the redundant overrides 2017-11-19 12:55:28 +00:00
FrameLimiter
e6f73dc68f Updated weapon trait names for drop-zone-w mission 2017-11-19 12:55:28 +00:00
FrameLimiter
c70efd4a14 Fixes PortaTesla Warhead override for drop-zone-tikiaki 2017-11-19 12:49:43 +00:00
FrameLimiter
b7c3bc4e25 Fixes PortaTesla Warhead override for drop-zone 2017-11-19 12:49:43 +00:00
RoosterDragon
471d7ae40d Run GC more during loading.
This helps reduce the peak GC size by trimming temporary loading garbage a bit more often, rather than just doing it at the end of loading.
2017-11-19 12:42:25 +00:00
RoosterDragon
a71a5cc71d Reduce the size of the sheets created for fonts.
- A 512x512 sheet is about half full after precaching and some usage, but uses 16x less memory than the default 2048x2048 sheet. This saving occurs twice as we maintain a managed buffer for this sheet.
- Only precache smaller fonts, as the larger fonts are less used and take up more space than is worthwhile.
- Only precache in white, as red is largely unused.
2017-11-19 12:40:04 +00:00
RoosterDragon
713cdaef5d Reduce allocations needed by ReplayConnection.
Packets from each chunk are now saved directly in an array, removing the overhead of a list. Additionally, a list is reused as a buffer for decoding packets into, preventing a new buffer from needing to be allocated for each chunk.
2017-11-19 12:11:38 +00:00
FrameLimiter
f4502e9aa7 Map editor fix for misplaced T09.Husk 2017-11-19 01:32:52 +01:00
FrameLimiter
a5d7218dee Removed TilesetOverride for desert tree husk t08 2017-11-19 01:29:30 +01:00
FrameLimiter
773a619d2d Palette fix for desert tree husks 2017-11-19 01:26:13 +01:00
Smittytron
20a25c2f5e TD disable DetectCloaked on low power 2017-11-18 16:06:25 +01:00
Smittytron
070c54d069 Disable DetectCloaked on low power Tesla Coil 2017-11-17 13:42:51 +01:00
abcdefg30
2836cec0a3 Fix reinforcements in Nod06a not playing a notification 2017-11-17 13:28:10 +01:00
Mustafa Alperen Seki
f35f6c0813 Move Sprite handling from Disguise to WithDisguisingInfantryBody 2017-11-15 15:48:24 +00:00
Mustafa Alperen Seki
ecdfcda43e Add DisguisedAsCondition 2017-11-15 15:48:24 +00:00
Arular101
3efce265a8 Reduce barrel explosion damage vs wood armor 2017-11-15 15:17:08 +00:00
Smittytron
9d45528496 Remove Airstrike from nod06a Normal Difficulty 2017-11-15 14:47:55 +00:00
Arular101
8bec765925 Campaign file changes and cleanup
Remove NoAutoTarget
2017-11-15 15:43:44 +01:00
Arular101
fd9cd78810 Fix spy voices 2017-11-15 15:20:34 +01:00
Mustafa Alperen Seki
bb600620a9 Add ShowTicks to InfiltrateForCash 2017-11-15 15:16:39 +01:00
Forcecore
c762453607 (External)Capturable are now conditional 2017-11-15 13:54:15 +02:00
reaperrr
a7620c97f0 Some internal renamings in power-related traits 2017-11-13 03:09:05 +02:00
reaperrr
7f5f2eac6f Don't scale RepairIndicator with zoom 2017-11-13 03:09:05 +02:00
reaperrr
d328b9b7d8 Adapt RA to low power/power-down refactor 2017-11-13 03:09:05 +02:00
reaperrr
bb790b83e6 Adapt TD to low power/power-down refactor
Removed CanPowerDown from TD buildings.

Manual power-down is intentionally not supported by the in-game UI, the remaining CanPowerDown entries were effectively bit-rot.
2017-11-13 03:09:05 +02:00
reaperrr
e0e1f56af5 Adapt D2k to low power/power down refactor 2017-11-13 03:09:05 +02:00
reaperrr
7537daf74e Adapt TS to power-related trait refactorings 2017-11-13 03:09:05 +02:00
reaperrr
99edc71c54 PowerDown/low power refactor upgrade rule 2017-11-13 03:09:05 +02:00
reaperrr
917c6884ed Make WithSpriteBody a pausable trait
Allowing to drop the PauseAnimationWhenDisabled property (in favor of using PausOnCondition).
2017-11-13 03:09:05 +02:00
reaperrr
786a0eb07f Refactor PowerManager and RequiresPower to use conditions
Instead of Actor.IsDisabled.
Added INotifyPowerLevelChanged interface to do so as efficiently as possible.
2017-11-13 03:09:05 +02:00
reaperrr
6c02e3f2b7 Refactor CanPowerDown to provide a condition instead of triggering IsDisabled 2017-11-13 03:09:05 +02:00
Pavel Penev
8d7eb0bc47 Fix order deserialization when there is no indended order subject 2017-11-13 00:37:44 +01:00
reaperrr
d602ec6485 Rename SelfReloads to AutoReloads 2017-11-12 19:48:06 +00:00
reaperrr
18c371d702 Changed AmmoCondition to support condition stacking 2017-11-12 19:48:06 +00:00
reaperrr
9dabc9d672 Upgrade rule for adapting to removal of Armament.OutOfAmmo 2017-11-12 19:48:06 +00:00
reaperrr
8b7a71685c Remove AmmoPool-awareness from Armament 2017-11-12 19:48:06 +00:00
reaperrr
ded92f394c Upgrade rule for AmmoPool.SelfReloads removal 2017-11-12 19:48:06 +00:00
reaperrr
6f95080aa4 Add ReloadAmmoPool and adapt AmmoPool
Refactored and simplified Rearm activity.
Uses local Reload now.

Removed AmmoPool.SelfReloads.
2017-11-12 19:48:06 +00:00
reaperrr
a017018bee Fix official mods Aircraft.RearmBuildings setups
Only aircraft that a) have an AmmoPool and b) don't auto-reload should define RearmBuildings.
2017-11-12 19:48:06 +00:00
Forcecore
d49c98ce18 Move activity implementation now uses ChildActivity 2017-11-12 19:46:05 +01:00
abc013
3092927a7f Added a visual explosion to e4 2017-11-12 18:34:03 +00:00
Smittytron
3ce6cac9f3 Clarify gdi07 Secondary Objective 2017-11-12 18:29:54 +00:00
Arular101
fdac023a8d Rename V2 Rocket to V2 Rocket Launcher 2017-11-12 18:26:46 +00:00
Andrii Yukhymchak
f4dbf55510 Chrono into map edge fix 2017-11-12 18:23:17 +00:00
Smittytron
e65ffc8be2 Change remaining Vulcan vs Light values to 50 2017-11-12 18:18:11 +00:00
Forcecore
ef878c6aeb Introduced CaptureTypes for TransformOnCapture 2017-11-12 18:14:30 +00:00
Mustafa Alperen Seki
3aa91a08cc Fix Fake Naval Yard's Name 2017-11-12 12:30:28 +00:00
Arular101
a8d27257eb Remove custom aud for cruiser in shellmap 2017-11-11 11:21:27 +01:00
Mustafa Alperen Seki
dadfc0b09c Fix AutoTargetPriority traits of ra infantry 2017-11-09 22:38:52 +02:00
abcdefg30
0377c8ae20 Replace bogus values in the Fort Lonestar rules 2017-11-09 22:24:14 +02:00
abcdefg30
eac9286b78 Fix tiling errors on some RA maps 2017-11-09 09:54:59 +01:00
Mustafa Alperen Seki
ba50fbba18 Dont show refund text if Sellable: trait is disabled. 2017-11-05 16:22:42 +02:00
Forcecore
4c78a05081 ADD GrantConditionOnAttack Trait
Useful for making Gattling weapons (Yuri's Revenge and C&C Generals) or
Boris (RA2) / Natasha (RA3) call for airstrike.
2017-11-04 17:18:24 +01:00
Mustafa Alperen Seki
8914227811 Add ShowTicks to sellable 2017-11-04 17:16:26 +01:00
Paul Chote
a3636c69d9 Update macOS launcher with space-in-filename metadata registration fix. 2017-11-04 16:23:41 +01:00
Paul Chote
faf2634e3d Upgrade mod rules. 2017-11-04 16:08:58 +01:00
Paul Chote
d2f0e5ac2d Replace Allow(Allies|Neutral|Enemies) with ValidStances enum. 2017-11-04 16:08:58 +01:00
Paul Chote
1caf982c1f Support multiple capture types on Capturable. 2017-11-04 16:08:58 +01:00
Mustafa Alperen Seki
77feb3f76d Add GPS Dots for Comm Center and Forward Command 2017-11-03 13:36:40 +01:00
Paul Chote
4fe7ffed85 Simplify and fix code quality in GpsDot. 2017-11-03 09:56:00 +01:00
Paul Chote
1376ad674e Remove Player.CanViewActor and .CanTargetActor. 2017-11-03 09:56:00 +01:00
Paul Chote
47634b25f9 Remove IFogVisibilityModifier. 2017-11-03 09:56:00 +01:00
Paul Chote
4dba9f5b88 Add RevealDisguiseType.Move. 2017-11-03 09:30:50 +01:00
Paul Chote
7afd219742 Expose Disguise ValidStances and TargetTypes to yaml. 2017-11-03 09:30:50 +01:00
Paul Chote
c9c7c5744c Explicitly implement interfaces in Disguise trait. 2017-11-03 09:30:50 +01:00
Mustafa Alperen Seki
bfe540cd8b Add Harkonnen 6a 2017-11-03 08:32:04 +01:00
abcdefg30
025d5c8d05 Fix the "Secure the convoy's path" objective being completed without securing a path 2017-11-03 07:54:48 +01:00
Mustafa Alperen Seki
817449c018 Fix Inconsistency with AcceptsDeliveredCash on RA mod 2017-11-01 20:41:00 +01:00
Andrii Yukhymchak
636a9a74a1 Font Crash gracious fallback 2017-11-01 18:50:15 +01:00
kosti1
6a750d7a65 Added buildradius checkbox to lobby options 2017-11-01 18:18:41 +01:00
Paul Chote
b8326bfead Fix crash with AllyRepair on dead structures. 2017-10-26 18:48:41 +03:00
Paul Chote
5f9a67ed87 Migrate frozen actors from ExtraData to Target. 2017-10-26 18:48:41 +03:00
Paul Chote
29c423772f Serialize order Target directly instead of TargetActor/TargetCell. 2017-10-26 18:48:41 +03:00
Andrii Yukhymchak
6e1c1eaa94 Submarine fix 2017-10-26 01:48:36 +02:00
Paul Chote
77a7453347 Improve WidgetUtils.WrapText performance on long lines. 2017-10-26 01:42:16 +02:00
Andre Mohren
88c94ee841 Allow remaining time to be hidden in ProductionPaletteWidget. 2017-10-26 01:31:32 +02:00
Andre Mohren
aadfcdac66 Allow RallyPoint cursor to be defined via Yaml 2017-10-26 01:28:42 +02:00
Andre Mohren
cfeeb6563e RallyPointIndicator now optional 2017-10-26 01:21:29 +02:00
abcdefg30
cba7c60f6f Fix the "Unit Ready" notification playing erroneously 2017-10-25 22:43:35 +01:00
Paul Chote
cfac996438 Generalize factionVariant to a TypeDictionary of Inits. 2017-10-22 03:32:21 +03:00
rob-v
315cc966f4 Clear team in lobby for spectators 2017-10-21 14:12:11 +01:00
Paul Chote
5540eb1f2f Specify C# 5 as the language version in the Makefile too. 2017-10-19 14:18:03 +02:00
RoosterDragon
3a9abda441 Specify C# 5 as the language version used in all projects.
Attempts to use features from a new version will generate compile errors (on Roslyn and possibly other compilers), preventing accidental adoption of new language features.
2017-10-19 14:18:03 +02:00
Pi Delport
7da7764a65 (Update my name) 2017-10-18 08:03:57 -05:00
Paul Chote
9bdedda43a Make float2 immutable. 2017-10-17 15:46:07 +02:00
Paul Chote
69b4b541d2 Fix mutable use of float2 in SpriteFont. 2017-10-17 15:46:07 +02:00
Paul Chote
bee3f33f5f Remove long dead ResourceBarStyle.Bevelled code. 2017-10-17 15:46:07 +02:00
Matthias
cdedd1c7ac Some packages have been renamed in openSUSE
tested with openSUSE Leap 42.3
2017-10-16 15:35:06 +02:00
Matthias
7675c87593 Nuget dependency was dropped in #13912 2017-10-16 15:35:06 +02:00
Sam Gleske
80ce6d9988 Update package-all.sh
- Remove subshells and instead use bash parameter expansion.
- Utilize a function with a subshell to make adding additional packages easy.
2017-10-16 15:12:42 +02:00
Matthias Mailänder
ebb982789e Display a blocked cursor when not allowed to MoveIntoShroud. 2017-10-16 14:38:26 +02:00
Matthias Mailänder
b27289106d Don't give an okay voice when not allowed to move into shroud. 2017-10-16 14:38:26 +02:00
Matthias Mailänder
d4340fa799 Add a MoveIntoShroud switch to AttackMove as well. 2017-10-16 14:38:26 +02:00
reaperrr
db2d432c39 Fix Helis facing target center instead of attacked position 2017-10-16 14:25:03 +02:00
reaperrr
95dbcc1273 Update upgrade rule dates
These were merged after the release preparation branch for release 20171014 was created.
2017-10-16 14:22:11 +02:00
Smittytron
5471a3e729 Assign Gunboat and Sub to Techlevel Low 2017-10-15 23:15:43 +02:00
Paul Chote
d967c564a2 Remove TargetActor and TargetLocation from order issuing. 2017-10-15 19:07:46 +02:00
Paul Chote
4896a90b8d Add plumbing to issue orders against a generic Target. 2017-10-15 19:07:46 +02:00
Paul Chote
19b2c33514 Remove unused code from AttackOrderPower. 2017-10-15 19:07:46 +02:00
Andre Mohren
10740de6e9 DefaultSpriteSequence now allows derived classes to enhance sprites. 2017-10-14 19:50:05 +02:00
Raffael Zica
f2b5040d30 YamlException is now thrown if WeaponInfo can not be found in Ruleset.Weapons
Removed invalid spacing at the end of the line 36 in ThrowsShrapnel

Prevented NullReferenceException in cases where weapons aren't optional
2017-10-14 14:12:08 +02:00
abc013
c8b2a7dc04 Changed Aircraft explosion 2017-10-14 13:59:59 +02:00
abcdefg30
7ccf41be77 Catch invalid default game speeds while loading the ruleset 2017-10-14 13:54:26 +02:00
gkaretka
97e0f17e15 Add notification sound when paratroopers land 2017-10-14 13:40:53 +02:00
abcdefg30
a8c6d12c43 Fix the map editor crashing on invisible actors 2017-10-14 13:35:33 +02:00
Paul Chote
0e3de71036 Add line limit to global chat. 2017-10-14 13:10:27 +02:00
Paul Chote
ef43d8a86f Add missing StringComparison to IngameChatLogic. 2017-10-14 13:10:27 +02:00
Paul Chote
75d4062698 Limit chat messages to 2500 characters. 2017-10-14 13:10:27 +02:00
Paul Chote
249e813444 Fix submarine autotarget behaviour. 2017-10-08 20:11:30 +02:00
Paul Chote
e3e5dc1b49 Fix Defend stance auto-targeting naval structures. 2017-10-08 20:11:30 +02:00
Paul Chote
f425cbe0a4 Fix double-chronoshift return location exploit. 2017-10-08 20:04:36 +02:00
Paul Chote
f96c2710cd Remove directory creation side effect from Platform.SupportDir. 2017-10-08 19:53:10 +02:00
abcdefg30
4bca0c4c24 Fix devastators being carryable while overloading 2017-10-08 18:48:28 +01:00
abcdefg30
49e1af7f3f Fix the devastator lint warnings 2017-10-08 18:48:28 +01:00
abcdefg30
0727f7e9ff Only lint system maps 2017-10-08 18:23:25 +01:00
abcdefg30
5fdb828b60 Log which maps are processed by UpgradeModCommand
Upgrading the maps can take quite some time,
and at the moment you are left without information if something is happening.
2017-10-08 18:23:25 +01:00
abcdefg30
98170b0452 Use MapCache.LoadMapsWithoutCaching in UpgradeModCommand 2017-10-08 18:23:25 +01:00
abcdefg30
27345c1f6a Fix map linting crashing for not existing directories 2017-10-08 18:23:25 +01:00
abcdefg30
24c293e821 Fix Cargo force-requiring ConditionManager 2017-10-08 13:32:22 +01:00
abcdefg30
2366490fa0 Add an domain index check 2017-10-08 13:08:51 +02:00
abcdefg30
f48ba0ff86 Find alternative landing spots for blocked reinforcements 2017-10-08 13:08:51 +02:00
DeadlySurprise
80bf36e1a8 Deprecate Actor.IsDisabled 2017-10-08 00:12:15 +02:00
abc013
fb74c8351f Change Road -> Bridge on bridge tiles 2017-10-07 10:27:18 +02:00
reaperrr
8533debc44 Require explicit INotifyAddedToWorld and INotifyRemovedFromWorld 2017-10-07 10:17:11 +02:00
reaperrr
8ec3d5ddb8 Require explicit ITickRender 2017-10-07 10:17:11 +02:00
reaperrr
0ce3c113e1 Require explicit ITargetableCells 2017-10-07 10:17:11 +02:00
reaperrr
7ca9679b34 Require explicit ITargetablePositions 2017-10-07 10:17:11 +02:00
Andre Mohren
edeb980f5b Implemented a percentual chance to play the ImpactSound to CreateEffectWarhead 2017-10-07 10:15:22 +02:00
Matthias Mailänder
5b16bb952f Count all zero images as valid. 2017-10-07 10:09:35 +02:00
Matthias Mailänder
a8288a38f8 Allow sprites with empty frames to pass the ShpTS checks. 2017-10-07 10:09:35 +02:00
Matthias Mailänder
d7323e07cc Avoid modData.MapCache during lint checks 2017-10-07 10:08:53 +02:00
Mustafa Alperen Seki
2102fad2b5 Add CashTricklerBar 2017-10-06 23:02:02 +02:00
Mustafa Alperen Seki
a4b0bf5c52 Make CashTrickler PausableConditional 2017-10-06 23:02:02 +02:00
Mustafa Alperen Seki
049096efd7 Add InitialDelay to CashTrickler 2017-10-06 23:02:02 +02:00
abc013
dfe2076826 Fix passive vehicles can become ranks 2017-10-06 22:24:24 +02:00
abc013
f84699454f Fix some tiling errors in the map Haos Ridges [RA] 2017-10-06 22:04:32 +02:00
abc013
8c387c3bcf Fix a tile error in map Asymetric Battle [RA] 2017-10-06 22:02:39 +02:00
Andre Mohren
4e45747b41 Game no longer crashes if no ResourceLayer is used. 2017-10-06 21:57:57 +02:00
Matthias Mailänder
d64a9e6afc Add Devastator reactor overload upon deployment 2017-10-06 18:14:17 +02:00
Matthias Mailänder
370ee0841f Add GrantsCondition to KillsSelf 2017-10-06 18:14:17 +02:00
Forcecore
18c6fe09db Sound and Animation support for DonateCash 2017-10-06 18:07:18 +02:00
Paul Chote
d170262e09 Fix yaml merging of nodes that define their own overrides. 2017-10-06 17:54:11 +02:00
Paul Chote
18f6317560 Add a unit test for merging duplicated yaml nodes. 2017-10-06 17:54:11 +02:00
abcdefg30
178e7afd30 Remove a tiny bit of duplication on the TS walls 2017-10-06 15:38:53 +02:00
Paul Chote
4aee91cd47 Remove unnecessary trait lookups when cloaking/decloaking. 2017-10-06 10:09:12 +02:00
gkaretka
cf4c16b6bb Add .idea to .gitignore 2017-10-04 11:20:19 +02:00
abc013
b55ae1dcf0 Fixed TS Hand of Nod lights 2017-10-03 13:52:06 +02:00
Paul Chote
a1347a7f32 Populate MapLocations from LoadMaps.
This ensures that the map locations won't be created by the utility
but means that LoadMaps must be called before attempting to use MapLocations.
2017-09-30 17:37:52 +02:00
Paul Chote
64afe4cfde Display (attack|assault)move-blocked cursor outside map bounds.
This makes them consistent with the regular move order.
2017-09-30 14:33:40 +02:00
Paul Chote
e93183f0eb Clamp out of bounds attack-move orders to the edge of the map. 2017-09-30 14:33:40 +02:00
Paul Chote
36af2107d6 Fix the attack move order generator activating with an empty selection. 2017-09-30 14:33:40 +02:00
Paul Chote
d8f53a9ccd Fix crash when selection contains destroyed actors. 2017-09-30 14:33:40 +02:00
Paul Chote
fa8ab19dfd Fix global chat history buffer timestamps. 2017-09-28 15:09:12 +02:00
reaperrr
9aaf800bca Require explicit INotifyActorDisposing 2017-09-28 13:14:08 +02:00
reaperrr
5026dfe5d3 Require explicit INotifyCreated 2017-09-28 13:14:08 +02:00
reaperrr
6f790938d0 Require explicit IGameOver 2017-09-28 13:14:08 +02:00
reaperrr
2b391d5724 Require explicit ICreatePlayers 2017-09-28 13:14:08 +02:00
reaperrr
d7c2c6afc4 Require explicit INotifySelected and INotifySelection 2017-09-28 13:14:08 +02:00
reaperrr
a8e6cd2604 Require explicit IRenderOverlay 2017-09-28 13:14:08 +02:00
Paul Chote
69daa7e7cc Don't draw mine overlay for single-cell fields. 2017-09-27 21:00:56 +02:00
Paul Chote
a0e028ee0e Fix mine field overlay extending to map corner for the first tick. 2017-09-27 21:00:56 +02:00
Paul Chote
cc17b7419a Add voice feedback when issuing a mine deploy order. 2017-09-27 21:00:56 +02:00
Paul Chote
1d6cd81690 Add mine deployment to the command bar. 2017-09-27 21:00:56 +02:00
Jean-Rémy Buchs
2b96c2ed78 Add RequiresCondition to ProvidesPrerequisite 2017-09-24 14:56:47 +01:00
rob-v
c9b4568117 Add Statistics options hotkeys 2017-09-24 15:32:02 +02:00
Forcecore
b86d632c8c Land activity now lets the actor to land midair
Midair, at the altitude of the target actor.
2017-09-24 15:03:20 +02:00
Matthias Mailänder
205c947670 Settings documentation is no longer aimed at server admins. 2017-09-24 14:18:20 +02:00
Matthias Mailänder
030c942f73 Describe SendSystemInformation so people know how to opt-out. 2017-09-24 14:18:20 +02:00
Matthias Mailänder
da02620c4e Add field description from current Settings wiki entry. 2017-09-24 14:18:20 +02:00
Matthias Mailänder
2cf102d81a Remove trailing spaces. 2017-09-24 14:18:20 +02:00
Matthias Mailänder
95278b1f07 Don't add the launch arguments to the settings documentation. 2017-09-24 14:18:20 +02:00
Matthias Mailänder
faad941714 git ignore the auto-generated settings documentation 2017-09-24 14:18:20 +02:00
Matthias Mailänder
7549abc516 Wiki publish the auto-generated settings documentation. 2017-09-24 14:18:20 +02:00
Matthias Mailänder
1d8a50cdab Add build script support for the settings documentation. 2017-09-24 14:18:20 +02:00
Matthias Mailänder
0018bf3063 Add start and stop repair overlay animation support. 2017-09-24 11:43:49 +01:00
RoosterDragon
7160c8a1a9 Allow multiple VocFormat streams.
VocFormat.GetPCMInputStream now returns independent streams, allowing multiple instances of the same source to be streamed.
2017-09-24 11:23:39 +01:00
RoosterDragon
7ed769421e Providing streaming WavFormat data.
WavFormat.GetPCMInputStream now returns data that is streamed, rather than a MemoryStream.
2017-09-24 11:23:39 +01:00
reaperrr
4ae92a5c22 Only add partitioned effects to ScreenMap if (current) bounds are valid
This serves to avoid adding effect where either width or height is 0.
2017-09-24 10:54:58 +01:00
reaperrr
593e86325b Fix SpriteEffect not updating ScreenMap 2017-09-24 10:54:58 +01:00
reaperrr
eea14ec922 Fix DepthCharge dealing no damage to surfaced subs 2017-09-23 20:06:23 +01:00
reaperrr
eb3b68e24a Fix DepthCharge not showing an explosion when hitting emerged subs 2017-09-23 20:06:23 +01:00
reaperrr
1d402d4f8b Fix submerged sub vulnerability to most weapons 2017-09-23 20:06:23 +01:00
Andre Mohren
800f6adc21 Made WithSpriteTurret.TurretOffset protected virtual to be overridden by mod traits. 2017-09-22 13:29:15 +02:00
Matthias Mailänder
d17e414648 Fix the overlay to actor rule for crates 2017-09-22 13:04:29 +02:00
Matthias Mailänder
44cfa793df Don't import veinholedummy and correct it's index
as it is a Westwood hack we don't need to immitate.
There are already better ways to limit vein growth.
2017-09-22 13:04:29 +02:00
Paul Chote
48a018d994 Fix cheats reenabling OneShot support powers. 2017-09-19 17:53:53 +02:00
Matthias Mailänder
3af0b1a7a0 Remove PlayerPaletteFromCurrentTileset. 2017-09-17 16:20:38 +02:00
RoosterDragon
3b4904775a Remove OpenAlStreamingSound.
Whilst the implementation appears to work most of the time, it sometimes stops playing audio mid-track. This could be an implementation issue, or bugs in the underlying streaming APIs of the OpenAL library. Either way, it is not currently reliable enough to be used.
2017-09-17 15:50:49 +02:00
RoosterDragon
94942cb140 Create OpenAlAsyncLoadSound.
This loads the sound inside a task, then begins playing it, avoiding blocking the UI thread. Unlike OpenAlStreamingSound it does not stream the sound from disk, rather, it loads the entire sound into memory and plays it in one go.
2017-09-17 15:50:49 +02:00
Paul Chote
f4c2b36778 Fix height calculations in custom terrain layers. 2017-09-17 15:35:40 +02:00
reaperrr
c3ece99796 Spatially partition SatelliteLaunch and GpsSatellite effects 2017-09-17 12:52:04 +01:00
reaperrr
be370cb855 Spatially partition NukeLaunch effect 2017-09-17 12:52:04 +01:00
reaperrr
9aa861eca9 Spatially partition CrateEffect 2017-09-17 12:52:04 +01:00
reaperrr
46132ed5c6 Spatially partition SpriteEffect 2017-09-17 12:52:04 +01:00
Matthias Mailänder
1aebf9857c Add support for only rendering effects inside screen bounds 2017-09-17 12:52:04 +01:00
Matthias Mailänder
8ca43e3d6b Import map Hidden Valley 2017-09-17 11:45:45 +01:00
abcdefg30
1f6b9a7638 Add missing wall tiles to the interior tileset 2017-09-16 17:31:15 +02:00
reaperrr
32df83d3c4 A few more misc explicit interface changes
By-products of making ITick require explicit implementation.
2017-09-16 15:51:37 +02:00
reaperrr
28e1f391e0 Make ITick require explicit implementation 2017-09-16 15:51:37 +02:00
reaperrr
85e60ef77f More misc explicit interface implementations
By-product from making ITick explicit in various places.
2017-09-16 15:51:37 +02:00
reaperrr
03c3a5f310 Make ITick implementations explicit in OpenRA.Game 2017-09-16 15:51:37 +02:00
reaperrr
ec354f89cd Make ITick implementations explicit in Mods.Cnc and Mods.D2k 2017-09-16 15:51:37 +02:00
reaperrr
83afcc3448 Some misc explicit interfaces
Just a by-product from a previous commit, not meant to do anything specific other than bringing us a tiny step closer to requiring explicit implementations for these interfaces too, at some point.
2017-09-16 15:51:37 +02:00
reaperrr
761a4f29ab Make INotifyIdle and INotifyBecomingIdle require explicit implementation 2017-09-16 15:51:37 +02:00
reaperrr
ddfed13db4 Make Tick and TickIdle explicit in WithInfantryBody 2017-09-16 15:51:37 +02:00
Smittytron
a1ef581749 Remove EjectOnDeath from Vehicles 2017-09-16 14:10:54 +01:00
reaperrr
0c73baab3e Add large blue tib crystals to Hot Springs
Matching original positions.
2017-09-16 13:50:17 +02:00
reaperrr
89c143ae85 Fix remapable TS big blue tib crystal sequence offset 2017-09-16 13:50:17 +02:00
abcdefg30
d99a472456 Fix HackyAI trying to deploy undeployable actors 2017-09-16 11:01:53 +01:00
Paul Chote
42c4c7b7eb Fix compile error in CommandBarLogic. 2017-09-15 22:49:56 +01:00
Paul Chote
e04ae9aa2c Move remaining hardcoded selection power keys into yaml. 2017-09-15 23:06:38 +02:00
Paul Chote
83d522d945 Split unrelated hotkeys from WorldInteractionController. 2017-09-15 23:06:38 +02:00
Paul Chote
0e3bfcfb35 Replace WorldCommandWidget with individual logic classes. 2017-09-15 23:06:38 +02:00
Paul Chote
983f9c4cde Remove hardcoded DevReloadChrome and HideUserInterface hotkeys. 2017-09-15 23:06:38 +02:00
Paul Chote
25e88008ef Make the mute hotkey global. 2017-09-15 23:06:38 +02:00
Paul Chote
c759a68492 Move hardcoded screenshot hotkey into yaml. 2017-09-15 23:06:38 +02:00
Paul Chote
8c4afa414a Add a base single-hotkey logic class to avoid future duplication. 2017-09-15 23:06:38 +02:00
Paul Chote
c6fe1639db Support multiple key handlers. 2017-09-15 23:06:38 +02:00
Paul Chote
7459050af9 Rework global music hotkey bindings. 2017-09-15 23:06:38 +02:00
reaperrr
08aaa998aa Fix AI BuildingLimits
The AI BaseBuilder would allow building a structure not only when the current number was lower, but also of it was equal to the limit, which allowed the AI to build one too much of every building.
2017-09-15 19:31:57 +01:00
reaperrr
dbcfb0c92e Rename and move WithTurretedSpriteBody
The name wasn't in line with our implicit naming convention and ambigous on top of that.
Also, only used by RA and TD so moved to Mods.Cnc.
2017-09-15 18:10:13 +01:00
reaperrr
c611b5aeeb Remove 'ed' from WithTurretedAttackAnimation
Too confusing, as people might think it requires 'WithTurretedSpriteBody', also not entirely in line with our (admittedly implicit) render trait naming convention.
2017-09-15 18:10:13 +01:00
Mustafa Alperen Seki
c0b8bb3fcf Add Generals Pilot Logic 2017-09-14 23:26:10 +02:00
gwenzek
11a990e352 check if target is alive 2017-09-14 23:19:28 +02:00
gwenzek
7721d0b328 add a "capturing" condition for ExternalCapture 2017-09-14 23:19:28 +02:00
Forcecore
8027bed6b2 Separated ship squad from ground unit squad
Just like aircrafts are independent from ground squads.
2017-09-13 23:31:26 +02:00
abcdefg30
5ddf28fa81 Bomber John rules cleanup
Removed hacks, default values and duplicates
2017-09-13 22:13:49 +02:00
abcdefg30
0f95febefa Bomber John sequence cleanup
Most of those sequences were unused or default values
Tick count fixed as well
2017-09-13 22:13:49 +02:00
abcdefg30
ed598d2ab4 Make it possible to disable adjacency 2017-09-13 22:13:49 +02:00
abcdefg30
17f7aac35c Add more RevealDisguiseOn types 2017-09-13 21:18:21 +02:00
abcdefg30
71f2026b32 Remove the hardcoded cloak reference from activities 2017-09-13 21:18:21 +02:00
Paul Chote
049ed086f9 Add assault move to TS. 2017-09-11 19:08:33 +02:00
Paul Chote
646a6aa1e8 Fix TS target types. 2017-09-11 19:08:33 +02:00
Paul Chote
3575e82078 Add assault move to D2K. 2017-09-11 19:08:33 +02:00
Paul Chote
d588854cc5 Add assault move to TD. 2017-09-11 19:08:33 +02:00
Paul Chote
c46d1944d9 Add assault move to RA. 2017-09-11 19:08:33 +02:00
Paul Chote
a81749e102 Add frontend code for issuing assault move orders. 2017-09-11 19:08:33 +02:00
Paul Chote
ffee45cd76 Add AttackMove support for AssaultMove orders and condition granting. 2017-09-11 19:08:33 +02:00
Paul Chote
1e4640dc0b Clean up SupportPowerTooltipLogic:
- Avoid creating unnecessary bindings
- Avoid duplicated text size calculations
- Relayout panel when (and only when) needed
- Color timer red when paused
2017-09-10 21:30:55 +02:00
Paul Chote
458c913264 Clean up ProductionTooltipLogic:
- Avoid creating unnecessary bindings
- Fix time display not updating on low power
- Fix power usage color not updating on power changes
- Avoid duplicated text size calculations
2017-09-10 21:30:55 +02:00
Paul Chote
b3b2efa781 Move hardcoded viewport keys into yaml. 2017-09-10 21:30:55 +02:00
Paul Chote
2a6bb0678e Move hardcoded support power keys into yaml. 2017-09-10 21:30:55 +02:00
Paul Chote
4f00d62237 Move hardcoded production keys into yaml. 2017-09-10 21:30:55 +02:00
Paul Chote
19000219af Allow widgets to expose custom hotkey names to the linter. 2017-09-10 21:30:55 +02:00
Paul Chote
1180c5ddaa Move hardcoded production tab keys into yaml. 2017-09-10 21:30:55 +02:00
Paul Chote
f5e38a4e2c Move ISeedableResource interface to Mods.Common. 2017-09-10 18:36:39 +02:00
Paul Chote
1de767d9bf Move objectives interfaces to Mods.Common. 2017-09-10 18:36:39 +02:00
Paul Chote
3f72076e1a Move radar signature interfaces to Mods.Common. 2017-09-10 18:36:39 +02:00
Paul Chote
e54cf9acc3 Move linter interfaces to Mods.Common. 2017-09-10 18:36:39 +02:00
Paul Chote
0648fc9f31 Move damage notification interfaces to Mods.Common. 2017-09-10 18:36:39 +02:00
Paul Chote
e3212d1e64 Move the IMove interface to Mods.Common. 2017-09-10 18:36:39 +02:00
Paul Chote
0f6dda3f5f Calculate building placement offsets in screen space. 2017-09-10 18:26:54 +02:00
reaperrr
0161d68237 Fix RA TorpTube bridge explosions
There were two issues at work here:
1) The combination of default MaximumLaunchAngle and default CruiseAltitude made torpedoes fly a ballistic curve, which combined with BoundToTerrain type made them explode 'in the air' when hitting non-water tiles. This would result in AIr returned as target type, which is invalid for torpedoes.
2) The explosion warheads were missing the Ground target type, which is actually the (only) target type that the bridge tiles you hit with torpedoes have.
2017-09-10 18:10:24 +02:00
reaperrr
b68c935807 Fix water splash on bridge in RA
On bleed, both the explosion as well as the water splash warhead of weapons triggered on bridges, since they have both the Ground and Water target types.

Ground cannot be made invalid for water splashes without causing regressions elsewhere, so a new Bridge target type is introduced to explicitly make bridges invalid targets for water splash effect warheads.
2017-09-10 18:10:24 +02:00
Paul Chote
23382dc508 Remove dummy d2k support bin overlay. 2017-09-10 15:25:43 +02:00
SoScared
b78736acb7 Replace all Creep with Neutral actors on map Agenda 2017-09-10 15:06:41 +02:00
Taryn Hill
48a071c756 Always use noget in fetch-thirdparty-deps-windows.sh 2017-09-10 14:52:13 +02:00
Forcecore
1dfba51665 Removed dead WSB from Refinery trait 2017-09-09 11:15:31 +01:00
reaperrr
43664a12b5 Tweaked TS tiberium colors
- excluded tiberium palettes from global lighting, just like in the original
- tweaked colors to match original more closely
2017-09-07 21:40:29 +01:00
Ishan Bhargava
9cd81a6310 Add myself to AUTHORS
Pull request #5666
2017-09-07 18:33:34 +02:00
reaperrr
024887b268 Move Contrail to Traits
And make interface implementations explicit while we're at it.
2017-09-06 22:07:02 +01:00
Paul Chote
898ef8fe50 Add support for queuing Guard orders. 2017-09-03 12:54:09 +02:00
Paul Chote
f4f27f8980 Add support for queuing AttackMove orders. 2017-09-03 12:54:09 +02:00
SoScared
2bfc7cdd46 RevealOnDeath with Aircraft 2017-09-02 20:17:05 +02:00
abcdefg30
1649f3aa60 Don't spawn aircraft husks when not airborne 2017-09-02 16:26:12 +02:00
Matthias Mailänder
3ce8026337 Tweak the atomic missile SpawnOffsets. 2017-09-02 15:10:35 +02:00
Matthias Mailänder
6774c9ce01 Setup the Temple of Nod smoke in Tiberian Dawn. 2017-09-02 15:10:35 +02:00
Matthias Mailänder
fd0c34bf61 Fix death hand missile offsets. 2017-09-02 15:10:35 +02:00
Matthias Mailänder
2980decc93 Fix the Dune 2000 missile overlay offset. 2017-09-02 15:10:35 +02:00
Matthias Mailänder
855f1816f4 Tweak the time until hatch opening and rocket away. 2017-09-02 15:10:35 +02:00
Matthias Mailänder
4526344eed Add WithNukeLaunchAnimation and -Overlay traits. 2017-09-02 15:10:35 +02:00
Matthias Mailänder
c63620e979 Remove trailing spaces. 2017-09-02 15:10:35 +02:00
SoScared
df9c07da41 change damage vs None with ^Cannon 2017-09-02 14:58:19 +02:00
reaperrr
96e98a24ae Polish RA Jeep and APC muzzle offsets
And Jeep turret offset.
2017-09-02 14:29:34 +02:00
Matthias Mailänder
e9e3e9c22e Bring back the MiGs to the desert shellmap. 2017-09-02 14:14:50 +02:00
rob-v
6eb7a98cd2 Change default Observer shroud option for Explored map 2017-09-02 14:02:24 +02:00
Matthias Mailänder
49919e3d73 Add missing AutoTarget to DOGGIE. 2017-09-02 13:33:35 +02:00
Paul Chote
3c1e7896a5 Clear editor search fields and yield focus on escape. 2017-09-02 12:48:42 +02:00
Matthias Mailänder
0ce4d91dd6 Add map No where to run 2017-09-02 12:14:39 +02:00
Matthias Mailänder
b3dbeb13ca Add map Tiers of Sorrow. 2017-09-02 12:14:13 +02:00
reaperrr
15c636525d Fixed no explosions showing on some RA civ structures
The combination of HitShape, but not Targetable makes the actors be considered invalid for effect warheads.
Lack of Targetable makes them invulnerable anyway, so removing HitShape and Health (and Explodes) is the most logical fix.
2017-09-02 09:27:30 +01:00
Matthias Mailänder
918690f9b2 Remove a bogus ore map in a water segment. 2017-09-02 09:13:14 +01:00
Matthias Mailänder
3c0b95cf6e Fix indentions of weapon documentation table of contents. 2017-09-02 08:56:31 +01:00
Paul Chote
3cf9d47b39 Fix TD stance bar naming. 2017-09-01 11:56:37 +02:00
Paul Chote
4a2988e676 Remove UnitCommandWidget. 2017-09-01 11:56:37 +02:00
Paul Chote
2a3a27aa65 Add CommandBarBlacklist to TS. 2017-09-01 11:56:37 +02:00
Paul Chote
9607c09516 Add command bar to TS. 2017-09-01 11:56:37 +02:00
Paul Chote
7997a24559 Adjust chrome.png and yaml layout.
Also cleans up stray whitespace.
2017-09-01 11:56:37 +02:00
reaperrr
2f66f83051 Remove IsPlane hack
The new VTOL boolean together with CanHover is enough to replace this.
2017-08-31 18:03:59 +02:00
reaperrr
28ffcfea3c Upgrade rule for setting VTOL to true for CanHover actors 2017-08-31 18:03:59 +02:00
reaperrr
3b01da737c Introduce Aircraft VTOL boolean
Rather than hard-linking vertical take-off/land to the CanHover = Helicopter assumption.
2017-08-31 18:03:59 +02:00
reaperrr
5d58374962 Remove FlyCircleTimed activity
A FlyCircle overload is sufficient.
2017-08-31 18:03:59 +02:00
reaperrr
df94765eac Upgrade rule for TakeOffOnResupply 2017-08-31 18:03:59 +02:00
reaperrr
c9ff362ea2 Add TakeOffOnCreation and TakeOffOnResupply to Aircraft
Before this, it was impossible to replicate the behavior of the original games (staying on pad/airfield after reload) without hacking around in Mods.Common.
This allows modders to disable these without meddling with code.
2017-08-31 18:03:59 +02:00
abcdefg30
5cfb5aaf2d Rework the addition and removal of building influence 2017-08-31 17:36:22 +02:00
reaperrr
93f385e9fc Give TS JumpJet shadow when airborne 2017-08-31 16:54:03 +02:00
reaperrr
d1a4133752 Fix WithShadow
It *modifies* the actor rendering, so returning an empty renderable was bogus, as were the IsInWorld/IsDead checks.
2017-08-31 16:54:03 +02:00
Paul Chote
69251d508b Suppress scroll events from the DropDownWidget fullscreen mask. 2017-08-31 12:43:17 +02:00
Paul Chote
1bd1a185f5 Unhardcode spectator combined/world view hotkeys. 2017-08-31 12:33:34 +02:00
Paul Chote
0525408664 Unhardcode map editor zooming hotkey. 2017-08-31 12:33:34 +02:00
Paul Chote
9a5b5d9b6f Unhardcode music control hotkeys. 2017-08-31 12:33:34 +02:00
Paul Chote
8d4ffee32a Add hotkey linting support for logic args. 2017-08-31 12:33:34 +02:00
SoScared
1a95aeda19 patch tent cost increase 2017-08-28 20:41:22 +02:00
atlimit8
fd6b2c0107 Move timers from ConditionManager to ExternalCondition 2017-08-27 12:43:27 +01:00
atlimit8
c61cd37bec ExternalCondition: List<struct TimedToken> timedTokens 2017-08-27 12:43:27 +01:00
atlimit8
62c76b8ae9 ExternalCondition: remove timedTokensBySource 2017-08-27 12:43:27 +01:00
atlimit8
f51323daa3 ExternalCondition: remove unneeded source references 2017-08-27 12:43:27 +01:00
atlimit8
c7d3c3ec73 Changes to ExternalCondition.TryRevokeCondition 2017-08-27 12:43:27 +01:00
Forcecore
72236b66f6 D2K Thumper deploy animation fix 2017-08-27 13:22:00 +02:00
abcdefg30
d6d8cf05b0 Adjust IsAtGroundLevel fixing EjectOnDeath 2017-08-26 15:54:02 -07:00
Taryn Hill
eed9af88f5 Always use noget in fetch-thirdparty-deps.sh 2017-08-26 15:50:47 -07:00
reaperrr
b1c63a4752 Fix FindActorsOnLine overscan 2017-08-26 15:43:44 -07:00
Paul Chote
a7c2f3e7e3 Add mod-specific appdata metadata. 2017-08-26 15:43:26 -07:00
Matthias Mailänder
8ae2a65d9d Shroud is actually a player trait. 2017-08-26 22:47:48 +01:00
Matthias Mailänder
a445049e3d Fix the indentions. 2017-08-26 18:35:41 +01:00
Paul Chote
49d5283ce1 Use curl instead of wget in fetch-thirdparty-deps. 2017-08-26 13:49:56 +02:00
Forcecore
739d0c0bb2 Disabled Ground squads finding targets individually
* Now they will tend to focus fire.
* Provides performance when when the AI designer implements better
  (and complex) target selection code
2017-08-25 23:13:26 +02:00
Zimmermann Gyula
cb36dfa532 Implement instant refineries.
AKA RA2 refinery logic.
2017-08-25 22:19:03 +02:00
Paul Chote
a29360f313 Add a lint test for hotkeys. 2017-08-25 21:56:52 +02:00
Paul Chote
1e0148e092 Migrate ButtonWidget to NamedHotkey. 2017-08-25 21:56:52 +02:00
Paul Chote
42bf232b37 Introduce NamedHotkey type. 2017-08-25 21:56:52 +02:00
Paul Chote
13a06c6e8d Move hardcoded static asset browser hotkeys to yaml. 2017-08-25 21:56:52 +02:00
Matthias Mailänder
988353e431 Remove cloak sounds not featured in the original.
closes #9519
2017-08-25 19:42:38 +02:00
SoScared
1e1109034f Additional tweak of the Artillery weapon 2017-08-25 15:29:18 +02:00
atlimit8
43880ea7b8 Make AppearsOnRadar trait conditional 2017-08-25 15:10:28 +02:00
atlimit8
8a825f2999 Add VariableExpression.NoVariables 2017-08-25 15:10:28 +02:00
Matthias Mailänder
2ccba3768b Add overlay animations to CAARAY. 2017-08-25 10:48:35 +01:00
Matthias Mailänder
dc72b8080c Fix Dune 2000 harvester husk offset.
Closes #10087.
2017-08-24 11:28:06 +02:00
C. Helmig
80b69af30c d2k: sonic weapon fix.
Works around buggy friend-foe discrimination (/Falloff) for sonic weapon. Damage values closer to original game. (Fixes #13850)
2017-08-23 13:02:54 +02:00
reaperrr
120d7f9976 WithHarvestAnimation style fixes
Make interface implementations explicit.
2017-08-23 12:34:00 +02:00
reaperrr
0103b7ca87 Make WithSpriteBody.PlayCustomAnimation resume loop of Sequence
...after it finished playing.
2017-08-23 12:34:00 +02:00
reaperrr
90a4fe7ca1 Resume looping default WithSpriteTurret.Sequence after custom anim
It doesn't really make sense not to, and the only trait using PlayCustomAnimation previously did this manually anyway.
2017-08-23 12:34:00 +02:00
reaperrr
c75e64a952 Properly account for disabled Armaments in various places
These places didn't care if an Armament was disabled, which could lead to unexpected behavior.
2017-08-22 20:55:46 +01:00
reaperrr
951ee2210c Consider disabled armaments invalid 2017-08-22 20:55:46 +01:00
SoScared
28e28bf34c fix ore spots on Dual Cold Front 2017-08-22 19:28:06 +02:00
Matthias Mailänder
cba0fc3475 Remove CreateGroup order as the ActorGroupProxy is gone. 2017-08-22 18:44:35 +02:00
rob-v
01d631b228 Load replays also in sub directories 2017-08-21 14:54:46 +02:00
Mustafa Alperen Seki
f7983692ae Make Captures Upgradeable 2017-08-21 13:06:00 +02:00
Matthias Mailänder
287290bbdf Fix indentions of trait documentation table of contents. 2017-08-20 23:06:59 +02:00
reaperrr
a4ef199b75 Tweak TS Juggernaut BurstDelays and Report
Since the original sound contains 3 shots, it could happen that the sound played even though only 1 shot had been fired.
Additionally, it could happen that no sound was played when shooting because the first burst had already been fired.
Finally, in the original the 3rd shot was delayed a little compared to the first two.

This adresses all these issues.
2017-08-20 20:48:25 +02:00
reaperrr
c4bfb052d9 Apply BurstDelays rename to TS 2017-08-20 20:48:25 +02:00
reaperrr
98357cf2a9 Apply BurstDelays rename to RA 2017-08-20 20:48:25 +02:00
reaperrr
5581688781 Apply BurstDelays rename to TD 2017-08-20 20:48:25 +02:00
reaperrr
7704ae655e Apply BurstDelays rename to D2k 2017-08-20 20:48:25 +02:00
reaperrr
834a40b18d Refactor BurstDelay to BurstDelays
Allowing to set custom per-burst delays.
2017-08-20 20:48:25 +02:00
abcdefg30
4ce2e82ff0 AI States style fixes and minor polish 2017-08-17 23:25:51 +01:00
reaperrr
98b1468801 Clean up Harvester TickIdle a bit
Triggering UnblockRefinery each idle tick is bogus.
2017-08-17 22:58:37 +01:00
reaperrr
406f482a19 Cache INotifyHarvesterAction trait look-ups 2017-08-17 22:58:37 +01:00
reaperrr
bac8d3233a Streamline Harvester.SetProcLines return check 2017-08-17 22:58:37 +01:00
reaperrr
e9a420a9e5 Remove redundant TakeOff from Aircraft
ResupplyAircraft queues a TakeOff anyway, if the aircraft is a helicopter.
2017-08-17 22:19:05 +01:00
reaperrr
95d3d61ffa Make some Aircraft interface implementations explicit with subclassing 2017-08-17 22:19:05 +01:00
reaperrr
6c3ff0dc02 Make AircraftInfo more readable
Added some descriptions where I considered it necessary, added newlines between entries, reordered entries where it made sense.
2017-08-17 22:19:05 +01:00
reaperrr
cab6a96b16 Allow skipping make anim for actors with WithMakeAnimation and GrantConditionOnDeploy 2017-08-17 22:03:42 +01:00
reaperrr
f3f2621eeb Allow skipping 'sell'/reversed make anim on Sell 2017-08-17 22:03:42 +01:00
reaperrr
eb5fb5abba Allow skipping 'sell'/undeploy anim for actors that TransformOnCapture 2017-08-17 22:03:42 +01:00
rob-v
c848b30e9e Add chat tab to multiplayer/replays ingame menu 2017-08-17 21:54:19 +01:00
Paul Chote
daee217431 Add x64process stat. 2017-08-17 19:45:11 +02:00
Paul Chote
9fc0970cfc Force '.' decimal point for windowscale stat. 2017-08-17 19:45:11 +02:00
abcdefg30
7156c2c09d Add the missing base implementation to AttackFrontal's RulesetLoaded 2017-08-17 19:28:51 +02:00
rob-v
dd9d5450bc Add EnableDebugCommandsInReplays option 2017-08-17 19:23:22 +02:00
abcdefg30
6840177776 Fix actors being able to walk on cliffs after the sietch was destroyed 2017-08-17 19:05:38 +02:00
abcdefg30
d78b29b545 Fix sietches being repairable and remove the Power trait (unused) 2017-08-17 19:05:38 +02:00
abcdefg30
b66605033a Let the fremen fallback in atreides04 instead of attacking the base 2017-08-17 19:05:38 +02:00
abcdefg30
b329bf2642 Add more fail conditions to the spice harvesting missions 2017-08-17 19:05:38 +02:00
abcdefg30
02c371f68a Fix a bogus condition 2017-08-17 19:05:38 +02:00
abcdefg30
6907929cb4 Fix the harvest protection logic 2017-08-17 19:05:38 +02:00
abcdefg30
e07779942e Add a warning when attacking the Outpost to ordos04 2017-08-17 19:05:38 +02:00
abcdefg30
a942ca8f57 Add a global script for campaign missions in d2k 2017-08-17 19:05:38 +02:00
Mustafa Alperen Seki
7f0c7cee0d Make D2K Missions use Vanilla Remap Colors 2017-08-17 18:46:06 +02:00
Mustafa Alperen Seki
3bf217aaf7 Rename Emparor to Corrino on Shellmap 2017-08-17 18:46:06 +02:00
SoScared
6d1ca72a80 probe new civilian structures 2017-08-17 15:24:39 +02:00
abc013
944dfeb476 Corrected gnrl sequences & added TakeCover: to him 2017-08-13 21:06:14 +02:00
AoAGeneral
ab5d2766f6 TD Bridges.
Currently in both playtest and release the bridges for TD used to be 500hp with no armor. Meaning that minigunners and other unit types were able to kill them off fairly quickly. Giving them extra HP and an armor type means units such as minigunners, humvees, flamethrowers, and APCs can't kill bridges off quickly.
2017-08-13 20:17:15 +02:00
rob-v
eb21c4bddd Keep Attack M./Guard action while holding hotkey (revert Playtest change) 2017-08-13 17:44:26 +01:00
Allan Greis Eriksen
5435e34b9a Audio device names is now shown correcly in the Audio Device combolist. 2017-08-13 18:38:18 +02:00
Oliver Brakmann
8f26b4e92b Fix Mobile not sanitizing target location of Move orders 2017-08-13 17:23:37 +01:00
Paul Chote
62a006ed3e Change beaconlib reference to match other deps. 2017-08-13 18:15:55 +02:00
Paul Chote
cf386c6149 Update BeaconLib to 1.0.1. 2017-08-13 18:15:55 +02:00
abcdefg30
84d6fa1540 Fix monster tanks not attacking radar domes 2017-08-13 18:01:02 +02:00
abcdefg30
2dad2de0ff Add the win and loss videos to Monster Tank Madness 2017-08-13 18:01:02 +02:00
Paul Chote
34a44acf85 Update macOS launcher to 20170812 tag. 2017-08-13 17:53:23 +02:00
Paul Chote
b937bf43f7 Switch to .NET 4.5 compatible Marshal.SizeOf overload. 2017-08-13 17:39:00 +02:00
Paul Chote
f4d4a7eed4 Fix d2k commandbar artwork glitch. 2017-08-13 14:07:47 +02:00
Paul Chote
4e493f265f Revert TicksBeforePathing move from Mobile to Move.
This partially reverts commit 11c8cda0c3.
2017-08-13 13:31:58 +02:00
abc013
6e8dd5058e Changed SAM launch offset 2017-08-13 12:49:04 +02:00
Paul Chote
ad2c46ea73 Add a community code of conduct.
This adopts the standard 1.4 version of the Contributor Covenant.
http://contributor-covenant.org/
2017-08-13 12:32:01 +02:00
Jean-Rémy Buchs
49f42401e6 Improve the description of LandOnCondition of Aircraft 2017-08-11 10:56:27 -05:00
abcdefg30
de7a0c9861 Remove TargetWhenIdle and TargetWhenDamaged from AutoTarget 2017-08-10 19:11:47 +02:00
abcdefg30
0209c1c848 Add TRUCK.Husk to Tiberian Dawn 2017-08-10 18:24:19 +02:00
SoScared
64dfc38c2e final RA balance patch for the next playtest/release 2017-08-10 18:16:58 +02:00
abcdefg30
d1ab421240 Don't spawn new actors before all RemovedFromWorld callbacks have run 2017-08-10 17:57:11 +02:00
Paul Chote
22d7031819 Focus the ViewportController on Standard/Inverted scroll modes. 2017-08-10 12:50:00 +02:00
Paul Chote
d0d8062dbf Extract ViewportEdgeScrollMargin settings entry. 2017-08-10 12:44:44 +02:00
Paul Chote
dd709a2679 Revert "Have some activities count as idle activities"
This reverts commit ae111248f3.
2017-08-10 12:22:28 +02:00
rob-v
86cff9a774 Disable click sound on command bar buttons 2017-08-09 21:50:34 +01:00
rob-v
0c67ca3321 Add ButtonWidget.DisableKeySound property 2017-08-09 21:50:34 +01:00
reaperrr
f744a429a4 Fix SpawnActorPower not playing launch sounds 2017-08-09 21:27:42 +01:00
reaperrr
11d6dc2d3a Add TS base-building hit-shapes 2017-08-09 21:25:05 +01:00
reaperrr
6e2418d940 Add TS civilian hit-shapes 2017-08-09 21:25:05 +01:00
reaperrr
1b28cb9f60 Add TS default hit-shapes 2017-08-09 21:25:05 +01:00
reaperrr
ab6d18eab6 Center two-legged billboard sprites
They are supposed to be positioned in the middle of the two cells they occupy.
2017-08-09 21:25:05 +01:00
reaperrr
c40ec62511 Tweak sprite offsets of some TS aban civ buildings
In cases where the sprite has enough margin to the footprint cell edges, moving down the sprite a bit is much easier than manually setting up custom targetable offsets.
2017-08-09 21:25:05 +01:00
abcdefg30
94a386eda8 Move the interior tile 384 from the Floor to the Wall category 2017-08-09 20:52:00 +02:00
rob-v
88a3656f0a Fix Client tooltip crash in Lobby 2017-08-09 19:17:00 +01:00
SoScared
3c4a75e4b6 Add Gap effect values to new vision range changes and ironing out misc. values 2017-08-09 20:13:58 +02:00
Jean-Rémy Buchs
1d1802a163 Add LandOnCondition to the trait Aircraft which triggers a landing and prevents takeoffs while the condition is met 2017-08-08 15:13:50 -05:00
abc013
c4d2fdbd83 Fix ground units can stay on water (tile 409) 2017-08-08 12:32:43 +02:00
AoAGeneral
baa4b61d3f TD Prerequisite changes.
Adds prerequisite labels to E1, E3, E6, and TDTRUCK.

Closes #13780
2017-08-08 12:21:37 +02:00
reaperrr
b388c6186a Shrink shape of TD SAM 2017-08-07 14:49:24 +02:00
reaperrr
97bbfd34fd Shrink shape of RA SAM
To adress balancing concerns.
Also tweaked sprite offsets to make it more centered.
2017-08-07 14:49:24 +02:00
reaperrr
6945ae1747 Rename tdtruk to truck 2017-08-07 11:01:21 +02:00
reaperrr
f4499c1e84 Rename TD TowerMissle to TowerMissile 2017-08-07 11:01:21 +02:00
reaperrr
20efd1fc29 Fix RA effect waheads
- take Trees into consideration (they don't have Ground TargetType)
- fixed the delayed effect warheads on Vulcan
2017-08-07 11:01:21 +02:00
Oliver Brakmann
d61939f59f Fix harv docking activities getting lost under certain circumstances 2017-08-07 10:45:45 +02:00
forcecore
d1328212c6 AttackFrontal's FacingTolerance is now in effect 2017-08-07 10:26:26 +02:00
reaperrr
d949e17b88 Perform FogObscures as late as possible
FogObscures is more expensive than simpler boolean, player or HasTraitInfo checks, so in these places it makes sense to perform the other checks first.
2017-08-07 09:42:12 +02:00
AoAGeneral
99dac05fd7 TD Balance Changes. (20170802)
Engineer movement speed reduced to 48 from 56.

Helicopter crash damage increased to 100 from 40.

Rocket infantry spread damage reduced to 32 from 128.

------

Engineer movement was the same speed as minigunners and at times would enable them to escape the minigunners. This slight speed reduction helps for this.

Helicopter crash damage was lacking and a slight increase in this enables them to do some damage. With the cost of the helicopter it is still not a good idea to sacrifice aircraft just for exta damage.

Rocket infantry spread is reduced because of the damage done to infantry packs. Games turned into tanks mixed with rocket infantry. Reducing this spread enables them to single target units more closely then in larger armies.
2017-08-07 09:35:20 +02:00
Matthias Mailänder
fdad5e2c48 Add weapon rules documentation. 2017-08-05 21:00:06 +02:00
Matthias Mailänder
1a54b36e2b Cache shouldn't be exposed at all so drop it. 2017-08-05 21:00:06 +02:00
Matthias Mailänder
55ea54ff2d Explain the color notation. 2017-08-05 21:00:06 +02:00
Matthias Mailänder
59d88199cf Give Dictionaries a friendlier notation. 2017-08-05 21:00:06 +02:00
Matthias Mailänder
0a150bcc75 Move friendly type names into shared helper class. 2017-08-05 21:00:06 +02:00
reaperrr
879ab2ac1f Remove redundant Disposed checks
IsDead returns true if Disposed is true.
2017-08-05 18:31:15 +02:00
Lars Beckers
68607872bd Fix RA Aftermath installer metadata on Linux 2017-08-02 20:09:28 +02:00
FrameLimiter
9fdb7dc51b Fix duplicate Targetable value on ^Ammobox 2017-08-02 19:40:08 +02:00
Taryn Hill
57fb551545 Improve the exception message when a Bridge's DemolishWeapon cannot be found 2017-07-29 22:26:30 +02:00
reaperrr
8ee742d66a Split MuzzleOffset and MuzzleOrientation calcs to protected methods
To make overriding easier for downstream traits inheriting Armament.
2017-07-29 22:14:58 +02:00
reaperrr
26be2a3e99 Split Burst updating to separate method 2017-07-29 22:14:58 +02:00
reaperrr
e847f49256 Split barrel firing to separate method 2017-07-29 22:14:58 +02:00
reaperrr
514bd12885 Merge some Armament CanFire checks 2017-07-29 22:14:58 +02:00
reaperrr
bdfa8a361b Split Barrel can-fire checks to separate CanFire method 2017-07-29 22:14:58 +02:00
AoAGeneral
dc61c98098 TD Engineer Description.
Closes #13720
2017-07-25 18:59:47 -05:00
Paul Chote
4c222e5f2b Add Demo Truck and MAD Tank to Deploy description. 2017-07-23 20:10:08 +02:00
Paul Chote
cd9fc96c37 Implement IIssueDeployOrder on MADTank. 2017-07-23 20:10:08 +02:00
GSonderling
ef0b0847bc Removed references to make docs 2017-07-23 15:33:39 +02:00
rob-v
97306f2bcd Move DeveloperMode trait to Mods.Common 2017-07-23 15:08:24 +02:00
rob-v
d4e9e0e069 Add Visualization chat commands 2017-07-23 15:08:24 +02:00
reaperrr
8403adba37 Implement damage-based slowdown on TD Gunboats
In line with original.
2017-07-23 15:06:03 +02:00
reaperrr
9a25b33beb Adapt TD missions to TDGunboat refactor 2017-07-23 15:06:03 +02:00
reaperrr
94fa24088b Add dedicated TDGunboat traits
And get rid of Mobile.OnRails hack.
2017-07-23 15:06:03 +02:00
reaperrr
3bdd35fd2d Move AttackPopupTurreted to Attack subfolder 2017-07-23 15:06:03 +02:00
SoScared
4903912e3c Revert 4 cell adjacency of Kennel/Silo back to 2. 2017-07-22 22:48:33 +01:00
abcdefg30
aaaf66a6a3 Disable bots on Poland Raid (again) 2017-07-22 22:33:44 +01:00
Forcecore
0573f52a37 Add Railgun projectile 2017-07-21 23:18:38 +01:00
1064 changed files with 40481 additions and 15444 deletions

16
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,16 @@
Thank you for your contribution to OpenRA!
Please be aware that we do not have enough project maintainers to match the rate of contributions, so it may take several days before somebody is able to respond to your Pull Request.
You can help speed up the review process by following a few steps:
* Make sure that you have read and understand the OpenRA Coding Standard (see https://github.com/OpenRA/OpenRA/wiki/Coding-Standard).
* Write quality commit messages (see https://chris.beams.io/posts/git-commit/).
* Only commit changes that directly relate to your Pull Request. Use your Git interface to unstage any unrelated changes to project files, line endings, whitespace, or other files.
* Review the code diff view below to double check that your changes satisfy the above three points.
* Use the `make test` and `make check` commands to check for (and fix!) any issues that are reported by our automated tests.
* If you are changing shared mod or engine code, make sure that you have tested your changes in all four default mods.
* Respond to review comments as soon as you reasonably can. Reviewers will usually prioritize Pull Requests that are still fresh in their minds. Make sure to leave a comment when you push new changes, otherwise GitHub does not automatically notify reviewers!
* Leave a polite comment asking for reviews if a week or more has passed without feedback.
If you need any help you can ask in the #openra IRC channel on freenode (most active during European evenings).

5
.gitignore vendored
View File

@@ -62,7 +62,9 @@ OpenRA.Launcher.Mac/OpenRA.xcodeproj/*.mode1v3
# auto-generated documentation
DOCUMENTATION.md
WEAPONS.md
Lua-API.md
Settings.md
*.html
openra.6
@@ -80,3 +82,6 @@ StyleCopViolations.xml
# Support directory
/Support
# IntelliJ files
.idea

View File

@@ -78,6 +78,7 @@ deploy:
file:
- build/OpenRA-${TRAVIS_TAG}.exe
- build/OpenRA-${TRAVIS_TAG}.dmg
- build/OpenRA-${TRAVIS_TAG}-source.tar.bz2
- build/openra_${DOTVERSION}_all.deb
skip_cleanup: true
on:

View File

@@ -69,12 +69,14 @@ Also thanks to:
* Ian T. Jacobsen (Smilex)
* Imago
* Iran
* Ishan Bhargava (ishantheperson)
* Jacob Dufault (jacobdufault)
* James Dunne (jsd)
* James Gilbert (DSUK)
* Jan-Willem Buurlage (jwbuurlage)
* Jason (atlimit8)
* Jeff Harris (jeff_1amstudios)
* Jefri Sevkin (Arular)
* Jes
* Joakim Lindberg (booom3)
* John Turner (whinis)
@@ -97,6 +99,7 @@ Also thanks to:
* Max621
* Max Ugrumov (katzsmile)
* Michael Rätzel
* Michael Silber (frühstück)
* Michael Sztolcman (s1w_)
* Mustafa Alperen Seki (MustaphaTR)
* Neil Shivkar (havok13888)
@@ -108,8 +111,9 @@ Also thanks to:
* Paul Dovydaitis (pdovy)
* Pavlos Touboulidis (pav)
* Pedro Ferreira Ramos (bateramos)
* Peter Amrehn (jongleur1983)
* Pizzaoverhead
* Piët Delport (pjdelport)
* Pi Delport (pjdelport)
* Psydev
* Raphael Vogt (TheRaffy, Yellow)
* Raymond Bedrossian (Squiggles211)
@@ -168,9 +172,6 @@ under the MIT license.
Using ICSharpCode.SharpZipLib initially by Mike
Krueger and distributed under the GNU GPL terms.
Using SmartIrc4Net developed by Mirco Bauer
distributed under the LGPL version 2.1 or later.
Using rix0rrr.BeaconLib developed by Rico Huijbers
distributed under MIT License.

76
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by private-messaging a project team member (users with a + in front
of their name) via our IRC channel (#openra on freenode
[webchat](http://webchat.freenode.net/?channels=openra)). All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@@ -14,7 +14,7 @@ Windows
* [OpenAL](http://kcat.strangesoft.net/openal.html) (included)
* [liblua 5.1](http://luabinaries.sourceforge.net/download.html) (included)
You need to fetch the thirdparty dependencies using [NuGet](http://www.nuget.org) and place them at the appropriate places by typing `make dependencies` in a command terminal.
You need to fetch the thirdparty dependencies and place them at the appropriate places by typing `make dependencies` in a command terminal.
To compile OpenRA, open the `OpenRA.sln` solution in the main folder, build it from the command-line with MSBuild or use the Makefile analogue command `make all` scripted in PowerShell syntax.
@@ -23,7 +23,7 @@ Run the game with `OpenRA.Game.exe Game.Mod=ra` for Red Alert or `OpenRA.Game.ex
Linux
=====
Use `make dependencies` to map the native libraries to your system, fetch the remaining CLI dependencies using [NuGet](http://www.nuget.org) and place them at the appropriate places.
Use `make dependencies` to map the native libraries to your system and fetch the remaining CLI dependencies to place them at the appropriate places.
To compile OpenRA, run `make all` from the command line.
@@ -34,7 +34,6 @@ Type `sudo make install` for system wide installation. Run `make install-linux-s
Debian/Ubuntu
-------------
* nuget
* mono-devel
* libfreetype6
* libopenal1
@@ -47,22 +46,15 @@ Debian/Ubuntu
openSUSE
--------
* mono-devel
* nuget
* openal
* freetype2
* SDL2
* lua51
* xdg-utils
* zenity
* curl
```
sudo zypper in mono-devel openal-soft freetype2 SDL2 lua51 xdg-utils zenity curl
```
Gentoo
------
* dev-lang/mono
* dev-dotnet/libgdiplus
* dev-dotnet/nuget
* media-libs/freetype:2
* media-libs/libsdl2
* media-libs/openal

View File

@@ -14,9 +14,6 @@
# to check the official mod dlls for StyleCop violations, run:
# make check
#
# to generate documentation aimed at modders, run:
# make docs
#
# to install, run:
# make [prefix=/foo] [bindir=/bar/bin] install
#
@@ -42,9 +39,9 @@
#
SDK ?=
CSC = mcs $(SDK)
CSFLAGS = -nologo -warn:4 -codepage:utf8 -unsafe -warnaserror
CSFLAGS = -nologo -warn:4 -codepage:utf8 -langversion:5 -unsafe -warnaserror
DEFINE = TRACE
COMMON_LIBS = System.dll System.Core.dll System.Data.dll System.Data.DataSetExtensions.dll System.Drawing.dll System.Xml.dll thirdparty/download/ICSharpCode.SharpZipLib.dll thirdparty/download/FuzzyLogicLibrary.dll thirdparty/download/MaxMind.Db.dll thirdparty/download/Eluant.dll thirdparty/download/SmarIrc4net.dll thirdparty/download/rix0rrr.BeaconLib.dll
COMMON_LIBS = System.dll System.Core.dll System.Data.dll System.Data.DataSetExtensions.dll System.Drawing.dll System.Xml.dll thirdparty/download/ICSharpCode.SharpZipLib.dll thirdparty/download/FuzzyLogicLibrary.dll thirdparty/download/MaxMind.Db.dll thirdparty/download/Eluant.dll thirdparty/download/rix0rrr.BeaconLib.dll
NUNIT_LIBS_PATH :=
NUNIT_LIBS := $(NUNIT_LIBS_PATH)nunit.framework.dll
@@ -360,7 +357,6 @@ install-engine:
@$(CP) SharpFont.dll.config "$(DATA_INSTALL_DIR)"
@$(INSTALL_PROGRAM) Open.Nat.dll "$(DATA_INSTALL_DIR)"
@$(INSTALL_PROGRAM) MaxMind.Db.dll "$(DATA_INSTALL_DIR)"
@$(INSTALL_PROGRAM) SmarIrc4net.dll "$(DATA_INSTALL_DIR)"
@$(INSTALL_PROGRAM) rix0rrr.BeaconLib.dll "$(DATA_INSTALL_DIR)"
install-common-mod-files:
@@ -414,7 +410,13 @@ install-linux-mime:
install-linux-appdata:
@$(INSTALL_DIR) "$(DESTDIR)$(datadir)/appdata/"
@$(INSTALL_DATA) packaging/linux/openra.appdata.xml "$(DESTDIR)$(datadir)/appdata/"
@sed 's/{MOD}/ra/g' packaging/linux/openra.appdata.xml.in | sed 's/{MOD_NAME}/Red Alert/g' | sed 's/{SCREENSHOT_RA}/ type="default"/g' | sed 's/{SCREENSHOT_CNC}//g' | sed 's/{SCREENSHOT_D2K}//g'> packaging/linux/openra-ra.appdata.xml
@$(INSTALL_DATA) packaging/linux/openra-ra.appdata.xml "$(DESTDIR)$(datadir)/appdata/"
@sed 's/{MOD}/cnc/g' packaging/linux/openra.appdata.xml.in | sed 's/{MOD_NAME}/Tiberian Dawn/g' | sed 's/{SCREENSHOT_RA}//g' | sed 's/{SCREENSHOT_CNC}/ type="default"/g' | sed 's/{SCREENSHOT_D2K}//g'> packaging/linux/openra-cnc.appdata.xml
@$(INSTALL_DATA) packaging/linux/openra-cnc.appdata.xml "$(DESTDIR)$(datadir)/appdata/"
@sed 's/{MOD}/d2k/g' packaging/linux/openra.appdata.xml.in | sed 's/{MOD_NAME}/Dune 2000/g' | sed 's/{SCREENSHOT_RA}//g' | sed 's/{SCREENSHOT_CNC}//g' | sed 's/{SCREENSHOT_D2K}/ type="default"/g'> packaging/linux/openra-d2k.appdata.xml
@$(INSTALL_DATA) packaging/linux/openra-d2k.appdata.xml "$(DESTDIR)$(datadir)/appdata/"
@-$(RM) packaging/linux/openra-ra.appdata.xml packaging/linux/openra-cnc.appdata.xml packaging/linux/openra-d2k.appdata.xml
install-man-page: man-page
@$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man6/"
@@ -495,9 +497,6 @@ help:
@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

View File

@@ -152,7 +152,6 @@ namespace OpenRA.Activities
set { NextActivity = value; }
}
public bool IsIdle { get; protected set; }
public bool IsInterruptible { get; protected set; }
public bool IsCanceled { get { return State == ActivityState.Canceled; } }

View File

@@ -28,8 +28,9 @@ namespace OpenRA
internal struct SyncHash
{
public readonly ISync Trait;
public readonly int Hash;
public SyncHash(ISync trait, int hash) { Trait = trait; Hash = hash; }
readonly Func<object, int> hashFunction;
public SyncHash(ISync trait) { Trait = trait; hashFunction = Sync.GetHashFunction(trait); }
public int Hash() { return hashFunction(Trait); }
}
public readonly ActorInfo Info;
@@ -47,13 +48,11 @@ namespace OpenRA
public int Generation;
public Rectangle Bounds { get; private set; }
public Rectangle VisualBounds { get; private set; }
public IEffectiveOwner EffectiveOwner { get; private set; }
public IOccupySpace OccupiesSpace { get; private set; }
public ITargetable[] Targetables { get; private set; }
public bool IsIdle { get { return CurrentActivity == null || CurrentActivity.IsIdle; } }
public bool IsIdle { get { return CurrentActivity == null; } }
public bool IsDead { get { return Disposed || (health != null && health.IsDead); } }
public CPos Location { get { return OccupiesSpace.TopLeft; } }
@@ -69,13 +68,13 @@ namespace OpenRA
}
}
internal IEnumerable<SyncHash> SyncHashes { get; private set; }
internal SyncHash[] SyncHashes { get; private set; }
readonly IFacing facing;
readonly IHealth health;
readonly IRenderModifier[] renderModifiers;
readonly IRender[] renders;
readonly IDisable[] disables;
readonly IMouseBounds[] mouseBounds;
readonly IVisibilityModifier[] visibilityModifiers;
readonly IDefaultVisibility defaultVisibility;
@@ -110,51 +109,17 @@ namespace OpenRA
// PERF: Cache all these traits as soon as the actor is created. This is a fairly cheap one-off cost per
// actor that allows us to provide some fast implementations of commonly used methods that are relied on by
// performance-sensitive parts of the core game engine, such as pathfinding, visibility and rendering.
Bounds = DetermineBounds();
VisualBounds = DetermineVisualBounds();
EffectiveOwner = TraitOrDefault<IEffectiveOwner>();
facing = TraitOrDefault<IFacing>();
health = TraitOrDefault<IHealth>();
renderModifiers = TraitsImplementing<IRenderModifier>().ToArray();
renders = TraitsImplementing<IRender>().ToArray();
disables = TraitsImplementing<IDisable>().ToArray();
mouseBounds = TraitsImplementing<IMouseBounds>().ToArray();
visibilityModifiers = TraitsImplementing<IVisibilityModifier>().ToArray();
defaultVisibility = Trait<IDefaultVisibility>();
Targetables = TraitsImplementing<ITargetable>().ToArray();
SyncHashes =
TraitsImplementing<ISync>()
.Select(sync => Pair.New(sync, Sync.GetHashFunction(sync)))
.ToArray()
.Select(pair => new SyncHash(pair.First, pair.Second(pair.First)));
}
Rectangle DetermineBounds()
{
var si = Info.TraitInfoOrDefault<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);
}
Rectangle DetermineVisualBounds()
{
var sd = Info.TraitInfoOrDefault<ISelectionDecorationsInfo>();
if (sd == null || sd.SelectionBoxBounds == null)
return Bounds;
var size = new int2(sd.SelectionBoxBounds[0], sd.SelectionBoxBounds[1]);
var offset = -size / 2;
if (sd.SelectionBoxBounds.Length > 2)
offset += new int2(sd.SelectionBoxBounds[2], sd.SelectionBoxBounds[3]);
return new Rectangle(offset.X, offset.Y, size.X, size.Y);
SyncHashes = TraitsImplementing<ISync>().Select(sync => new SyncHash(sync)).ToArray();
}
public void Tick()
@@ -190,6 +155,35 @@ namespace OpenRA
yield return renderable;
}
public IEnumerable<Rectangle> ScreenBounds(WorldRenderer wr)
{
var bounds = Bounds(wr);
foreach (var modifier in renderModifiers)
bounds = modifier.ModifyScreenBounds(this, wr, bounds);
return bounds;
}
IEnumerable<Rectangle> Bounds(WorldRenderer wr)
{
// PERF: Avoid LINQ. See comments for Renderables
foreach (var render in renders)
foreach (var r in render.ScreenBounds(this, wr))
if (!r.IsEmpty)
yield return r;
}
public Rectangle MouseBounds(WorldRenderer wr)
{
foreach (var mb in mouseBounds)
{
var bounds = mb.MouseoverBounds(this, wr);
if (!bounds.IsEmpty)
return bounds;
}
return Rectangle.Empty;
}
public void QueueActivity(bool queued, Activity nextActivity)
{
if (!queued)
@@ -282,27 +276,33 @@ namespace OpenRA
// TODO: move elsewhere.
public void ChangeOwner(Player newOwner)
{
World.AddFrameEndTask(w =>
{
if (Disposed)
return;
World.AddFrameEndTask(_ => ChangeOwnerSync(newOwner));
}
var oldOwner = Owner;
var wasInWorld = IsInWorld;
/// <summary>
/// Change the actors owner without queuing a FrameEndTask.
/// This must only be called from inside an existing FrameEndTask.
/// </summary>
public void ChangeOwnerSync(Player newOwner)
{
if (Disposed)
return;
// momentarily remove from world so the ownership queries don't get confused
if (wasInWorld)
w.Remove(this);
var oldOwner = Owner;
var wasInWorld = IsInWorld;
Owner = newOwner;
Generation++;
// momentarily remove from world so the ownership queries don't get confused
if (wasInWorld)
World.Remove(this);
foreach (var t in TraitsImplementing<INotifyOwnerChanged>())
t.OnOwnerChanged(this, oldOwner, newOwner);
Owner = newOwner;
Generation++;
if (wasInWorld)
w.Add(this);
});
foreach (var t in TraitsImplementing<INotifyOwnerChanged>())
t.OnOwnerChanged(this, oldOwner, newOwner);
if (wasInWorld)
World.Add(this);
}
public DamageState GetDamageState()
@@ -329,15 +329,6 @@ namespace OpenRA
health.Kill(this, attacker);
}
public bool IsDisabled()
{
// PERF: Avoid LINQ.
foreach (var disable in disables)
if (disable.Disabled)
return true;
return false;
}
public bool CanBeViewedByPlayer(Player player)
{
// PERF: Avoid LINQ.

View File

@@ -20,5 +20,8 @@ namespace OpenRA.Effects
IEnumerable<IRenderable> Render(WorldRenderer r);
}
// Identifier interface for effects that are added to ScreenMap
public interface ISpatiallyPartitionable { }
public interface IEffectAboveShroud { IEnumerable<IRenderable> RenderAboveShroud(WorldRenderer wr); }
}

View File

@@ -70,7 +70,7 @@ namespace OpenRA
}
}
void LoadMod(MiniYaml yaml, string path = null)
void LoadMod(MiniYaml yaml, string path = null, bool forceRegistration = false)
{
var mod = FieldLoader.Load<ExternalMod>(yaml);
var iconNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon");
@@ -83,7 +83,7 @@ namespace OpenRA
// Avoid possibly overwriting a valid mod with an obviously bogus one
var key = ExternalMod.MakeKey(mod);
if (File.Exists(mod.LaunchPath) && (path == null || Path.GetFileNameWithoutExtension(path) == key))
if ((forceRegistration || File.Exists(mod.LaunchPath)) && (path == null || Path.GetFileNameWithoutExtension(path) == key))
mods[key] = mod;
}
@@ -119,13 +119,10 @@ namespace OpenRA
sources.Add(Platform.SupportDir);
// Make sure the mod is available for this session, even if saving it fails
LoadMod(yaml.First().Value);
LoadMod(yaml.First().Value, forceRegistration: true);
foreach (var source in sources.Distinct())
{
if (!Directory.Exists(source))
continue;
var metadataPath = Path.Combine(source, "ModMetadata");
try
@@ -157,6 +154,7 @@ namespace OpenRA
if (registration.HasFlag(ModRegistration.User))
sources.Add(Platform.SupportDir);
var activeModKey = ExternalMod.MakeKey(activeMod);
foreach (var source in sources.Distinct())
{
var metadataPath = Path.Combine(source, "ModMetadata");
@@ -172,6 +170,10 @@ namespace OpenRA
var m = FieldLoader.Load<ExternalMod>(yaml);
modKey = ExternalMod.MakeKey(m);
// Continue to the next entry if it is the active mod (even if the LaunchPath is bogus)
if (modKey == activeModKey)
continue;
// Continue to the next entry if this one is valid
if (File.Exists(m.LaunchPath) && Path.GetFileNameWithoutExtension(path) == modKey &&
!(activeMod != null && m.LaunchPath == activeMod.LaunchPath && m.Id == activeMod.Id && m.Version != activeMod.Version))

View File

@@ -494,14 +494,30 @@ namespace OpenRA
return long.TryParse(s, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out i);
}
public static bool IsTraitEnabled(this object trait)
public static bool IsTraitEnabled<T>(this T trait)
{
return trait as IDisabledTrait == null || !(trait as IDisabledTrait).IsTraitDisabled;
var disabledTrait = trait as IDisabledTrait;
return disabledTrait == null || !disabledTrait.IsTraitDisabled;
}
public static bool IsTraitEnabled<T>(T t)
public static T FirstEnabledTraitOrDefault<T>(this IEnumerable<T> ts)
{
return IsTraitEnabled(t as object);
// PERF: Avoid LINQ.
foreach (var t in ts)
if (t.IsTraitEnabled())
return t;
return default(T);
}
public static T FirstEnabledTraitOrDefault<T>(this T[] ts)
{
// PERF: Avoid LINQ.
foreach (var t in ts)
if (t.IsTraitEnabled())
return t;
return default(T);
}
}

View File

@@ -71,6 +71,11 @@ namespace OpenRA
static readonly ConcurrentCache<MemberInfo, bool> MemberHasTranslateAttribute =
new ConcurrentCache<MemberInfo, bool>(member => member.HasAttribute<TranslateAttribute>());
static readonly ConcurrentCache<string, BooleanExpression> BooleanExpressionCache =
new ConcurrentCache<string, BooleanExpression>(expression => new BooleanExpression(expression));
static readonly ConcurrentCache<string, IntegerExpression> IntegerExpressionCache =
new ConcurrentCache<string, IntegerExpression>(expression => new IntegerExpression(expression));
static readonly object TranslationsLock = new object();
static Dictionary<string, string> translations;
@@ -273,6 +278,10 @@ namespace OpenRA
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(HotkeyReference))
{
return Game.ModData.Hotkeys[value];
}
else if (fieldType == typeof(WDist))
{
WDist res;
@@ -404,7 +413,7 @@ namespace OpenRA
{
try
{
return new BooleanExpression(value);
return BooleanExpressionCache[value];
}
catch (InvalidDataException e)
{
@@ -420,7 +429,7 @@ namespace OpenRA
{
try
{
return new IntegerExpression(value);
return IntegerExpressionCache[value];
}
catch (InvalidDataException e)
{

View File

@@ -149,7 +149,7 @@ namespace OpenRA.FileFormats
{
var cp = temp.Palette;
for (var i = 0; i < 256; i++)
cp.Entries[i] = palette[i]; // finalize the palette.
cp.Entries[i] = palette[i]; // finalize the palette.
bitmap.Palette = cp;
return bitmap;
}

View File

@@ -44,7 +44,7 @@ namespace OpenRA.FileSystem
using (var z = pkg.GetInputStream(entry))
{
var ms = new MemoryStream();
var ms = new MemoryStream((int)entry.Size);
z.CopyTo(ms);
ms.Seek(0, SeekOrigin.Begin);
return ms;
@@ -104,7 +104,13 @@ namespace OpenRA.FileSystem
// SharpZipLib breaks when asked to update archives loaded from outside streams or files
// We can work around this by creating a clean in-memory-only file, cutting all outside references
if (!create)
new MemoryStream(File.ReadAllBytes(filename)).CopyTo(pkgStream);
{
using (var copy = new MemoryStream(File.ReadAllBytes(filename)))
{
pkgStream.Capacity = (int)copy.Length;
copy.CopyTo(pkgStream);
}
}
pkgStream.Position = 0;
pkg = ZipFileHelper.Create(pkgStream);

View File

@@ -21,7 +21,6 @@ using System.Net;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using OpenRA.Chat;
using OpenRA.Graphics;
using OpenRA.Network;
using OpenRA.Primitives;
@@ -55,11 +54,10 @@ namespace OpenRA
public static bool BenchmarkMode = false;
public static GlobalChat GlobalChat;
public static string EngineVersion { get; private set; }
static Task discoverNat;
static bool takeScreenshot = false;
public static OrderManager JoinServer(string host, int port, string password, bool recordReplay = true)
{
@@ -169,9 +167,13 @@ namespace OpenRA
worldRenderer = new WorldRenderer(ModData, OrderManager.World);
GC.Collect();
using (new PerfTimer("LoadComplete"))
OrderManager.World.LoadComplete(worldRenderer);
GC.Collect();
if (OrderManager.GameStarted)
return;
@@ -244,7 +246,14 @@ namespace OpenRA
Settings = new Settings(Platform.ResolvePath(Path.Combine("^", "settings.yaml")), args);
}
internal static void Initialize(Arguments args)
public static RunStatus InitializeAndRun(string[] args)
{
Initialize(new Arguments(args));
GC.Collect();
return Run();
}
static void Initialize(Arguments args)
{
Console.WriteLine("Platform is {0}", Platform.CurrentPlatform);
@@ -318,15 +327,8 @@ namespace OpenRA
GeoIP.Initialize();
if (!Settings.Server.DiscoverNatDevices)
Settings.Server.AllowPortForward = false;
else
{
if (Settings.Server.DiscoverNatDevices)
discoverNat = UPnP.DiscoverNatDevices(Settings.Server.NatDiscoveryTimeout);
Settings.Server.AllowPortForward = true;
}
GlobalChat = new GlobalChat();
var modSearchArg = args.GetValue("Engine.ModSearchPaths", null);
var modSearchPaths = modSearchArg != null ?
@@ -454,7 +456,6 @@ namespace OpenRA
{
Console.WriteLine("NAT discovery failed: {0}", e.Message);
Log.Write("nat", e.ToString());
Settings.Server.AllowPortForward = false;
}
ModData.LoadScreen.StartGame(args);
@@ -630,7 +631,10 @@ namespace OpenRA
InnerLogicTick(worldRenderer.World.OrderManager);
}
public static bool TakeScreenshot = false;
public static void TakeScreenshot()
{
takeScreenshot = true;
}
static void RenderTick()
{
@@ -666,9 +670,9 @@ namespace OpenRA
using (new PerfSample("render_flip"))
Renderer.EndFrame(new DefaultInputHandler(OrderManager.World));
if (TakeScreenshot)
if (takeScreenshot)
{
TakeScreenshot = false;
takeScreenshot = false;
TakeScreenshotInner();
}
}
@@ -782,7 +786,7 @@ namespace OpenRA
}
}
internal static RunStatus Run()
static RunStatus Run()
{
if (Settings.Graphics.MaxFramerate < 1)
{
@@ -806,7 +810,6 @@ namespace OpenRA
ModData.Dispose();
ChromeProvider.Deinitialize();
GlobalChat.Dispose();
Sound.Dispose();
Renderer.Dispose();
@@ -862,8 +865,7 @@ namespace OpenRA
{
Name = "Skirmish Game",
Map = map,
AdvertiseOnline = false,
AllowPortForward = false
AdvertiseOnline = false
};
server = new Server.Server(new IPEndPoint(IPAddress.Loopback, 0), settings, ModData, false);

View File

@@ -69,8 +69,9 @@ namespace OpenRA.GameRules
[Desc("What types of targets are unaffected.", "Overrules ValidTargets.")]
public readonly HashSet<string> InvalidTargets = new HashSet<string>();
[Desc("Delay in ticks between firing shots from the same ammo magazine.")]
public readonly int BurstDelay = 5;
[Desc("Delay in ticks between firing shots from the same ammo magazine. If one entry, it will be used for all bursts.",
"If multiple entries, their number needs to match Burst - 1.")]
public readonly int[] BurstDelays = { 5 };
[Desc("The minimum range the weapon can fire.")]
public readonly WDist MinRange = WDist.Zero;

View File

@@ -1,387 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections;
using System.Linq;
using System.Threading;
using Meebey.SmartIrc4net;
using OpenRA.Primitives;
namespace OpenRA.Chat
{
public enum ChatConnectionStatus { Disconnected, Connecting, Connected, Disconnecting, Joined, Error }
public enum ChatMessageType { Message, Notification }
public sealed class ChatUser
{
public readonly string Name;
public bool IsOp;
public bool IsVoiced;
public ChatUser(string name, bool isOp, bool isVoice)
{
Name = name;
IsOp = isOp;
IsVoiced = isVoice;
}
}
public sealed class ChatMessage
{
static long nextUID;
public readonly DateTime Time;
public readonly ChatMessageType Type;
public readonly string Nick;
public readonly string Message;
public readonly string UID;
public ChatMessage(DateTime time, ChatMessageType type, string nick, string message)
{
Time = time;
Type = type;
Nick = nick;
Message = message;
UID = Interlocked.Increment(ref nextUID).ToString();
}
public override string ToString()
{
var time = Time.ToString(Game.Settings.Chat.TimestampFormat);
if (Type == ChatMessageType.Notification)
return "{0} {1}".F(time, Message);
return "{0} {1}: {2}".F(time, Nick, Message);
}
}
public sealed class GlobalChat : IDisposable
{
readonly IrcClient client = new IrcClient();
volatile Channel channel;
public readonly ObservableSortedDictionary<string, ChatUser> Users = new ObservableSortedDictionary<string, ChatUser>(StringComparer.InvariantCultureIgnoreCase);
public readonly ObservableList<ChatMessage> History = new ObservableList<ChatMessage>();
volatile string topic;
public string Topic { get { return topic; } }
volatile ChatConnectionStatus connectionStatus = ChatConnectionStatus.Disconnected;
public ChatConnectionStatus ConnectionStatus { get { return connectionStatus; } }
string nickname;
public GlobalChat()
{
client.Encoding = System.Text.Encoding.UTF8;
client.SendDelay = 100;
client.ActiveChannelSyncing = true;
client.OnConnecting += OnConnecting;
client.OnConnected += OnConnected;
client.OnDisconnecting += OnDisconnecting;
client.OnDisconnected += OnDisconnected;
client.OnError += OnError;
client.OnKick += OnKick;
client.OnRawMessage += (_, e) => Game.RunAfterTick(() => Log.Write("irc", e.Data.RawMessage));
client.OnJoin += OnJoin;
client.OnChannelActiveSynced += OnChannelActiveSynced;
client.OnTopic += (_, e) => topic = e.Topic;
client.OnTopicChange += (_, e) => topic = e.NewTopic;
client.OnNickChange += OnNickChange;
client.OnChannelMessage += (_, e) => AddMessage(e.Data.Nick, e.Data.Message);
client.OnOp += (_, e) => SetUserOp(e.Whom, true);
client.OnDeop += (_, e) => SetUserOp(e.Whom, false);
client.OnVoice += (_, e) => SetUserVoiced(e.Whom, true);
client.OnDevoice += (_, e) => SetUserVoiced(e.Whom, false);
client.OnPart += OnPart;
client.OnQuit += OnQuit;
}
void SetUserOp(string whom, bool isOp)
{
Game.RunAfterTick(() =>
{
ChatUser user;
if (Users.TryGetValue(whom, out user))
user.IsOp = isOp;
});
}
void SetUserVoiced(string whom, bool isVoiced)
{
Game.RunAfterTick(() =>
{
ChatUser user;
if (Users.TryGetValue(whom, out user))
user.IsVoiced = isVoiced;
});
}
public void Connect(string nickname)
{
if (client.IsConnected || !IsValidNickname(nickname))
return;
this.nickname = nickname;
new Thread(() =>
{
try
{
client.Connect(Game.Settings.Chat.Hostname, Game.Settings.Chat.Port);
}
catch (Exception e)
{
connectionStatus = ChatConnectionStatus.Error;
AddNotification(e.Message);
Game.RunAfterTick(() => Log.Write("irc", e.ToString()));
return;
}
client.Listen();
}) { Name = "IrcListenThread", IsBackground = true }.Start();
}
void AddNotification(string text)
{
var message = new ChatMessage(DateTime.Now, ChatMessageType.Notification, null, text);
Game.RunAfterTick(() =>
{
History.Add(message);
Log.Write("irc", text);
});
}
void AddMessage(string nick, string text)
{
var message = new ChatMessage(DateTime.Now, ChatMessageType.Message, nick, text);
Game.RunAfterTick(() =>
{
History.Add(message);
Log.Write("irc", text);
});
}
void OnConnecting(object sender, EventArgs e)
{
AddNotification("Connecting to {0}:{1}...".F(Game.Settings.Chat.Hostname, Game.Settings.Chat.Port));
connectionStatus = ChatConnectionStatus.Connecting;
}
void OnConnected(object sender, EventArgs e)
{
AddNotification("Connected.");
connectionStatus = ChatConnectionStatus.Connected;
client.Login(nickname, "in-game IRC client", 0, "OpenRA");
client.RfcJoin("#" + Game.Settings.Chat.Channel);
}
void OnDisconnecting(object sender, EventArgs e)
{
if (connectionStatus != ChatConnectionStatus.Error)
connectionStatus = ChatConnectionStatus.Disconnecting;
}
void OnDisconnected(object sender, EventArgs e)
{
Game.RunAfterTick(Users.Clear);
// Keep the chat window open if there is an error
// It will be cleared by the Disconnect button
if (connectionStatus != ChatConnectionStatus.Error)
{
Game.RunAfterTick(History.Clear);
topic = null;
connectionStatus = ChatConnectionStatus.Disconnected;
}
}
void OnError(object sender, ErrorEventArgs e)
{
// Ignore any errors that happen during disconnect
if (connectionStatus != ChatConnectionStatus.Disconnecting)
{
connectionStatus = ChatConnectionStatus.Error;
AddNotification("Error: " + e.ErrorMessage);
}
}
void OnKick(object sender, KickEventArgs e)
{
if (e.Whom == client.Nickname)
{
Disconnect();
connectionStatus = ChatConnectionStatus.Error;
AddNotification("You were kicked from the chat by {0}. ({1})".F(e.Who, e.KickReason));
}
else
{
Users.Remove(e.Whom);
AddNotification("{0} was kicked from the chat by {1}. ({2})".F(e.Whom, e.Who, e.KickReason));
}
}
void OnJoin(object sender, JoinEventArgs e)
{
if (e.Who == client.Nickname || channel == null || e.Channel != channel.Name)
return;
AddNotification("{0} joined the chat.".F(e.Who));
Game.RunAfterTick(() => Users.Add(e.Who, new ChatUser(e.Who, false, false)));
}
void OnChannelActiveSynced(object sender, IrcEventArgs e)
{
channel = client.GetChannel(e.Data.Channel);
AddNotification("{0} users online".F(channel.Users.Count));
connectionStatus = ChatConnectionStatus.Joined;
foreach (DictionaryEntry user in channel.Users)
{
var u = (ChannelUser)user.Value;
Game.RunAfterTick(() => Users.Add(u.Nick, new ChatUser(u.Nick, u.IsOp, u.IsVoice)));
}
}
void OnNickChange(object sender, NickChangeEventArgs e)
{
AddNotification("{0} is now known as {1}.".F(e.OldNickname, e.NewNickname));
Game.RunAfterTick(() =>
{
ChatUser user;
if (!Users.TryGetValue(e.OldNickname, out user))
return;
Users.Remove(e.OldNickname);
Users.Add(e.NewNickname, new ChatUser(e.NewNickname, user.IsOp, user.IsVoiced));
});
}
void OnQuit(object sender, QuitEventArgs e)
{
AddNotification("{0} left the chat.".F(e.Who));
Game.RunAfterTick(() => Users.Remove(e.Who));
}
void OnPart(object sender, PartEventArgs e)
{
if (channel == null || e.Data.Channel != channel.Name)
return;
AddNotification("{0} left the chat.".F(e.Who));
Game.RunAfterTick(() => Users.Remove(e.Who));
}
public string SanitizedName(string dirty)
{
if (string.IsNullOrEmpty(dirty))
return null;
// There is no need to mangle the nick if it is already valid
if (Rfc2812.IsValidNickname(dirty))
return dirty;
// TODO: some special chars are allowed as well, but not at every position
var clean = new string(dirty.Where(c => char.IsLetterOrDigit(c)).ToArray());
if (string.IsNullOrEmpty(clean))
return null;
if (char.IsDigit(clean[0]))
return SanitizedName(clean.Substring(1));
// Source: https://tools.ietf.org/html/rfc2812#section-1.2.1
if (clean.Length > 9)
clean = clean.Substring(0, 9);
return clean;
}
public bool IsValidNickname(string name)
{
return Rfc2812.IsValidNickname(name);
}
public void SendMessage(string text)
{
if (connectionStatus != ChatConnectionStatus.Joined)
return;
// Guard against a last-moment disconnection
try
{
client.SendMessage(SendType.Message, channel.Name, text);
AddMessage(client.Nickname, text);
}
catch (NotConnectedException) { }
}
public bool TrySetNickname(string nick)
{
// TODO: This is inconsistent with the other check
if (Rfc2812.IsValidNickname(nick))
{
client.RfcNick(nick);
return true;
}
return false;
}
public void Disconnect()
{
// Error is an alias for disconnect, but keeps the panel open
// so that clients can see the error
if (connectionStatus == ChatConnectionStatus.Error)
{
Game.RunAfterTick(History.Clear);
topic = null;
connectionStatus = ChatConnectionStatus.Disconnected;
}
else
connectionStatus = ChatConnectionStatus.Disconnecting;
if (!client.IsConnected)
return;
client.RfcQuit(Game.Settings.Chat.QuitMessage);
AddNotification("Disconnecting from {0}...".F(client.Address));
Game.RunAfterTick(() => { Game.Settings.Chat.ConnectAutomatically = false; Game.Settings.Save(); });
}
public void Dispose()
{
// HACK: The IRC library we are using has terrible thread-handling code that relies on Thread.Abort.
// There is a thread reading from the network socket which is aborted, however on Windows this is inside
// native code so this abort call hangs until the network socket reads something and returns to managed
// code where it can then be aborted.
//
// This means we may hang for several seconds during shutdown (until we receive something over IRC!) before
// closing.
//
// Since our IRC client currently lives forever, the only time we call this Dispose method is during the
// shutdown of our process. Therefore, we can work around the problem by just not bothering to disconnect
// properly. Since our process is about to die anyway, it's not like anyone will care.
////if (client.IsConnected)
//// client.Disconnect();
}
}
}

View File

@@ -11,6 +11,7 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Support;
@@ -66,6 +67,17 @@ namespace OpenRA.Graphics
return new IRenderable[] { imageRenderable };
}
public Rectangle ScreenBounds(WorldRenderer wr, WPos pos, WVec offset, float scale)
{
var xy = wr.ScreenPxPosition(pos) + wr.ScreenPxOffset(offset);
var cb = CurrentSequence.Bounds;
return Rectangle.FromLTRB(
xy.X + (int)(cb.Left * scale),
xy.Y + (int)(cb.Top * scale),
xy.X + (int)(cb.Right * scale),
xy.Y + (int)(cb.Bottom * scale));
}
public IEnumerable<IRenderable> Render(WPos pos, PaletteReference palette)
{
return Render(pos, WVec.Zero, 0, palette, 1f);

View File

@@ -11,6 +11,7 @@
using System;
using System.Collections.Generic;
using System.Drawing;
namespace OpenRA.Graphics
{
@@ -44,6 +45,14 @@ namespace OpenRA.Graphics
return Animation.Render(center, offset, z, pal, scale);
}
public Rectangle ScreenBounds(Actor self, WorldRenderer wr, float scale)
{
var center = self.CenterPosition;
var offset = OffsetFunc != null ? OffsetFunc() : WVec.Zero;
return Animation.ScreenBounds(wr, center, offset, scale);
}
public static implicit operator AnimationWithOffset(Animation a)
{
return new AnimationWithOffset(a, null, null, null);

View File

@@ -10,6 +10,7 @@
#endregion
using System;
using System.Drawing;
using OpenRA.FileSystem;
namespace OpenRA.Graphics
@@ -23,6 +24,9 @@ namespace OpenRA.Graphics
float[] Size { get; }
float[] Bounds(uint frame);
ModelRenderData RenderData(uint section);
/// <summary>Returns the smallest rectangle that covers all rotations of all frames in a model</summary>
Rectangle AggregateBounds { get; }
}
public struct ModelRenderData

View File

@@ -11,6 +11,7 @@
using System;
using System.Collections.Generic;
using System.Drawing;
namespace OpenRA.Graphics
{
@@ -32,5 +33,26 @@ namespace OpenRA.Graphics
FrameFunc = frame;
ShowShadow = showshadow;
}
public Rectangle ScreenBounds(WPos pos, WorldRenderer wr, float scale)
{
var r = Model.AggregateBounds;
var offset = OffsetFunc != null ? OffsetFunc() : WVec.Zero;
var xy = wr.ScreenPxPosition(pos) + wr.ScreenPxOffset(offset);
return Rectangle.FromLTRB(
xy.X + (int)(r.Left * scale),
xy.Y + (int)(r.Top * scale),
xy.X + (int)(r.Right * scale),
xy.Y + (int)(r.Bottom * scale));
}
public bool IsVisible
{
get
{
return DisableFunc == null || !DisableFunc();
}
}
}
}

View File

@@ -51,7 +51,8 @@ namespace OpenRA.Graphics
readonly Stack<KeyValuePair<Sheet, IFrameBuffer>> unmappedBuffers = new Stack<KeyValuePair<Sheet, IFrameBuffer>>();
readonly List<Pair<Sheet, Action>> doRender = new List<Pair<Sheet, Action>>();
SheetBuilder sheetBuilder;
SheetBuilder sheetBuilderForFrame;
bool isInFrame;
public ModelRenderer(Renderer renderer, IShader shader)
{
@@ -83,6 +84,9 @@ namespace OpenRA.Graphics
float[] groundNormal, WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
PaletteReference color, PaletteReference normals, PaletteReference shadowPalette)
{
if (!isInFrame)
throw new InvalidOperationException("BeginFrame has not been called. You cannot render until a frame has been started.");
// Correct for inverted y-axis
var scaleTransform = Util.ScaleMatrix(scale, scale, scale);
@@ -163,8 +167,11 @@ namespace OpenRA.Graphics
CalculateSpriteGeometry(tl, br, 1, out spriteSize, out spriteOffset);
CalculateSpriteGeometry(stl, sbr, 2, out shadowSpriteSize, out shadowSpriteOffset);
var sprite = sheetBuilder.Allocate(spriteSize, 0, spriteOffset);
var shadowSprite = sheetBuilder.Allocate(shadowSpriteSize, 0, shadowSpriteOffset);
if (sheetBuilderForFrame == null)
sheetBuilderForFrame = new SheetBuilder(SheetType.BGRA, AllocateSheet);
var sprite = sheetBuilderForFrame.Allocate(spriteSize, 0, spriteOffset);
var shadowSprite = sheetBuilderForFrame.Allocate(shadowSpriteSize, 0, shadowSpriteOffset);
var sb = sprite.Bounds;
var ssb = shadowSprite.Bounds;
var spriteCenter = new float2(sb.Left + sb.Width / 2, sb.Top + sb.Height / 2);
@@ -276,12 +283,14 @@ namespace OpenRA.Graphics
public void BeginFrame()
{
if (isInFrame)
throw new InvalidOperationException("BeginFrame has already been called. A new frame cannot be started until EndFrame has been called.");
isInFrame = true;
foreach (var kv in mappedBuffers)
unmappedBuffers.Push(kv);
mappedBuffers.Clear();
sheetBuilder = new SheetBuilder(SheetType.BGRA, AllocateSheet);
doRender.Clear();
}
IFrameBuffer EnableFrameBuffer(Sheet s)
@@ -303,6 +312,12 @@ namespace OpenRA.Graphics
public void EndFrame()
{
if (!isInFrame)
throw new InvalidOperationException("BeginFrame has not been called. There is no frame to end.");
isInFrame = false;
sheetBuilderForFrame = null;
if (doRender.Count == 0)
return;
@@ -325,6 +340,8 @@ namespace OpenRA.Graphics
if (fbo != null)
DisableFrameBuffer(fbo);
doRender.Clear();
}
public Sheet AllocateSheet()

View File

@@ -204,11 +204,6 @@ namespace OpenRA.Graphics
}
}
public void DrawLine(IEnumerable<float2> points, float width, Color color, bool connectSegments = false)
{
DrawLine(points.Select(p => new float3(p, 0)), width, color, connectSegments);
}
public void DrawLine(IEnumerable<float3> points, float width, Color color, bool connectSegments = false)
{
if (!connectSegments)

View File

@@ -11,7 +11,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using OpenRA.FileSystem;
namespace OpenRA.Graphics
@@ -31,6 +31,7 @@ namespace OpenRA.Graphics
int ShadowStart { get; }
int ShadowZOffset { get; }
int[] Frames { get; }
Rectangle Bounds { get; }
Sprite GetSprite(int frame);
Sprite GetSprite(int frame, int facing);

View File

@@ -49,8 +49,8 @@ namespace OpenRA.Graphics
Func<char, float> characterWidth = character => glyphs[Pair.New(character, Color.White)].Advance;
lineWidth = line => line.Sum(characterWidth) / deviceScale;
PrecacheColor(Color.White, name);
PrecacheColor(Color.Red, name);
if (size <= 24)
PrecacheColor(Color.White, name);
}
public void SetScale(float scale)
@@ -70,25 +70,28 @@ namespace OpenRA.Graphics
public void DrawText(string text, float2 location, Color c)
{
location.Y += size; // baseline vs top
// Offset from the baseline position to the top-left of the glyph for rendering
location += new float2(0, size);
var p = location;
foreach (var s in text)
{
if (s == '\n')
{
location.Y += size;
location += new float2(0, size);
p = location;
continue;
}
var g = glyphs[Pair.New(s, c)];
Game.Renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite,
new float2(
(int)Math.Round(p.X * deviceScale + g.Offset.X, 0) / deviceScale,
p.Y + g.Offset.Y / deviceScale),
g.Sprite.Size / deviceScale);
p.X += g.Advance / deviceScale;
if (g.Sprite != null)
Game.Renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite,
new float2(
(int)Math.Round(p.X * deviceScale + g.Offset.X, 0) / deviceScale,
p.Y + g.Offset.Y / deviceScale),
g.Sprite.Size / deviceScale);
p += new float2(g.Advance / deviceScale, 0);
}
}
@@ -134,7 +137,20 @@ namespace OpenRA.Graphics
GlyphInfo CreateGlyph(Pair<char, Color> c)
{
face.LoadChar(c.First, LoadFlags.Default, LoadTarget.Normal);
try
{
face.LoadChar(c.First, LoadFlags.Default, LoadTarget.Normal);
}
catch (FreeTypeException)
{
return new GlyphInfo
{
Sprite = null,
Advance = 0,
Offset = int2.Zero
};
}
face.Glyph.RenderGlyph(RenderMode.Normal);
var size = new Size((int)face.Glyph.Metrics.Width, (int)face.Glyph.Metrics.Height);

View File

@@ -34,7 +34,7 @@ namespace OpenRA.Graphics
readonly HardwarePalette palette = new HardwarePalette();
readonly Dictionary<string, PaletteReference> palettes = new Dictionary<string, PaletteReference>();
readonly TerrainRenderer terrainRenderer;
readonly Lazy<DeveloperMode> devTrait;
readonly Lazy<DebugVisualizations> debugVis;
readonly Func<string, PaletteReference> createPaletteReference;
readonly bool enableDepthBuffer;
@@ -61,7 +61,7 @@ namespace OpenRA.Graphics
Theater = new Theater(world.Map.Rules.TileSet);
terrainRenderer = new TerrainRenderer(world, this);
devTrait = Exts.Lazy(() => world.LocalPlayer != null ? world.LocalPlayer.PlayerActor.Trait<DeveloperMode>() : null);
debugVis = Exts.Lazy(() => world.WorldActor.TraitOrDefault<DebugVisualizations>());
}
public void UpdatePalettesForPlayer(string internalName, HSLColor color, bool replaceExisting)
@@ -100,9 +100,9 @@ namespace OpenRA.Graphics
palettes[name].Palette = pal;
}
List<IFinalizedRenderable> GenerateRenderables()
List<IFinalizedRenderable> GenerateRenderables(HashSet<Actor> actorsInBox)
{
var actors = World.ScreenMap.ActorsInBox(Viewport.TopLeft, Viewport.BottomRight).Append(World.WorldActor);
var actors = actorsInBox.Append(World.WorldActor);
if (World.RenderPlayer != null)
actors = actors.Append(World.RenderPlayer.PlayerActor);
@@ -110,7 +110,13 @@ namespace OpenRA.Graphics
if (World.OrderGenerator != null)
worldRenderables = worldRenderables.Concat(World.OrderGenerator.Render(this, World));
worldRenderables = worldRenderables.Concat(World.Effects.SelectMany(e => e.Render(this)));
// Unpartitioned effects
worldRenderables = worldRenderables.Concat(World.UnpartitionedEffects.SelectMany(e => e.Render(this)));
// Partitioned, currently on-screen effects
var effectRenderables = World.ScreenMap.RenderableEffectsInBox(Viewport.TopLeft, Viewport.BottomRight);
worldRenderables = worldRenderables.Concat(effectRenderables.SelectMany(e => e.Render(this)));
worldRenderables = worldRenderables.OrderBy(RenderableScreenZPositionComparisonKey);
Game.Renderer.WorldModelRenderer.BeginFrame();
@@ -120,21 +126,53 @@ namespace OpenRA.Graphics
return renderables;
}
List<IFinalizedRenderable> GenerateOverlayRenderables(HashSet<Actor> actorsInBox)
{
var aboveShroud = World.ActorsWithTrait<IRenderAboveShroud>()
.Where(a => a.Actor.IsInWorld && !a.Actor.Disposed && (!a.Trait.SpatiallyPartitionable || actorsInBox.Contains(a.Actor)))
.SelectMany(a => a.Trait.RenderAboveShroud(a.Actor, this));
var aboveShroudSelected = World.Selection.Actors.Where(a => a.IsInWorld && !a.Disposed)
.SelectMany(a => a.TraitsImplementing<IRenderAboveShroudWhenSelected>()
.Where(t => !t.SpatiallyPartitionable || actorsInBox.Contains(a))
.SelectMany(t => t.RenderAboveShroud(a, this)));
var aboveShroudEffects = World.Effects.Select(e => e as IEffectAboveShroud)
.Where(e => e != null)
.SelectMany(e => e.RenderAboveShroud(this));
var aboveShroudOrderGenerator = SpriteRenderable.None;
if (World.OrderGenerator != null)
aboveShroudOrderGenerator = World.OrderGenerator.RenderAboveShroud(this, World);
var overlayRenderables = aboveShroud
.Concat(aboveShroudSelected)
.Concat(aboveShroudEffects)
.Concat(aboveShroudOrderGenerator);
Game.Renderer.WorldModelRenderer.BeginFrame();
var finalOverlayRenderables = overlayRenderables.Select(r => r.PrepareRender(this)).ToList();
Game.Renderer.WorldModelRenderer.EndFrame();
return finalOverlayRenderables;
}
public void Draw()
{
if (World.WorldActor.Disposed)
return;
if (devTrait.Value != null)
if (debugVis.Value != null)
{
Game.Renderer.WorldSpriteRenderer.SetDepthPreviewEnabled(devTrait.Value.ShowDepthPreview);
Game.Renderer.WorldRgbaSpriteRenderer.SetDepthPreviewEnabled(devTrait.Value.ShowDepthPreview);
Game.Renderer.WorldRgbaColorRenderer.SetDepthPreviewEnabled(devTrait.Value.ShowDepthPreview);
Game.Renderer.WorldSpriteRenderer.SetDepthPreviewEnabled(debugVis.Value.DepthBuffer);
Game.Renderer.WorldRgbaSpriteRenderer.SetDepthPreviewEnabled(debugVis.Value.DepthBuffer);
Game.Renderer.WorldRgbaColorRenderer.SetDepthPreviewEnabled(debugVis.Value.DepthBuffer);
}
RefreshPalette();
var renderables = GenerateRenderables();
var onScreenActors = World.ScreenMap.RenderableActorsInBox(Viewport.TopLeft, Viewport.BottomRight).ToHashSet();
var renderables = GenerateRenderables(onScreenActors);
var bounds = Viewport.GetScissorBounds(World.Type != WorldType.Editor);
Game.Renderer.EnableScissor(bounds);
@@ -167,45 +205,39 @@ namespace OpenRA.Graphics
Game.Renderer.DisableScissor();
var aboveShroud = World.ActorsWithTrait<IRenderAboveShroud>().Where(a => a.Actor.IsInWorld && !a.Actor.Disposed)
.SelectMany(a => a.Trait.RenderAboveShroud(a.Actor, this));
var aboveShroudSelected = World.Selection.Actors.Where(a => !a.Disposed)
.SelectMany(a => a.TraitsImplementing<IRenderAboveShroudWhenSelected>()
.SelectMany(t => t.RenderAboveShroud(a, this)));
var aboveShroudEffects = World.Effects.Select(e => e as IEffectAboveShroud)
.Where(e => e != null)
.SelectMany(e => e.RenderAboveShroud(this));
var aboveShroudOrderGenerator = SpriteRenderable.None;
if (World.OrderGenerator != null)
aboveShroudOrderGenerator = World.OrderGenerator.RenderAboveShroud(this, World);
Game.Renderer.WorldModelRenderer.BeginFrame();
var finalOverlayRenderables = aboveShroud
.Concat(aboveShroudSelected)
.Concat(aboveShroudEffects)
.Concat(aboveShroudOrderGenerator)
.Select(r => r.PrepareRender(this))
.ToList();
Game.Renderer.WorldModelRenderer.EndFrame();
var finalOverlayRenderables = GenerateOverlayRenderables(onScreenActors);
// HACK: Keep old grouping behaviour
foreach (var g in finalOverlayRenderables.GroupBy(prs => prs.GetType()))
var groupedOverlayRenderables = finalOverlayRenderables.GroupBy(prs => prs.GetType());
foreach (var g in groupedOverlayRenderables)
foreach (var r in g)
r.Render(this);
if (devTrait.Value != null && devTrait.Value.ShowDebugGeometry)
if (debugVis.Value != null && debugVis.Value.RenderGeometry)
{
for (var i = 0; i < renderables.Count; i++)
renderables[i].RenderDebugGeometry(this);
foreach (var g in finalOverlayRenderables.GroupBy(prs => prs.GetType()))
foreach (var g in groupedOverlayRenderables)
foreach (var r in g)
r.RenderDebugGeometry(this);
}
if (debugVis.Value != null && debugVis.Value.ScreenMap)
{
foreach (var r in World.ScreenMap.RenderBounds(World.RenderPlayer))
Game.Renderer.WorldRgbaColorRenderer.DrawRect(
new float3(r.Left, r.Top, r.Bottom),
new float3(r.Right, r.Bottom, r.Bottom),
1 / Viewport.Zoom, Color.MediumSpringGreen);
foreach (var r in World.ScreenMap.MouseBounds(World.RenderPlayer))
Game.Renderer.WorldRgbaColorRenderer.DrawRect(
new float3(r.Left, r.Top, r.Bottom),
new float3(r.Right, r.Bottom, r.Bottom),
1 / Viewport.Zoom, Color.OrangeRed);
}
Game.Renderer.Flush();
}

View File

@@ -0,0 +1,40 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Linq;
namespace OpenRA
{
public sealed class HotkeyDefinition
{
public readonly string Name;
public readonly Hotkey Default = Hotkey.Invalid;
public readonly string Description = "";
public readonly HashSet<string> Types = new HashSet<string>();
public HotkeyDefinition(string name, MiniYaml node)
{
Name = name;
if (!string.IsNullOrEmpty(node.Value))
Default = FieldLoader.GetValue<Hotkey>("value", node.Value);
var descriptionNode = node.Nodes.FirstOrDefault(n => n.Key == "Description");
if (descriptionNode != null)
Description = descriptionNode.Value.Value;
var typesNode = node.Nodes.FirstOrDefault(n => n.Key == "Types");
if (typesNode != null)
Types = FieldLoader.GetValue<HashSet<string>>("Types", typesNode.Value.Value);
}
}
}

View File

@@ -0,0 +1,77 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using OpenRA.FileSystem;
namespace OpenRA
{
public sealed class HotkeyManager
{
readonly Dictionary<string, Hotkey> settings;
readonly Dictionary<string, HotkeyDefinition> definitions = new Dictionary<string, HotkeyDefinition>();
readonly Dictionary<string, Hotkey> keys = new Dictionary<string, Hotkey>();
public HotkeyManager(IReadOnlyFileSystem fileSystem, Dictionary<string, Hotkey> settings, Manifest manifest)
{
this.settings = settings;
var keyDefinitions = MiniYaml.Load(fileSystem, manifest.Hotkeys, null);
foreach (var kd in keyDefinitions)
{
var definition = new HotkeyDefinition(kd.Key, kd.Value);
definitions[kd.Key] = definition;
keys[kd.Key] = definition.Default;
}
foreach (var kv in settings)
keys[kv.Key] = kv.Value;
}
internal Func<Hotkey> GetHotkeyReference(string name)
{
// Is this a mod-defined hotkey?
if (keys.ContainsKey(name))
return () => keys[name];
// Try and parse as a hardcoded definition
Hotkey key;
if (!Hotkey.TryParse(name, out key))
key = Hotkey.Invalid;
return () => key;
}
public void Set(string name, Hotkey value)
{
HotkeyDefinition definition;
if (!definitions.TryGetValue(name, out definition))
return;
keys[name] = value;
if (value != definition.Default)
settings[name] = value;
else
settings.Remove(name);
}
public HotkeyReference this[string name]
{
get
{
return new HotkeyReference(GetHotkeyReference(name));
}
}
public IEnumerable<HotkeyDefinition> Definitions { get { return definitions.Values; } }
}
}

View File

@@ -0,0 +1,46 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
namespace OpenRA
{
/// <summary>
/// A reference to either a named hotkey (defined in the game settings) or a statically assigned hotkey
/// </summary>
public class HotkeyReference
{
static readonly Func<Hotkey> Invalid = () => Hotkey.Invalid;
readonly Func<Hotkey> getValue;
public HotkeyReference()
{
getValue = Invalid;
}
internal HotkeyReference(Func<Hotkey> getValue)
{
this.getValue = getValue;
}
public Hotkey GetValue()
{
return getValue();
}
public bool IsActivatedBy(KeyInput e)
{
var currentValue = getValue();
return currentValue.Key == e.Key && currentValue.Modifiers == e.Modifiers;
}
}
}

View File

@@ -59,7 +59,7 @@ namespace OpenRA
Rules, ServerTraits,
Sequences, ModelSequences, Cursors, Chrome, Assemblies, ChromeLayout,
Weapons, Voices, Notifications, Music, Translations, TileSets,
ChromeMetrics, MapCompatibility, Missions;
ChromeMetrics, MapCompatibility, Missions, Hotkeys;
public readonly IReadOnlyDictionary<string, string> Packages;
public readonly IReadOnlyDictionary<string, string> MapFolders;
@@ -72,7 +72,7 @@ namespace OpenRA
readonly string[] reservedModuleNames = { "Metadata", "Folders", "MapFolders", "Packages", "Rules",
"Sequences", "ModelSequences", "Cursors", "Chrome", "Assemblies", "ChromeLayout", "Weapons",
"Voices", "Notifications", "Music", "Translations", "TileSets", "ChromeMetrics", "Missions",
"Voices", "Notifications", "Music", "Translations", "TileSets", "ChromeMetrics", "Missions", "Hotkeys",
"ServerTraits", "LoadScreen", "Fonts", "SupportsMapsFrom", "SoundFormats", "SpriteFormats",
"RequiresMods", "PackageFormats" };
@@ -111,6 +111,7 @@ namespace OpenRA
TileSets = YamlList(yaml, "TileSets");
ChromeMetrics = YamlList(yaml, "ChromeMetrics");
Missions = YamlList(yaml, "Missions");
Hotkeys = YamlList(yaml, "Hotkeys");
ServerTraits = YamlList(yaml, "ServerTraits");

View File

@@ -40,6 +40,16 @@ namespace OpenRA
mapBottomRight = BottomRight.ToMPos(gridType);
}
public CellRegion(MapGridType gridType, MPos topLeft, MPos bottomRight)
{
this.gridType = gridType;
mapTopLeft = topLeft;
mapBottomRight = bottomRight;
TopLeft = topLeft.ToCPos(gridType);
BottomRight = bottomRight.ToCPos(gridType);
}
/// <summary>Expand the specified region with an additional cordon. This may expand the region outside the map borders.</summary>
public static CellRegion Expand(CellRegion region, int cordon)
{

View File

@@ -19,6 +19,7 @@ using System.Reflection;
using System.Text;
using OpenRA.FileSystem;
using OpenRA.Graphics;
using OpenRA.Primitives;
using OpenRA.Support;
using OpenRA.Traits;
@@ -253,16 +254,27 @@ namespace OpenRA
if (!contents.Contains(required))
throw new FileNotFoundException("Required file {0} not present in this map".F(required));
using (var ms = new MemoryStream())
var streams = new List<Stream>();
try
{
foreach (var filename in contents)
if (filename.EndsWith(".yaml") || filename.EndsWith(".bin") || filename.EndsWith(".lua"))
using (var s = package.GetStream(filename))
s.CopyTo(ms);
streams.Add(package.GetStream(filename));
// Take the SHA1
ms.Seek(0, SeekOrigin.Begin);
return CryptoUtil.SHA1Hash(ms);
if (streams.Count == 0)
return CryptoUtil.SHA1Hash(new byte[0]);
var merged = streams[0];
for (var i = 1; i < streams.Count; i++)
merged = new MergedStream(merged, streams[i]);
return CryptoUtil.SHA1Hash(merged);
}
finally
{
foreach (var stream in streams)
stream.Dispose();
}
}
@@ -429,7 +441,7 @@ namespace OpenRA
{
var uv = cell.ToMPos(Grid.Type);
cellProjection[uv] = new PPos[0];
inverseCellProjection[uv] = new List<MPos>();
inverseCellProjection[uv] = new List<MPos>(1);
}
// Initialize projections
@@ -784,6 +796,19 @@ namespace OpenRA
return new WDist(delta.Z);
}
/// <summary>
/// The size of the map Height step in world units
/// </summary>
public WDist CellHeightStep
{
get
{
// RectangularIsometric defines 1024 units along the diagonal axis,
// giving a half-tile height step of sqrt(2) * 512
return new WDist(Grid.Type == MapGridType.RectangularIsometric ? 724 : 512);
}
}
public CPos CellContaining(WPos pos)
{
if (Grid.Type == MapGridType.Rectangular)

View File

@@ -28,6 +28,7 @@ namespace OpenRA
{
public static readonly MapPreview UnknownMap = new MapPreview(null, null, MapGridType.Rectangular, null);
public readonly IReadOnlyDictionary<IReadOnlyPackage, MapClassification> MapLocations;
readonly Dictionary<IReadOnlyPackage, MapClassification> mapLocations = new Dictionary<IReadOnlyPackage, MapClassification>();
readonly Cache<string, MapPreview> previews;
readonly ModData modData;
@@ -45,8 +46,16 @@ namespace OpenRA
previews = new Cache<string, MapPreview>(uid => new MapPreview(modData, uid, gridType.Value, this));
sheetBuilder = new SheetBuilder(SheetType.BGRA);
MapLocations = new ReadOnlyDictionary<IReadOnlyPackage, MapClassification>(mapLocations);
}
public void LoadMaps()
{
// Utility mod that does not support maps
if (!modData.Manifest.Contains<MapGrid>())
return;
// Enumerate map directories
var mapLocations = new Dictionary<IReadOnlyPackage, MapClassification>();
foreach (var kv in modData.Manifest.MapFolders)
{
var name = kv.Key;
@@ -82,15 +91,6 @@ namespace OpenRA
mapLocations.Add(package, classification);
}
MapLocations = new ReadOnlyDictionary<IReadOnlyPackage, MapClassification>(mapLocations);
}
public void LoadMaps()
{
// Utility mod that does not support maps
if (!modData.Manifest.Contains<MapGrid>())
return;
var mapGrid = modData.Manifest.Get<MapGrid>();
foreach (var kv in MapLocations)
{
@@ -122,6 +122,47 @@ namespace OpenRA
}
}
public IEnumerable<Map> EnumerateMapsWithoutCaching(MapClassification classification = MapClassification.System)
{
// Utility mod that does not support maps
if (!modData.Manifest.Contains<MapGrid>())
yield break;
// Enumerate map directories
foreach (var kv in modData.Manifest.MapFolders)
{
MapClassification packageClassification;
if (!Enum.TryParse(kv.Value, out packageClassification))
continue;
if (!classification.HasFlag(packageClassification))
continue;
var name = kv.Key;
var optional = name.StartsWith("~", StringComparison.Ordinal);
if (optional)
name = name.Substring(1);
// Don't try to open the map directory in the support directory if it doesn't exist
if (name.StartsWith("^", StringComparison.Ordinal))
{
var resolved = Platform.ResolvePath(name);
if (!Directory.Exists(resolved) || !File.Exists(resolved))
continue;
}
using (var package = (IReadWritePackage)modData.ModFiles.OpenPackage(name))
{
foreach (var map in package.Contents)
{
var mapPackage = package.OpenPackage(map, modData.ModFiles);
if (mapPackage != null)
yield return new Map(modData, mapPackage);
}
}
}
}
public void QueryRemoteMapDetails(string repositoryUrl, IEnumerable<string> uids, Action<MapPreview> mapDetailsReceived = null, Action queryFailed = null)
{
var maps = uids.Distinct()

View File

@@ -28,7 +28,14 @@ namespace OpenRA
public enum MapStatus { Available, Unavailable, Searching, DownloadAvailable, Downloading, DownloadError }
// Used for grouping maps in the UI
public enum MapClassification { Unknown, System, User, Remote }
[Flags]
public enum MapClassification
{
Unknown = 0,
System = 1,
User = 2,
Remote = 4
}
// Used for verifying map availability in the lobby
public enum MapRuleStatus { Unknown, Cached, Invalid }
@@ -420,7 +427,7 @@ namespace OpenRA
public void Install(string mapRepositoryUrl, Action onSuccess)
{
if (Status != MapStatus.DownloadAvailable || !Game.Settings.Game.AllowDownloading)
if ((Status != MapStatus.DownloadError && Status != MapStatus.DownloadAvailable) || !Game.Settings.Game.AllowDownloading)
return;
innerData.Status = MapStatus.Downloading;

View File

@@ -185,7 +185,6 @@ namespace OpenRA
public readonly string Id;
public readonly int SheetSize = 512;
public readonly string Palette;
public readonly string PlayerPalette;
public readonly Color[] HeightDebugColors = new[] { Color.Red };
public readonly string[] EditorTemplateOrder;
public readonly bool IgnoreTileSpriteOffsets;

View File

@@ -257,7 +257,9 @@ namespace OpenRA
if (!sources.Any())
return new List<MiniYamlNode>();
var tree = sources.Where(s => s != null).Aggregate(MergePartial)
var tree = sources.Where(s => s != null)
.Select(MergeSelfPartial)
.Aggregate(MergePartial)
.ToDictionary(n => n.Key, n => n.Value);
var resolved = new Dictionary<string, MiniYaml>();
@@ -325,6 +327,42 @@ namespace OpenRA
return resolved;
}
/// <summary>
/// Merges any duplicate keys that are defined within the same set of nodes.
/// Does not resolve inheritance or node removals.
/// </summary>
static MiniYaml MergeSelfPartial(MiniYaml existingNodes)
{
// Nothing to do
if (existingNodes.Nodes == null || existingNodes.Nodes.Count == 0)
return existingNodes;
return new MiniYaml(existingNodes.Value, MergeSelfPartial(existingNodes.Nodes));
}
/// <summary>
/// Merges any duplicate keys that are defined within the same set of nodes.
/// Does not resolve inheritance or node removals.
/// </summary>
static List<MiniYamlNode> MergeSelfPartial(List<MiniYamlNode> existingNodes)
{
var keys = new HashSet<string>();
var ret = new List<MiniYamlNode>();
foreach (var n in existingNodes)
{
if (keys.Add(n.Key))
ret.Add(n);
else
{
// Node with the same key has already been added: merge new node over the existing one
var original = ret.First(r => r.Key == n.Key);
original.Value = MergePartial(original.Value, n.Value);
}
}
return ret;
}
static MiniYaml MergePartial(MiniYaml existingNodes, MiniYaml overrideNodes)
{
if (existingNodes == null)

View File

@@ -31,6 +31,7 @@ namespace OpenRA
public readonly ISpriteLoader[] SpriteLoaders;
public readonly ISpriteSequenceLoader SpriteSequenceLoader;
public readonly IModelSequenceLoader ModelSequenceLoader;
public readonly HotkeyManager Hotkeys;
public ILoadScreen LoadScreen { get; private set; }
public CursorProvider CursorProvider { get; private set; }
public FS ModFiles;
@@ -89,6 +90,8 @@ namespace OpenRA
ModelSequenceLoader = (IModelSequenceLoader)modelCtor.Invoke(new[] { this });
ModelSequenceLoader.OnMissingModelError = s => Log.Write("debug", s);
Hotkeys = new HotkeyManager(ModFiles, Game.Settings.Keys, Manifest);
defaultRules = Exts.Lazy(() => Ruleset.LoadDefaults(this));
defaultTileSets = Exts.Lazy(() =>
{

View File

@@ -77,10 +77,10 @@ namespace OpenRA.Network
public virtual void SendSync(int frame, byte[] syncData)
{
var ms = new MemoryStream();
var ms = new MemoryStream(4 + syncData.Length);
ms.Write(BitConverter.GetBytes(frame));
ms.Write(syncData);
Send(ms.ToArray());
Send(ms.GetBuffer());
}
protected virtual void Send(byte[] packet)
@@ -197,10 +197,10 @@ namespace OpenRA.Network
public override void SendSync(int frame, byte[] syncData)
{
var ms = new MemoryStream();
var ms = new MemoryStream(4 + syncData.Length);
ms.Write(BitConverter.GetBytes(frame));
ms.Write(syncData);
queuedSyncPackets.Add(ms.ToArray());
queuedSyncPackets.Add(ms.GetBuffer());
}
protected override void Send(byte[] packet)

View File

@@ -9,62 +9,220 @@
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using OpenRA.Graphics;
namespace OpenRA.Network
{
public class GameClient
{
public readonly string Name;
public readonly HSLColor Color;
public readonly string Faction;
public readonly int Team;
public readonly int SpawnPoint;
public readonly bool IsAdmin;
public readonly bool IsSpectator;
public readonly bool IsBot;
public GameClient() { }
public GameClient(Session.Client c)
{
Name = c.Name;
Color = c.Color;
Faction = c.Faction;
Team = c.Team;
SpawnPoint = c.SpawnPoint;
IsAdmin = c.IsAdmin;
IsSpectator = c.Slot == null && c.Bot == null;
IsBot = c.Bot != null;
}
}
public class GameServer
{
public readonly int Id = 0;
static readonly string[] SerializeFields = { "Name", "Address", "Mod", "Version", "Map", "State", "MaxPlayers", "Protected" };
public const int ProtocolVersion = 2;
/// <summary>Online game number or -1 for LAN games</summary>
public readonly int Id = -1;
/// <summary>Name of the server</summary>
public readonly string Name = null;
/// <summary>ip:port string to connect to.</summary>
public readonly string Address = null;
/// <summary>Port of the server</summary>
public readonly int Port = 0;
/// <summary>The current state of the server (waiting/playing/completed)</summary>
public readonly int State = 0;
public readonly int Players = 0;
/// <summary>The number of slots available for non-bot players</summary>
public readonly int MaxPlayers = 0;
public readonly int Bots = 0;
public readonly int Spectators = 0;
/// <summary>UID of the map</summary>
public readonly string Map = null;
public readonly string Mods = "";
public readonly int TTL = 0;
/// <summary>Mod ID</summary>
public readonly string Mod = "";
/// <summary>Mod Version</summary>
public readonly string Version = "";
/// <summary>Password protected</summary>
public readonly bool Protected = false;
/// <summary>UTC datetime string when the game changed to the Playing state</summary>
public readonly string Started = null;
/// <summary>Number of non-spectator, non-bot players. Only defined if GameServer is parsed from yaml.</summary>
public readonly int Players = 0;
/// <summary>Number of bot players. Only defined if GameServer is parsed from yaml.</summary>
public readonly int Bots = 0;
/// <summary>Number of spectators. Only defined if GameServer is parsed from yaml.</summary>
public readonly int Spectators = 0;
/// <summary>Number of seconds that the game has been in the Playing state. Only defined if GameServer is parsed from yaml.</summary>
public readonly int PlayTime = -1;
/// <summary>Can we join this server (after switching mods if required)? Only defined if GameServer is parsed from yaml.</summary>
[FieldLoader.Ignore]
public readonly bool IsCompatible = false;
/// <summary>Can we join this server (after switching mods if required)? Only defined if GameServer is parsed from yaml.</summary>
[FieldLoader.Ignore]
public readonly bool IsJoinable = false;
/// <summary>Label to display in the multiplayer browser. Only defined if GameServer is parsed from yaml.</summary>
[FieldLoader.Ignore]
public readonly string ModLabel = "";
public readonly string ModId = "";
public readonly string ModVersion = "";
[FieldLoader.LoadUsing("LoadClients")]
public readonly GameClient[] Clients;
static object LoadClients(MiniYaml yaml)
{
var clients = new List<GameClient>();
var clientsNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Clients");
if (clientsNode != null)
{
var regex = new Regex(@"Client@\d+");
foreach (var client in clientsNode.Value.Nodes)
if (regex.IsMatch(client.Key))
clients.Add(FieldLoader.Load<GameClient>(client.Value));
}
return clients.ToArray();
}
public GameServer(MiniYaml yaml)
{
FieldLoader.Load(this, yaml);
// Games advertised using the old API used a single Mods field
if (Mod == null || Version == null)
{
var modsNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Mods");
if (modsNode != null)
{
var modVersion = modsNode.Value.Value.Split('@');
Mod = modVersion[0];
Version = modVersion[1];
}
}
// Games advertised using the old API calculated the play time locally
if (State == 2 && PlayTime < 0)
{
DateTime startTime;
if (DateTime.TryParse(Started, out startTime))
PlayTime = (int)(DateTime.UtcNow - startTime).TotalSeconds;
}
Manifest mod;
ExternalMod external;
var modVersion = Mods.Split('@');
ModLabel = "Unknown mod: {0}".F(Mods);
if (modVersion.Length == 2)
var externalKey = ExternalMod.MakeKey(Mod, Version);
if (Game.ExternalMods.TryGetValue(externalKey, out external) && external.Version == Version)
{
ModId = modVersion[0];
ModVersion = modVersion[1];
ModLabel = "{0} ({1})".F(external.Title, external.Version);
IsCompatible = true;
}
else if (Game.Mods.TryGetValue(Mod, out mod))
{
// Use internal mod data to populate the section header, but
// on-connect switching must use the external mod plumbing.
ModLabel = "{0} ({1})".F(mod.Metadata.Title, Version);
}
else
{
// Some platforms (e.g. macOS) package each mod separately, so the Mods check above won't work.
// Guess based on the most recent ExternalMod instead.
var guessMod = Game.ExternalMods.Values
.OrderByDescending(m => m.Version)
.FirstOrDefault(m => m.Id == Mod);
var externalKey = ExternalMod.MakeKey(modVersion[0], modVersion[1]);
if (Game.ExternalMods.TryGetValue(externalKey, out external)
&& external.Version == modVersion[1])
{
ModLabel = "{0} ({1})".F(external.Title, external.Version);
IsCompatible = true;
}
else if (Game.Mods.TryGetValue(modVersion[0], out mod))
{
// Use internal mod data to populate the section header, but
// on-connect switching must use the external mod plumbing.
ModLabel = "{0} ({1})".F(mod.Metadata.Title, modVersion[1]);
}
if (guessMod != null)
ModLabel = "{0} ({1})".F(guessMod.Title, Version);
else
ModLabel = "Unknown mod: {0} ({1})".F(Mod, Version);
}
var mapAvailable = Game.Settings.Game.AllowDownloading || Game.ModData.MapCache[Map].Status == MapStatus.Available;
IsJoinable = IsCompatible && State == 1 && mapAvailable;
}
public GameServer(Server.Server server)
{
Name = server.Settings.Name;
// IP address will be replaced with a real value by the master server / receiving LAN client
Address = "0.0.0.0:" + server.Settings.ListenPort.ToString();
State = (int)server.State;
MaxPlayers = server.LobbyInfo.Slots.Count(s => !s.Value.Closed) - server.LobbyInfo.Clients.Count(c1 => c1.Bot != null);
Map = server.Map.Uid;
Mod = server.ModData.Manifest.Id;
Version = server.ModData.Manifest.Metadata.Version;
Protected = !string.IsNullOrEmpty(server.Settings.Password);
Clients = server.LobbyInfo.Clients.Select(c => new GameClient(c)).ToArray();
}
public string ToPOSTData(bool lanGame)
{
var root = new List<MiniYamlNode>() { new MiniYamlNode("Protocol", ProtocolVersion.ToString()) };
foreach (var field in SerializeFields)
root.Add(FieldSaver.SaveField(this, field));
if (lanGame)
{
// Add fields that are normally generated by the master server
// LAN games overload the Id with a GUID string (rather than an ID) to allow deduplication
root.Add(new MiniYamlNode("Id", Platform.SessionGUID.ToString()));
root.Add(new MiniYamlNode("Players", Clients.Count(c => !c.IsBot && !c.IsSpectator).ToString()));
root.Add(new MiniYamlNode("Spectators", Clients.Count(c => c.IsSpectator).ToString()));
root.Add(new MiniYamlNode("Bots", Clients.Count(c => c.IsBot).ToString()));
// Included for backwards compatibility with older clients that don't support separated Mod/Version.
root.Add(new MiniYamlNode("Mods", Mod + "@" + Version));
}
var clientsNode = new MiniYaml("");
var i = 0;
foreach (var c in Clients)
clientsNode.Nodes.Add(new MiniYamlNode("Client@" + (i++).ToString(), FieldSaver.Save(c)));
root.Add(new MiniYamlNode("Clients", clientsNode));
return new MiniYaml("", root)
.ToLines("Game")
.JoinWith("\n");
}
}
}

View File

@@ -11,15 +11,16 @@
using System;
using System.IO;
using System.Linq;
using OpenRA.Network;
using OpenRA.Traits;
namespace OpenRA
{
[Flags]
enum OrderFields : byte
{
TargetActor = 0x01,
TargetLocation = 0x02,
Target = 0x01,
TargetString = 0x04,
Queued = 0x08,
ExtraLocation = 0x10,
@@ -39,8 +40,7 @@ namespace OpenRA
public readonly string OrderString;
public readonly Actor Subject;
public readonly bool Queued;
public Actor TargetActor;
public CPos TargetLocation;
public readonly Target Target;
public string TargetString;
public CPos ExtraLocation;
public uint ExtraData;
@@ -48,15 +48,29 @@ namespace OpenRA
public bool SuppressVisualFeedback;
public Actor VisualFeedbackTarget;
/// <summary>
/// DEPRECATED: Use Target instead.
/// </summary>
public Actor TargetActor { get { return Target.SerializableActor; } }
/// <summary>
/// DEPRECATED: Use Target instead.
/// </summary>
public CPos TargetLocation
{
get
{
return Target.SerializableCell.HasValue ? Target.SerializableCell.Value : CPos.Zero;
}
}
public Player Player { get { return Subject != null ? Subject.Owner : null; } }
Order(string orderString, Actor subject,
Actor targetActor, CPos targetLocation, string targetString, bool queued, CPos extraLocation, uint extraData)
Order(string orderString, Actor subject, Target target, string targetString, bool queued, CPos extraLocation, uint extraData)
{
OrderString = orderString;
Subject = subject;
TargetActor = targetActor;
TargetLocation = targetLocation;
Target = target;
TargetString = targetString;
Queued = queued;
ExtraLocation = extraLocation;
@@ -74,21 +88,65 @@ namespace OpenRA
var subjectId = r.ReadUInt32();
var flags = (OrderFields)r.ReadByte();
var targetActorId = flags.HasField(OrderFields.TargetActor) ? r.ReadUInt32() : uint.MaxValue;
var targetLocation = (CPos)(flags.HasField(OrderFields.TargetLocation) ? r.ReadInt2() : int2.Zero);
Actor subject = null;
if (world != null)
TryGetActorFromUInt(world, subjectId, out subject);
var target = Target.Invalid;
if (flags.HasField(OrderFields.Target))
{
switch ((TargetType)r.ReadByte())
{
case TargetType.Actor:
{
Actor targetActor;
if (world != null && TryGetActorFromUInt(world, r.ReadUInt32(), out targetActor))
target = Target.FromActor(targetActor);
break;
}
case TargetType.FrozenActor:
{
var playerActorID = r.ReadUInt32();
var frozenActorID = r.ReadUInt32();
Actor playerActor;
if (world == null || !TryGetActorFromUInt(world, playerActorID, out playerActor))
break;
var frozenLayer = playerActor.TraitOrDefault<FrozenActorLayer>();
if (frozenLayer == null)
break;
var frozen = frozenLayer.FromID(frozenActorID);
if (frozen != null)
target = Target.FromFrozenActor(frozen);
break;
}
case TargetType.Terrain:
{
if (world != null)
target = Target.FromCell(world, (CPos)r.ReadInt2());
break;
}
}
}
var targetString = flags.HasField(OrderFields.TargetString) ? r.ReadString() : null;
var queued = flags.HasField(OrderFields.Queued);
var extraLocation = (CPos)(flags.HasField(OrderFields.ExtraLocation) ? r.ReadInt2() : int2.Zero);
var extraData = flags.HasField(OrderFields.ExtraData) ? r.ReadUInt32() : 0;
if (world == null)
return new Order(order, null, null, targetLocation, targetString, queued, extraLocation, extraData);
return new Order(order, null, target, targetString, queued, extraLocation, extraData);
Actor subject, targetActor;
if (!TryGetActorFromUInt(world, subjectId, out subject) || !TryGetActorFromUInt(world, targetActorId, out targetActor))
if (subject == null && subjectId != uint.MaxValue)
return null;
return new Order(order, subject, targetActor, targetLocation, targetString, queued, extraLocation, extraData);
return new Order(order, subject, target, targetString, queued, extraLocation, extraData);
}
case 0xfe:
@@ -169,68 +227,83 @@ namespace OpenRA
// For scripting special powers
public Order()
: this(null, null, null, CPos.Zero, null, false, CPos.Zero, 0) { }
: this(null, null, Target.Invalid, null, false, CPos.Zero, 0) { }
public Order(string orderString, Actor subject, bool queued)
: this(orderString, subject, null, CPos.Zero, null, queued, CPos.Zero, 0) { }
: this(orderString, subject, Target.Invalid, null, queued, CPos.Zero, 0) { }
public Order(string orderString, Actor subject, Target target, bool queued)
: this(orderString, subject, target, null, queued, CPos.Zero, 0) { }
public Order(string orderstring, Order order)
: this(orderstring, order.Subject, order.TargetActor, order.TargetLocation,
: this(orderstring, order.Subject, order.Target,
order.TargetString, order.Queued, order.ExtraLocation, order.ExtraData) { }
public byte[] Serialize()
{
var minLength = OrderString.Length + 1 + (IsImmediate ? 1 + TargetString.Length + 1 : 6);
var ret = new MemoryStream(minLength);
var w = new BinaryWriter(ret);
if (IsImmediate)
{
var ret = new MemoryStream();
var w = new BinaryWriter(ret);
w.Write((byte)0xfe);
w.Write((byte)0xFE);
w.Write(OrderString);
w.Write(TargetString);
return ret.ToArray();
}
switch (OrderString)
w.Write((byte)0xFF);
w.Write(OrderString);
w.Write(UIntFromActor(Subject));
OrderFields fields = 0;
if (Target.SerializableType != TargetType.Invalid)
fields |= OrderFields.Target;
if (TargetString != null)
fields |= OrderFields.TargetString;
if (Queued)
fields |= OrderFields.Queued;
if (ExtraLocation != CPos.Zero)
fields |= OrderFields.ExtraLocation;
if (ExtraData != 0)
fields |= OrderFields.ExtraData;
w.Write((byte)fields);
if (fields.HasField(OrderFields.Target))
{
/*
* Format:
* u8: orderID.
* 0xFF: Full serialized order.
* varies: rest of order.
*/
default:
// TODO: specific serializers for specific orders.
{
var ret = new MemoryStream();
var w = new BinaryWriter(ret);
w.Write((byte)0xFF);
w.Write(OrderString);
w.Write(UIntFromActor(Subject));
OrderFields fields = 0;
if (TargetActor != null) fields |= OrderFields.TargetActor;
if (TargetLocation != CPos.Zero) fields |= OrderFields.TargetLocation;
if (TargetString != null) fields |= OrderFields.TargetString;
if (Queued) fields |= OrderFields.Queued;
if (ExtraLocation != CPos.Zero) fields |= OrderFields.ExtraLocation;
if (ExtraData != 0) fields |= OrderFields.ExtraData;
w.Write((byte)fields);
if (TargetActor != null)
w.Write(UIntFromActor(TargetActor));
if (TargetLocation != CPos.Zero)
w.Write(TargetLocation);
if (TargetString != null)
w.Write(TargetString);
if (ExtraLocation != CPos.Zero)
w.Write(ExtraLocation);
if (ExtraData != 0)
w.Write(ExtraData);
return ret.ToArray();
}
w.Write((byte)Target.SerializableType);
switch (Target.SerializableType)
{
case TargetType.Actor:
w.Write(UIntFromActor(Target.SerializableActor));
break;
case TargetType.FrozenActor:
w.Write(Target.FrozenActor.Viewer.PlayerActor.ActorID);
w.Write(Target.FrozenActor.ID);
break;
case TargetType.Terrain:
// SerializableCell is guaranteed to be non-null if Type == TargetType.Terrain
w.Write(Target.SerializableCell.Value);
break;
}
}
if (fields.HasField(OrderFields.TargetString))
w.Write(TargetString);
if (fields.HasField(OrderFields.ExtraLocation))
w.Write(ExtraLocation);
if (fields.HasField(OrderFields.ExtraData))
w.Write(ExtraData);
return ret.ToArray();
}
public override string ToString()

View File

@@ -38,14 +38,14 @@ namespace OpenRA.Network
public static byte[] SerializeSync(int sync)
{
var ms = new MemoryStream();
var ms = new MemoryStream(1 + 4);
using (var writer = new BinaryWriter(ms))
{
writer.Write((byte)0x65);
writer.Write(sync);
}
return ms.ToArray();
return ms.GetBuffer();
}
public static int2 ReadInt2(this BinaryReader r)

View File

@@ -22,7 +22,7 @@ namespace OpenRA.Network
class Chunk
{
public int Frame;
public List<Pair<int, byte[]>> Packets = new List<Pair<int, byte[]>>();
public Pair<int, byte[]>[] Packets;
}
Queue<Chunk> chunks = new Queue<Chunk>();
@@ -45,6 +45,8 @@ namespace OpenRA.Network
// to avoid issues with all immediate orders being resolved on the first tick.
using (var rs = File.OpenRead(replayFilename))
{
var packets = new List<Pair<int, byte[]>>();
var chunk = new Chunk();
while (rs.Position < rs.Length)
@@ -55,7 +57,7 @@ namespace OpenRA.Network
var packetLen = rs.ReadInt32();
var packet = rs.ReadBytes(packetLen);
var frame = BitConverter.ToInt32(packet, 0);
chunk.Packets.Add(Pair.New(client, packet));
packets.Add(Pair.New(client, packet));
if (frame != int.MaxValue &&
(!lastClientsFrame.ContainsKey(client) || frame > lastClientsFrame[client]))
@@ -81,6 +83,8 @@ namespace OpenRA.Network
{
// Regular order - finalize the chunk
chunk.Frame = frame;
chunk.Packets = packets.ToArray();
packets.Clear();
chunks.Enqueue(chunk);
chunk = new Chunk();
@@ -123,10 +127,10 @@ namespace OpenRA.Network
public void SendSync(int frame, byte[] syncData)
{
var ms = new MemoryStream();
var ms = new MemoryStream(4 + syncData.Length);
ms.Write(BitConverter.GetBytes(frame));
ms.Write(syncData);
sync.Add(ms.ToArray());
sync.Add(ms.GetBuffer());
// Store the current frame so Receive() can return the next chunk of orders.
ordersFrame = frame + LobbyInfo.GlobalSettings.OrderLatency;

View File

@@ -180,13 +180,11 @@ namespace OpenRA.Network
public class LobbyOptionState
{
public bool Locked;
public string Value;
public string PreferredValue;
public LobbyOptionState() { }
public bool Enabled { get { return Value == "True"; } }
public bool IsLocked;
public bool IsEnabled { get { return Value == "True"; } }
}
public class Global
@@ -228,7 +226,7 @@ namespace OpenRA.Network
{
LobbyOptionState option;
if (LobbyOptions.TryGetValue(id, out option))
return option.Enabled;
return option.IsEnabled;
return def;
}

View File

@@ -19,7 +19,6 @@ using OpenRA.Primitives;
namespace OpenRA.Network
{
using System.Globalization;
using NamesValuesPair = Pair<string[], object[]>;
class SyncReport
{
@@ -31,13 +30,13 @@ namespace OpenRA.Network
readonly Report[] syncReports = new Report[NumSyncReports];
int curIndex = 0;
static NamesValuesPair DumpSyncTrait(ISync sync)
static Pair<string[], Values> DumpSyncTrait(ISync sync)
{
var type = sync.GetType();
TypeInfo typeInfo;
lock (typeInfoCache)
typeInfo = typeInfoCache[type];
var values = new object[typeInfo.Names.Length];
var values = new Values(typeInfo.Names.Length);
var index = 0;
foreach (var func in typeInfo.SerializableCopyOfMemberFunctions)
@@ -68,28 +67,37 @@ namespace OpenRA.Network
report.Effects.Clear();
foreach (var actor in orderManager.World.ActorsHavingTrait<ISync>())
{
foreach (var syncHash in actor.SyncHashes)
if (syncHash.Hash != 0)
{
var hash = syncHash.Hash();
if (hash != 0)
{
report.Traits.Add(new TraitReport()
{
ActorID = actor.ActorID,
Type = actor.Info.Name,
Owner = (actor.Owner == null) ? "null" : actor.Owner.PlayerName,
Trait = syncHash.Trait.GetType().Name,
Hash = syncHash.Hash,
Hash = hash,
NamesValues = DumpSyncTrait(syncHash.Trait)
});
}
}
}
foreach (var sync in orderManager.World.SyncedEffects)
{
var hash = Sync.Hash(sync);
if (hash != 0)
{
report.Effects.Add(new EffectReport()
{
Name = sync.GetType().Name,
Hash = hash,
NamesValues = DumpSyncTrait(sync)
});
}
}
}
@@ -156,14 +164,14 @@ namespace OpenRA.Network
public string Owner;
public string Trait;
public int Hash;
public NamesValuesPair NamesValues;
public Pair<string[], Values> NamesValues;
}
struct EffectReport
{
public string Name;
public int Hash;
public NamesValuesPair NamesValues;
public Pair<string[], Values> NamesValues;
}
struct TypeInfo
@@ -249,5 +257,68 @@ namespace OpenRA.Network
return Expression.Lambda<Func<ISync, string>>(getString, name, new[] { SyncParam }).Compile();
}
}
/// <summary>
/// Holds up to 4 objects directly, or else allocates an array to hold the items. This allows us to record
/// trait values for traits with up to 4 sync members inline without having to allocate extra memory.
/// </summary>
struct Values
{
static readonly object Sentinel = new object();
object item1OrArray;
object item2OrSentinel;
object item3;
object item4;
public Values(int size)
{
item1OrArray = null;
item2OrSentinel = null;
item3 = null;
item4 = null;
if (size > 4)
{
item1OrArray = new object[size];
item2OrSentinel = Sentinel;
}
}
public object this[int index]
{
get
{
if (item2OrSentinel == Sentinel)
return ((object[])item1OrArray)[index];
switch (index)
{
case 0: return item1OrArray;
case 1: return item2OrSentinel;
case 2: return item3;
case 3: return item4;
default: throw new ArgumentOutOfRangeException("index");
}
}
set
{
if (item2OrSentinel == Sentinel)
{
((object[])item1OrArray)[index] = value;
return;
}
switch (index)
{
case 0: item1OrArray = value; break;
case 1: item2OrSentinel = value; break;
case 2: item3 = value; break;
case 3: item4 = value; break;
default: throw new ArgumentOutOfRangeException("index");
}
}
}
}
}
}

View File

@@ -20,15 +20,22 @@ using Open.Nat;
namespace OpenRA.Network
{
public enum UPnPStatus { Enabled, Disabled, NotSupported }
public class UPnP
{
static NatDevice natDevice;
static Mapping mapping;
static bool initialized;
public static IPAddress ExternalIP { get; private set; }
public static UPnPStatus Status { get { return initialized ? natDevice != null ?
UPnPStatus.Enabled : UPnPStatus.NotSupported : UPnPStatus.Disabled; } }
public static async Task DiscoverNatDevices(int timeout)
{
initialized = true;
NatDiscoverer.TraceSource.Switch.Level = SourceLevels.Verbose;
var logChannel = Log.Channel("nat");
NatDiscoverer.TraceSource.Listeners.Add(new TextWriterTraceListener(logChannel.Writer));

View File

@@ -16,8 +16,9 @@ using OpenRA.Traits;
namespace OpenRA.Network
{
static class UnitOrders
public static class UnitOrders
{
public const int ChatMessageMaxLength = 2500;
const string ServerChatName = "Battlefield Control";
static Player FindPlayerByClient(this World world, Session.Client c)
@@ -28,7 +29,7 @@ namespace OpenRA.Network
p => (p.ClientIndex == c.Index && p.PlayerReference.Playable));
}
public static void ProcessOrder(OrderManager orderManager, World world, int clientId, Order order)
internal static void ProcessOrder(OrderManager orderManager, World world, int clientId, Order order)
{
if (world != null)
{
@@ -42,6 +43,12 @@ namespace OpenRA.Network
case "Chat":
{
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
// Cut chat messages to the hard limit to avoid exploits
var message = order.TargetString;
if (message.Length > ChatMessageMaxLength)
message = order.TargetString.Substring(0, ChatMessageMaxLength);
if (client != null)
{
var player = world != null ? world.FindPlayerByClient(client) : null;
@@ -51,10 +58,10 @@ namespace OpenRA.Network
if (orderManager.LocalClient != null && client != orderManager.LocalClient && client.Team > 0 && client.Team == orderManager.LocalClient.Team)
suffix += " (Ally)";
Game.AddChatLine(client.Color.RGB, client.Name + suffix, order.TargetString);
Game.AddChatLine(client.Color.RGB, client.Name + suffix, message);
}
else
Game.AddChatLine(Color.White, "(player {0})".F(clientId), order.TargetString);
Game.AddChatLine(Color.White, "(player {0})".F(clientId), message);
break;
}

View File

@@ -43,6 +43,7 @@
<UseVSHostingProcess>false</UseVSHostingProcess>
<StartArguments>Game.Mod=ra</StartArguments>
<Commandlineparameters>Game.Mod=ra</Commandlineparameters>
<LangVersion>5</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<DebugSymbols>true</DebugSymbols>
@@ -97,10 +98,6 @@
<HintPath>..\thirdparty\download\MaxMind.Db.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="SmarIrc4net">
<HintPath>..\thirdparty\download\SmarIrc4net.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Activities\Activity.cs" />
@@ -175,21 +172,22 @@
<Compile Include="Server\ServerOrder.cs" />
<Compile Include="Server\TraitInterfaces.cs" />
<Compile Include="Support\Arguments.cs" />
<Compile Include="Support\ExceptionHandler.cs" />
<Compile Include="Support\LaunchArguments.cs" />
<Compile Include="Support\PerfHistory.cs" />
<Compile Include="Support\Program.cs" />
<Compile Include="Sync.cs" />
<Compile Include="TraitDictionary.cs" />
<Compile Include="Traits\LintAttributes.cs" />
<Compile Include="Traits\Player\DeveloperMode.cs" />
<Compile Include="Traits\Player\PlayerResources.cs" />
<Compile Include="Traits\Player\Shroud.cs" />
<Compile Include="Traits\Selectable.cs" />
<Compile Include="Traits\Target.cs" />
<Compile Include="Traits\TraitsInterfaces.cs" />
<Compile Include="Traits\World\Faction.cs" />
<Compile Include="Traits\World\ResourceType.cs" />
<Compile Include="Traits\World\ScreenShaker.cs" />
<Compile Include="Traits\World\Shroud.cs" />
<Compile Include="Traits\World\DebugVisualizations.cs" />
<Compile Include="World.cs" />
<Compile Include="WorldUtils.cs" />
<Compile Include="VoiceExts.cs" />
@@ -223,10 +221,10 @@
<Compile Include="Input\InputHandler.cs" />
<Compile Include="Input\Keycode.cs" />
<Compile Include="Input\Hotkey.cs" />
<Compile Include="Input\HotkeyReference.cs" />
<Compile Include="Graphics\PlatformInterfaces.cs" />
<Compile Include="Sound\Sound.cs" />
<Compile Include="Sound\SoundDevice.cs" />
<Compile Include="Graphics\SelectionBarsRenderable.cs" />
<Compile Include="Graphics\TargetLineRenderable.cs" />
<Compile Include="Graphics\UISpriteRenderable.cs" />
<Compile Include="Graphics\SoftwareCursor.cs" />
@@ -245,7 +243,6 @@
<Compile Include="Renderer.cs" />
<Compile Include="Platform.cs" />
<Compile Include="GameSpeed.cs" />
<Compile Include="GlobalChat.cs" />
<Compile Include="Primitives\ObservableList.cs" />
<Compile Include="Graphics\RgbaColorRenderer.cs" />
<Compile Include="Traits\Player\IndexedPlayerPalette.cs" />
@@ -259,6 +256,9 @@
<Compile Include="UtilityCommands\RegisterModCommand.cs" />
<Compile Include="UtilityCommands\UnregisterModCommand.cs" />
<Compile Include="UtilityCommands\ClearInvalidModRegistrationsCommand.cs" />
<Compile Include="HotkeyManager.cs" />
<Compile Include="HotkeyDefinition.cs" />
<Compile Include="Traits\Interactable.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="FileSystem\Folder.cs" />

View File

@@ -10,6 +10,8 @@
#endregion
using System.Collections.Generic;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Orders
{
@@ -49,8 +51,10 @@ namespace OpenRA.Orders
if (mi.Button == ExpectedButton && world.Map.Contains(cell))
{
world.CancelInputMode();
var queued = mi.Modifiers.HasModifier(Modifiers.Shift);
foreach (var subject in Subjects)
yield return new Order(OrderName, subject, false) { TargetLocation = cell };
yield return new Order(OrderName, subject, Target.FromCell(world, cell), queued);
}
}
@@ -59,7 +63,7 @@ namespace OpenRA.Orders
return world.Map.Contains(cell) ? Cursor : "generic-blocked";
}
public override bool InputOverridesSelection(World world, int2 xy, MouseInput mi)
public override bool InputOverridesSelection(WorldRenderer wr, World world, int2 xy, MouseInput mi)
{
// Custom order generators always override selection
return true;

View File

@@ -12,6 +12,7 @@
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Orders
@@ -20,14 +21,14 @@ namespace OpenRA.Orders
{
static Target TargetForInput(World world, CPos cell, int2 worldPixel, MouseInput mi)
{
var actor = world.ScreenMap.ActorsAt(mi)
.Where(a => !world.FogObscures(a) && a.Info.HasTraitInfo<ITargetableInfo>())
var actor = world.ScreenMap.ActorsAtMouse(mi)
.Where(a => a.Actor.Info.HasTraitInfo<ITargetableInfo>() && !world.FogObscures(a.Actor))
.WithHighestSelectionPriority(worldPixel);
if (actor != null)
return Target.FromActor(actor);
var frozen = world.ScreenMap.FrozenActorsAt(world.RenderPlayer, mi)
var frozen = world.ScreenMap.FrozenActorsAtMouse(world.RenderPlayer, mi)
.Where(a => a.Info.HasTraitInfo<ITargetableInfo>() && a.Visible && a.HasRenderables)
.WithHighestSelectionPriority(worldPixel);
@@ -50,11 +51,6 @@ namespace OpenRA.Orders
if (!actorsInvolved.Any())
yield break;
yield return new Order("CreateGroup", actorsInvolved.First().Owner.PlayerActor, false)
{
TargetString = actorsInvolved.Select(a => a.ActorID).JoinWith(",")
};
foreach (var o in orders)
yield return CheckSameOrder(o.Order, o.Trait.IssueOrder(o.Actor, o.Order, o.Target, mi.Modifiers.HasModifier(Modifiers.Shift)));
}
@@ -83,16 +79,18 @@ namespace OpenRA.Orders
}
// Used for classic mouse orders, determines whether or not action at xy is move or select
public virtual bool InputOverridesSelection(World world, int2 xy, MouseInput mi)
public virtual bool InputOverridesSelection(WorldRenderer wr, World world, int2 xy, MouseInput mi)
{
var actor = world.ScreenMap.ActorsAt(xy).WithHighestSelectionPriority(xy);
var actor = world.ScreenMap.ActorsAtMouse(xy).WithHighestSelectionPriority(xy);
if (actor == null)
return true;
var target = Target.FromActor(actor);
var cell = world.Map.CellContaining(target.CenterPosition);
var actorsAt = world.ActorMap.GetActorsAt(cell).ToList();
var underCursor = world.Selection.Actors.WithHighestSelectionPriority(xy);
var underCursor = world.Selection.Actors
.Select(a => new ActorBoundsPair(a, a.MouseBounds(wr)))
.WithHighestSelectionPriority(xy);
var o = OrderForUnit(underCursor, target, actorsAt, cell, mi);
if (o != null)

View File

@@ -93,9 +93,6 @@ namespace OpenRA
break;
}
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
return dir + Path.DirectorySeparatorChar;
}

View File

@@ -24,7 +24,14 @@ using OpenRA.Widgets;
namespace OpenRA
{
public enum PowerState { Normal, Low, Critical }
[Flags]
public enum PowerState
{
Normal = 1,
Low = 2,
Critical = 4
}
public enum WinState { Undefined, Won, Lost }
public class Player : IScriptBindable, IScriptNotifyBind, ILuaTableBinding, ILuaEqualityBinding, ILuaToStringBinding
@@ -61,7 +68,6 @@ namespace OpenRA
public Shroud Shroud;
public World World { get; private set; }
readonly IFogVisibilityModifier[] fogVisibilities;
readonly StanceColors stanceColors;
static FactionInfo ChooseFaction(World world, string name, bool requireSelectable = true)
@@ -134,8 +140,6 @@ namespace OpenRA
PlayerActor = world.CreateActor("Player", new TypeDictionary { new OwnerInit(this) });
Shroud = PlayerActor.Trait<Shroud>();
fogVisibilities = PlayerActor.TraitsImplementing<IFogVisibilityModifier>().ToArray();
// Enable the bot logic on the host
IsBot = BotType != null;
if (IsBot && Game.IsHost)
@@ -165,35 +169,6 @@ namespace OpenRA
return p == null || Stances[p] == Stance.Ally || (p.Spectating && !NonCombatant);
}
public bool CanViewActor(Actor a)
{
return a.CanBeViewedByPlayer(this);
}
public bool CanTargetActor(Actor a)
{
// PERF: Avoid LINQ.
if (HasFogVisibility)
foreach (var fogVisibility in fogVisibilities)
if (fogVisibility.IsVisible(a))
return true;
return CanViewActor(a);
}
public bool HasFogVisibility
{
get
{
// PERF: Avoid LINQ.
foreach (var fogVisibility in fogVisibilities)
if (fogVisibility.HasFogVisibility())
return true;
return false;
}
}
public Color PlayerStanceColor(Actor a)
{
var player = a.World.RenderPlayer ?? a.World.LocalPlayer;

View File

@@ -23,6 +23,7 @@ namespace OpenRA.Primitives
{
readonly Queue<byte> data = new Queue<byte>(1024);
readonly Stream baseStream;
bool baseStreamEmpty;
protected ReadOnlyAdapterStream(Stream stream)
{
@@ -38,7 +39,7 @@ namespace OpenRA.Primitives
public sealed override bool CanRead { get { return true; } }
public sealed override bool CanWrite { get { return false; } }
public sealed override long Length { get { throw new NotSupportedException(); } }
public override long Length { get { throw new NotSupportedException(); } }
public sealed override long Position
{
get { throw new NotSupportedException(); }
@@ -55,10 +56,9 @@ namespace OpenRA.Primitives
var copied = 0;
ConsumeData(buffer, offset, count, ref copied);
var finished = false;
while (copied < count && !finished)
while (copied < count && !baseStreamEmpty)
{
finished = BufferData(baseStream, data);
baseStreamEmpty = BufferData(baseStream, data);
ConsumeData(buffer, offset, count, ref copied);
}

View File

@@ -51,10 +51,20 @@ namespace OpenRA.Primitives
MutateBins(item, itemBounds[item] = bounds, addItem);
}
public void Remove(T item)
public bool Remove(T item)
{
MutateBins(item, itemBounds[item], removeItem);
Rectangle bounds;
if (!itemBounds.TryGetValue(item, out bounds))
return false;
MutateBins(item, bounds, removeItem);
itemBounds.Remove(item);
return true;
}
public bool Contains(T item)
{
return itemBounds.ContainsKey(item);
}
Dictionary<T, Rectangle> BinAt(int row, int col)
@@ -128,5 +138,7 @@ namespace OpenRA.Primitives
}
}
}
public IEnumerable<Rectangle> ItemBounds { get { return itemBounds.Values; } }
}
}

View File

@@ -20,7 +20,7 @@ namespace OpenRA
[StructLayout(LayoutKind.Sequential)]
public struct float2 : IEquatable<float2>
{
public float X, Y;
public readonly float X, Y;
public float2(float x, float y) { X = x; Y = y; }
public float2(PointF p) { X = p.X; Y = p.Y; }

View File

@@ -84,7 +84,7 @@ namespace OpenRA
{
if (fontSheetBuilder != null)
fontSheetBuilder.Dispose();
fontSheetBuilder = new SheetBuilder(SheetType.BGRA);
fontSheetBuilder = new SheetBuilder(SheetType.BGRA, 512);
Fonts = modData.Manifest.Fonts.ToDictionary(x => x.Key,
x => new SpriteFont(x.Value.First, modData.DefaultFileSystem.Open(x.Value.First).ReadAllBytes(),
x.Value.Second, Device.WindowScale, fontSheetBuilder)).AsReadOnly();

View File

@@ -13,6 +13,8 @@ using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA.Traits
{
@@ -45,21 +47,29 @@ namespace OpenRA.Traits
}
}
public static Actor WithHighestSelectionPriority(this IEnumerable<Actor> actors, int2 selectionPixel)
public static Actor WithHighestSelectionPriority(this IEnumerable<ActorBoundsPair> actors, int2 selectionPixel)
{
return actors.MaxByOrDefault(a => CalculateActorSelectionPriority(a.Info, a.Bounds, selectionPixel));
if (!actors.Any())
return null;
return actors.MaxBy(a => CalculateActorSelectionPriority(a.Actor.Info, a.Bounds, selectionPixel)).Actor;
}
public static FrozenActor WithHighestSelectionPriority(this IEnumerable<FrozenActor> actors, int2 selectionPixel)
{
return actors.MaxByOrDefault(a => CalculateActorSelectionPriority(a.Info, a.Bounds, selectionPixel));
return actors.MaxByOrDefault(a => CalculateActorSelectionPriority(a.Info, a.MouseBounds, selectionPixel));
}
static long CalculateActorSelectionPriority(ActorInfo info, Rectangle bounds, int2 selectionPixel)
{
var centerPixel = new int2(bounds.X, bounds.Y);
var pixelDistance = (centerPixel - selectionPixel).Length;
if (bounds.IsEmpty)
return info.SelectionPriority();
var centerPixel = new int2(
bounds.Left + bounds.Size.Width / 2,
bounds.Top + bounds.Size.Height / 2);
var pixelDistance = (centerPixel - selectionPixel).Length;
return ((long)-pixelDistance << 32) + info.SelectionPriority();
}

View File

@@ -25,6 +25,7 @@ namespace OpenRA.Server
public int ExpectLength = 8;
public int Frame = 0;
public int MostRecentFrame = 0;
public bool Validated;
public long TimeSinceLastResponse { get { return Game.RunTime - lastReceivedTime; } }
public bool TimeoutMessageShown = false;

View File

@@ -134,8 +134,7 @@ namespace OpenRA.Server
randomSeed = (int)DateTime.Now.ToBinary();
// UPnP is only supported for servers created by the game client.
if (!dedicated && Settings.AllowPortForward)
if (UPnP.Status == UPnPStatus.Enabled)
UPnP.ForwardPort(Settings.ListenPort, Settings.ExternalPort).Wait();
foreach (var trait in modData.Manifest.ServerTraits)
@@ -206,7 +205,7 @@ namespace OpenRA.Server
if (State == ServerState.ShuttingDown)
{
EndGame();
if (!dedicated && Settings.AllowPortForward)
if (UPnP.Status == UPnPStatus.Enabled)
UPnP.RemovePortForward().Wait();
break;
}
@@ -361,6 +360,8 @@ namespace OpenRA.Server
PreConns.Remove(newConn);
Conns.Add(newConn);
LobbyInfo.Clients.Add(client);
newConn.Validated = true;
var clientPing = new Session.ClientPing { Index = client.Index };
LobbyInfo.ClientPings.Add(clientPing);
@@ -477,6 +478,23 @@ namespace OpenRA.Server
void InterpretServerOrder(Connection conn, ServerOrder so)
{
// Only accept handshake responses from unvalidated clients
// Anything else may be an attempt to exploit the server
if (!conn.Validated)
{
if (so.Name == "HandshakeResponse")
ValidateClient(conn, so.Data);
else
{
Log.Write("server", "Rejected connection from {0}; Order `{1}` is not a `HandshakeResponse`.",
conn.Socket.RemoteEndPoint, so.Name);
DropClient(conn);
}
return;
}
switch (so.Name)
{
case "Command":
@@ -493,9 +511,6 @@ namespace OpenRA.Server
break;
}
case "HandshakeResponse":
ValidateClient(conn, so.Data);
break;
case "Chat":
case "TeamChat":
case "PauseGame":

View File

@@ -49,7 +49,7 @@ namespace OpenRA.Server
public byte[] Serialize()
{
var ms = new MemoryStream();
var ms = new MemoryStream(1 + Name.Length + 1 + Data.Length + 1);
var bw = new BinaryWriter(ms);
bw.Write((byte)0xfe);

View File

@@ -54,9 +54,6 @@ namespace OpenRA
[Desc("Allow users to enable NAT discovery for external IP detection and automatic port forwarding.")]
public bool DiscoverNatDevices = false;
[Desc("Set this to false to disable UPnP even if compatible devices are found.")]
public bool AllowPortForward = true;
[Desc("Time in milliseconds to search for UPnP enabled NAT devices.")]
public int NatDiscoveryTimeout = 1000;
@@ -86,14 +83,27 @@ namespace OpenRA
public bool LuaDebug = false;
public bool PerfText = false;
public bool PerfGraph = false;
[Desc("Amount of time required for triggering perf.log output.")]
public float LongTickThresholdMs = 1;
public bool SanityCheckUnsyncedCode = false;
public int Samples = 25;
[Desc("Show incompatible games in server browser.")]
public bool IgnoreVersionMismatch = false;
public bool StrictActivityChecking = false;
[Desc("Check whether a newer version is available online.")]
public bool CheckVersion = true;
[Desc("Allow the collection of anonymous data such as Operating System, .NET runtime, OpenGL version and language settings.")]
public bool SendSystemInformation = true;
public int SystemInformationVersionPrompt = 0;
public string UUID = System.Guid.NewGuid().ToString();
public bool EnableDebugCommandsInReplays = false;
}
public class GraphicSettings
@@ -147,6 +157,7 @@ namespace OpenRA
public class PlayerSettings
{
[Desc("Sets the player nickname for in-game and IRC chat.")]
public string Name = "Newbie";
public HSLColor Color = new HSLColor(75, 255, 180);
public string LastServer = "localhost:1234";
@@ -157,6 +168,8 @@ namespace OpenRA
public string Platform = "Default";
public bool ViewportEdgeScroll = true;
public int ViewportEdgeScrollMargin = 5;
public bool LockMouseWindow = false;
public MouseScrollType MiddleMouseScroll = MouseScrollType.Standard;
public MouseScrollType RightMouseScroll = MouseScrollType.Disabled;
@@ -181,158 +194,6 @@ namespace OpenRA
public MPGameFilters MPGameFilters = MPGameFilters.Waiting | MPGameFilters.Empty | MPGameFilters.Protected | MPGameFilters.Started;
}
public class KeySettings
{
public Hotkey CycleBaseKey = new Hotkey(Keycode.H, Modifiers.None);
public Hotkey ToLastEventKey = new Hotkey(Keycode.SPACE, Modifiers.None);
public Hotkey ToSelectionKey = new Hotkey(Keycode.HOME, Modifiers.None);
public Hotkey SelectAllUnitsKey = new Hotkey(Keycode.Q, Modifiers.None);
public Hotkey SelectUnitsByTypeKey = new Hotkey(Keycode.W, Modifiers.None);
public Hotkey MapScrollUp = new Hotkey(Keycode.UP, Modifiers.None);
public Hotkey MapScrollDown = new Hotkey(Keycode.DOWN, Modifiers.None);
public Hotkey MapScrollLeft = new Hotkey(Keycode.LEFT, Modifiers.None);
public Hotkey MapScrollRight = new Hotkey(Keycode.RIGHT, Modifiers.None);
public Hotkey MapPushTop = new Hotkey(Keycode.UP, Modifiers.Alt);
public Hotkey MapPushBottom = new Hotkey(Keycode.DOWN, Modifiers.Alt);
public Hotkey MapPushLeftEdge = new Hotkey(Keycode.LEFT, Modifiers.Alt);
public Hotkey MapPushRightEdge = new Hotkey(Keycode.RIGHT, Modifiers.Alt);
public Hotkey ViewPortBookmarkSaveSlot1 = new Hotkey(Keycode.Q, Modifiers.Ctrl);
public Hotkey ViewPortBookmarkSaveSlot2 = new Hotkey(Keycode.W, Modifiers.Ctrl);
public Hotkey ViewPortBookmarkSaveSlot3 = new Hotkey(Keycode.E, Modifiers.Ctrl);
public Hotkey ViewPortBookmarkSaveSlot4 = new Hotkey(Keycode.R, Modifiers.Ctrl);
public Hotkey ViewPortBookmarkUseSlot1 = new Hotkey(Keycode.Q, Modifiers.Alt);
public Hotkey ViewPortBookmarkUseSlot2 = new Hotkey(Keycode.W, Modifiers.Alt);
public Hotkey ViewPortBookmarkUseSlot3 = new Hotkey(Keycode.E, Modifiers.Alt);
public Hotkey ViewPortBookmarkUseSlot4 = new Hotkey(Keycode.R, Modifiers.Alt);
public Hotkey PauseKey = new Hotkey(Keycode.PAUSE, Modifiers.None);
public Hotkey PlaceBeaconKey = new Hotkey(Keycode.B, Modifiers.None);
public Hotkey SellKey = new Hotkey(Keycode.Z, Modifiers.None);
public Hotkey PowerDownKey = new Hotkey(Keycode.X, Modifiers.None);
public Hotkey RepairKey = new Hotkey(Keycode.C, Modifiers.None);
public Hotkey NextProductionTabKey = new Hotkey(Keycode.PAGEDOWN, Modifiers.None);
public Hotkey PreviousProductionTabKey = new Hotkey(Keycode.PAGEUP, Modifiers.None);
public Hotkey CycleProductionBuildingsKey = new Hotkey(Keycode.TAB, Modifiers.None);
public Hotkey AttackMoveKey = new Hotkey(Keycode.A, Modifiers.None);
public Hotkey StopKey = new Hotkey(Keycode.S, Modifiers.None);
public Hotkey ScatterKey = new Hotkey(Keycode.X, Modifiers.Ctrl);
public Hotkey DeployKey = new Hotkey(Keycode.F, Modifiers.None);
public Hotkey StanceHoldFireKey = new Hotkey(Keycode.F, Modifiers.Alt);
public Hotkey StanceReturnFireKey = new Hotkey(Keycode.D, Modifiers.Alt);
public Hotkey StanceDefendKey = new Hotkey(Keycode.S, Modifiers.Alt);
public Hotkey StanceAttackAnythingKey = new Hotkey(Keycode.A, Modifiers.Alt);
public Hotkey GuardKey = new Hotkey(Keycode.D, Modifiers.None);
public Hotkey ObserverCombinedView = new Hotkey(Keycode.MINUS, Modifiers.None);
public Hotkey ObserverWorldView = new Hotkey(Keycode.EQUALS, Modifiers.None);
public Hotkey CycleStatusBarsKey = new Hotkey(Keycode.COMMA, Modifiers.None);
public Hotkey TogglePixelDoubleKey = new Hotkey(Keycode.PERIOD, Modifiers.None);
public Hotkey TogglePlayerStanceColorsKey = new Hotkey(Keycode.COMMA, Modifiers.Ctrl);
public Hotkey DevReloadChromeKey = new Hotkey(Keycode.C, Modifiers.Ctrl | Modifiers.Shift);
public Hotkey HideUserInterfaceKey = new Hotkey(Keycode.H, Modifiers.Ctrl | Modifiers.Shift);
public Hotkey TakeScreenshotKey = new Hotkey(Keycode.P, Modifiers.Ctrl);
public Hotkey ToggleMuteKey = new Hotkey(Keycode.M, Modifiers.None);
public Hotkey Production01Key = new Hotkey(Keycode.F1, Modifiers.None);
public Hotkey Production02Key = new Hotkey(Keycode.F2, Modifiers.None);
public Hotkey Production03Key = new Hotkey(Keycode.F3, Modifiers.None);
public Hotkey Production04Key = new Hotkey(Keycode.F4, Modifiers.None);
public Hotkey Production05Key = new Hotkey(Keycode.F5, Modifiers.None);
public Hotkey Production06Key = new Hotkey(Keycode.F6, Modifiers.None);
public Hotkey Production07Key = new Hotkey(Keycode.F7, Modifiers.None);
public Hotkey Production08Key = new Hotkey(Keycode.F8, Modifiers.None);
public Hotkey Production09Key = new Hotkey(Keycode.F9, Modifiers.None);
public Hotkey Production10Key = new Hotkey(Keycode.F10, Modifiers.None);
public Hotkey Production11Key = new Hotkey(Keycode.F11, Modifiers.None);
public Hotkey Production12Key = new Hotkey(Keycode.F12, Modifiers.None);
public Hotkey Production13Key = new Hotkey(Keycode.F1, Modifiers.Ctrl);
public Hotkey Production14Key = new Hotkey(Keycode.F2, Modifiers.Ctrl);
public Hotkey Production15Key = new Hotkey(Keycode.F3, Modifiers.Ctrl);
public Hotkey Production16Key = new Hotkey(Keycode.F4, Modifiers.Ctrl);
public Hotkey Production17Key = new Hotkey(Keycode.F5, Modifiers.Ctrl);
public Hotkey Production18Key = new Hotkey(Keycode.F6, Modifiers.Ctrl);
public Hotkey Production19Key = new Hotkey(Keycode.F7, Modifiers.Ctrl);
public Hotkey Production20Key = new Hotkey(Keycode.F8, Modifiers.Ctrl);
public Hotkey Production21Key = new Hotkey(Keycode.F9, Modifiers.Ctrl);
public Hotkey Production22Key = new Hotkey(Keycode.F10, Modifiers.Ctrl);
public Hotkey Production23Key = new Hotkey(Keycode.F11, Modifiers.Ctrl);
public Hotkey Production24Key = new Hotkey(Keycode.F12, Modifiers.Ctrl);
public Hotkey ProductionTypeBuildingKey = new Hotkey(Keycode.E, Modifiers.None);
public Hotkey ProductionTypeDefenseKey = new Hotkey(Keycode.R, Modifiers.None);
public Hotkey ProductionTypeInfantryKey = new Hotkey(Keycode.T, Modifiers.None);
public Hotkey ProductionTypeVehicleKey = new Hotkey(Keycode.Y, Modifiers.None);
public Hotkey ProductionTypeAircraftKey = new Hotkey(Keycode.U, Modifiers.None);
public Hotkey ProductionTypeNavalKey = new Hotkey(Keycode.I, Modifiers.None);
public Hotkey ProductionTypeTankKey = new Hotkey(Keycode.I, Modifiers.None);
public Hotkey ProductionTypeMerchantKey = new Hotkey(Keycode.O, Modifiers.None);
public Hotkey ProductionTypeUpgradeKey = new Hotkey(Keycode.R, Modifiers.None);
public Hotkey SupportPower01Key = new Hotkey(Keycode.UNKNOWN, Modifiers.None);
public Hotkey SupportPower02Key = new Hotkey(Keycode.UNKNOWN, Modifiers.None);
public Hotkey SupportPower03Key = new Hotkey(Keycode.UNKNOWN, Modifiers.None);
public Hotkey SupportPower04Key = new Hotkey(Keycode.UNKNOWN, Modifiers.None);
public Hotkey SupportPower05Key = new Hotkey(Keycode.UNKNOWN, Modifiers.None);
public Hotkey SupportPower06Key = new Hotkey(Keycode.UNKNOWN, Modifiers.None);
public Hotkey ReplaySpeedSlowKey = new Hotkey(Keycode.F5, Modifiers.None);
public Hotkey ReplaySpeedRegularKey = new Hotkey(Keycode.F6, Modifiers.None);
public Hotkey ReplaySpeedFastKey = new Hotkey(Keycode.F7, Modifiers.None);
public Hotkey ReplaySpeedMaxKey = new Hotkey(Keycode.F8, Modifiers.None);
public Hotkey NextTrack = new Hotkey(Keycode.AUDIONEXT, Modifiers.None);
public Hotkey PreviousTrack = new Hotkey(Keycode.AUDIOPREV, Modifiers.None);
public Hotkey StopMusic = new Hotkey(Keycode.AUDIOSTOP, Modifiers.None);
public Hotkey PauseMusic = new Hotkey(Keycode.AUDIOPLAY, Modifiers.None);
static readonly Func<KeySettings, Hotkey>[] ProductionKeys = GetKeys(24, "Production");
static readonly Func<KeySettings, Hotkey>[] SupportPowerKeys = GetKeys(6, "SupportPower");
static Func<KeySettings, Hotkey>[] GetKeys(int count, string prefix)
{
var keySettings = Expression.Parameter(typeof(KeySettings), "keySettings");
return Exts.MakeArray(count, i => Expression.Lambda<Func<KeySettings, Hotkey>>(
Expression.Field(keySettings, "{0}{1:D2}Key".F(prefix, i + 1)), keySettings).Compile());
}
public Hotkey GetProductionHotkey(int index)
{
return GetKey(ProductionKeys, index);
}
public Hotkey GetSupportPowerHotkey(int index)
{
return GetKey(SupportPowerKeys, index);
}
Hotkey GetKey(Func<KeySettings, Hotkey>[] keys, int index)
{
if (index < 0 || index >= keys.Length)
return Hotkey.Invalid;
return keys[index](this);
}
}
public class ChatSettings
{
public string Hostname = "irc.openra.net";
public int Port = 6667;
public string Channel = "lobby";
public string QuitMessage = "Battle control terminated!";
public string TimestampFormat = "HH:mm";
public bool ConnectAutomatically = false;
}
public class Settings
{
readonly string settingsFile;
@@ -343,8 +204,7 @@ namespace OpenRA
public readonly GraphicSettings Graphics = new GraphicSettings();
public readonly ServerSettings Server = new ServerSettings();
public readonly DebugSettings Debug = new DebugSettings();
public readonly KeySettings Keys = new KeySettings();
public readonly ChatSettings Chat = new ChatSettings();
internal Dictionary<string, Hotkey> Keys = new Dictionary<string, Hotkey>();
public readonly Dictionary<string, object> Sections;
@@ -364,8 +224,6 @@ namespace OpenRA
{ "Graphics", Graphics },
{ "Server", Server },
{ "Debug", Debug },
{ "Keys", Keys },
{ "Chat", Chat }
};
// Override fieldloader to ignore invalid entries
@@ -384,6 +242,11 @@ namespace OpenRA
if (Sections.TryGetValue(yamlSection.Key, out settingsSection))
LoadSectionYaml(yamlSection.Value, settingsSection);
}
var keysNode = yamlCache.FirstOrDefault(n => n.Key == "Keys");
if (keysNode != null)
foreach (var node in keysNode.Value.Nodes)
Keys[node.Key] = FieldLoader.GetValue<Hotkey>(node.Key, node.Value.Value);
}
// Override with commandline args
@@ -433,6 +296,17 @@ namespace OpenRA
}
}
var keysYaml = yamlCache.FirstOrDefault(x => x.Key == "Keys");
if (keysYaml == null)
{
keysYaml = new MiniYamlNode("Keys", new MiniYaml(""));
yamlCache.Add(keysYaml);
}
keysYaml.Value.Nodes.Clear();
foreach (var kv in Keys)
keysYaml.Value.Nodes.Add(new MiniYamlNode(kv.Key, FieldSaver.FormatValue(kv.Value)));
yamlCache.WriteToFile(settingsFile);
}

View File

@@ -40,7 +40,7 @@ namespace OpenRA
ISoundLoader[] loaders;
IReadOnlyFileSystem fileSystem;
Cache<string, ISoundSource> sounds;
ISoundSource rawSource;
ISoundSource videoSource;
ISound music;
ISound video;
MusicInfo currentMusic;
@@ -149,8 +149,9 @@ namespace OpenRA
public void PlayVideo(byte[] raw, int channels, int sampleBits, int sampleRate)
{
rawSource = soundEngine.AddSoundSourceFromMemory(raw, channels, sampleBits, sampleRate);
video = soundEngine.Play2D(rawSource, false, true, WPos.Zero, InternalSoundVolume, false);
StopVideo();
videoSource = soundEngine.AddSoundSourceFromMemory(raw, channels, sampleBits, sampleRate);
video = soundEngine.Play2D(videoSource, false, true, WPos.Zero, InternalSoundVolume, false);
}
public void PlayVideo()
@@ -168,7 +169,12 @@ namespace OpenRA
public void StopVideo()
{
if (video != null)
{
soundEngine.StopSound(video);
videoSource.Dispose();
videoSource = null;
video = null;
}
}
public void Tick()

View File

@@ -0,0 +1,93 @@
using System;
using System.Diagnostics;
using System.Globalization;
using System.Text;
namespace OpenRA
{
public static class ExceptionHandler
{
public static void HandleFatalError(Exception ex)
{
var exceptionName = "exception-" + DateTime.UtcNow.ToString("yyyy-MM-ddTHHmmssZ", CultureInfo.InvariantCulture) + ".log";
Log.AddChannel("exception", exceptionName);
if (Game.EngineVersion != null)
Log.Write("exception", "OpenRA engine version {0}", Game.EngineVersion);
if (Game.ModData != null)
{
var mod = Game.ModData.Manifest.Metadata;
Log.Write("exception", "{0} mod version {1}", mod.Title, mod.Version);
}
if (Game.OrderManager != null && Game.OrderManager.World != null && Game.OrderManager.World.Map != null)
{
var map = Game.OrderManager.World.Map;
Log.Write("exception", "on map {0} ({1} by {2}).", map.Uid, map.Title, map.Author);
}
Log.Write("exception", "Date: {0:u}", DateTime.UtcNow);
Log.Write("exception", "Operating System: {0} ({1})", Platform.CurrentPlatform, Environment.OSVersion);
Log.Write("exception", "Runtime Version: {0}", Platform.RuntimeVersion);
var rpt = BuildExceptionReport(ex).ToString();
Log.Write("exception", "{0}", rpt);
Console.Error.WriteLine(rpt);
}
static StringBuilder BuildExceptionReport(Exception ex)
{
return BuildExceptionReport(ex, new StringBuilder(), 0);
}
static StringBuilder AppendIndentedFormatLine(this StringBuilder sb, int indent, string format, params object[] args)
{
return sb.Append(new string(' ', indent * 2)).AppendFormat(format, args).AppendLine();
}
static StringBuilder BuildExceptionReport(Exception ex, StringBuilder sb, int indent)
{
if (ex == null)
return sb;
sb.AppendIndentedFormatLine(indent, "Exception of type `{0}`: {1}", ex.GetType().FullName, ex.Message);
var tle = ex as TypeLoadException;
var oom = ex as OutOfMemoryException;
if (tle != null)
{
sb.AppendIndentedFormatLine(indent, "TypeName=`{0}`", tle.TypeName);
}
else if (oom != null)
{
var gcMemoryBeforeCollect = GC.GetTotalMemory(false);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
sb.AppendIndentedFormatLine(indent, "GC Memory (post-collect)={0:N0}", GC.GetTotalMemory(false));
sb.AppendIndentedFormatLine(indent, "GC Memory (pre-collect)={0:N0}", gcMemoryBeforeCollect);
using (var p = Process.GetCurrentProcess())
{
sb.AppendIndentedFormatLine(indent, "Working Set={0:N0}", p.WorkingSet64);
sb.AppendIndentedFormatLine(indent, "Private Memory={0:N0}", p.PrivateMemorySize64);
sb.AppendIndentedFormatLine(indent, "Virtual Memory={0:N0}", p.VirtualMemorySize64);
}
}
else
{
// TODO: more exception types
}
if (ex.InnerException != null)
{
sb.AppendIndentedFormatLine(indent, "Inner");
BuildExceptionReport(ex.InnerException, sb, indent + 1);
}
sb.AppendIndentedFormatLine(indent, "{0}", ex.StackTrace);
return sb;
}
}
}

View File

@@ -82,7 +82,7 @@ namespace OpenRA.Support
var label = type == typeof(string) || type.IsGenericType ? item.ToString() : type.Name;
Log.Write("perf", FormatString,
1000f * (endStopwatchTicks - startStopwatchTicks) / Stopwatch.Frequency,
"[{0}] {1}: {2}".F(Game.LocalTick, name, label));
"[" + Game.LocalTick + "] " + name + ": " + label);
}
public static long LongTickThresholdInStopwatchTicks

View File

@@ -11,10 +11,7 @@
using System;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
namespace OpenRA
{
@@ -31,109 +28,19 @@ namespace OpenRA
static int Main(string[] args)
{
if (Debugger.IsAttached || args.Contains("--just-die"))
return (int)Run(args);
return (int)Game.InitializeAndRun(args);
AppDomain.CurrentDomain.UnhandledException += (_, e) => FatalError((Exception)e.ExceptionObject);
AppDomain.CurrentDomain.UnhandledException += (_, e) => ExceptionHandler.HandleFatalError((Exception)e.ExceptionObject);
try
{
return (int)Run(args);
return (int)Game.InitializeAndRun(args);
}
catch (Exception e)
{
FatalError(e);
ExceptionHandler.HandleFatalError(e);
return (int)RunStatus.Error;
}
}
static void FatalError(Exception ex)
{
var exceptionName = "exception-" + DateTime.UtcNow.ToString("yyyy-MM-ddTHHmmssZ", CultureInfo.InvariantCulture) + ".log";
Log.AddChannel("exception", exceptionName);
if (Game.EngineVersion != null)
Log.Write("exception", "OpenRA engine version {0}", Game.EngineVersion);
if (Game.ModData != null)
{
var mod = Game.ModData.Manifest.Metadata;
Log.Write("exception", "{0} mod version {1}", mod.Title, mod.Version);
}
if (Game.OrderManager != null && Game.OrderManager.World != null && Game.OrderManager.World.Map != null)
{
var map = Game.OrderManager.World.Map;
Log.Write("exception", "on map {0} ({1} by {2}).", map.Uid, map.Title, map.Author);
}
Log.Write("exception", "Date: {0:u}", DateTime.UtcNow);
Log.Write("exception", "Operating System: {0} ({1})", Platform.CurrentPlatform, Environment.OSVersion);
Log.Write("exception", "Runtime Version: {0}", Platform.RuntimeVersion);
var rpt = BuildExceptionReport(ex).ToString();
Log.Write("exception", "{0}", rpt);
Console.Error.WriteLine(rpt);
}
static StringBuilder BuildExceptionReport(Exception ex)
{
return BuildExceptionReport(ex, new StringBuilder(), 0);
}
static StringBuilder AppendIndentedFormatLine(this StringBuilder sb, int indent, string format, params object[] args)
{
return sb.Append(new string(' ', indent * 2)).AppendFormat(format, args).AppendLine();
}
static StringBuilder BuildExceptionReport(Exception ex, StringBuilder sb, int indent)
{
if (ex == null)
return sb;
sb.AppendIndentedFormatLine(indent, "Exception of type `{0}`: {1}", ex.GetType().FullName, ex.Message);
var tle = ex as TypeLoadException;
var oom = ex as OutOfMemoryException;
if (tle != null)
{
sb.AppendIndentedFormatLine(indent, "TypeName=`{0}`", tle.TypeName);
}
else if (oom != null)
{
var gcMemoryBeforeCollect = GC.GetTotalMemory(false);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
sb.AppendIndentedFormatLine(indent, "GC Memory (post-collect)={0:N0}", GC.GetTotalMemory(false));
sb.AppendIndentedFormatLine(indent, "GC Memory (pre-collect)={0:N0}", gcMemoryBeforeCollect);
using (var p = Process.GetCurrentProcess())
{
sb.AppendIndentedFormatLine(indent, "Working Set={0:N0}", p.WorkingSet64);
sb.AppendIndentedFormatLine(indent, "Private Memory={0:N0}", p.PrivateMemorySize64);
sb.AppendIndentedFormatLine(indent, "Virtual Memory={0:N0}", p.VirtualMemorySize64);
}
}
else
{
// TODO: more exception types
}
if (ex.InnerException != null)
{
sb.AppendIndentedFormatLine(indent, "Inner");
BuildExceptionReport(ex.InnerException, sb, indent + 1);
}
sb.AppendIndentedFormatLine(indent, "{0}", ex.StackTrace);
return sb;
}
static RunStatus Run(string[] args)
{
Game.Initialize(new Arguments(args));
GC.Collect();
return Game.Run();
}
}
}

View File

@@ -20,6 +20,8 @@ namespace OpenRA.Support
{
public abstract class VariableExpression
{
public static readonly IReadOnlyDictionary<string, int> NoVariables = new ReadOnlyDictionary<string, int>(new Dictionary<string, int>());
public readonly string Expression;
readonly HashSet<string> variables = new HashSet<string>();
public IEnumerable<string> Variables { get { return variables; } }

View File

@@ -0,0 +1,80 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Traits
{
[Desc("Used to enable mouse interaction on actors that are not Selectable.")]
public class InteractableInfo : ITraitInfo, IMouseBoundsInfo, IDecorationBoundsInfo
{
[Desc("Defines a custom rectangle for mouse interaction with the actor.",
"If null, the engine will guess an appropriate size based on the With*Body trait.",
"The first two numbers define the width and height of the rectangle.",
"The (optional) second two numbers define an x and y offset from the actor center.")]
public readonly int[] Bounds = null;
[Desc("Defines a custom rectangle for Decorations (e.g. the selection box).",
"If null, Bounds will be used instead")]
public readonly int[] DecorationBounds = null;
public virtual object Create(ActorInitializer init) { return new Interactable(this); }
}
public class Interactable : INotifyCreated, IMouseBounds, IDecorationBounds
{
readonly InteractableInfo info;
IAutoMouseBounds[] autoBounds;
public Interactable(InteractableInfo info)
{
this.info = info;
}
void INotifyCreated.Created(Actor self)
{
autoBounds = self.TraitsImplementing<IAutoMouseBounds>().ToArray();
}
Rectangle AutoBounds(Actor self, WorldRenderer wr)
{
return autoBounds.Select(s => s.AutoMouseoverBounds(self, wr)).FirstOrDefault(r => !r.IsEmpty);
}
Rectangle Bounds(Actor self, WorldRenderer wr, int[] bounds)
{
if (bounds == null)
return AutoBounds(self, wr);
var size = new int2(bounds[0], bounds[1]);
var offset = -size / 2;
if (bounds.Length > 2)
offset += new int2(bounds[2], bounds[3]);
var xy = wr.ScreenPxPosition(self.CenterPosition) + offset;
return new Rectangle(xy.X, xy.Y, size.X, size.Y);
}
Rectangle IMouseBounds.MouseoverBounds(Actor self, WorldRenderer wr)
{
return Bounds(self, wr, info.Bounds);
}
Rectangle IDecorationBounds.DecorationBounds(Actor self, WorldRenderer wr)
{
return Bounds(self, wr, info.DecorationBounds ?? info.Bounds);
}
}
}

View File

@@ -31,12 +31,12 @@ namespace OpenRA.Traits
{
public readonly PPos[] Footprint;
public readonly WPos CenterPosition;
public readonly Rectangle Bounds;
public readonly HashSet<string> TargetTypes;
readonly Actor actor;
readonly Player viewer;
readonly Shroud shroud;
public Player Owner { get; private set; }
public HashSet<string> TargetTypes { get; private set; }
public ITooltipInfo TooltipInfo { get; private set; }
public Player TooltipOwner { get; private set; }
@@ -50,14 +50,21 @@ namespace OpenRA.Traits
public bool Shrouded { get; private set; }
public bool NeedRenderables { get; set; }
public IRenderable[] Renderables = NoRenderables;
public Rectangle[] ScreenBounds = NoBounds;
// TODO: Replace this with an int2[] polygon
public Rectangle MouseBounds = Rectangle.Empty;
static readonly IRenderable[] NoRenderables = new IRenderable[0];
static readonly Rectangle[] NoBounds = new Rectangle[0];
int flashTicks;
public FrozenActor(Actor self, PPos[] footprint, Shroud shroud, bool startsRevealed)
public FrozenActor(Actor actor, PPos[] footprint, Player viewer, bool startsRevealed)
{
actor = self;
this.shroud = shroud;
this.actor = actor;
this.viewer = viewer;
shroud = viewer.Shroud;
NeedRenderables = startsRevealed;
// Consider all cells inside the map area (ignoring the current map bounds)
@@ -76,12 +83,11 @@ namespace OpenRA.Traits
footprint.Select(p => p.ToString()).JoinWith("|"),
footprint.Select(p => shroud.Contains(p).ToString()).JoinWith("|")));
CenterPosition = self.CenterPosition;
Bounds = self.Bounds;
TargetTypes = self.GetEnabledTargetTypes().ToHashSet();
CenterPosition = actor.CenterPosition;
TargetTypes = new HashSet<string>();
tooltips = self.TraitsImplementing<ITooltip>().ToArray();
health = self.TraitOrDefault<IHealth>();
tooltips = actor.TraitsImplementing<ITooltip>().ToArray();
health = actor.TraitOrDefault<IHealth>();
UpdateVisibility();
}
@@ -90,10 +96,12 @@ namespace OpenRA.Traits
public bool IsValid { get { return Owner != null; } }
public ActorInfo Info { get { return actor.Info; } }
public Actor Actor { get { return !actor.IsDead ? actor : null; } }
public Player Viewer { get { return viewer; } }
public void RefreshState()
{
Owner = actor.Owner;
TargetTypes = actor.GetEnabledTargetTypes().ToHashSet();
if (health != null)
{
@@ -101,7 +109,7 @@ namespace OpenRA.Traits
DamageState = health.DamageState;
}
var tooltip = tooltips.FirstOrDefault(Exts.IsTraitEnabled);
var tooltip = tooltips.FirstEnabledTraitOrDefault();
if (tooltip != null)
{
TooltipInfo = tooltip.TooltipInfo;
@@ -178,6 +186,7 @@ namespace OpenRA.Traits
readonly SpatiallyPartitioned<uint> partitionedFrozenActorIds;
readonly bool[] dirtyBins;
readonly HashSet<uint> dirtyFrozenActorIds = new HashSet<uint>();
readonly int rows, cols;
public FrozenActorLayer(Actor self, FrozenActorLayerInfo info)
{
@@ -191,16 +200,17 @@ namespace OpenRA.Traits
// expensive visibility update for frozen actors in these regions.
partitionedFrozenActorIds = new SpatiallyPartitioned<uint>(
world.Map.MapSize.X, world.Map.MapSize.Y, binSize);
var maxX = world.Map.MapSize.X / binSize + 1;
var maxY = world.Map.MapSize.Y / binSize + 1;
dirtyBins = new bool[maxX * maxY];
cols = world.Map.MapSize.X / binSize + 1;
rows = world.Map.MapSize.Y / binSize + 1;
dirtyBins = new bool[cols * rows];
self.Trait<Shroud>().CellsChanged += cells =>
{
foreach (var cell in cells)
{
var x = cell.U / binSize;
var y = cell.V / binSize;
dirtyBins[y * maxX + x] = true;
dirtyBins[y * cols + x] = true;
}
};
}
@@ -208,7 +218,7 @@ namespace OpenRA.Traits
public void Add(FrozenActor fa)
{
frozenActorsById.Add(fa.ID, fa);
world.ScreenMap.Add(owner, fa);
world.ScreenMap.AddOrUpdate(owner, fa);
partitionedFrozenActorIds.Add(fa.ID, FootprintBounds(fa));
}
@@ -242,49 +252,53 @@ namespace OpenRA.Traits
return Rectangle.FromLTRB(minU, minV, maxU + 1, maxV + 1);
}
public void Tick(Actor self)
void ITick.Tick(Actor self)
{
UpdateDirtyFrozenActorsFromDirtyBins();
var frozenActorsToRemove = new List<FrozenActor>();
VisibilityHash = 0;
FrozenHash = 0;
foreach (var kvp in frozenActorsById)
// Update visibility at the end of the tick to make sure that
// the fog/shroud state has been updated for the tick
self.World.AddFrameEndTask(w =>
{
var id = kvp.Key;
var hash = (int)id;
FrozenHash += hash;
UpdateDirtyFrozenActorsFromDirtyBins();
var frozenActor = kvp.Value;
frozenActor.Tick();
if (dirtyFrozenActorIds.Contains(id))
frozenActor.UpdateVisibility();
var frozenActorsToRemove = new List<FrozenActor>();
VisibilityHash = 0;
FrozenHash = 0;
if (frozenActor.Visible)
VisibilityHash += hash;
else if (frozenActor.Actor == null)
frozenActorsToRemove.Add(frozenActor);
}
foreach (var kvp in frozenActorsById)
{
var id = kvp.Key;
var hash = (int)id;
FrozenHash += hash;
dirtyFrozenActorIds.Clear();
var frozenActor = kvp.Value;
frozenActor.Tick();
if (dirtyFrozenActorIds.Contains(id))
frozenActor.UpdateVisibility();
foreach (var fa in frozenActorsToRemove)
Remove(fa);
if (frozenActor.Visible)
VisibilityHash += hash;
else if (frozenActor.Actor == null)
frozenActorsToRemove.Add(frozenActor);
}
dirtyFrozenActorIds.Clear();
foreach (var fa in frozenActorsToRemove)
Remove(fa);
});
}
void UpdateDirtyFrozenActorsFromDirtyBins()
{
// Check which bins on the map were dirtied due to changes in the shroud and gather the frozen actors whose
// footprint overlap with these bins.
var maxX = world.Map.MapSize.X / binSize + 1;
var maxY = world.Map.MapSize.Y / binSize + 1;
for (var y = 0; y < maxY; y++)
for (var y = 0; y < rows; y++)
{
for (var x = 0; x < maxX; x++)
for (var x = 0; x < cols; x++)
{
if (!dirtyBins[y * maxX + x])
if (!dirtyBins[y * cols + x])
continue;
var box = new Rectangle(x * binSize, y * binSize, binSize, binSize);
dirtyFrozenActorIds.UnionWith(partitionedFrozenActorIds.InBox(box));
}
@@ -295,11 +309,17 @@ namespace OpenRA.Traits
public virtual IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr)
{
return world.ScreenMap.FrozenActorsInBox(owner, wr.Viewport.TopLeft, wr.Viewport.BottomRight)
return world.ScreenMap.RenderableFrozenActorsInBox(owner, wr.Viewport.TopLeft, wr.Viewport.BottomRight)
.Where(f => f.Visible)
.SelectMany(ff => ff.Render(wr));
}
public IEnumerable<Rectangle> ScreenBounds(Actor self, WorldRenderer wr)
{
// Player-actor render traits don't require screen bounds
yield break;
}
public FrozenActor FromID(uint id)
{
FrozenActor fa;
@@ -308,5 +328,12 @@ namespace OpenRA.Traits
return fa;
}
public IEnumerable<FrozenActor> FrozenActorsInRegion(CellRegion region)
{
var tl = region.TopLeft;
var br = region.BottomRight;
return partitionedFrozenActorIds.InBox(Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y)).Select(FromID);
}
}
}

View File

@@ -17,6 +17,14 @@ namespace OpenRA.Traits
{
public class PlayerResourcesInfo : ITraitInfo, ILobbyOptions
{
[Translate]
[Desc("Descriptive label for the starting cash option in the lobby.")]
public readonly string DefaultCashDropdownLabel = "Starting Cash";
[Translate]
[Desc("Tooltip description for the starting cash option in the lobby.")]
public readonly string DefaultCashDropdownDescription = "Change the amount of cash that players start with";
[Desc("Starting cash options that are available in the lobby options.")]
public readonly int[] SelectableCash = { 2500, 5000, 10000, 20000 };
@@ -24,7 +32,13 @@ namespace OpenRA.Traits
public readonly int DefaultCash = 5000;
[Desc("Force the DefaultCash option by disabling changes in the lobby.")]
public readonly bool DefaultCashLocked = false;
public readonly bool DefaultCashDropdownLocked = false;
[Desc("Whether to display the DefaultCash option in the lobby.")]
public readonly bool DefaultCashDropdownVisible = true;
[Desc("Display order for the DefaultCash option.")]
public readonly int DefaultCashDropdownDisplayOrder = 0;
[Desc("Speech notification to play when the player does not have any funds.")]
public readonly string InsufficientFundsNotification = null;
@@ -37,7 +51,8 @@ namespace OpenRA.Traits
var startingCash = SelectableCash.ToDictionary(c => c.ToString(), c => "$" + c.ToString());
if (startingCash.Any())
yield return new LobbyOption("startingcash", "Starting Cash", new ReadOnlyDictionary<string, string>(startingCash), DefaultCash.ToString(), DefaultCashLocked);
yield return new LobbyOption("startingcash", DefaultCashDropdownLabel, DefaultCashDropdownDescription, DefaultCashDropdownVisible, DefaultCashDropdownDisplayOrder,
new ReadOnlyDictionary<string, string>(startingCash), DefaultCash.ToString(), DefaultCashDropdownLocked);
}
public object Create(ActorInitializer init) { return new PlayerResources(init.Self, this); }
@@ -155,11 +170,13 @@ namespace OpenRA.Traits
return true;
}
public void Tick(Actor self)
void ITick.Tick(Actor self)
{
ResourceCapacity = self.World.ActorsWithTrait<IStoreResources>()
.Where(a => a.Actor.Owner == owner)
.Sum(a => a.Trait.Capacity);
// PERF: Avoid LINQ.
ResourceCapacity = 0;
foreach (var tp in self.World.ActorsWithTrait<IStoreResources>())
if (tp.Actor.Owner == owner)
ResourceCapacity += tp.Trait.Capacity;
if (Resources > ResourceCapacity)
Resources = ResourceCapacity;

View File

@@ -17,22 +17,52 @@ namespace OpenRA.Traits
[Desc("Required for shroud and fog visibility checks. Add this to the player actor.")]
public class ShroudInfo : ITraitInfo, ILobbyOptions
{
[Translate]
[Desc("Descriptive label for the fog checkbox in the lobby.")]
public readonly string FogCheckboxLabel = "Fog of War";
[Translate]
[Desc("Tooltip description for the fog checkbox in the lobby.")]
public readonly string FogCheckboxDescription = "Line of sight is required to view enemy forces";
[Desc("Default value of the fog checkbox in the lobby.")]
public bool FogEnabled = true;
public readonly bool FogCheckboxEnabled = true;
[Desc("Prevent the fog enabled state from being changed in the lobby.")]
public bool FogLocked = false;
public readonly bool FogCheckboxLocked = false;
[Desc("Whether to display the fog checkbox in the lobby.")]
public readonly bool FogCheckboxVisible = true;
[Desc("Display order for the fog checkbox in the lobby.")]
public readonly int FogCheckboxDisplayOrder = 0;
[Translate]
[Desc("Descriptive label for the explored map checkbox in the lobby.")]
public readonly string ExploredMapCheckboxLabel = "Explored Map";
[Translate]
[Desc("Tooltip description for the explored map checkbox in the lobby.")]
public readonly string ExploredMapCheckboxDescription = "Initial map shroud is revealed";
[Desc("Default value of the explore map checkbox in the lobby.")]
public bool ExploredMapEnabled = false;
public readonly bool ExploredMapCheckboxEnabled = false;
[Desc("Prevent the explore map enabled state from being changed in the lobby.")]
public bool ExploredMapLocked = false;
public readonly bool ExploredMapCheckboxLocked = false;
[Desc("Whether to display the explore map checkbox in the lobby.")]
public readonly bool ExploredMapCheckboxVisible = true;
[Desc("Display order for the explore map checkbox in the lobby.")]
public readonly int ExploredMapCheckboxDisplayOrder = 0;
IEnumerable<LobbyOption> ILobbyOptions.LobbyOptions(Ruleset rules)
{
yield return new LobbyBooleanOption("explored", "Explored Map", ExploredMapEnabled, ExploredMapLocked);
yield return new LobbyBooleanOption("fog", "Fog of War", FogEnabled, FogLocked);
yield return new LobbyBooleanOption("explored", ExploredMapCheckboxLabel, ExploredMapCheckboxDescription,
ExploredMapCheckboxVisible, ExploredMapCheckboxDisplayOrder, ExploredMapCheckboxEnabled, ExploredMapCheckboxLocked);
yield return new LobbyBooleanOption("fog", FogCheckboxLabel, FogCheckboxDescription,
FogCheckboxVisible, FogCheckboxDisplayOrder, FogCheckboxEnabled, FogCheckboxLocked);
}
public object Create(ActorInitializer init) { return new Shroud(init.Self, this); }
@@ -118,9 +148,9 @@ namespace OpenRA.Traits
void INotifyCreated.Created(Actor self)
{
var gs = self.World.LobbyInfo.GlobalSettings;
fogEnabled = gs.OptionOrDefault("fog", info.FogEnabled);
fogEnabled = gs.OptionOrDefault("fog", info.FogCheckboxEnabled);
ExploreMapEnabled = gs.OptionOrDefault("explored", info.ExploredMapEnabled);
ExploreMapEnabled = gs.OptionOrDefault("explored", info.ExploredMapCheckboxEnabled);
if (ExploreMapEnabled)
self.World.AddFrameEndTask(w => ExploreAll());
}

View File

@@ -9,32 +9,33 @@
*/
#endregion
using System.Collections.Generic;
using System.Drawing;
using OpenRA.Graphics;
namespace OpenRA.Traits
{
[Desc("This actor is selectable. Defines bounds of selectable area, selection class and selection priority.")]
public class SelectableInfo : ITraitInfo
public class SelectableInfo : InteractableInfo
{
public readonly int Priority = 10;
[Desc("Bounds for the selectable area.")]
public readonly int[] Bounds = null;
[Desc("All units having the same selection class specified will be selected with select-by-type commands (e.g. double-click). "
+ "Defaults to the actor name when not defined or inherited.")]
public readonly string Class = null;
[VoiceReference] public readonly string Voice = "Select";
public object Create(ActorInitializer init) { return new Selectable(init.Self, this); }
public override object Create(ActorInitializer init) { return new Selectable(init.Self, this); }
}
public class Selectable
public class Selectable : Interactable
{
public readonly string Class = null;
public SelectableInfo Info;
public readonly SelectableInfo Info;
public Selectable(Actor self, SelectableInfo info)
: base(info)
{
Class = string.IsNullOrEmpty(info.Class) ? self.Info.Name : info.Class;
Info = info;

View File

@@ -15,7 +15,7 @@ using System.Linq;
namespace OpenRA.Traits
{
public enum TargetType { Invalid, Actor, Terrain, FrozenActor }
public enum TargetType : byte { Invalid, Actor, Terrain, FrozenActor }
public struct Target
{
public static readonly Target[] None = { };
@@ -25,14 +25,19 @@ namespace OpenRA.Traits
Actor actor;
FrozenActor frozen;
WPos pos;
CPos? cell;
int generation;
public static Target FromPos(WPos p) { return new Target { pos = p, type = TargetType.Terrain }; }
public static Target FromCell(World w, CPos c, SubCell subCell = SubCell.FullCell)
{
return new Target { pos = w.Map.CenterOfSubCell(c, subCell), type = TargetType.Terrain };
return new Target { pos = w.Map.CenterOfSubCell(c, subCell), cell = c, type = TargetType.Terrain };
}
/// <summary>
/// DEPRECATED: Use Order.Target instead.
/// This method is kept to maintain compatibility with legacy code that may not understand TargetType.FrozenActor.
/// </summary>
public static Target FromOrder(World w, Order o)
{
return o.TargetActor != null
@@ -191,5 +196,10 @@ namespace OpenRA.Traits
return "Invalid";
}
}
// Expose internal state for serialization by the orders code *only*
internal TargetType SerializableType { get { return type; } }
internal Actor SerializableActor { get { return actor; } }
internal CPos? SerializableCell { get { return cell; } }
}
}

View File

@@ -92,12 +92,24 @@ namespace OpenRA.Traits
}
}
[RequireExplicitImplementation]
public interface ITick { void Tick(Actor self); }
[RequireExplicitImplementation]
public interface ITickRender { void TickRender(WorldRenderer wr, Actor self); }
public interface IRender { IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr); }
public interface IRender
{
IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr);
IEnumerable<Rectangle> ScreenBounds(Actor self, WorldRenderer wr);
}
public interface IAutoSelectionSizeInfo : ITraitInfoInterface { }
public interface IAutoSelectionSize { int2 SelectionSize(Actor self); }
// TODO: Replace Rectangle with an int2[] polygon
public interface IMouseBounds { Rectangle MouseoverBounds(Actor self, WorldRenderer wr); }
public interface IMouseBoundsInfo : ITraitInfoInterface { }
public interface IAutoMouseBounds { Rectangle AutoMouseoverBounds(Actor self, WorldRenderer wr); }
// HACK: This provides a shim for legacy code until it can be rewritten
public interface IDecorationBounds { Rectangle DecorationBounds(Actor self, WorldRenderer wr); }
public interface IDecorationBoundsInfo : ITraitInfoInterface { }
public interface IIssueOrder
{
@@ -128,23 +140,20 @@ namespace OpenRA.Traits
public interface IResolveOrder { void ResolveOrder(Actor self, Order order); }
public interface IValidateOrder { bool OrderValidation(OrderManager orderManager, World world, int clientId, Order order); }
public interface IOrderVoice { string VoicePhraseForOrder(Actor self, Order order); }
[RequireExplicitImplementation]
public interface INotifyCreated { void Created(Actor self); }
[RequireExplicitImplementation]
public interface INotifyAddedToWorld { void AddedToWorld(Actor self); }
[RequireExplicitImplementation]
public interface INotifyRemovedFromWorld { void RemovedFromWorld(Actor self); }
public interface INotifyDamage { void Damaged(Actor self, AttackInfo e); }
public interface INotifyKilled { void Killed(Actor self, AttackInfo e); }
[RequireExplicitImplementation]
public interface INotifyActorDisposing { void Disposing(Actor self); }
public interface INotifyAppliedDamage { void AppliedDamage(Actor self, Actor damaged, AttackInfo e); }
public interface INotifyOwnerChanged { void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner); }
public interface INotifyEffectiveOwnerChanged { void OnEffectiveOwnerChanged(Actor self, Player oldEffectiveOwner, Player newEffectiveOwner); }
public interface ISeedableResource { void Seed(Actor self); }
public interface ISelectionDecorationsInfo : ITraitInfoInterface
{
int[] SelectionBoxBounds { get; }
}
public interface IVoiced
{
string VoiceSet { get; }
@@ -180,32 +189,11 @@ namespace OpenRA.Traits
}
public interface IDisabledTrait { bool IsTraitDisabled { get; } }
public interface IDisable { bool Disabled { get; } }
public interface IExplodeModifier { bool ShouldExplode(Actor self); }
public interface IHuskModifier { string HuskActor(Actor self); }
public interface IRadarSignature
{
IEnumerable<Pair<CPos, Color>> RadarSignatureCells(Actor self);
}
public interface IDefaultVisibilityInfo : ITraitInfoInterface { }
public interface IDefaultVisibility { bool IsVisible(Actor self, Player byPlayer); }
public interface IVisibilityModifier { bool IsVisible(Actor self, Player byPlayer); }
public interface IFogVisibilityModifier
{
bool IsVisible(Actor actor);
bool HasFogVisibility();
}
public interface IRadarColorModifier { Color RadarColorOverride(Actor self, Color color); }
public interface ITargetableCells
{
IEnumerable<Pair<CPos, SubCell>> TargetableCells();
}
public interface IOccupySpaceInfo : ITraitInfoInterface
{
IReadOnlyDictionary<CPos, SubCell> OccupiedCells(ActorInfo info, CPos location, SubCell subCell = SubCell.Any);
@@ -216,27 +204,7 @@ namespace OpenRA.Traits
{
WPos CenterPosition { get; }
CPos TopLeft { get; }
IEnumerable<Pair<CPos, SubCell>> OccupiedCells();
}
public static class IOccupySpaceExts
{
public static CPos NearestCellTo(this IOccupySpace ios, CPos other)
{
var nearest = ios.TopLeft;
var nearestDistance = int.MaxValue;
foreach (var cell in ios.OccupiedCells())
{
var dist = (other - cell.First).LengthSquared;
if (dist < nearestDistance)
{
nearest = cell.First;
nearestDistance = dist;
}
}
return nearest;
}
Pair<CPos, SubCell>[] OccupiedCells();
}
public enum SubCell { Invalid = int.MinValue, Any = int.MinValue / 2, FullCell = 0, First = 1 }
@@ -263,7 +231,15 @@ namespace OpenRA.Traits
IEnumerable<Actor> ActorsInBox(WPos a, WPos b);
}
public interface IRenderModifier { IEnumerable<IRenderable> ModifyRender(Actor self, WorldRenderer wr, IEnumerable<IRenderable> r); }
public interface IRenderModifier
{
IEnumerable<IRenderable> ModifyRender(Actor self, WorldRenderer wr, IEnumerable<IRenderable> r);
// HACK: This is here to support the WithShadow trait.
// That trait should be rewritten using standard techniques, and then this interface method removed
IEnumerable<Rectangle> ModifyScreenBounds(Actor self, WorldRenderer wr, IEnumerable<Rectangle> r);
}
public interface ILoadsPalettes { void LoadPalettes(WorldRenderer wr); }
public interface ILoadsPlayerPalettes { void LoadPlayerPalettes(WorldRenderer wr, string playerName, HSLColor playerColor, bool replaceExisting); }
public interface IPaletteModifier { void AdjustPalette(IReadOnlyDictionary<string, MutablePalette> b); }
@@ -288,24 +264,6 @@ namespace OpenRA.Traits
void SetVisualPosition(Actor self, WPos pos);
}
public interface IMoveInfo : ITraitInfoInterface { }
public interface IMove
{
Activity MoveTo(CPos cell, int nearEnough);
Activity MoveTo(CPos cell, Actor ignoreActor);
Activity MoveWithinRange(Target target, WDist range);
Activity MoveWithinRange(Target target, WDist minRange, WDist maxRange);
Activity MoveFollow(Actor self, Target target, WDist minRange, WDist maxRange);
Activity MoveIntoWorld(Actor self, CPos cell, SubCell subCell = SubCell.Any);
Activity MoveToTarget(Actor self, Target target);
Activity MoveIntoTarget(Actor self, Target target);
Activity VisualMove(Actor self, WPos fromPos, WPos toPos);
CPos NearestMoveableCell(CPos target);
bool IsMoving { get; set; }
bool IsMovingVertically { get; set; }
bool CanEnterTargetNow(Actor self, Target target);
}
[RequireExplicitImplementation]
public interface ITemporaryBlocker
{
@@ -332,9 +290,14 @@ namespace OpenRA.Traits
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1302:InterfaceNamesMustBeginWithI", Justification = "Not a real interface, but more like a tag.")]
public interface UsesInit<T> : ITraitInfo where T : IActorInit { }
[RequireExplicitImplementation]
public interface INotifySelected { void Selected(Actor self); }
[RequireExplicitImplementation]
public interface INotifySelection { void SelectionChanged(); }
public interface IWorldLoaded { void WorldLoaded(World w, WorldRenderer wr); }
[RequireExplicitImplementation]
public interface ICreatePlayers { void CreatePlayers(World w); }
public interface IBotInfo : ITraitInfoInterface
@@ -349,14 +312,28 @@ namespace OpenRA.Traits
IBotInfo Info { get; }
}
[RequireExplicitImplementation]
public interface IRenderOverlay { void Render(WorldRenderer wr); }
[RequireExplicitImplementation]
public interface INotifyBecomingIdle { void OnBecomingIdle(Actor self); }
[RequireExplicitImplementation]
public interface INotifyIdle { void TickIdle(Actor self); }
public interface IRenderAboveWorld { void RenderAboveWorld(Actor self, WorldRenderer wr); }
public interface IRenderShroud { void RenderShroud(Shroud shroud, WorldRenderer wr); }
public interface IRenderAboveShroud { IEnumerable<IRenderable> RenderAboveShroud(Actor self, WorldRenderer wr); }
public interface IRenderAboveShroudWhenSelected { IEnumerable<IRenderable> RenderAboveShroud(Actor self, WorldRenderer wr); }
public interface IRenderAboveShroud
{
IEnumerable<IRenderable> RenderAboveShroud(Actor self, WorldRenderer wr);
bool SpatiallyPartitionable { get; }
}
public interface IRenderAboveShroudWhenSelected
{
IEnumerable<IRenderable> RenderAboveShroud(Actor self, WorldRenderer wr);
bool SpatiallyPartitionable { get; }
}
public interface ITargetableInfo : ITraitInfoInterface
{
@@ -371,30 +348,13 @@ namespace OpenRA.Traits
bool RequiresForceFire { get; }
}
[RequireExplicitImplementation]
public interface ITargetablePositions
{
IEnumerable<WPos> TargetablePositions(Actor self);
}
public interface ILintPass { void Run(Action<string> emitError, Action<string> emitWarning, ModData modData); }
public interface ILintMapPass { void Run(Action<string> emitError, Action<string> emitWarning, Map map); }
public interface ILintRulesPass { void Run(Action<string> emitError, Action<string> emitWarning, Ruleset rules); }
public interface IObjectivesPanel
{
string PanelName { get; }
int ExitDelay { get; }
}
public interface INotifyObjectivesUpdated
{
void OnPlayerWon(Player winner);
void OnPlayerLost(Player loser);
void OnObjectiveAdded(Player player, int objectiveID);
void OnObjectiveCompleted(Player player, int objectiveID);
void OnObjectiveFailed(Player player, int objectiveID);
}
[RequireExplicitImplementation]
public interface IGameOver { void GameOver(World world); }
public interface IWarhead
@@ -418,17 +378,24 @@ namespace OpenRA.Traits
{
public readonly string Id;
public readonly string Name;
public readonly string Description;
public readonly IReadOnlyDictionary<string, string> Values;
public readonly string DefaultValue;
public readonly bool Locked;
public readonly bool IsLocked;
public readonly bool IsVisible;
public readonly int DisplayOrder;
public LobbyOption(string id, string name, IReadOnlyDictionary<string, string> values, string defaultValue, bool locked)
public LobbyOption(string id, string name, string description, bool visible, int displayorder,
IReadOnlyDictionary<string, string> values, string defaultValue, bool locked)
{
Id = id;
Name = name;
Description = description;
IsVisible = visible;
DisplayOrder = displayorder;
Values = values;
DefaultValue = defaultValue;
Locked = locked;
IsLocked = locked;
}
public virtual string ValueChangedMessage(string playerName, string newValue)
@@ -445,8 +412,8 @@ namespace OpenRA.Traits
{ false.ToString(), "disabled" }
};
public LobbyBooleanOption(string id, string name, bool defaultValue, bool locked)
: base(id, name, new ReadOnlyDictionary<string, string>(BoolValues), defaultValue.ToString(), locked) { }
public LobbyBooleanOption(string id, string name, string description, bool visible, int displayorder, bool defaultValue, bool locked)
: base(id, name, description, visible, displayorder, new ReadOnlyDictionary<string, string>(BoolValues), defaultValue.ToString(), locked) { }
public override string ValueChangedMessage(string playerName, string newValue)
{

View File

@@ -9,28 +9,17 @@
*/
#endregion
using OpenRA.Activities;
using OpenRA.Mods.Common.Traits;
namespace OpenRA.Mods.Common.Activities
namespace OpenRA.Traits
{
public class FlyCircleTimed : FlyCircle
[Desc("Enables visualization commands. Attach this to the world actor.")]
public class DebugVisualizationsInfo : TraitInfo<DebugVisualizations> { }
public class DebugVisualizations
{
int remainingTicks;
public FlyCircleTimed(Actor self, int ticks) : base(self)
{
remainingTicks = ticks;
}
public override Activity Tick(Actor self)
{
if (IsCanceled || remainingTicks-- == 0)
return NextActivity;
base.Tick(self);
return this;
}
public bool CombatGeometry;
public bool RenderGeometry;
public bool ScreenMap;
public bool DepthBuffer;
public bool ActorTags;
}
}

View File

@@ -13,11 +13,32 @@ using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Effects;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA.Traits
{
public struct ActorBoundsPair : IEquatable<ActorBoundsPair>
{
public readonly Actor Actor;
// TODO: Replace this with an int2[] polygon
public readonly Rectangle Bounds;
public ActorBoundsPair(Actor actor, Rectangle bounds) { Actor = actor; Bounds = bounds; }
public static bool operator ==(ActorBoundsPair me, ActorBoundsPair other) { return me.Actor == other.Actor && Equals(me.Bounds, other.Bounds); }
public static bool operator !=(ActorBoundsPair me, ActorBoundsPair other) { return !(me == other); }
public override int GetHashCode() { return Actor.GetHashCode() ^ Bounds.GetHashCode(); }
public bool Equals(ActorBoundsPair other) { return this == other; }
public override bool Equals(object obj) { return obj is ActorBoundsPair && Equals((ActorBoundsPair)obj); }
public override string ToString() { return "{0}->{1}".F(Actor.Info.Name, Bounds.GetType().Name); }
}
public class ScreenMapInfo : ITraitInfo
{
[Desc("Size of partition bins (world pixels)")]
@@ -31,8 +52,21 @@ namespace OpenRA.Traits
static readonly IEnumerable<FrozenActor> NoFrozenActors = new FrozenActor[0];
readonly Func<FrozenActor, bool> frozenActorIsValid = fa => fa.IsValid;
readonly Func<Actor, bool> actorIsInWorld = a => a.IsInWorld;
readonly Cache<Player, SpatiallyPartitioned<FrozenActor>> partitionedFrozenActors;
readonly SpatiallyPartitioned<Actor> partitionedActors;
readonly Func<Actor, ActorBoundsPair> selectActorAndBounds;
readonly Cache<Player, SpatiallyPartitioned<FrozenActor>> partitionedMouseFrozenActors;
readonly SpatiallyPartitioned<Actor> partitionedMouseActors;
readonly Dictionary<Actor, ActorBoundsPair> partitionedMouseActorBounds = new Dictionary<Actor, ActorBoundsPair>();
readonly Cache<Player, SpatiallyPartitioned<FrozenActor>> partitionedRenderableFrozenActors;
readonly SpatiallyPartitioned<Actor> partitionedRenderableActors;
readonly SpatiallyPartitioned<IEffect> partitionedRenderableEffects;
// Updates are done in one pass to ensure all bound changes have been applied
readonly HashSet<Actor> addOrUpdateActors = new HashSet<Actor>();
readonly HashSet<Actor> removeActors = new HashSet<Actor>();
readonly Cache<Player, HashSet<FrozenActor>> addOrUpdateFrozenActors;
readonly Cache<Player, HashSet<FrozenActor>> removeFrozenActors;
WorldRenderer worldRenderer;
public ScreenMap(World world, ScreenMapInfo info)
@@ -40,74 +74,114 @@ namespace OpenRA.Traits
var size = world.Map.Grid.TileSize;
var width = world.Map.MapSize.X * size.Width;
var height = world.Map.MapSize.Y * size.Height;
partitionedFrozenActors = new Cache<Player, SpatiallyPartitioned<FrozenActor>>(
partitionedMouseFrozenActors = new Cache<Player, SpatiallyPartitioned<FrozenActor>>(
_ => new SpatiallyPartitioned<FrozenActor>(width, height, info.BinSize));
partitionedActors = new SpatiallyPartitioned<Actor>(width, height, info.BinSize);
partitionedMouseActors = new SpatiallyPartitioned<Actor>(width, height, info.BinSize);
selectActorAndBounds = a => partitionedMouseActorBounds[a];
partitionedRenderableFrozenActors = new Cache<Player, SpatiallyPartitioned<FrozenActor>>(
_ => new SpatiallyPartitioned<FrozenActor>(width, height, info.BinSize));
partitionedRenderableActors = new SpatiallyPartitioned<Actor>(width, height, info.BinSize);
partitionedRenderableEffects = new SpatiallyPartitioned<IEffect>(width, height, info.BinSize);
addOrUpdateFrozenActors = new Cache<Player, HashSet<FrozenActor>>(_ => new HashSet<FrozenActor>());
removeFrozenActors = new Cache<Player, HashSet<FrozenActor>>(_ => new HashSet<FrozenActor>());
}
public void WorldLoaded(World w, WorldRenderer wr) { worldRenderer = wr; }
Rectangle FrozenActorBounds(FrozenActor fa)
public void AddOrUpdate(Player viewer, FrozenActor fa)
{
var pos = worldRenderer.ScreenPxPosition(fa.CenterPosition);
var bounds = fa.Bounds;
bounds.Offset(pos.X, pos.Y);
return bounds;
}
if (removeFrozenActors[viewer].Contains(fa))
removeFrozenActors[viewer].Remove(fa);
Rectangle ActorBounds(Actor a)
{
var pos = worldRenderer.ScreenPxPosition(a.CenterPosition);
var bounds = a.Bounds;
bounds.Offset(pos.X, pos.Y);
return bounds;
}
public void Add(Player viewer, FrozenActor fa)
{
partitionedFrozenActors[viewer].Add(fa, FrozenActorBounds(fa));
addOrUpdateFrozenActors[viewer].Add(fa);
}
public void Remove(Player viewer, FrozenActor fa)
{
partitionedFrozenActors[viewer].Remove(fa);
removeFrozenActors[viewer].Add(fa);
}
public void Add(Actor a)
public void AddOrUpdate(Actor a)
{
partitionedActors.Add(a, ActorBounds(a));
}
if (removeActors.Contains(a))
removeActors.Remove(a);
public void Update(Actor a)
{
partitionedActors.Update(a, ActorBounds(a));
addOrUpdateActors.Add(a);
}
public void Remove(Actor a)
{
partitionedActors.Remove(a);
removeActors.Add(a);
}
public IEnumerable<FrozenActor> FrozenActorsAt(Player viewer, int2 worldPx)
public void Add(IEffect effect, WPos position, Size size)
{
var screenPos = worldRenderer.ScreenPxPosition(position);
var screenWidth = Math.Abs(size.Width);
var screenHeight = Math.Abs(size.Height);
var screenBounds = new Rectangle(screenPos.X - screenWidth / 2, screenPos.Y - screenHeight / 2, screenWidth, screenHeight);
if (ValidBounds(screenBounds))
partitionedRenderableEffects.Add(effect, screenBounds);
}
public void Add(IEffect effect, WPos position, Sprite sprite)
{
var size = new Size((int)sprite.Size.X, (int)sprite.Size.Y);
Add(effect, position, size);
}
public void Update(IEffect effect, WPos position, Size size)
{
Remove(effect);
Add(effect, position, size);
}
public void Update(IEffect effect, WPos position, Sprite sprite)
{
var size = new Size((int)sprite.Size.X, (int)sprite.Size.Y);
Update(effect, position, size);
}
public void Remove(IEffect effect)
{
partitionedRenderableEffects.Remove(effect);
}
static bool ValidBounds(Rectangle bounds)
{
return bounds.Width > 0 && bounds.Height > 0;
}
public IEnumerable<FrozenActor> FrozenActorsAtMouse(Player viewer, int2 worldPx)
{
if (viewer == null)
return NoFrozenActors;
return partitionedFrozenActors[viewer].At(worldPx).Where(frozenActorIsValid);
return partitionedMouseFrozenActors[viewer]
.At(worldPx)
.Where(frozenActorIsValid)
.Where(x => x.MouseBounds.Contains(worldPx));
}
public IEnumerable<FrozenActor> FrozenActorsAt(Player viewer, MouseInput mi)
public IEnumerable<FrozenActor> FrozenActorsAtMouse(Player viewer, MouseInput mi)
{
return FrozenActorsAt(viewer, worldRenderer.Viewport.ViewToWorldPx(mi.Location));
return FrozenActorsAtMouse(viewer, worldRenderer.Viewport.ViewToWorldPx(mi.Location));
}
public IEnumerable<Actor> ActorsAt(int2 worldPx)
public IEnumerable<ActorBoundsPair> ActorsAtMouse(int2 worldPx)
{
return partitionedActors.At(worldPx).Where(actorIsInWorld);
return partitionedMouseActors.At(worldPx)
.Where(actorIsInWorld)
.Select(selectActorAndBounds)
.Where(x => x.Bounds.Contains(worldPx));
}
public IEnumerable<Actor> ActorsAt(MouseInput mi)
public IEnumerable<ActorBoundsPair> ActorsAtMouse(MouseInput mi)
{
return ActorsAt(worldRenderer.Viewport.ViewToWorldPx(mi.Location));
return ActorsAtMouse(worldRenderer.Viewport.ViewToWorldPx(mi.Location));
}
static Rectangle RectWithCorners(int2 a, int2 b)
@@ -115,26 +189,156 @@ namespace OpenRA.Traits
return 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(int2 a, int2 b)
public IEnumerable<ActorBoundsPair> ActorsInMouseBox(int2 a, int2 b)
{
return ActorsInBox(RectWithCorners(a, b));
return ActorsInMouseBox(RectWithCorners(a, b));
}
public IEnumerable<Actor> ActorsInBox(Rectangle r)
public IEnumerable<ActorBoundsPair> ActorsInMouseBox(Rectangle r)
{
return partitionedActors.InBox(r).Where(actorIsInWorld);
return partitionedMouseActors.InBox(r)
.Where(actorIsInWorld)
.Select(selectActorAndBounds)
.Where(x => r.IntersectsWith(x.Bounds));
}
public IEnumerable<FrozenActor> FrozenActorsInBox(Player p, int2 a, int2 b)
public IEnumerable<Actor> RenderableActorsInBox(int2 a, int2 b)
{
return FrozenActorsInBox(p, RectWithCorners(a, b));
return partitionedRenderableActors.InBox(RectWithCorners(a, b)).Where(actorIsInWorld);
}
public IEnumerable<FrozenActor> FrozenActorsInBox(Player p, Rectangle r)
public IEnumerable<IEffect> RenderableEffectsInBox(int2 a, int2 b)
{
return partitionedRenderableEffects.InBox(RectWithCorners(a, b));
}
public IEnumerable<FrozenActor> RenderableFrozenActorsInBox(Player p, int2 a, int2 b)
{
if (p == null)
return NoFrozenActors;
return partitionedFrozenActors[p].InBox(r).Where(frozenActorIsValid);
return partitionedRenderableFrozenActors[p].InBox(RectWithCorners(a, b)).Where(frozenActorIsValid);
}
Rectangle AggregateBounds(IEnumerable<Rectangle> screenBounds)
{
if (!screenBounds.Any())
return Rectangle.Empty;
var bounds = screenBounds.First();
foreach (var b in screenBounds.Skip(1))
bounds = Rectangle.Union(bounds, b);
return bounds;
}
Rectangle AggregateBounds(IEnumerable<int2> vertices)
{
if (!vertices.Any())
return Rectangle.Empty;
var first = vertices.First();
var rect = new Rectangle(first.X, first.Y, 0, 0);
foreach (var v in vertices.Skip(1))
rect = Rectangle.Union(rect, new Rectangle(v.X, v.Y, 0, 0));
return rect;
}
public void Tick()
{
foreach (var a in addOrUpdateActors)
{
var mouseBounds = a.MouseBounds(worldRenderer);
if (!mouseBounds.Size.IsEmpty)
{
if (partitionedMouseActors.Contains(a))
partitionedMouseActors.Update(a, mouseBounds);
else
partitionedMouseActors.Add(a, mouseBounds);
partitionedMouseActorBounds[a] = new ActorBoundsPair(a, mouseBounds);
}
else
partitionedMouseActors.Remove(a);
var screenBounds = AggregateBounds(a.ScreenBounds(worldRenderer));
if (!screenBounds.Size.IsEmpty)
{
if (partitionedRenderableActors.Contains(a))
partitionedRenderableActors.Update(a, screenBounds);
else
partitionedRenderableActors.Add(a, screenBounds);
}
else
partitionedRenderableActors.Remove(a);
}
foreach (var a in removeActors)
{
partitionedMouseActors.Remove(a);
partitionedMouseActorBounds.Remove(a);
partitionedRenderableActors.Remove(a);
}
addOrUpdateActors.Clear();
removeActors.Clear();
foreach (var kv in addOrUpdateFrozenActors)
{
foreach (var fa in kv.Value)
{
var mouseBounds = fa.MouseBounds;
if (!mouseBounds.Size.IsEmpty)
{
if (partitionedMouseFrozenActors[kv.Key].Contains(fa))
partitionedMouseFrozenActors[kv.Key].Update(fa, mouseBounds);
else
partitionedMouseFrozenActors[kv.Key].Add(fa, mouseBounds);
}
else
partitionedMouseFrozenActors[kv.Key].Remove(fa);
var screenBounds = AggregateBounds(fa.ScreenBounds);
if (!screenBounds.Size.IsEmpty)
{
if (partitionedRenderableFrozenActors[kv.Key].Contains(fa))
partitionedRenderableFrozenActors[kv.Key].Update(fa, screenBounds);
else
partitionedRenderableFrozenActors[kv.Key].Add(fa, screenBounds);
}
else
partitionedRenderableFrozenActors[kv.Key].Remove(fa);
}
kv.Value.Clear();
}
foreach (var kv in removeFrozenActors)
{
foreach (var fa in kv.Value)
{
partitionedMouseFrozenActors[kv.Key].Remove(fa);
partitionedRenderableFrozenActors[kv.Key].Remove(fa);
}
kv.Value.Clear();
}
}
public IEnumerable<Rectangle> RenderBounds(Player viewer)
{
var bounds = partitionedRenderableActors.ItemBounds
.Concat(partitionedRenderableEffects.ItemBounds);
return viewer != null ? bounds.Concat(partitionedRenderableFrozenActors[viewer].ItemBounds) : bounds;
}
public IEnumerable<Rectangle> MouseBounds(Player viewer)
{
var bounds = partitionedMouseActors.ItemBounds;
return viewer != null ? bounds.Concat(partitionedMouseFrozenActors[viewer].ItemBounds) : bounds;
}
}
}

View File

@@ -36,9 +36,9 @@ namespace OpenRA.Traits
this.info = info;
}
public void WorldLoaded(World w, WorldRenderer wr) { worldRenderer = wr; }
void IWorldLoaded.WorldLoaded(World w, WorldRenderer wr) { worldRenderer = wr; }
public void Tick(Actor self)
void ITick.Tick(Actor self)
{
if (shakeEffects.Any())
{

View File

@@ -128,29 +128,6 @@ namespace OpenRA.Widgets
/// <param name="e">Key input data</param>
public static bool HandleKeyPress(KeyInput e)
{
if (e.Event == KeyInputEvent.Down)
{
var hk = Hotkey.FromKeyInput(e);
if (hk == Game.Settings.Keys.DevReloadChromeKey)
{
ChromeProvider.Initialize(Game.ModData);
return true;
}
if (hk == Game.Settings.Keys.HideUserInterfaceKey)
{
Root.Visible ^= true;
return true;
}
if (hk == Game.Settings.Keys.TakeScreenshotKey)
{
Game.TakeScreenshot = true;
return true;
}
}
if (KeyboardFocusWidget != null)
return KeyboardFocusWidget.HandleKeyPressOuter(e);
@@ -307,8 +284,15 @@ namespace OpenRA.Widgets
// PERF: Avoid LINQ.
var bounds = EventBounds;
foreach (var child in Children)
{
if (child.IsVisible())
bounds = Rectangle.Union(bounds, child.GetEventBounds());
{
var childBounds = child.GetEventBounds();
if (childBounds != Rectangle.Empty)
bounds = Rectangle.Union(bounds, childBounds);
}
}
return bounds;
}

View File

@@ -203,29 +203,29 @@ namespace OpenRA.Widgets
for (var i = 0; i < lines.Count; i++)
{
var line = lines[i];
var m = font.Measure(line);
if (m.X <= width)
if (font.Measure(line).X <= width)
continue;
var bestSpaceIndex = -1;
var start = line.Length - 1;
while (m.X > width)
// Scan forwards until we find the last word that fits
// This guarantees a small bound on the amount of string we need to search before a linebreak
var start = 0;
while (true)
{
var spaceIndex = line.LastIndexOf(' ', start);
var spaceIndex = line.IndexOf(' ', start);
if (spaceIndex == -1)
break;
bestSpaceIndex = spaceIndex;
start = spaceIndex - 1;
m = font.Measure(line.Substring(0, spaceIndex));
var fragmentWidth = font.Measure(line.Substring(0, spaceIndex)).X;
if (fragmentWidth > width)
break;
start = spaceIndex + 1;
}
if (bestSpaceIndex != -1)
if (start > 0)
{
lines[i] = line.Substring(0, bestSpaceIndex);
lines.Insert(i + 1, line.Substring(bestSpaceIndex + 1));
lines[i] = line.Substring(0, start - 1);
lines.Insert(i + 1, line.Substring(start));
}
}

View File

@@ -30,6 +30,7 @@ namespace OpenRA
internal readonly TraitDictionary TraitDict = new TraitDictionary();
readonly SortedDictionary<uint, Actor> actors = new SortedDictionary<uint, Actor>();
readonly List<IEffect> effects = new List<IEffect>();
readonly List<IEffect> unpartitionedEffects = new List<IEffect>();
readonly List<ISync> syncedEffects = new List<ISync>();
readonly Queue<Action<World>> frameEndActions = new Queue<Action<World>>();
@@ -76,7 +77,7 @@ namespace OpenRA
set { renderPlayer = value; }
}
public bool FogObscures(Actor a) { return RenderPlayer != null && !RenderPlayer.CanViewActor(a); }
public bool FogObscures(Actor a) { return RenderPlayer != null && !a.CanBeViewedByPlayer(RenderPlayer); }
public bool FogObscures(CPos p) { return RenderPlayer != null && !RenderPlayer.Shroud.IsVisible(p); }
public bool FogObscures(WPos pos) { return RenderPlayer != null && !RenderPlayer.Shroud.IsVisible(pos); }
public bool ShroudObscures(CPos p) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(p); }
@@ -130,7 +131,7 @@ namespace OpenRA
}
}
public Selection Selection = new Selection();
public readonly Selection Selection = new Selection();
public void CancelInputMode() { OrderGenerator = new UnitOrderGenerator(); }
@@ -190,9 +191,7 @@ namespace OpenRA
{
ActorMap.AddInfluence(self, ios);
ActorMap.AddPosition(self, ios);
if (!self.Bounds.Size.IsEmpty)
ScreenMap.Add(self);
ScreenMap.AddOrUpdate(self);
}
public void UpdateMaps(Actor self, IOccupySpace ios)
@@ -200,9 +199,7 @@ namespace OpenRA
if (!self.IsInWorld)
return;
if (!self.Bounds.Size.IsEmpty)
ScreenMap.Update(self);
ScreenMap.AddOrUpdate(self);
ActorMap.UpdatePosition(self, ios);
}
@@ -210,9 +207,7 @@ namespace OpenRA
{
ActorMap.RemoveInfluence(self, ios);
ActorMap.RemovePosition(self, ios);
if (!self.Bounds.Size.IsEmpty)
ScreenMap.Remove(self);
ScreenMap.Remove(self);
}
public void LoadComplete(WorldRenderer wr)
@@ -285,6 +280,11 @@ namespace OpenRA
public void Add(IEffect e)
{
effects.Add(e);
var sp = e as ISpatiallyPartitionable;
if (sp == null)
unpartitionedEffects.Add(e);
var se = e as ISync;
if (se != null)
syncedEffects.Add(se);
@@ -293,6 +293,11 @@ namespace OpenRA
public void Remove(IEffect e)
{
effects.Remove(e);
var sp = e as ISpatiallyPartitionable;
if (sp == null)
unpartitionedEffects.Remove(e);
var se = e as ISync;
if (se != null)
syncedEffects.Remove(se);
@@ -301,6 +306,7 @@ namespace OpenRA
public void RemoveAll(Predicate<IEffect> predicate)
{
effects.RemoveAll(predicate);
unpartitionedEffects.RemoveAll(e => predicate((IEffect)e));
syncedEffects.RemoveAll(e => predicate((IEffect)e));
}
@@ -347,6 +353,7 @@ namespace OpenRA
ActorsWithTrait<ITick>().DoTimed(x => x.Trait.Tick(x.Actor), "Trait");
effects.DoTimed(e => e.Tick(this), "Effect");
ScreenMap.Tick();
}
while (frameEndActions.Count != 0)
@@ -361,6 +368,7 @@ namespace OpenRA
public IEnumerable<Actor> Actors { get { return actors.Values; } }
public IEnumerable<IEffect> Effects { get { return effects; } }
public IEnumerable<IEffect> UnpartitionedEffects { get { return unpartitionedEffects; } }
public IEnumerable<ISync> SyncedEffects { get { return syncedEffects; } }
public Actor GetActorById(uint actorId)
@@ -391,7 +399,7 @@ namespace OpenRA
// Hash fields marked with the ISync interface.
foreach (var actor in ActorsHavingTrait<ISync>())
foreach (var syncHash in actor.SyncHashes)
ret += n++ * (int)(1 + actor.ActorID) * syncHash.Hash;
ret += n++ * (int)(1 + actor.ActorID) * syncHash.Hash();
// Hash game state relevant effects such as projectiles.
foreach (var sync in SyncedEffects)
@@ -467,6 +475,6 @@ namespace OpenRA
public bool Equals(TraitPair<T> other) { return this == other; }
public override bool Equals(object obj) { return obj is TraitPair<T> && Equals((TraitPair<T>)obj); }
public override string ToString() { return "{0}->{1}".F(Actor.Info.Name, Trait.GetType().Name); }
public override string ToString() { return Actor.Info.Name + "->" + Trait.GetType().Name; }
}
}

View File

@@ -9,6 +9,7 @@
*/
#endregion
using System.Linq;
using OpenRA.Activities;
using OpenRA.Mods.Cnc.Traits;
using OpenRA.Mods.Common.Activities;
@@ -21,14 +22,14 @@ namespace OpenRA.Mods.Cnc.Activities
{
readonly Actor target;
readonly Infiltrates infiltrates;
readonly Cloak cloak;
readonly INotifyInfiltration[] notifiers;
public Infiltrate(Actor self, Actor target, Infiltrates infiltrate)
: base(self, target, infiltrate.Info.EnterBehaviour)
{
this.target = target;
infiltrates = infiltrate;
cloak = self.TraitOrDefault<Cloak>();
notifiers = self.TraitsImplementing<INotifyInfiltration>().ToArray();
}
protected override void OnInside(Actor self)
@@ -40,11 +41,11 @@ namespace OpenRA.Mods.Cnc.Activities
if (!infiltrates.Info.ValidStances.HasStance(stance))
return;
if (cloak != null && cloak.Info.UncloakOn.HasFlag(UncloakType.Infiltrate))
cloak.Uncloak();
foreach (var ini in notifiers)
ini.Infiltrating(self);
foreach (var t in target.TraitsImplementing<INotifyInfiltrated>())
t.Infiltrated(target, self);
t.Infiltrated(target, self, infiltrates.Info.Types);
var exp = self.Owner.PlayerActor.TraitOrDefault<PlayerExperience>();
if (exp != null)

View File

@@ -96,7 +96,7 @@ namespace OpenRA.Mods.Cnc.Activities
var pool = ammoPools.FirstOrDefault(x => x.Info.Name == info.AmmoPoolName);
if (pool == null)
return;
pool.TakeAmmo();
pool.TakeAmmo(self, 1);
}
self.World.AddFrameEndTask(

View File

@@ -1,149 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using OpenRA.Effects;
using OpenRA.Graphics;
using OpenRA.Mods.Cnc.Traits;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Effects
{
[Desc("Attach this to actors to render pictograms while hidden.")]
class GpsDotInfo : ITraitInfo
{
[Desc("Sprite collection for symbols.")]
public readonly string Image = "gpsdot";
[Desc("Sprite used for this actor.")]
[SequenceReference("Image")] public readonly string String = "Infantry";
[PaletteReference(true)] public readonly string IndicatorPalettePrefix = "player";
public object Create(ActorInitializer init)
{
return new GpsDot(init.Self, this);
}
}
class GpsDot : IEffect, IEffectAboveShroud
{
readonly Actor self;
readonly GpsDotInfo info;
readonly Animation anim;
readonly PlayerDictionary<DotState> dotStates;
readonly Lazy<HiddenUnderFog> huf;
readonly Lazy<FrozenUnderFog> fuf;
readonly Lazy<Disguise> disguise;
readonly Lazy<Cloak> cloak;
readonly Cache<Player, FrozenActorLayer> frozen;
class DotState
{
public readonly GpsWatcher Gps;
public bool IsTargetable;
public bool ShouldRender;
public DotState(GpsWatcher gps)
{
Gps = gps;
}
}
public GpsDot(Actor self, GpsDotInfo info)
{
this.self = self;
this.info = info;
anim = new Animation(self.World, info.Image);
anim.PlayRepeating(info.String);
self.World.AddFrameEndTask(w => w.Add(this));
huf = Exts.Lazy(() => self.TraitOrDefault<HiddenUnderFog>());
fuf = Exts.Lazy(() => self.TraitOrDefault<FrozenUnderFog>());
disguise = Exts.Lazy(() => self.TraitOrDefault<Disguise>());
cloak = Exts.Lazy(() => self.TraitOrDefault<Cloak>());
frozen = new Cache<Player, FrozenActorLayer>(p => p.PlayerActor.Trait<FrozenActorLayer>());
dotStates = new PlayerDictionary<DotState>(self.World, player => new DotState(player.PlayerActor.Trait<GpsWatcher>()));
}
public bool IsDotVisible(Player toPlayer)
{
return dotStates[toPlayer].IsTargetable;
}
bool IsTargetableBy(Player toPlayer, out bool shouldRenderIndicator)
{
shouldRenderIndicator = false;
if (cloak.Value != null && cloak.Value.Cloaked)
return false;
if (disguise.Value != null && disguise.Value.Disguised)
return false;
if (huf.Value != null && !huf.Value.IsVisible(self, toPlayer)
&& toPlayer.Shroud.IsExplored(self.CenterPosition))
{
var f1 = FrozenActorForPlayer(toPlayer);
shouldRenderIndicator = f1 == null || !f1.HasRenderables;
return true;
}
if (fuf.Value == null)
return false;
var f2 = FrozenActorForPlayer(toPlayer);
if (f2 == null)
return false;
shouldRenderIndicator = !f2.HasRenderables;
return f2.Visible && !f2.Shrouded && toPlayer.Shroud.IsExplored(self.CenterPosition);
}
FrozenActor FrozenActorForPlayer(Player player)
{
return frozen[player].FromID(self.ActorID);
}
void IEffect.Tick(World world)
{
if (self.Disposed)
world.AddFrameEndTask(w => w.Remove(this));
for (var playerIndex = 0; playerIndex < dotStates.Count; playerIndex++)
{
var state = dotStates[playerIndex];
var shouldRender = false;
if (self.IsInWorld && !self.IsDead)
state.IsTargetable = (state.Gps.Granted || state.Gps.GrantedAllies) && IsTargetableBy(world.Players[playerIndex], out shouldRender);
state.ShouldRender = state.IsTargetable && shouldRender;
}
}
IEnumerable<IRenderable> IEffect.Render(WorldRenderer wr) { return SpriteRenderable.None; }
IEnumerable<IRenderable> IEffectAboveShroud.RenderAboveShroud(WorldRenderer wr)
{
if (self.World.RenderPlayer == null || !dotStates[self.World.RenderPlayer].ShouldRender || self.Disposed)
return SpriteRenderable.None;
var palette = wr.Palette(info.IndicatorPalettePrefix + self.Owner.InternalName);
return anim.Render(self.CenterPosition, palette);
}
}
}

View File

@@ -0,0 +1,112 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Effects;
using OpenRA.Graphics;
using OpenRA.Mods.Cnc.Traits;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Effects
{
class GpsDotEffect : IEffect, IEffectAboveShroud
{
readonly Actor actor;
readonly GpsDotInfo info;
readonly Animation anim;
readonly PlayerDictionary<DotState> dotStates;
readonly IDefaultVisibility visibility;
readonly IVisibilityModifier[] visibilityModifiers;
class DotState
{
public readonly GpsWatcher Watcher;
public readonly FrozenActor FrozenActor;
public bool Visible;
public DotState(Actor a, GpsWatcher watcher, FrozenActorLayer frozenLayer)
{
Watcher = watcher;
if (frozenLayer != null)
FrozenActor = frozenLayer.FromID(a.ActorID);
}
}
public GpsDotEffect(Actor actor, GpsDotInfo info)
{
this.actor = actor;
this.info = info;
anim = new Animation(actor.World, info.Image);
anim.PlayRepeating(info.String);
visibility = actor.Trait<IDefaultVisibility>();
visibilityModifiers = actor.TraitsImplementing<IVisibilityModifier>().ToArray();
dotStates = new PlayerDictionary<DotState>(actor.World,
p => new DotState(actor, p.PlayerActor.Trait<GpsWatcher>(), p.PlayerActor.TraitOrDefault<FrozenActorLayer>()));
}
bool ShouldRender(DotState state, Player toPlayer)
{
// Hide the indicator if no watchers are available
if (!state.Watcher.Granted && !state.Watcher.GrantedAllies)
return false;
// Hide the indicator if a frozen actor portrait is visible
if (state.FrozenActor != null && state.FrozenActor.HasRenderables)
return false;
// Hide the indicator if the unit appears to be owned by an allied player
if (actor.EffectiveOwner != null && actor.EffectiveOwner.Owner != null &&
toPlayer.IsAlliedWith(actor.EffectiveOwner.Owner))
return false;
// Hide indicator if the actor wouldn't otherwise be visible if there wasn't fog
foreach (var visibilityModifier in visibilityModifiers)
if (!visibilityModifier.IsVisible(actor, toPlayer))
return false;
// Hide the indicator behind shroud
if (!toPlayer.Shroud.IsExplored(actor.CenterPosition))
return false;
return !visibility.IsVisible(actor, toPlayer) && toPlayer.Shroud.IsExplored(actor.CenterPosition);
}
void IEffect.Tick(World world)
{
for (var playerIndex = 0; playerIndex < dotStates.Count; playerIndex++)
{
var state = dotStates[playerIndex];
state.Visible = ShouldRender(state, world.Players[playerIndex]);
}
}
IEnumerable<IRenderable> IEffect.Render(WorldRenderer wr)
{
return SpriteRenderable.None;
}
IEnumerable<IRenderable> IEffectAboveShroud.RenderAboveShroud(WorldRenderer wr)
{
if (actor.World.RenderPlayer == null || !dotStates[actor.World.RenderPlayer].Visible)
return SpriteRenderable.None;
var effectiveOwner = actor.EffectiveOwner != null && actor.EffectiveOwner.Owner != null ?
actor.EffectiveOwner.Owner : actor.Owner;
var palette = wr.Palette(info.IndicatorPalettePrefix + effectiveOwner.InternalName);
return anim.Render(actor.CenterPosition, palette);
}
}
}

View File

@@ -16,7 +16,7 @@ using OpenRA.Mods.Cnc.Traits;
namespace OpenRA.Mods.Cnc.Effects
{
class GpsSatellite : IEffect
class GpsSatellite : IEffect, ISpatiallyPartitionable
{
readonly Player launcher;
readonly Animation anim;
@@ -34,6 +34,7 @@ namespace OpenRA.Mods.Cnc.Effects
anim = new Animation(world, image);
anim.PlayRepeating(sequence);
world.ScreenMap.Add(this, pos, anim.Image);
}
public void Tick(World world)
@@ -45,8 +46,10 @@ namespace OpenRA.Mods.Cnc.Effects
{
var watcher = launcher.PlayerActor.Trait<GpsWatcher>();
watcher.ReachedOrbit(launcher);
world.AddFrameEndTask(w => w.Remove(this));
world.AddFrameEndTask(w => { w.Remove(this); w.ScreenMap.Remove(this); });
}
world.ScreenMap.Update(this, pos, anim.Image);
}
public IEnumerable<IRenderable> Render(WorldRenderer wr)

View File

@@ -16,7 +16,7 @@ using OpenRA.Mods.Cnc.Traits;
namespace OpenRA.Mods.Cnc.Effects
{
class SatelliteLaunch : IEffect
class SatelliteLaunch : IEffect, ISpatiallyPartitionable
{
readonly GpsPowerInfo info;
readonly Actor launcher;
@@ -31,19 +31,21 @@ namespace OpenRA.Mods.Cnc.Effects
doors = new Animation(launcher.World, info.DoorImage);
doors.PlayThen(info.DoorSequence,
() => launcher.World.AddFrameEndTask(w => w.Remove(this)));
() => launcher.World.AddFrameEndTask(w => { w.Remove(this); w.ScreenMap.Remove(this); }));
pos = launcher.CenterPosition;
launcher.World.ScreenMap.Add(this, pos, doors.Image);
}
public void Tick(World world)
{
doors.Tick();
world.ScreenMap.Update(this, pos, doors.Image);
if (++frame == 19)
{
var palette = info.SatellitePaletteIsPlayerPalette ? info.SatellitePalette + launcher.Owner.InternalName : info.SatellitePalette;
world.AddFrameEndTask(w => w.Add(new GpsSatellite(world, pos, info.SatelliteImage, info.SatelliteSequence, palette, info.RevealDelay * 25, launcher.Owner)));
world.AddFrameEndTask(w => w.Add(new GpsSatellite(world, pos, info.SatelliteImage, info.SatelliteSequence, palette, info.RevealDelay, launcher.Owner)));
}
}

View File

@@ -28,15 +28,16 @@ namespace OpenRA.Mods.Cnc.FileFormats
while (s.Peek() > -1)
{
var count = s.ReadInt32();
var chars = new List<char>();
for (var i = 0; i < count; i++)
{
var chars = new List<char>();
byte c;
// Read filename
while ((c = s.ReadUInt8()) != 0)
chars.Add((char)c);
entries.Add(new string(chars.ToArray()));
chars.Clear();
// Skip comment
while ((c = s.ReadUInt8()) != 0) { }

View File

@@ -26,14 +26,15 @@ namespace OpenRA.Mods.Cnc.FileFormats
var reader = new BinaryReader(s);
var count = reader.ReadInt32();
Entries = new string[count];
var chars = new List<char>();
for (var i = 0; i < count; i++)
{
var chars = new List<char>();
char c;
while ((c = reader.ReadChar()) != 0)
chars.Add(c);
Entries[i] = new string(chars.ToArray());
chars.Clear();
}
}

View File

@@ -150,7 +150,7 @@ namespace OpenRA.Mods.Cnc.FileSystem
{
var decrypted = fish.Decrypt(h);
var ms = new MemoryStream();
var ms = new MemoryStream(decrypted.Length * 4);
var writer = new BinaryWriter(ms);
foreach (var t in decrypted)
writer.Write(t);

View File

@@ -10,6 +10,7 @@
#endregion
using System;
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Cnc.FileFormats;
@@ -125,5 +126,33 @@ namespace OpenRA.Mods.Cnc.Graphics
return ret;
}
public Rectangle AggregateBounds
{
get
{
// Corner offsets
var ix = new uint[] { 0, 0, 0, 0, 3, 3, 3, 3 };
var iy = new uint[] { 1, 1, 4, 4, 1, 1, 4, 4 };
var iz = new uint[] { 2, 5, 2, 5, 2, 5, 2, 5 };
// Calculate the smallest sphere that covers the model limbs
var rSquared = 0f;
for (var f = 0U; f < frames; f++)
{
var bounds = Bounds(f);
for (var i = 0; i < 8; i++)
{
var x = bounds[ix[i]];
var y = bounds[iy[i]];
var z = bounds[iz[i]];
rSquared = Math.Max(rSquared, x * x + y * y + z * z);
}
}
var r = (int)Math.Sqrt(rSquared) + 1;
return Rectangle.FromLTRB(-r, -r, r, r);
}
}
}
}

View File

@@ -41,6 +41,7 @@
<OutputPath>bin\Debug\</OutputPath>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>5</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<DebugSymbols>true</DebugSymbols>
@@ -64,9 +65,10 @@
</ItemGroup>
<ItemGroup>
<Compile Include="CncLoadScreen.cs" />
<Compile Include="Traits\AttackPopupTurreted.cs" />
<Compile Include="Traits\Attack\AttackPopupTurreted.cs" />
<Compile Include="Traits\Buildings\ProductionAirdrop.cs" />
<Compile Include="Traits\Render\WithGunboatBody.cs" />
<Compile Include="Traits\Render\WithEmbeddedTurretSpriteBody.cs" />
<Compile Include="Traits\Render\WithCargo.cs" />
<Compile Include="Traits\Render\WithDeliveryAnimation.cs" />
<Compile Include="Traits\Render\WithReloadingSpriteTurret.cs" />
@@ -98,7 +100,7 @@
<Compile Include="Activities\LayMines.cs" />
<Compile Include="Activities\Leap.cs" />
<Compile Include="Activities\Teleport.cs" />
<Compile Include="Effects\GpsDot.cs" />
<Compile Include="Effects\GpsDotEffect.cs" />
<Compile Include="Effects\GpsSatellite.cs" />
<Compile Include="Effects\SatelliteLaunch.cs" />
<Compile Include="Projectiles\TeslaZap.cs" />
@@ -154,6 +156,9 @@
<Compile Include="FileFormats\HvaReader.cs" />
<Compile Include="FileFormats\VxlReader.cs" />
<Compile Include="Traits\World\VoxelNormalsPalette.cs" />
<Compile Include="Traits\TDGunboat.cs" />
<Compile Include="Traits\Attack\AttackTDGunboatTurreted.cs" />
<Compile Include="Traits\GpsDot.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj">
@@ -210,4 +215,4 @@
<Message Text="DEBUG TargetName: $(TargetName)"/>
-->
</Target>
</Project>
</Project>

View File

@@ -10,6 +10,7 @@
#endregion
using System.Linq;
using OpenRA.Mods.Cnc.Traits.Render;
using OpenRA.Mods.Common.Traits;
using OpenRA.Mods.Common.Traits.Render;
using OpenRA.Traits;
@@ -17,7 +18,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Traits
{
[Desc("Actor's turret rises from the ground before attacking.")]
class AttackPopupTurretedInfo : AttackTurretedInfo, Requires<BuildingInfo>, Requires<WithTurretedSpriteBodyInfo>
class AttackPopupTurretedInfo : AttackTurretedInfo, Requires<BuildingInfo>, Requires<WithEmbeddedTurretSpriteBodyInfo>
{
[Desc("How many game ticks should pass before closing the actor's turret.")]
public int CloseDelay = 125;

View File

@@ -0,0 +1,73 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Linq;
using OpenRA.Activities;
using OpenRA.Mods.Common.Traits;
using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Traits
{
[Desc("Actor has a visual turret used to attack.")]
public class AttackTDGunboatTurretedInfo : AttackTurretedInfo, Requires<TDGunboatInfo>
{
public override object Create(ActorInitializer init) { return new AttackTDGunboatTurreted(init.Self, this); }
}
public class AttackTDGunboatTurreted : AttackTurreted
{
public AttackTDGunboatTurreted(Actor self, AttackTDGunboatTurretedInfo info)
: base(self, info) { }
public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove, bool forceAttack)
{
return new AttackTDGunboatTurretedActivity(self, newTarget, allowMove, forceAttack);
}
class AttackTDGunboatTurretedActivity : Activity
{
readonly AttackTDGunboatTurreted attack;
readonly Target target;
readonly bool forceAttack;
bool hasTicked;
public AttackTDGunboatTurretedActivity(Actor self, Target target, bool allowMove, bool forceAttack)
{
attack = self.Trait<AttackTDGunboatTurreted>();
this.target = target;
this.forceAttack = forceAttack;
}
public override Activity Tick(Actor self)
{
if (IsCanceled || !target.IsValidFor(self))
return NextActivity;
if (attack.IsTraitDisabled)
return this;
var weapon = attack.ChooseArmamentsForTarget(target, forceAttack).FirstOrDefault();
if (weapon != null)
{
// Check that AttackTDGunboatTurreted hasn't cancelled the target by modifying attack.Target
// Having both this and AttackTDGunboatTurreted modify that field is a horrible hack.
if (hasTicked && attack.Target.Type == TargetType.Invalid)
return NextActivity;
attack.Target = target;
hasTicked = true;
}
return NextActivity;
}
}
}
}

View File

@@ -51,7 +51,7 @@ namespace OpenRA.Mods.Cnc.Traits
charges = info.MaxCharges;
}
public void Tick(Actor self)
void ITick.Tick(Actor self)
{
if (--timeToRecharge <= 0)
charges = info.MaxCharges;

View File

@@ -12,6 +12,7 @@
using System.Collections.Generic;
using OpenRA.Mods.Common;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Traits
@@ -39,7 +40,7 @@ namespace OpenRA.Mods.Cnc.Traits
faction = init.Contains<FactionInit>() ? init.Get<FactionInit, string>() : init.Self.Owner.Faction.InternalName;
}
public void UnitProducedByOther(Actor self, Actor producer, Actor produced)
public void UnitProducedByOther(Actor self, Actor producer, Actor produced, string productionType)
{
// No recursive cloning!
if (producer.Owner != self.Owner || producer.Info.HasTraitInfo<ClonesProducedUnitsInfo>())
@@ -49,7 +50,13 @@ namespace OpenRA.Mods.Cnc.Traits
if (ci == null || !info.CloneableTypes.Overlaps(ci.Types))
return;
production.Produce(self, produced.Info, faction);
var inits = new TypeDictionary
{
new OwnerInit(self.Owner),
new FactionInit(BuildableInfo.GetInitialFaction(produced.Info, faction))
};
production.Produce(self, produced.Info, productionType, inits);
}
}
}

View File

@@ -9,6 +9,7 @@
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Activities;
using OpenRA.Mods.Common;
@@ -38,8 +39,11 @@ namespace OpenRA.Mods.Cnc.Traits
this.info = info;
}
public override bool Produce(Actor self, ActorInfo producee, string factionVariant)
public override bool Produce(Actor self, ActorInfo producee, string productionType, TypeDictionary inits)
{
if (IsTraitDisabled || IsTraitPaused)
return false;
var owner = self.Owner;
var aircraftInfo = self.World.Map.Rules.Actors[info.ActorType].TraitInfo<AircraftInfo>();
@@ -79,7 +83,7 @@ namespace OpenRA.Mods.Cnc.Traits
foreach (var cargo in self.TraitsImplementing<INotifyDelivery>())
cargo.Delivered(self);
self.World.AddFrameEndTask(ww => DoProduction(self, producee, exit, factionVariant));
self.World.AddFrameEndTask(ww => DoProduction(self, producee, exit, productionType, inits));
Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", info.ReadyAudio, self.Owner.Faction.InternalName);
}));

View File

@@ -61,7 +61,7 @@ namespace OpenRA.Mods.Cnc.Traits
chronosphere = init.Get<ChronoshiftChronosphereInit, Actor>();
}
public void Tick(Actor self)
void ITick.Tick(Actor self)
{
if (!info.ReturnToOrigin || ReturnTicks <= 0)
return;
@@ -74,7 +74,7 @@ namespace OpenRA.Mods.Cnc.Traits
}
}
public void Created(Actor self)
void INotifyCreated.Created(Actor self)
{
iPositionable = self.TraitOrDefault<IPositionable>();
}
@@ -101,8 +101,14 @@ namespace OpenRA.Mods.Cnc.Traits
}
// Set up return-to-origin info
Origin = self.Location;
ReturnTicks = duration;
// If this actor is already counting down to return to
// an existing location then we shouldn't override it
if (ReturnTicks <= 0)
{
Origin = self.Location;
ReturnTicks = duration;
}
this.duration = duration;
this.chronosphere = chronosphere;
this.killCargo = killCargo;

View File

@@ -64,7 +64,11 @@ namespace OpenRA.Mods.Cnc.Traits
{
None = 0,
Attack = 1,
Damaged = 2
Damaged = 2,
Unload = 4,
Infiltrate = 8,
Demolish = 16,
Move = 32
}
[Desc("Provides access to the disguise command, which makes the actor appear to be another player's actor.")]
@@ -76,16 +80,31 @@ namespace OpenRA.Mods.Cnc.Traits
[Desc("The condition to grant to self while disguised.")]
public readonly string DisguisedCondition = null;
[Desc("Triggers which cause the actor to drop it's disguise. Possible values: None, Attack, Damaged.")]
[Desc("What diplomatic stances can this actor disguise as.")]
public readonly Stance ValidStances = Stance.Ally | Stance.Neutral | Stance.Enemy;
[Desc("Target types of actors that this actor disguise as.")]
public readonly HashSet<string> TargetTypes = new HashSet<string> { "Disguise" };
[Desc("Triggers which cause the actor to drop it's disguise. Possible values: None, Attack, Damaged,",
"Unload, Infiltrate, Demolish, Move.")]
public readonly RevealDisguiseType RevealDisguiseOn = RevealDisguiseType.Attack;
[Desc("Conditions to grant when disguised as specified actor.",
"A dictionary of [actor id]: [condition].")]
public readonly Dictionary<string, string> DisguisedAsConditions = new Dictionary<string, string>();
[GrantedConditionReference]
public IEnumerable<string> LinterConditions { get { return DisguisedAsConditions.Values; } }
public object Create(ActorInitializer init) { return new Disguise(init.Self, this); }
}
class Disguise : INotifyCreated, IEffectiveOwner, IIssueOrder, IResolveOrder, IOrderVoice, IRadarColorModifier, INotifyAttack, INotifyDamage
class Disguise : INotifyCreated, IEffectiveOwner, IIssueOrder, IResolveOrder, IOrderVoice, IRadarColorModifier, INotifyAttack,
INotifyDamage, INotifyUnload, INotifyDemolition, INotifyInfiltration, ITick
{
public ActorInfo AsActor { get; private set; }
public Player AsPlayer { get; private set; }
public string AsSprite { get; private set; }
public ITooltipInfo AsTooltipInfo { get; private set; }
public bool Disguised { get { return AsPlayer != null; } }
@@ -96,11 +115,15 @@ namespace OpenRA.Mods.Cnc.Traits
ConditionManager conditionManager;
int disguisedToken = ConditionManager.InvalidConditionToken;
int disguisedAsToken = ConditionManager.InvalidConditionToken;
CPos? lastPos;
public Disguise(Actor self, DisguiseInfo info)
{
this.self = self;
this.info = info;
AsActor = self.Info;
}
void INotifyCreated.Created(Actor self)
@@ -108,37 +131,41 @@ namespace OpenRA.Mods.Cnc.Traits
conditionManager = self.TraitOrDefault<ConditionManager>();
}
public IEnumerable<IOrderTargeter> Orders
IEnumerable<IOrderTargeter> IIssueOrder.Orders
{
get
{
yield return new TargetTypeOrderTargeter(new HashSet<string> { "Disguise" }, "Disguise", 7, "ability", true, true) { ForceAttack = false };
yield return new DisguiseOrderTargeter(info);
}
}
public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
Order IIssueOrder.IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{
if (order.OrderID == "Disguise")
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
return new Order(order.OrderID, self, target, queued);
return null;
}
public void ResolveOrder(Actor self, Order order)
void IResolveOrder.ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "Disguise")
{
var target = order.TargetActor != self && order.TargetActor.IsInWorld ? order.TargetActor : null;
DisguiseAs(target);
var target = order.Target;
if (target.Type == TargetType.Actor)
DisguiseAs((target.Actor != self && target.Actor.IsInWorld) ? target.Actor : null);
if (target.Type == TargetType.FrozenActor)
DisguiseAs(target.FrozenActor.Info, target.FrozenActor.Owner);
}
}
public string VoicePhraseForOrder(Actor self, Order order)
string IOrderVoice.VoicePhraseForOrder(Actor self, Order order)
{
return order.OrderString == "Disguise" ? info.Voice : null;
}
public Color RadarColorOverride(Actor self, Color color)
Color IRadarColorModifier.RadarColorOverride(Actor self, Color color)
{
if (!Disguised || self.Owner.IsAlliedWith(self.World.RenderPlayer))
return color;
@@ -148,8 +175,9 @@ namespace OpenRA.Mods.Cnc.Traits
public void DisguiseAs(Actor target)
{
var oldDisguiseSetting = Disguised;
var oldEffectiveActor = AsActor;
var oldEffectiveOwner = AsPlayer;
var oldDisguiseSetting = Disguised;
if (target != null)
{
@@ -158,15 +186,15 @@ namespace OpenRA.Mods.Cnc.Traits
var targetDisguise = target.TraitOrDefault<Disguise>();
if (targetDisguise != null && targetDisguise.Disguised)
{
AsSprite = targetDisguise.AsSprite;
AsPlayer = targetDisguise.AsPlayer;
AsActor = targetDisguise.AsActor;
AsTooltipInfo = targetDisguise.AsTooltipInfo;
}
else
{
AsSprite = target.Trait<RenderSprites>().GetImage(target);
var tooltip = target.TraitsImplementing<ITooltip>().FirstOrDefault();
AsPlayer = tooltip.Owner;
AsActor = target.Info;
AsTooltipInfo = tooltip.TooltipInfo;
}
}
@@ -174,36 +202,49 @@ namespace OpenRA.Mods.Cnc.Traits
{
AsTooltipInfo = null;
AsPlayer = null;
AsSprite = null;
AsActor = self.Info;
}
HandleDisguise(oldEffectiveOwner, oldDisguiseSetting);
HandleDisguise(oldEffectiveActor, oldEffectiveOwner, oldDisguiseSetting);
}
public void DisguiseAs(ActorInfo actorInfo, Player newOwner)
{
var oldDisguiseSetting = Disguised;
var oldEffectiveActor = AsActor;
var oldEffectiveOwner = AsPlayer;
var oldDisguiseSetting = Disguised;
var renderSprites = actorInfo.TraitInfoOrDefault<RenderSpritesInfo>();
AsSprite = renderSprites == null ? null : renderSprites.GetImage(actorInfo, self.World.Map.Rules.Sequences, newOwner.Faction.InternalName);
AsPlayer = newOwner;
AsActor = actorInfo;
AsTooltipInfo = actorInfo.TraitInfos<TooltipInfo>().FirstOrDefault();
HandleDisguise(oldEffectiveOwner, oldDisguiseSetting);
HandleDisguise(oldEffectiveActor, oldEffectiveOwner, oldDisguiseSetting);
}
void HandleDisguise(Player oldEffectiveOwner, bool oldDisguiseSetting)
void HandleDisguise(ActorInfo oldEffectiveActor, Player oldEffectiveOwner, bool oldDisguiseSetting)
{
foreach (var t in self.TraitsImplementing<INotifyEffectiveOwnerChanged>())
t.OnEffectiveOwnerChanged(self, oldEffectiveOwner, AsPlayer);
if (Disguised != oldDisguiseSetting && conditionManager != null)
if (conditionManager != null)
{
if (Disguised && disguisedToken == ConditionManager.InvalidConditionToken && !string.IsNullOrEmpty(info.DisguisedCondition))
disguisedToken = conditionManager.GrantCondition(self, info.DisguisedCondition);
else if (!Disguised && disguisedToken != ConditionManager.InvalidConditionToken)
disguisedToken = conditionManager.RevokeCondition(self, disguisedToken);
if (Disguised != oldDisguiseSetting)
{
if (Disguised && disguisedToken == ConditionManager.InvalidConditionToken && !string.IsNullOrEmpty(info.DisguisedCondition))
disguisedToken = conditionManager.GrantCondition(self, info.DisguisedCondition);
else if (!Disguised && disguisedToken != ConditionManager.InvalidConditionToken)
disguisedToken = conditionManager.RevokeCondition(self, disguisedToken);
}
if (AsActor != oldEffectiveActor)
{
if (disguisedAsToken != ConditionManager.InvalidConditionToken)
disguisedAsToken = conditionManager.RevokeCondition(self, disguisedAsToken);
string disguisedAsCondition;
if (info.DisguisedAsConditions.TryGetValue(AsActor.Name, out disguisedAsCondition))
disguisedAsToken = conditionManager.GrantCondition(self, disguisedAsCondition);
}
}
}
@@ -220,5 +261,63 @@ namespace OpenRA.Mods.Cnc.Traits
if (info.RevealDisguiseOn.HasFlag(RevealDisguiseType.Damaged) && e.Damage.Value > 0)
DisguiseAs(null);
}
void INotifyUnload.Unloading(Actor self)
{
if (info.RevealDisguiseOn.HasFlag(RevealDisguiseType.Unload))
DisguiseAs(null);
}
void INotifyDemolition.Demolishing(Actor self)
{
if (info.RevealDisguiseOn.HasFlag(RevealDisguiseType.Demolish))
DisguiseAs(null);
}
void INotifyInfiltration.Infiltrating(Actor self)
{
if (info.RevealDisguiseOn.HasFlag(RevealDisguiseType.Infiltrate))
DisguiseAs(null);
}
void ITick.Tick(Actor self)
{
if (info.RevealDisguiseOn.HasFlag(RevealDisguiseType.Move) && lastPos != null && lastPos.Value != self.Location)
DisguiseAs(null);
lastPos = self.Location;
}
}
class DisguiseOrderTargeter : UnitOrderTargeter
{
readonly DisguiseInfo info;
public DisguiseOrderTargeter(DisguiseInfo info)
: base("Disguise", 7, "ability", true, true)
{
this.info = info;
ForceAttack = false;
}
public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
{
var stance = self.Owner.Stances[target.Owner];
if (!info.ValidStances.HasStance(stance))
return false;
return info.TargetTypes.Overlaps(target.GetAllTargetTypes());
}
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
{
var stance = self.Owner.Stances[target.Owner];
if (!info.ValidStances.HasStance(stance))
return false;
return info.TargetTypes.Overlaps(target.Info.TraitInfos<ITargetableInfo>().SelectMany(ti => ti.GetTargetTypes()));
}
}
}

View File

@@ -103,7 +103,7 @@ namespace OpenRA.Mods.Cnc.Traits
return !active;
}
public override void AddedToWorld(Actor self)
protected override void AddedToWorld(Actor self)
{
base.AddedToWorld(self);
blockedPositions = info.Tiles(self.Location);

View File

@@ -69,7 +69,7 @@ namespace OpenRA.Mods.Cnc.Traits
ActOnFrozenActorsForAllPlayers(Refresh);
}
public void Disposing(Actor self)
void INotifyActorDisposing.Disposing(Actor self)
{
ActOnFrozenActorsForAllPlayers(Remove);
}

View File

@@ -0,0 +1,53 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using OpenRA.Graphics;
using OpenRA.Mods.Cnc.Effects;
using OpenRA.Traits;
namespace OpenRA.Mods.Cnc.Traits
{
[Desc("Show an indicator revealing the actor underneath the fog when a GPSWatcher is activated.")]
class GpsDotInfo : ITraitInfo
{
[Desc("Sprite collection for symbols.")]
public readonly string Image = "gpsdot";
[Desc("Sprite used for this actor.")]
[SequenceReference("Image")] public readonly string String = "Infantry";
[PaletteReference(true)] public readonly string IndicatorPalettePrefix = "player";
public object Create(ActorInitializer init) { return new GpsDot(this); }
}
class GpsDot : INotifyAddedToWorld, INotifyRemovedFromWorld
{
readonly GpsDotInfo info;
GpsDotEffect effect;
public GpsDot(GpsDotInfo info)
{
this.info = info;
}
void INotifyAddedToWorld.AddedToWorld(Actor self)
{
effect = new GpsDotEffect(self, info);
self.World.AddFrameEndTask(w => w.Add(effect));
}
void INotifyRemovedFromWorld.RemovedFromWorld(Actor self)
{
self.World.AddFrameEndTask(w => w.Remove(effect));
}
}
}

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