Compare commits

...

342 Commits

Author SHA1 Message Date
Matthias Mailänder
86a714a588 Enable itch app auto updates on Windows. 2020-09-06 19:48:10 +02:00
ABrandau
c01c39954a Update Harpy Rotor 2020-09-01 18:57:43 +02:00
Matthias Mailänder
c48eb572e3 Remove MuzzleSplitFacings and expose MuzzleSequence to testing. 2020-09-01 18:53:43 +02:00
Paul Chote
b03ab1212f Remove install-core target. 2020-09-01 18:48:54 +02:00
Paul Chote
8a9b5e7e01 Split a separate "compat" macOS package that uses the system mono. 2020-09-01 18:48:54 +02:00
Paul Chote
9b90e4f25a Remove Coverity references. 2020-09-01 18:48:54 +02:00
Paul Chote
8a4401bdcb Fix Appveyor GeoIP download. 2020-09-01 18:48:54 +02:00
Paul Chote
d52e90cf23 Improve .travis.yml compatibility for non-OpenRA deploys. 2020-09-01 18:48:54 +02:00
Paul Chote
14ef0a7740 Remove markdown/html pages from windows install. 2020-09-01 18:48:54 +02:00
Paul Chote
c1f79b348a Generate platform-specific artwork from common source files. 2020-09-01 18:48:54 +02:00
Paul Chote
62166a50d9 Move itch upload script. 2020-09-01 18:48:54 +02:00
Brent Gardner
283b330403 Workaround for GLES 2.0 hardware 2020-09-01 18:00:51 +02:00
teinarss
2cf6b74295 Refactoring on GetEventBounds in Widget 2020-09-01 17:56:12 +02:00
abcdefg30
1a9f707d18 Remove the last reference to DedicatedLoop 2020-08-30 18:12:48 +02:00
Aigamo
06a1c88e86 Replace 0x7FFFFFFF with int.MaxValue 2020-08-29 12:37:20 +01:00
ycanardeau
f358b566b1 Replace NetFrameNumber >= 1 with GameStarted 2020-08-29 12:37:20 +01:00
reaperrr
15fc27d142 Use cached selected in SelectionDecorationsBase 2020-08-28 12:24:07 +02:00
reaperrr
ad20597d74 Cache hue picker sprite in HueSliderWidget 2020-08-27 21:17:37 +02:00
reaperrr
6d409a7c97 Cache indicator sprite in ResourceBarWidget at initialization 2020-08-27 21:17:37 +02:00
reaperrr
36d5ae5421 Cache SupportPowersWidget offsets at initialization
As well as overlay font.
2020-08-27 21:17:37 +02:00
tovl
db9744ea7f Let TS aircraft turn slower when circling. 2020-08-27 21:15:38 +02:00
tovl
5e62fe86fc Add IdleSpeed to aircraft trait. 2020-08-27 21:15:38 +02:00
Paul Chote
6cfa27c33b Replace per-color font caches with tinted rendering. 2020-08-24 18:38:08 +02:00
Taryn Hill
a405969199 docs: link to docs.openra.net instead of wiki.openra.net in readme for generated trait docs 2020-08-23 11:20:02 +02:00
Paul Praet
9a6f3b4c05 Reset Ready status of players in Lobby when options change
Addresses #11274
2020-08-23 00:05:55 +03:00
Matthias Mailänder
7be059a79b Download our cached version after they blocked Travis CI... 2020-08-22 13:06:08 +02:00
reaperrr
29b55de042 Cache rectangles and font in ProductionTabsWidget 2020-08-21 18:06:18 +02:00
reaperrr
235fb19aa8 Cache overlay traits in ProductionPaletteWidget
Instead of looking this up every Draw tick,
cache and update it only when a non-null new
CurrentQueue is set (as the overlays can only change
at that time).
2020-08-21 18:06:18 +02:00
reaperrr
c0f54fa4fc Cache offsets in ProductionPaletteWidget
At least those that never change.
2020-08-21 18:06:18 +02:00
reaperrr
cdc216aca0 Fix airborne husk target types
Doesn't really make a difference, since it only matters
for effect warheads and those already could target both
air and ground actors, but GroundActor was still wrong.
2020-08-20 20:46:58 +02:00
reaperrr
4505053618 Simplify CreateEffectWarhead code
Simplified and streamlined code,
based on past feedback and suggestions.

Note: The new methods will move to
Warhead later, once they're used by more
than one warhead.
2020-08-20 20:46:58 +02:00
abcdefg30
e0d53126d6 Remove the Light Tank husk 2020-08-20 19:17:39 +02:00
abcdefg30
12ff1dd14c Add RAGL balance changes 2020-08-20 19:17:39 +02:00
abcdefg30
50db3152f6 Fix the windows uninstaller name 2020-08-20 17:33:48 +02:00
teinarss
9c4fd0e3d3 Use Null-Propagation Operator 2020-08-19 18:11:07 +01:00
teinarss
8d27d22100 Use discard syntax 2020-08-19 18:11:07 +01:00
teinarss
27f1a7ab27 Use out var syntax 2020-08-19 18:11:07 +01:00
teinarss
d52e4793fe Refactor classes to structs 2020-08-19 11:54:29 +02:00
teinarss
544ac6cb33 Fix crash after entering manage content 2020-08-19 11:40:43 +02:00
Paul Chote
06fbc1a6cf Hide TraitInfo.InstanceName from FieldLoader. 2020-08-17 20:13:32 +02:00
abcdefg30
edab10e6a6 Make the Phase Transport uncloak during repair by mechanics 2020-08-16 21:01:16 +02:00
abcdefg30
dd99fc93e4 Uncloak during resupply when "UncloakOn: Dock" is defined 2020-08-16 21:01:16 +02:00
abcdefg30
dbe824d4e5 Correct "offseted" to "offset" 2020-08-16 15:02:56 +02:00
Paul Chote
6e73d7f5c2 Tidy MapEditorLogic ctor. 2020-08-16 14:17:45 +02:00
Paul Chote
960056d300 Fix mod switcher icon handling. 2020-08-16 14:17:45 +02:00
Paul Chote
3efac3287e Fix Session.Deserialize error handling. 2020-08-16 14:17:45 +02:00
Paul Chote
c4b4a8c8a5 Fix incorrect ramp fill in Map.Resize. 2020-08-16 14:17:45 +02:00
Paul Chote
b833f033bf Remove redundant check from LabelWithHighlightWidget. 2020-08-16 14:17:45 +02:00
Paul Chote
ad75e2be89 Remove redundant check from SupportPowerInstance. 2020-08-16 14:17:45 +02:00
Orb
7ee4fbeb0d New Money Settings 2020-08-15 21:02:01 +01:00
Paul Chote
9886f0ca9a Fix Harvester crash when multiple resource renderers are used. 2020-08-15 18:43:40 +02:00
Paul Chote
46cf56d6ff Remove editor-specific resource rendering.
Mods must manually move their *ResourceRenderer definitions from
World onto BaseWorld to restore resource rendering in the editor.
2020-08-15 18:43:40 +02:00
Matthias Mailänder
e7af295b5e Allow news per mod. 2020-08-15 16:21:21 +01:00
Matthias Mailänder
9d179d9a1a Initialize the font only once and make it configurable. 2020-08-15 16:13:21 +01:00
Matthias Mailänder
15010f9567 Fix production palette ignoring yaml font overrides. 2020-08-15 16:13:21 +01:00
Oliver Brakmann
a7f4f6c1cf Use LeaveMapAtClosestEdge for scripted MiGs on Intervention 2020-08-15 13:38:57 +01:00
Oliver Brakmann
3eeb677f14 Add LeaveMapAtClosestEdge idle behaviour for Aircraft 2020-08-15 13:38:57 +01:00
Paul Chote
ef69a3de66 Use nameof() in PaletteReference. 2020-08-15 13:41:45 +02:00
abcdefg30
0aa5e07252 Send the 'minefieldStart' along with a 'PlaceMinefield' order 2020-08-15 11:34:00 +01:00
abcdefg30
07d58337f1 Let order generators account for selection changes 2020-08-15 11:34:00 +01:00
abcdefg30
b5e3f25418 Fix CashTrickler crashing without IOccupiesSpace 2020-08-15 11:21:39 +01:00
teinarss
19b02875c7 Use Tuple syntax 2020-08-15 10:37:10 +01:00
Taryn Hill
8a74f6ea18 change whitespace-only lines to empty lines in Lua scripts 2020-08-14 15:08:14 +02:00
abcdefg30
a847f3eafa Fix actors not yet in the world improperly updating power state 2020-08-14 11:46:38 +02:00
Curtis Shmyr
a751f074e7 Added actor parameter to Lua UnloadPassenger 2020-08-10 10:38:35 +02:00
Paul Chote
75cb5c2166 Convert turret facings to WAngle relative to the body. 2020-08-09 19:43:53 +02:00
Paul Chote
70a86bed7a Optimize WRot negation.
The conjugate of a quaternion just negates the x/y/z components, so
there is no need to recalculate from scratch and throw away precision
by forcing a quat->euler->quat round trip.
2020-08-09 19:43:53 +02:00
dnqbob
f67f8ed05e WithLandingCraftAnimation can stop on all movement 2020-08-09 13:22:56 +02:00
Mustafa Alperen Seki
1ae53220d6 Add CurrentMuzzleFacing to FireClusterWarhead. 2020-08-09 13:18:39 +02:00
Matthias Mailänder
c546cb552e Make the fonts configurable. 2020-08-09 13:14:32 +02:00
Matthias Mailänder
10f8836d7b Fix a null reference exception. 2020-08-09 13:14:32 +02:00
dnqbob
7ecd4124ce Make "RepairableNear" public, helpful for modder 2020-08-08 13:20:39 +02:00
abcdefg30
54cd77be8e Add Tiberium near the transformable tree in cnc64gdi01 2020-08-05 11:45:11 +02:00
Mustafa Alperen Seki
43388cb7fc Don't check HasAdequateRefineryCount if no RefineryTypes defined. 2020-08-05 11:29:22 +02:00
Mustafa Alperen Seki
4cc5104fde Unhardcode AI's MinimumRefineryCount numbers. 2020-08-05 11:29:22 +02:00
Curtis Shmyr
d519cabae3 Add actor experience to the Lua API 2020-08-03 18:35:50 +02:00
abcdefg30
9852e29835 Fix subs targeting naval structures by default 2020-08-03 17:50:36 +02:00
Matthias Mailänder
3a427c3630 Add a sequence reference attribute to label fallbacks. 2020-08-02 22:15:13 +02:00
Matthias Mailänder
336656e8f7 Remove superflous warning as null is a valid value here. 2020-08-02 22:15:13 +02:00
Matthias Mailänder
06ad9666e8 Replace burns with more modular and testable trait combinations. 2020-08-02 22:15:13 +02:00
Matthias Mailänder
728e0c6600 If it defaults to the actor type, then it shouldn't be required. 2020-08-02 22:15:13 +02:00
Matthias Mailänder
657e690bdd Add an image override. 2020-08-02 22:15:13 +02:00
Matthias Mailänder
2d36d0a659 Reflect in naming that negative SelfHealing is a thing. 2020-08-02 22:15:13 +02:00
Matthias Mailänder
c42fd5d2e2 Add IsDecoration as a configurable option. 2020-08-02 22:15:13 +02:00
Trevor Nichols
10bf97eff6 Reduce severity of editorconfig and prefer multiline braces 2020-08-02 14:37:39 +02:00
Matthias Mailänder
ea9992247d Reorder string placeholders. 2020-08-02 12:00:53 +02:00
Matthias Mailänder
b90fecff76 Remove the prefixed method name. 2020-08-02 12:00:53 +02:00
Matthias Mailänder
a4fc9fea3b Unify AIUtils.BotDebug prefixes. 2020-08-02 12:00:53 +02:00
abcdefg30
b7c7eff2a2 Fix the position of the red cross in RA 2020-08-02 11:56:15 +02:00
Stuart McHattie
8c10dc406a Prevent Mac's Cmd+Q from exiting the game
This operation can be devastating if you were trying to assign a control group using Cmd + 1 but accidentally catch the Q key during gameplay.
2020-07-28 23:12:39 +01:00
Stuart McHattie
05c3861426 Add myself to the authors list 2020-07-28 23:12:39 +01:00
Matthias Mailänder
1ef5db8896 Document the built in hitshape types. 2020-07-28 23:42:26 +02:00
Andre Mohren
006a87692a Removed unused imports. 2020-07-28 18:22:51 +02:00
Oliver Brakmann
e019b70420 Fix actor previews for actors with types written in capital letters 2020-07-26 10:57:18 +02:00
Smittytron
70ec5b0344 Add flash to mission paranukes 2020-07-26 10:52:06 +02:00
abcdefg30
5401ace540 Add a death animation to Visceroids 2020-07-24 18:13:11 +02:00
abcdefg30
ab9081c852 Force enable Visceroids in the default rules 2020-07-24 18:13:11 +02:00
Matthias Mailänder
3a9b35980c Revert "Reduce order latency locally"
This reverts commit 20e5219cf4.
2020-07-21 21:58:31 +02:00
Matthias Mailänder
150439d215 Revert "Change where we send orders"
This reverts commit 616d9421d6.
2020-07-21 21:58:31 +02:00
Matthias Mailänder
b01a534a98 Revert "Refactor the OrderManager and world tick loop, improves input latency"
This reverts commit f642cead44.
2020-07-21 21:58:31 +02:00
Trevor Nichols
758b0b08d0 Add to .editorconfig additional naming rules to match this codebase's style 2020-07-21 16:15:30 +02:00
teinarss
f87ba1d8a4 Update SP maps with transformable trees 2020-07-20 14:05:52 +02:00
teinarss
67fa7bdcc9 Add TransformsNearResources 2020-07-20 14:05:52 +02:00
Matthias Mailänder
0b03aca104 Fix linter for player palettes. 2020-07-19 10:44:55 +02:00
Paul Chote
3bf61f1043 Fix incorrect rotation calculation in ThrowsShrapnel. 2020-07-19 10:41:05 +02:00
Paul Chote
ac975f4139 Convert yaml-exposed facings to WAngle. 2020-07-19 10:41:05 +02:00
abcdefg30
6d12301f88 Fix the aircraft facing slider in the editor 2020-07-18 01:45:00 +02:00
Andre Mohren
914950c4a5 When zooming using the mousewheel, zoom towards the pointer location. 2020-07-17 20:12:10 +02:00
Paul Chote
b417b267dd Store quaternion components directly.
This avoids precision loss when combining rotations.
The equivalent Euler angles are calculated for external use
but the quaternion components are preferred for any further
internal calculations.
2020-07-17 15:02:32 +02:00
Paul Chote
aae497eff1 Pass pre-combined rotations into the rendering code. 2020-07-17 15:02:32 +02:00
Paul Chote
3c9db4c2ac Add WRot.Rotate to allow rotations to be combined. 2020-07-17 15:02:32 +02:00
Paul Chote
8c3793e7ea Rename WRot.Zero to WRot.None. 2020-07-17 15:02:32 +02:00
Oliver Brakmann
60a7f53491 Fix target lines appearing not long enough on screen
Also changes the Delay attribute from ticks to milliseconds.
2020-07-16 00:28:05 +02:00
Mustafa Alperen Seki
04bfd62f2f Fix FireClusterWarhead playing wrong Report sound. 2020-07-15 23:55:01 +02:00
Paul Chote
117b8b3653 Change tiberium colors to match the original game. 2020-07-13 14:02:02 +02:00
Paul Chote
70cb0d2924 Improve TS map importer and reimport classic maps. 2020-07-13 14:02:02 +02:00
Paul Chote
c5ea496c45 Add terrain lighting definitions to TS. 2020-07-13 14:02:02 +02:00
Paul Chote
01e955ca37 Implement TS-style terrain lighting. 2020-07-13 14:02:02 +02:00
Paul Chote
fdb66c769c Change terrain layers to track sequences instead of sprites. 2020-07-13 14:02:02 +02:00
Paul Chote
38b3fbbdbe Fix [RequireExplicitImplementation] failures on event properties. 2020-07-13 14:02:02 +02:00
Paul Chote
3bc5d2d02c Add INotifyEditorPlacementInfo interface.
This allows TraitInfos to act when the actor preview is placed
in the editor, returning arbitrary data which the editor stores
and gives back if the preview is removed.
2020-07-13 14:02:02 +02:00
Paul Chote
ac7eda8ca2 Add support for rendering tinted artwork. 2020-07-13 14:02:02 +02:00
reaperrr
baf58f53b3 Adapt SpreadDamageWH to ImpactOrientation
The ImpactOrientation needs to be computed from
point of impact to target if the target wasn't hit directly.

Also adapted warhead code to use WarheadArgs consistently,
as well as pass HitShape instead of just HitShapeInfo
(both needed for future and/or downstream features).
2020-07-12 19:52:55 +02:00
reaperrr
8513a83331 Add ImpactOrientation to WarheadArgs
Allows to pass the horizontal facing/yaw
and vertical angle/pitch of the carrier
projectile to warheads for further use.

Add ImpactPosition to WarheadArgs

InflictDamage doesn't pass the impact pos
directly, and the very point of WarheadArgs
is to avoid adding more and more arguments
to the warhead methods.
2020-07-12 19:52:55 +02:00
Orb
bf7fecff10 TD Balance Summer 2020 2020-07-12 19:31:59 +02:00
Oliver Brakmann
6e1f2f636c Increase GameOverDelay for missions from 1.5 to 3 secs 2020-07-12 15:06:55 +02:00
Oliver Brakmann
8b3db6f3d6 Allow granting a condition to Carryalls currently carrying units 2020-07-12 14:36:01 +02:00
Matthias Mailänder
39d0abe982 Remove bit rot. 2020-07-12 14:32:32 +02:00
teinarss
c2026dc254 Add Discord Rich Presence 2020-07-12 14:27:59 +02:00
Matthias Mailänder
cae6c28754 Change the prefix in front of replays. 2020-07-12 13:43:05 +02:00
Sly
3b99924799 Corrected spelling error 2020-07-10 14:48:22 +02:00
Sly
b2b548b103 Corrected spelling error 2020-07-10 14:46:40 +02:00
Pavel Penev
c2e3806a77 Added a FlashPaletteEffect to RA nukes and removed from D2k 2020-07-10 13:22:54 +02:00
Pavel Penev
25500a7dda Updated a stale copyright header 2020-07-10 13:22:54 +02:00
Pavel Penev
8c394a4cb5 Created FlashPaletteEffectWarhead and removed the hardcoded flashing from NukeLaunch
Not actually hardcoded, but there was a hard dependency in NukeLaunch on FlashPaletteEffect and explicit flashing checks.
2020-07-10 13:22:54 +02:00
Pavel Penev
2e7bd4de4b Fixed a bug with the NukeLaunch's Player palette 2020-07-10 13:22:54 +02:00
Matthias Mailänder
7261322e41 Add support for Visual Studio Code. 2020-07-10 12:33:57 +03:00
abcdefg30
cada396733 Fix a crash when previewing an actor using WithCrateBody in the editor 2020-07-09 20:31:03 +02:00
Chris
9f9709f058 Add python3 support to launch-game.sh 2020-07-09 20:21:48 +02:00
abcdefg30
26fc65209d Fix the bogus Actors iterator 2020-07-09 18:11:53 +02:00
Adam Mitchell
f642cead44 Refactor the OrderManager and world tick loop, improves input latency 2020-07-09 13:48:55 +02:00
Adam Mitchell
616d9421d6 Change where we send orders 2020-07-09 13:48:55 +02:00
Adam Mitchell
20e5219cf4 Reduce order latency locally 2020-07-09 13:48:55 +02:00
Vapre
9edda21b06 Avoid three time array lookup in TraitContainer.Actors. 2020-07-09 12:16:04 +02:00
Paul Chote
6d6822ca15 Update map inits in RA/TD/D2k. 2020-07-08 20:38:30 +02:00
Paul Chote
a2269e7ee7 Convert (Dynamic)FacingInit, (Dynamic)TurretFacingInit to WAngle. 2020-07-08 20:38:30 +02:00
Paul Chote
e8f443f4a9 Remove TurretFacingsInit and PlugsInit. 2020-07-08 20:38:30 +02:00
Ivaylo Draganov
67f8452178 Add button to override duplicate hotkey bindings 2020-07-08 19:59:07 +02:00
Ivaylo Draganov
47f6e407d9 Cancel hotkey rebind on Esc key press 2020-07-08 19:59:07 +02:00
abcdefg30
889e2152a4 Cache ProjectedCellBounds during load time 2020-07-08 18:37:50 +02:00
abcdefg30
173aae1f81 Directly check the map bounds instead of converting to PPos 2020-07-08 18:37:50 +02:00
abcdefg30
baed80983b Fix the launch-game.sh newlines not displaying in the console 2020-07-07 22:30:55 +02:00
abcdefg30
b00423dc76 Correct the crash dialog title 2020-07-07 22:30:55 +02:00
abcdefg30
376ed15079 Correct the support folder location in the crash dialog 2020-07-07 22:30:55 +02:00
abcdefg30
a6d8d6cd8e Use tabs as indentation consistently 2020-07-07 22:30:55 +02:00
Paul Chote
b3ee8b447e Reduce duplication between Map and PlayerRadarTerrain. 2020-07-07 22:12:22 +02:00
Paul Chote
5f588561b6 Rewrite TS minimap rendering:
* Rename LeftColor and RightColor to MinColor and MaxColor
  These are mapped from LowRadarColor and HighRadarColor in
  the original inis, and appear to be used to set the bounding
  values for selecting a random colour, NOT for left/right
  pixels (which caused noticeably wrong banding).

* Adjust brightness based on terrain height.
  MinHeightColorBrightness and MaxHeightColorBrightness
  were chosen by trial/error to match the original
  map preview rendering.
2020-07-07 22:12:22 +02:00
Smittytron
83c53e17e0 Allow repairable tech buildings in campaign-rules 2020-07-07 19:47:10 +02:00
Smittytron
c028488894 Add Soviet 09 2020-07-07 19:47:10 +02:00
abcdefg30
b066005f7e Fix AttackBomber marking aircraft that are not in the world as in range 2020-07-06 22:30:41 +02:00
abcdefg30
87e33a75c6 Fix a crash when calling an airstrike at the map edge 2020-07-06 22:30:41 +02:00
Matthias Mailänder
4961b0943b Add a text base spy disguise decoration. 2020-07-05 21:43:12 +02:00
Smittytron
eda1e9c266 Add Counterstrike mission Siberian Conflict 1: Fresh Tracks 2020-07-05 21:33:56 +02:00
abcdefg30
bc7bf174d8 Don't tick the announcement timer when not in low power
When entering low power it will be reset to 0 again anyway
2020-07-05 20:37:17 +02:00
abcdefg30
2e06d5790b Update the PowerState only when power is added or removed 2020-07-05 20:37:17 +02:00
abcdefg30
ab8790e8f1 Style/Readability changes 2020-07-05 20:37:17 +02:00
abcdefg30
7d630e63e7 Let Railgun implement ISync 2020-07-05 18:10:14 +02:00
Smittytron
77899191f3 Add Aftermath mission Situation Critical 2020-07-05 17:21:43 +02:00
Smittytron
6871873e93 Add Counterstrike mission Fall of Greece 1: Personal War 2020-07-05 15:59:18 +02:00
Smittytron
fc4bd131cd Add Aftermath mission: Production Disruption 2020-07-05 15:58:10 +02:00
Pavel Penev
d866286f82 Added back the DescAttributes on projectiles' Inaccuracy fields 2020-07-05 13:04:18 +02:00
Pavel Penev
76dfda164e Moved projectile inaccuracy calculations to Common.Util
Also moved the InaccuracyType enum there. This also quietly adds the RangeModifiers to the calculations for all projectiles, while they were only used on Bullet so far, which seemed very wrong.
2020-07-05 13:04:18 +02:00
Pavel Penev
134d47e48c Added InaccuracyType.Absolute to projectiles 2020-07-05 13:04:18 +02:00
Pavel Penev
a2dbd5e013 Changed weapons in D2k to use the new PerCellIncrement inaccuracy
Also adjusted the inaccuracy values. This should bring inaccuracy in D2k pretty much in line with the original game, with the potential liberty of decreased inaccuracy for the Deviator tank.
2020-07-05 13:04:18 +02:00
Pavel Penev
c27412c83a Added InaccuracyType enum and updated projectiles accordingly
Also updated the inaccuracy calculations to account for the new inaccuracy type - either based on distance up to a max defined inaccuracy at max range (old style) or based on distance with each cell (1024 range) increasing the inaccuracy with a set step.
2020-07-05 13:04:18 +02:00
Pavel Penev
4143aba595 Added syncing to some Railgun projectile fields 2020-07-05 13:04:18 +02:00
Ivaylo Draganov
39ccac4022 Refactor command bar buttons to use unified widget state names 2020-07-05 11:50:45 +01:00
Ivaylo Draganov
7943f4deb6 Unify widget state image suffixes (disabled, pressed, hover, focus)
- Add a property for arrows image collection (in drop-downs, scrollbars
and production tabs)
- Add a property for separators image collection (in drop-downs)
- Add hover and disable states to the drop-down separator
- Unify button, textfield and checkbox state suffixes
2020-07-05 11:50:45 +01:00
Curtis Shmyr
2dda2d7689 Added lua IsCloaked actor property 2020-07-04 21:06:52 +02:00
Matthias Mailänder
b8f2a14ea0 Automatically upload release to itch.io using butler. 2020-07-04 20:59:10 +02:00
Oliver Brakmann
595809f090 Fix Carryalls waiting for actors on transit-only tiles 2020-07-04 16:47:17 +02:00
abcdefg30
477db9cd4a Fix travis builds not failing when errors occur during static checking 2020-07-03 21:29:25 +01:00
teinarss
67ff292d62 Refactor WorldRenderer to use less allocations 2020-07-03 17:41:41 +02:00
darkademic
ae882b85a9 Make AI randomly scan map for targets for its air squads. 2020-07-02 23:05:49 +02:00
abcdefg30
5e92915095 Fix the cargo unload regression with scripted transports
The activity is not interruptible, so we'd continue after the Move regardless
2020-07-02 21:14:35 +02:00
Smittytron
95809db03c Move Harvester overrides to campaign-rules.yaml 2020-07-02 19:29:37 +02:00
Smittytron
6d5a5121bc Remove worthless function from Allies08 2020-07-02 19:29:37 +02:00
Smittytron
86992751c7 Update Soviet-08a 2020-07-02 19:29:37 +02:00
Smittytron
2eba8b6c37 Add Soviet-08b 2020-07-02 19:29:37 +02:00
Smittytron
e1523e939d Utilize Panic function with added OnDamaged trigger 2020-06-28 17:30:24 +02:00
Smittytron
15a92f443d Replace paraprop power with scripted drops in Soviet01 2020-06-28 17:30:24 +02:00
KorGgenT
b57c68e392 Fix units walk over tunnels and under bridges. 2020-06-28 17:21:54 +02:00
Matthias Mailänder
e95fcb6bc0 Don't let blossom tree poof all at the same time. 2020-06-28 13:43:33 +02:00
Matthias Mailänder
6581fcb6a7 Add a random interval to idle animations. 2020-06-28 13:43:33 +02:00
tovl
6551337bd8 Make TS walkers AlwaysTurnInPlace. 2020-06-28 00:11:26 +02:00
tovl
137df52fdd Make infantry AlwaysTurnInPlace. 2020-06-28 00:11:26 +02:00
tovl
b79aa7eb6a Add AlwaysTurnInPlace option to Mobile. 2020-06-28 00:11:26 +02:00
Alfred Lang
1c8c49dc8e Fix do allow AI do place plugbuildings like RBG Tower on GDI Component Tower 2020-06-27 10:35:06 +02:00
Andrew Odintsov
f0c808d2fc Replace FloodFill with IEditorAction implementation 2020-06-24 19:08:54 +02:00
Curtis Shmyr
26d9ae88df Replace TargetAndAttack with a global aircraft attack function 2020-06-23 20:09:42 +02:00
Curtis Shmyr
3c7f119bb1 Add a damage parameter to Lua OnDamage callback 2020-06-23 19:58:31 +02:00
Ian T. Jacobsen
fea35923f0 Minefield now shows red when out of map bounds and Minelayer now does not get stuck at edge of map
Made it impossible to detect enemy units again

Fix whitespace
2020-06-21 20:16:57 +02:00
Paul Chote
9627776318 Add pitch and roll to TS aircraft. 2020-06-21 18:05:40 +02:00
Paul Chote
6dcde3af72 Allow voxel-based aircraft to pitch and roll. 2020-06-21 18:05:40 +02:00
Paul Chote
43717a89b5 Add Orientation getter to IFacing. 2020-06-21 18:05:40 +02:00
Matthias Mailänder
c7ba359688 Add drop pod reinforcements. 2020-06-21 17:28:24 +02:00
Matthias Mailänder
dc3dbf6d85 Remove uneccessary parentheses. 2020-06-21 17:28:24 +02:00
Smittytron
71664c85ff Fix TargetTypes regression in SnipeWeapon 2020-06-21 12:34:43 +02:00
Nikita Pozdeev
cb41be113a Fix map editor radar ignoring color from terrain 2020-06-20 14:37:23 +02:00
Matthias Mailänder
4fe7daa85e Fix juggernaut preview being disabled by default. 2020-06-19 21:30:34 +02:00
abcdefg30
f75afc6ee4 Fix a compilation error in WithInfantryBody.cs 2020-06-19 20:01:04 +02:00
abcdefg30
67fd71ab92 Add a ProjectedCellLayer and use it in Shroud.cs 2020-06-19 18:31:51 +02:00
abcdefg30
385e70552e Create CellLayerBase 2020-06-19 18:31:51 +02:00
Matthias Mailänder
1e2c67bfca Increase UPnP device discovery timeout. 2020-06-19 18:00:27 +02:00
Paul Chote
56739f87fb Allow plugs to be configured in the map editor. 2020-06-19 17:57:56 +02:00
Paul Chote
571eb7614f Support multiple turrets in the editor. 2020-06-19 17:57:56 +02:00
Paul Chote
c6c3a8c60d Make ActorPreview and EditorActorPreview wrap ActorReference. 2020-06-19 17:57:56 +02:00
Paul Chote
ae7cfa56b7 Restrict IActorPreviewInitInfo to ActorInit. 2020-06-19 17:57:56 +02:00
Paul Chote
b856613194 Add ISingleInstanceInit interface.
Inits that are logically singletons (e.g. actor
location or owner) should implement this interface
to avoid runtime inconsistencies.

Duplicate instances are rejected at init-time,
allowing simpler queries when they are used.
2020-06-19 17:57:56 +02:00
Paul Chote
86305879cb Parse Enum *ValueInit as string values, not integers. 2020-06-19 17:28:01 +02:00
Paul Chote
e5a1a8a706 Replace deprecated API usage. 2020-06-19 17:28:01 +02:00
Paul Chote
27602a4a97 Add WAngle-compatible airstrike/paratrooper APIs. 2020-06-19 17:28:01 +02:00
Paul Chote
a98e460257 Expose WAngle to Lua API and deprecate old Facing. 2020-06-19 17:28:01 +02:00
Paul Chote
0349435650 Remove deprecated Paratrooper API methods. 2020-06-19 17:28:01 +02:00
Paul Chote
c3fbdca18f Add yellow-shirt technician. 2020-06-19 17:09:13 +02:00
Paul Chote
acb5245a28 Restore correct palette and voices for RA civilians.
A new C11 infantry has been added to use the custom
c3 sprite that was originally added back when only
c1 and c2 were used in OpenRA.
2020-06-19 17:09:13 +02:00
Paul Chote
23561cd76b Add custom palette support to WithInfantryBody. 2020-06-19 17:09:13 +02:00
Paul Chote
d3ab3d7d78 Move IndexedPlayerPalette to Mods.Common and add a non-player version. 2020-06-19 17:09:13 +02:00
Matthias Mailänder
5b870be83f Let the 2nd civilian panic when his mate gets electrocuted. 2020-06-19 16:50:28 +02:00
Matthias Mailänder
6130d5622c Add a panic function to the Lua API. 2020-06-19 16:50:28 +02:00
abcdefg30
318c4e3456 Remove InitialStanceAI overrides from campaign missions 2020-06-19 16:46:34 +02:00
abcdefg30
ab701449e2 Change Subs to use the Defend stance by default 2020-06-19 16:46:34 +02:00
abcdefg30
9a3447d863 Fix airstrike and paratroopers power not removing cameras at the map edge 2020-06-19 16:04:54 +02:00
matjaeck
5280637adf Fix PickupUnit not validating cargo on first run. 2020-06-19 15:59:14 +02:00
Matthias Mailänder
3bce55ac44 Cancel the attack when no traits are active. 2020-06-19 13:43:03 +02:00
Curtis Shmyr
a3f79503ed Fix Lua DisplaySystemMessage writing twice 2020-06-16 13:11:02 +02:00
Curtis Shmyr
02d462a82c Fix copying public key to clipboard if already authed 2020-06-13 19:15:27 +02:00
Smittytron
3d17328d0d Fix regression and cleanup scu35ea 2020-06-12 23:50:44 +02:00
reaperrr
0e81abc21b Fix weapons not accounting for Air
If a weapon was aiming at a target position rather
than an actor target, it would always check target types
of the terrain below, ignoring altitude (and therefore ignoring
"InvalidTargets: Air").
2020-06-12 21:00:53 +02:00
Paul Chote
803b930405 Change IFacing.TurnSpeed to WAngle. 2020-06-12 18:35:41 +02:00
Paul Chote
6adf45bcb4 Convert IFacing.Facing and TurnSpeed to WAngle. 2020-06-12 18:35:41 +02:00
Matthias Mailänder
01417c88c5 Add missing actor reference for lint testing. 2020-06-10 19:07:14 +02:00
abcdefg30
60bbbe0d93 Fix the Death Hand launch notification playing for the player not enemy 2020-06-10 18:57:08 +02:00
Matthias Mailänder
888dfd3654 Stop the boat to remove it immediately. 2020-06-09 22:40:32 +02:00
Matthias Mailänder
12de56ff62 Fix crash notication "AlertBeep" not found. 2020-06-09 22:40:32 +02:00
Matthias Mailänder
b7cee41c54 Fix TD gunboat not updating actor map influence. 2020-06-09 22:40:32 +02:00
Paul Chote
10aac03f75 Add CompositeActorInit and simplify chronoshift inits. 2020-06-08 19:18:38 +02:00
Paul Chote
0eb0041f90 Allow ActorInits to target a specific trait by matching the @ suffix. 2020-06-08 19:18:38 +02:00
Paul Chote
b38018af9c Replace IActorInit with an abstract class.
A shared ValueActorInit<T> is introduced to reduce duplication
in the most common init cases, and an ActorInitActorReference
allow actors to be referenced by map.yaml name.
2020-06-08 19:18:38 +02:00
Unknown
4df5ac0385 Change default to ground-attack 2020-06-07 10:31:44 -05:00
Unknown
a7476bc303 Improve detail + double fire rate/half damage 2020-06-07 10:31:44 -05:00
Unknown
cc4b3cb361 Add APC ground attack sprite turret 2020-06-07 10:31:44 -05:00
Ivaylo Draganov
31a965b29a Add suffix to player name in shroud selector 2020-06-06 14:40:48 +01:00
Ivaylo Draganov
7a213338a2 Add helper method to add suffix to player name label 2020-06-06 14:40:48 +01:00
abcdefg30
fb27a25e52 Fix a crash with support powers and units without selection decorations 2020-06-06 13:46:27 +01:00
abcdefg30
534b09ae4a Fix bots not working after adminship was transferred 2020-06-03 18:30:18 +02:00
abcdefg30
341a9f370c Fix a crash in Evacuation 2020-06-02 22:53:40 +02:00
Matthias Mailänder
607d9b2d5c Fix index out of bounds exception for off world aircraft. 2020-06-02 19:25:53 +01:00
abcdefg30
507ce40ad2 Fix a crash in LegacyBridgeLayer 2020-06-02 19:22:19 +01:00
reaperrr
f58c3aed32 Use OccupiesSpace to save more trait look-ups
This time in Locomotor.IsBlockedBy.
2020-06-02 20:08:34 +02:00
reaperrr
55e85bd9ca Save Mobile look-up in BasePathSearch
By casting to OccupiesSpace and then
looking up Info.LocomotorInfo directly.
2020-06-02 20:08:34 +02:00
reaperrr
4bf614c5cd Use OccupiesSpace to avoid Mobile look-up in Move
While individual trait look-ups may be cheap,
if a large army that is currently standing still gets
its first move-including order, the look-ups of dozens
or even hundreds of actors may happen on the same tick.

Therefore this may help reducing that first-order lag spike,
at least a little bit.
2020-06-02 20:08:34 +02:00
abcdefg30
96b06c75d1 Make Resupply display target lines for all queued move activities 2020-06-02 18:23:54 +02:00
Matthias Mailänder
d261648ab0 Fix BeingCapturedCondition getting revoked from the wrong actor. 2020-06-02 10:45:49 +02:00
Paul Chote
7b81b9e806 Bullet Facing -> WAngle. 2020-06-01 21:34:32 +02:00
Paul Chote
a93aea3e4e AreaBeam Facing -> WAngle. 2020-06-01 21:34:32 +02:00
Paul Chote
2cfacc2c7d ProjectileArgs facing -> WAngle. 2020-06-01 21:34:32 +02:00
Paul Chote
6d6b21a0eb Convert Aircraft.Facing to WAngle. 2020-06-01 20:25:38 +02:00
Matthias Mailänder
7a78c37851 Add .NET Coding Conventions 2020-05-31 13:27:03 +01:00
Ivaylo Draganov
b8a9f41892 Add missing trait descriptions for cursors and unify the language 2020-05-31 00:12:04 +02:00
Ivaylo Draganov
227567dfe1 Formatting: wrap and indent long argument lists 2020-05-31 00:12:04 +02:00
Ivaylo Draganov
d5ff5c672b Add configurable cursors for entering allied actor targeters 2020-05-31 00:12:04 +02:00
Ivaylo Draganov
393f6eca3a Add configurable target cursors to various traits 2020-05-31 00:12:04 +02:00
Paul Chote
d193ef856e Fix harvest animation facing glitch. 2020-05-30 19:58:03 +02:00
abcdefg30
44d3691fa1 Assign Player.IsBot before calling INotifyCreated.Created 2020-05-30 19:47:29 +02:00
abcdefg30
27d0465891 Remove workarounds for querying the PlayerActor in Created 2020-05-30 19:47:29 +02:00
abcdefg30
52a9fcef3c Rename "Created" to "Initialize" and let it handle adding to the world 2020-05-30 19:47:29 +02:00
abcdefg30
7386816f52 Manually construct the PlayerActor to fix crashes during actor creation 2020-05-30 19:47:29 +02:00
abcdefg30
9c0075b233 Move hardcoded PlayerActorTypes to shared const variables 2020-05-30 19:47:29 +02:00
abcdefg30
e4c5700baf Remove an unused using 2020-05-30 19:47:29 +02:00
reaperrr
e1b7df8b6a Use OccupiesSpace to avoid Mobile look-ups
in PathFinder.FindUnitPath and FindUnitPathToRange.
2020-05-30 04:05:29 -05:00
Paul Chote
c999b2d778 Convert QuantizeFacing to WAngle facings. 2020-05-28 21:23:51 +02:00
Paul Chote
bfb6c671fb Change QuantizeFacing to return a facing instead of an index. 2020-05-28 21:23:51 +02:00
Paul Chote
7c6ec577dc Rewrite ActorInit queries. 2020-05-28 19:04:53 +02:00
Paul Chote
626b40f31b Account for ramps in terrain height calculations. 2020-05-28 09:41:55 -05:00
Paul Chote
5af12440ba Replace MapGrid.CellCorners with a new CellRamp struct. 2020-05-28 09:41:55 -05:00
Paul Chote
4614f6febe Cache cell ramps to avoid repeated tileset lookups. 2020-05-28 09:41:55 -05:00
Pavel Penev
1354ffc32e Added multiple production speedup to D2k
Based on the specification in issue 18051.
2020-05-27 10:32:35 +02:00
Pavel Penev
3723939c99 Adjusted D2k build times to match the original game
Removed custom production queue speedups and custom actor build time slowdowns and adjusted BuildDurations based on the specifications in issue 18051.
2020-05-27 10:32:35 +02:00
Pavel Penev
e099739e13 Reduced HitShape radius of D2k units to minimum
Since they are only used to hold the armor type anyway.
2020-05-27 10:28:25 +02:00
Pavel Penev
21a48cc41d Switched D2k to use the new DamageCalculationType
This brings D2k in line with the damage model of the original game.
2020-05-27 10:28:25 +02:00
Pavel Penev
4740266308 Added DamageCalculationType enum to SpreadDamageWarhead 2020-05-27 10:28:25 +02:00
Pavel Penev
78139413d7 Equalized the DoImpact methods in damage warheads 2020-05-27 10:28:25 +02:00
Pavel Penev
f0578a75f4 Cleaned up DamageWarhead
Reordered methods and fixed access modifiers. Also removed unused using statements from warheads.
2020-05-27 10:28:25 +02:00
abcdefg30
7a0e55a02a Restore trailing whitespaces to windows batch scripts 2020-05-26 22:57:11 +02:00
reaperrr
f132bac80d Add Tree armor type and remove tree-only warheads
This simplifies #12467.
Using a tree-exclusive amor type is far more efficient
than adding more warheads, which cost performance
due to their huge Spread

This also restores the 100% efficiency vs. trees for
some of the incendiary nuke warheads (which have
reduced efficiency vs. Wood since #13643).

Note: Atomic had two tree-only warheads with a Delay
of 15, I believe the first one to be a copy-paste error
and moved the damage to the regular SpreadDamage
with a Delay of 10.
2020-05-26 22:51:29 +02:00
reaperrr
109ea4fe5b Fix Barrel explosion damage 2020-05-26 22:46:26 +02:00
Matthias Mailänder
f33feafd0e Add TurnOnIdle. 2020-05-25 13:07:19 +02:00
reaperrr
9195356e3a Upgrade DepthCharge effect setup
- med_explosion instead of small_explosion on surface hit
- only play explosion on surfaced subs
- only play h2obomb2.aud when hitting a submerged sub
2020-05-24 13:26:40 +02:00
reaperrr
eff91108f4 Make RA ships show both explosion and splash
...when destroyed, to imply sinking.
2020-05-24 13:26:40 +02:00
reaperrr
e40c0516e6 Adapt RA warheads to new target types 2020-05-24 13:26:40 +02:00
reaperrr
c580a94ab7 Fix RA weapon ValidTargets 2020-05-24 13:26:40 +02:00
reaperrr
6a545bb942 Streamline RA target types
No more sharing of target types between terrain
and actors (except bridges), removed 'Ground(Actor)'
from WaterActors (was only used by weapons/warheads,
which can just list both ground- and water types.
2020-05-24 13:26:40 +02:00
Matthias Mailänder
672bd2d9fe Don't crash when putting the trait on the World actor. 2020-05-24 12:36:26 +02:00
Ivaylo Draganov
3ab4a584ab Remove trailing white-space from various files 2020-05-23 11:38:44 +02:00
Ivaylo Draganov
150d02ac0d Remove trailing white-space from lua files 2020-05-23 11:38:44 +02:00
Ivaylo Draganov
6d26f60904 Remove trailing white-space from yaml files 2020-05-23 11:38:44 +02:00
abcdefg30
b42276953f Fix a crash when a harvester is rebuilt in cnc64gdi01 2020-05-22 21:25:58 +02:00
abcdefg30
7c290b9f76 Fix not all harvesters in cnc64gdi01 being rebuilt 2020-05-22 21:25:58 +02:00
abcdefg30
51fe1d6629 Let AI autotargeting in D2k ignore sandworms 2020-05-22 20:40:36 +02:00
Matthias Mailänder
8f558d2b47 Add a bullet bounce sound. 2020-05-21 14:44:13 +02:00
abcdefg30
3f5fadf2e9 Move stray update rules into the correct subfolder 2020-05-21 14:08:14 +02:00
abcdefg30
07c16cee1d Add TargetTypes to HealActorsCrateAction 2020-05-21 14:08:14 +02:00
abcdefg30
24130dfcdc Add an update rule for the RenameHealCrateAction rename 2020-05-21 14:08:14 +02:00
abcdefg30
15a2341a91 Rename HealUnitsCrateAction to HealActorsCrateAction 2020-05-21 14:08:14 +02:00
Paul Chote
86f61298e6 Replace ITraitInfo interface with TraitInfo class. 2020-05-21 13:01:04 +02:00
abcdefg30
3cd7ec3878 Make the TS Test AI build aircraft 2020-05-21 10:05:05 +02:00
abcdefg30
8b13d3e4c7 Fix resupply not displaying target lines correctly 2020-05-19 22:11:20 +02:00
Ivaylo Draganov
327d451abc Add trim_trailing_whitespace to .editorconfig 2020-05-18 17:39:24 +02:00
atlimit8
2dac16ee02 add SquadManagerBotModuleInfo.IgnoredEnemyTargetTypes 2020-05-17 22:02:32 +01:00
abcdefg30
d4b92a19d7 Remove bogus weapon override defintions from TD campaign maps 2020-05-17 13:39:51 +01:00
tovl
e0357596f5 Correct aircraft repulsion direction when outside of the map. 2020-05-17 12:46:17 +02:00
atlimit8
1ef27d18c1 check name for Actor.GrantCondition() 2020-05-17 12:33:29 +02:00
Matthias Mailänder
1d2d8ed107 Don't hard-code the transparent background color. 2020-05-16 22:28:52 +01:00
reaperrr
1bf01bc214 Remove WaterStructure TargetType from RA SYRD/SPEN
- only used for auto-targeting
- inconsistent with their fakes (which didn't have this)
- unnecessary, since the 'Ship' target type covers all
  surface water actors we want to be auto-targetable by default,
  while 'Structure' is enough to add syard/spen in AttackAnything.
2020-05-15 08:22:02 +02:00
reaperrr
0015deca47 Fix TS Tiberium Fiend target types
Was missing Creep, preventing actors from
auto-targeting it (unlike all other critters).
2020-05-15 08:22:02 +02:00
reaperrr
ddfdc6e90f Clean up TD weapon ValidTargets
- Missiles can now force-fire on water like other weapons
- Superweapons can now target empty water
- made Chemspray null InvalidTargets to avoid yaml-merge issues
- Improved APCGun effect warhead perf by ignoring actors
- removed stale Tiberium weapon mission overrides
2020-05-15 08:22:02 +02:00
abcdefg30
5db2ad54f2 Fix a scripting error in nod04b 2020-05-14 22:40:44 +01:00
teinarss
b8a5750529 Add map.contains check to CanStayInCell 2020-05-14 20:06:21 +02:00
Andrew Odintsov
98d5b8c7cc Remove redundant call to OnTextEdited 2020-05-14 11:01:48 +02:00
Andrew Odintsov
3f34154a1e Add OnTextEdited call to RemoveSelectedText
This would allow expty text box to be processed and disable filtering
2020-05-14 11:01:48 +02:00
thisisjacob
3119f831b3 Added notice for entering vehicles in docs 2020-05-13 17:25:49 -05:00
thisisjacob
8a07b762a2 Changed documentation for OnEnterComplete 2020-05-13 17:25:49 -05:00
Matthias Mailänder
551ab2fc59 Cache the footprint LINQ for performance. 2020-05-12 20:53:05 -05:00
Zimmermann Gyula
99957e57b9 Update the default mods. 2020-05-12 20:53:05 -05:00
Matthias Mailänder
be2c59bc6e Add upgrade rule to convert ranges to footprints. 2020-05-12 20:53:05 -05:00
Zimmermann Gyula
57f9a49b66 Use footprints in ChronoshiftPower. 2020-05-12 20:53:05 -05:00
Zimmermann Gyula
54bd0eb99d Use footprints in GrantExternalConditionPower. 2020-05-12 20:53:05 -05:00
Zimmermann Gyula
485faac294 Implement SupportPower.CellsMatching. 2020-05-12 20:53:05 -05:00
Andrew Odintsov
d531d6f3ef Simplify groupActors condition 2020-05-12 15:06:50 +02:00
Andrew Odintsov
3a9fdb82f5 Add IsInWorld check for controlled groups 2020-05-12 15:06:50 +02:00
reaperrr
5024ae1156 TS ClusterMissile typo fix 2020-05-10 15:20:25 +02:00
1255 changed files with 44339 additions and 30578 deletions

View File

@@ -6,13 +6,101 @@ charset=utf-8
[*]
end_of_line = LF
insert_final_newline = true
trim_trailing_whitespace = true
; 4-column tab indentation
; 4-column tab indentation and .NET coding conventions
[*.cs]
indent_style = tab
indent_size = 4
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = true
csharp_style_var_elsewhere = true:suggestion
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_prefer_braces = when_multiline:suggestion
csharp_using_directive_placement = outside_namespace:suggestion
csharp_new_line_before_open_brace = all
csharp_space_around_binary_operators = before_and_after
#### Naming styles ####
dotnet_naming_style.camel_case.capitalization = camel_case
dotnet_naming_style.pascal_case.capitalization = pascal_case
# Symbol specifications
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal
dotnet_naming_symbols.const_private_field.applicable_kinds = field
dotnet_naming_symbols.const_private_field.required_modifiers = const
dotnet_naming_symbols.const_private_field.applicable_accessibilities = private
dotnet_naming_symbols.internal_field.applicable_kinds = field
dotnet_naming_symbols.internal_field.applicable_accessibilities = internal
dotnet_naming_symbols.static_private_or_internal_field.required_modifiers = static
dotnet_naming_symbols.static_private_or_internal_field.applicable_accessibilities = internal, private
dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal
# Naming rules
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.static_private_or_internal_field_should_be_pascal_case.severity = none
dotnet_naming_rule.static_private_or_internal_field_should_be_pascal_case.symbols = static_private_or_internal_field
dotnet_naming_rule.static_private_or_internal_field_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.const_private_field_should_be_pascal_case.severity = warning
dotnet_naming_rule.const_private_field_should_be_pascal_case.symbols = const_private_field
dotnet_naming_rule.const_private_field_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.const_private_or_internal_field_should_be_pascal_case.severity = warning
dotnet_naming_rule.const_private_or_internal_field_should_be_pascal_case.symbols = internal_field
dotnet_naming_rule.const_private_or_internal_field_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.private_or_internal_field_should_be_camel_case.severity = warning
dotnet_naming_rule.private_or_internal_field_should_be_camel_case.symbols = private_or_internal_field
dotnet_naming_rule.private_or_internal_field_should_be_camel_case.style = camel_case
# Naming rules
#require a space before the colon for bases or interfaces in a type declaration
csharp_space_after_colon_in_inheritance_clause = true
#require a space after a keyword in a control flow statement such as a for loop
csharp_space_after_keywords_in_control_flow_statements = true
#require a space before the colon for bases or interfaces in a type declaration
csharp_space_before_colon_in_inheritance_clause = true
#Formatting - wrapping options
#leave code block on single line
csharp_preserve_single_line_blocks = true
#leave statements and member declarations on the same line
csharp_preserve_single_line_statements = true
#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them
dotnet_style_predefined_type_for_member_access = true:suggestion
#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
; 4-column tab indentation
[*.yaml]
indent_style = tab
indent_size = 4
indent_size = 4

View File

@@ -3,6 +3,8 @@
language: csharp
mono: 6.4.0
os: linux
dist: xenial
jobs:
include:
@@ -12,17 +14,13 @@ jobs:
if: tag IS present
osx_image: xcode10
cache:
directories:
- thirdparty/download
addons:
apt:
packages:
- lua5.1
- dpkg
- zsync
- markdown
- imagemagick
# Environment variables
env:
@@ -35,11 +33,12 @@ env:
# Run the NUnit tests
script:
- make all
- test "$TRAVIS_OS_NAME" == "linux" && make check || echo "Skipping check"
- test "$TRAVIS_OS_NAME" == "linux" && make check-scripts || echo "Skipping scripts check"
- test "$TRAVIS_OS_NAME" == "linux" && make test || echo "Skipping tests"
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
mono ~/.nuget/packages/nunit.consolerunner/3.11.1/tools/nunit3-console.exe --noresult OpenRA.Test.dll;
- |
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
make check || travis_terminate 1;
make check-scripts || travis_terminate 1;
make test || travis_terminate 1;
mono ~/.nuget/packages/nunit.consolerunner/3.11.1/tools/nunit3-console.exe --noresult OpenRA.Test.dll || travis_terminate 1;
fi
# Only watch the development branch and tagged release.
@@ -47,13 +46,14 @@ branches:
only:
- /^release-.*$/
- /^playtest-.*$/
- /^pkgtest-.*$/
- /^devtest-.*$/
- /^prep-.*$/
- bleed
# Notify developers when build passed/failed.
notifications:
irc:
if: repo = OpenRA/OpenRA
template:
- "%{repository}#%{build_number} %{commit} %{author}: %{message} %{build_url}"
channels:
@@ -62,26 +62,31 @@ notifications:
skip_join: true
before_deploy:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
wget https://mirrors.edge.kernel.org/ubuntu/pool/universe/n/nsis/nsis-common_3.04-1_all.deb;
wget https://mirrors.edge.kernel.org/ubuntu/pool/universe/n/nsis/nsis_3.04-1_amd64.deb;
sudo dpkg -i nsis-common_3.04-1_all.deb;
sudo dpkg -i nsis_3.04-1_amd64.deb;
cd packaging && ./update-wiki.sh ${TRAVIS_TAG} && cd ..;
fi;
- export PATH=$PATH:$HOME/usr/bin
echo ${TRAVIS_REPO_SLUG};
if [[ "${TRAVIS_REPO_SLUG}" == "OpenRA/OpenRA" ]]; then
cd packaging && ./update-wiki.sh ${TRAVIS_TAG} && cd ..;
fi;
fi
- export PATH=${PATH}:${HOME}/usr/bin
- DOTVERSION=`echo ${TRAVIS_TAG} | sed "s/-/\\./g"`
- cd packaging
- mkdir build
- ./package-all.sh ${TRAVIS_TAG} ${PWD}/build/
- if [[ "${TRAVIS_REPO_SLUG}" == "OpenRA/OpenRA" ]]; then
./upload-itch.sh ${TRAVIS_TAG} ${PWD}/build/;
fi
deploy:
provider: releases
api_key:
secure: "g/LU11f+mjqv+lj0sR1UliHwogXL4ofJUwoG5Dbqlvdf5UTLWytw/OWSCv8RGyuh10miyWeaoqHh1cn2C1IFhUEqN1sSeKKKOWOTvJ2FR5mzi9uH3d/MOBzG5icQ7Qh0fZ1YPz5RaJJhYu6bmfvA/1gD49GoaX2kxQL4J5cEBgg="
token: ${GH_DEPLOY_API_KEY}
file_glob: true
file: build/*
skip_cleanup: true
on:
all_branches: true
tags: true
repo: OpenRA/OpenRA

7
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"recommendations": [
"ms-dotnettools.csharp",
"EditorConfig.EditorConfig",
"ms-vscode.mono-debug"
]
}

61
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,61 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch (TD)",
"type": "clr",
"linux": {
"type": "mono"
},
"osx": {
"type": "mono"
},
"request": "launch",
"program": "${workspaceRoot}/OpenRA.Game.exe",
"cwd": "${workspaceRoot}",
"args": ["Game.Mod=cnc"]
},
{
"name": "Launch (RA)",
"type": "clr",
"linux": {
"type": "mono"
},
"osx": {
"type": "mono"
},
"request": "launch",
"program": "${workspaceRoot}/OpenRA.Game.exe",
"cwd": "${workspaceRoot}",
"args": ["Game.Mod=ra"]
},
{
"name": "Launch (D2k)",
"type": "clr",
"linux": {
"type": "mono"
},
"osx": {
"type": "mono"
},
"request": "launch",
"program": "${workspaceRoot}/OpenRA.Game.exe",
"cwd": "${workspaceRoot}",
"args": ["Game.Mod=d2k"]
},
{
"name": "Launch (TS)",
"type": "clr",
"linux": {
"type": "mono"
},
"osx": {
"type": "mono"
},
"request": "launch",
"program": "${workspaceRoot}/OpenRA.Game.exe",
"cwd": "${workspaceRoot}",
"args": ["Game.Mod=ts"]
},
]
}

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"omnisharp.enableRoslynAnalyzers": true
}

13
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,13 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "make",
"args": ["all"],
"windows": {
"command": "make.cmd"
}
}
]
}

View File

@@ -40,6 +40,7 @@ Also thanks to:
* Biofreak
* Braxton Williams (Buddytex)
* Brendan Gluth (Mechanical_Man)
* Brent Gardner (bggardner)
* Bryan Wilbur
* Bugra Cuhadaroglu (BugraC)
* Chris Cameron (Vesuvian)
@@ -137,6 +138,7 @@ Also thanks to:
* Sebastien Kerguen (xanax)
* Shawn Collins (UberWaffe)
* Simon Verbeke (Saticmotion)
* Stuart McHattie (SDJMcHattie)
* Taryn Hill (Phrohdoh)
* Teemu Nieminen (Temeez)
* Tim Mylemans (gecko)
@@ -147,6 +149,7 @@ Also thanks to:
* Tristan Mühlbacher (MicroBit)
* UnknownProgrammer
* Vladimir Komarov (VrKomarov)
* Wojciech Walaszek (Voidwalker)
* Wuschel
Using GNU FreeFont distributed under the GNU GPL
@@ -179,6 +182,12 @@ Krueger and distributed under the GNU GPL terms.
Using rix0rrr.BeaconLib developed by Rico Huijbers
distributed under MIT License.
Using DiscordRichPresence developed by Lachee
distributed under MIT License.
Using Json.NET developed by James Newton-King
distributed under MIT License.
This site or product includes IP2Location LITE data
available from http://www.ip2location.com.

File diff suppressed because one or more lines are too long

View File

@@ -21,7 +21,7 @@ Linux
Mono, version 5.18 or later, is required to compile OpenRA. You can add the [upstream mono repository](https://www.mono-project.com/download/stable/#download-lin) for your distro to obtain the latest version if your system packages are not sufficient.
To compile OpenRA, run `make` from the command line. After this one can run the game with `./launch-game.sh`. It is also possible to specify the mod you wish to run from the command line, e.g. with `./launch-game.sh Game.Mod=ts` if you wish to try the experimental Tiberian Sun mod.
To compile OpenRA, run `make` from the command line. After this one can run the game with `./launch-game.sh`. It is also possible to specify the mod you wish to run from the command line, e.g. with `./launch-game.sh Game.Mod=ts` if you wish to try the experimental Tiberian Sun mod.
The default behaviour on the x86_64 architecture is to download several pre-compiled native libraries using the Nuget packaging manager. If you prefer to use system libraries, compile instead using `make TARGETPLATFORM=unix-generic`.
@@ -31,7 +31,7 @@ If you choose to use system libraries, or your system is not x86_64, you will ne
* [OpenAL](http://kcat.strangesoft.net/openal.html)
* [liblua 5.1](http://luabinaries.sourceforge.net/download.html)
Type `sudo make install` for system-wide installation. Run `sudo make install-linux-shortcuts` to get startup scripts, icons and desktop files. You can then run the Red Alert by executing the `openra-ra` command, the Dune 2000 mod by running the `openra-d2k` command and Tiberian Dawn by the `openra-cnc` command. Alternatively, you can also run these mods by clicking on their desktop shortcuts if you ran `sudo make install-linux-shortcuts`.
Type `sudo make install` for system-wide installation. Run `sudo make install-linux-shortcuts` to get startup scripts, icons and desktop files. You can then run the Red Alert by executing the `openra-ra` command, the Dune 2000 mod by running the `openra-d2k` command and Tiberian Dawn by the `openra-cnc` command. Alternatively, you can also run these mods by clicking on their desktop shortcuts if you ran `sudo make install-linux-shortcuts`.
Arch Linux
----------
@@ -89,7 +89,7 @@ sudo zypper in mono-devel openal-soft freetype2 SDL2 lua51 xdg-utils zenity
Red Hat Enterprise Linux (and rebuilds, e.g. CentOS)
----------------------------------------------------
The EPEL repository is required in order for the following command to run properly.
The EPEL repository is required in order for the following command to run properly.
```
sudo yum install "pkgconfig(mono)" SDL2 freetype "lua = 5.1" openal-soft xdg-utils zenity

View File

@@ -39,7 +39,7 @@
WHITELISTED_OPENRA_ASSEMBLIES = OpenRA.Game.exe OpenRA.Utility.exe OpenRA.Platforms.Default.dll OpenRA.Mods.Common.dll OpenRA.Mods.Cnc.dll OpenRA.Mods.D2k.dll OpenRA.Game.dll
# These are explicitly shipped alongside our core files by the packaging script
WHITELISTED_THIRDPARTY_ASSEMBLIES = ICSharpCode.SharpZipLib.dll FuzzyLogicLibrary.dll Eluant.dll BeaconLib.dll Open.Nat.dll SDL2-CS.dll OpenAL-CS.Core.dll
WHITELISTED_THIRDPARTY_ASSEMBLIES = ICSharpCode.SharpZipLib.dll FuzzyLogicLibrary.dll Eluant.dll BeaconLib.dll Open.Nat.dll SDL2-CS.dll OpenAL-CS.Core.dll DiscordRPC.dll Newtonsoft.Json.dll
# These are shipped in our custom minimal mono runtime and also available in the full system-installed .NET/mono stack
# This list *must* be kept in sync with the files packaged by the AppImageSupport and OpenRALauncherOSX repositories
@@ -158,7 +158,8 @@ version: VERSION mods/ra/mod.yaml mods/cnc/mod.yaml mods/d2k/mod.yaml mods/ts/mo
rm $${i}.tmp; \
done
install: core install-core
install: core install-engine install-common-mod-files install-default-mods
@$(CP) *.sh "$(DATA_INSTALL_DIR)"
install-linux-shortcuts: install-linux-scripts install-linux-icons install-linux-desktop
@@ -210,6 +211,8 @@ endif
@$(INSTALL_PROGRAM) FuzzyLogicLibrary.dll "$(DATA_INSTALL_DIR)"
@$(INSTALL_PROGRAM) Open.Nat.dll "$(DATA_INSTALL_DIR)"
@$(INSTALL_PROGRAM) BeaconLib.dll "$(DATA_INSTALL_DIR)"
@$(INSTALL_PROGRAM) DiscordRPC.dll "$(DATA_INSTALL_DIR)"
@$(INSTALL_PROGRAM) Newtonsoft.Json.dll "$(DATA_INSTALL_DIR)"
install-common-mod-files:
@-echo "Installing OpenRA common mod files to $(DATA_INSTALL_DIR)"
@@ -228,19 +231,16 @@ install-default-mods:
@$(INSTALL_PROGRAM) mods/d2k/OpenRA.Mods.D2k.dll "$(DATA_INSTALL_DIR)/mods/d2k"
@$(CP_R) mods/modcontent "$(DATA_INSTALL_DIR)/mods/"
install-core: install-engine install-common-mod-files install-default-mods
@$(CP) *.sh "$(DATA_INSTALL_DIR)"
install-linux-icons:
for SIZE in 16x16 32x32 48x48 64x64 128x128; do \
$(INSTALL_DIR) "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps"; \
$(INSTALL_DATA) packaging/linux/icons/ra_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-ra.png"; \
$(INSTALL_DATA) packaging/linux/icons/cnc_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-cnc.png"; \
$(INSTALL_DATA) packaging/linux/icons/d2k_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-d2k.png"; \
$(INSTALL_DATA) packaging/artwork/ra_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-ra.png"; \
$(INSTALL_DATA) packaging/artwork/cnc_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-cnc.png"; \
$(INSTALL_DATA) packaging/artwork/d2k_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-d2k.png"; \
done
$(INSTALL_DIR) "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps"
$(INSTALL_DATA) packaging/linux/icons/ra_scalable.svg "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/openra-ra.svg"
$(INSTALL_DATA) packaging/linux/icons/cnc_scalable.svg "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/openra-cnc.svg"
$(INSTALL_DATA) packaging/artwork/ra_scalable.svg "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/openra-ra.svg"
$(INSTALL_DATA) packaging/artwork/cnc_scalable.svg "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/openra-cnc.svg"
install-linux-desktop:
@$(INSTALL_DIR) "$(DESTDIR)$(datadir)/applications"
@@ -368,4 +368,4 @@ help:
.SUFFIXES:
.PHONY: check-scripts check test all core clean version install install-linux-shortcuts install-dependencies install-engine install-common-mod-files install-default-mods install-core install-linux-icons install-linux-desktop install-linux-mime install-linux-appdata install-man-page install-linux-scripts uninstall help
.PHONY: check-scripts check test all core clean version install install-linux-shortcuts install-dependencies install-engine install-common-mod-files install-default-mods install-linux-icons install-linux-desktop install-linux-mime install-linux-appdata install-man-page install-linux-scripts uninstall help

View File

@@ -185,8 +185,7 @@ namespace OpenRA.Activities
/// </summary>
internal void OnActorDisposeOuter(Actor self)
{
if (ChildActivity != null)
ChildActivity.OnActorDisposeOuter(self);
ChildActivity?.OnActorDisposeOuter(self);
OnActorDispose(self);
}
@@ -199,8 +198,7 @@ namespace OpenRA.Activities
if (!IsInterruptible)
return;
if (ChildActivity != null)
ChildActivity.Cancel(self);
ChildActivity?.Cancel(self);
// Directly mark activities that are queued and therefore didn't run yet as done
State = State == ActivityState.Queued ? ActivityState.Done : ActivityState.Canceling;
@@ -243,11 +241,9 @@ namespace OpenRA.Activities
Console.WriteLine(GetType().ToString().Split('.').Last());
if (ChildActivity != null)
ChildActivity.PrintActivityTree(self, origin, level + 1);
ChildActivity?.PrintActivityTree(self, origin, level + 1);
if (NextActivity != null)
NextActivity.PrintActivityTree(self, origin, level);
NextActivity?.PrintActivityTree(self, origin, level);
}
}

View File

@@ -26,7 +26,7 @@ namespace OpenRA.Activities
public override bool Tick(Actor self)
{
if (a != null) a();
a?.Invoke();
return true;
}
}

View File

@@ -11,6 +11,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Eluant;
using Eluant.ObjectBinding;
@@ -68,9 +69,7 @@ namespace OpenRA
{
get
{
// TODO: Support non-zero pitch/roll in IFacing (IOrientation?)
var facingValue = facing != null ? facing.Facing : 0;
return new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(facingValue));
return facing != null ? facing.Orientation : WRot.None;
}
}
@@ -116,14 +115,21 @@ namespace OpenRA
internal Actor(World world, string name, TypeDictionary initDict)
{
var duplicateInit = initDict.WithInterface<ISingleInstanceInit>().GroupBy(i => i.GetType())
.FirstOrDefault(i => i.Count() > 1);
if (duplicateInit != null)
throw new InvalidDataException("Duplicate initializer '{0}'".F(duplicateInit.Key.Name));
var init = new ActorInitializer(this, initDict);
readOnlyConditionCache = new ReadOnlyDictionary<string, int>(conditionCache);
World = world;
ActorID = world.NextAID();
if (initDict.Contains<OwnerInit>())
Owner = init.Get<OwnerInit, Player>();
var ownerInit = init.GetOrDefault<OwnerInit>();
if (ownerInit != null)
Owner = ownerInit.Value(world);
if (name != null)
{
@@ -171,7 +177,7 @@ namespace OpenRA
SyncHashes = TraitsImplementing<ISync>().Select(sync => new SyncHash(sync)).ToArray();
}
internal void Created()
internal void Initialize(bool addToWorld = true)
{
created = true;
@@ -225,6 +231,9 @@ namespace OpenRA
activity.Queue(CurrentActivity);
CurrentActivity = activity;
}
if (addToWorld)
World.Add(this);
}
public void Tick()
@@ -320,8 +329,7 @@ namespace OpenRA
public void CancelActivity()
{
if (CurrentActivity != null)
CurrentActivity.Cancel(this);
CurrentActivity?.Cancel(this);
}
public override int GetHashCode()
@@ -373,8 +381,7 @@ namespace OpenRA
{
// If CurrentActivity isn't null, run OnActorDisposeOuter in case some cleanups are needed.
// This should be done before the FrameEndTask to avoid dependency issues.
if (CurrentActivity != null)
CurrentActivity.OnActorDisposeOuter(this);
CurrentActivity?.OnActorDisposeOuter(this);
// Allow traits/activities to prevent a race condition when they depend on disposing the actor (e.g. Transforms)
WillDispose = true;
@@ -393,8 +400,7 @@ namespace OpenRA
World.TraitDict.RemoveActor(this);
Disposed = true;
if (luaInterface != null)
luaInterface.Value.OnActorDestroyed();
luaInterface?.Value.OnActorDestroyed();
});
}
@@ -528,10 +534,16 @@ namespace OpenRA
notify(this, readOnlyConditionCache);
}
/// <summary>Grants a specified condition.</summary>
/// <summary>
/// Grants a specified condition if it is valid.
/// Otherwise, just returns InvalidConditionToken.
/// </summary>
/// <returns>The token that is used to revoke this condition.</returns>
public int GrantCondition(string condition)
{
if (string.IsNullOrEmpty(condition))
return InvalidConditionToken;
var token = nextConditionToken++;
conditionTokens.Add(token, condition);
UpdateConditionState(condition, token, false);
@@ -545,8 +557,7 @@ namespace OpenRA
/// <returns>The invalid token ID.</returns>
public int RevokeCondition(int token)
{
string condition;
if (!conditionTokens.TryGetValue(token, out condition))
if (!conditionTokens.TryGetValue(token, out var condition))
throw new InvalidOperationException("Attempting to revoke condition with invalid token {0} for {1}.".F(token, this));
conditionTokens.Remove(token);
@@ -579,8 +590,7 @@ namespace OpenRA
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
{
Actor a, b;
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
if (!left.TryGetClrValue(out Actor a) || !right.TryGetClrValue(out Actor b))
return false;
return a == b;

View File

@@ -89,9 +89,7 @@ namespace OpenRA
public LuaValue Add(LuaRuntime runtime, LuaValue left, LuaValue right)
{
CPos a;
CVec b;
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
if (!left.TryGetClrValue(out CPos a) || !right.TryGetClrValue(out CVec b))
throw new LuaException("Attempted to call CPos.Add(CPos, CVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
return new LuaCustomClrObject(a + b);
@@ -99,21 +97,18 @@ namespace OpenRA
public LuaValue Subtract(LuaRuntime runtime, LuaValue left, LuaValue right)
{
CPos a;
var rightType = right.WrappedClrType();
if (!left.TryGetClrValue(out a))
if (!left.TryGetClrValue(out CPos a))
throw new LuaException("Attempted to call CPos.Subtract(CPos, (CPos|CVec)) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, rightType.Name));
if (rightType == typeof(CPos))
{
CPos b;
right.TryGetClrValue(out b);
right.TryGetClrValue(out CPos b);
return new LuaCustomClrObject(a - b);
}
else if (rightType == typeof(CVec))
{
CVec b;
right.TryGetClrValue(out b);
right.TryGetClrValue(out CVec b);
return new LuaCustomClrObject(a - b);
}
@@ -122,8 +117,7 @@ namespace OpenRA
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
{
CPos a, b;
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
if (!left.TryGetClrValue(out CPos a) || !right.TryGetClrValue(out CPos b))
return false;
return a == b;

View File

@@ -75,8 +75,7 @@ namespace OpenRA
public LuaValue Add(LuaRuntime runtime, LuaValue left, LuaValue right)
{
CVec a, b;
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
if (!left.TryGetClrValue(out CVec a) || !right.TryGetClrValue(out CVec b))
throw new LuaException("Attempted to call CVec.Add(CVec, CVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
return new LuaCustomClrObject(a + b);
@@ -84,8 +83,7 @@ namespace OpenRA
public LuaValue Subtract(LuaRuntime runtime, LuaValue left, LuaValue right)
{
CVec a, b;
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
if (!left.TryGetClrValue(out CVec a) || !right.TryGetClrValue(out CVec b))
throw new LuaException("Attempted to call CVec.Subtract(CVec, CVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
return new LuaCustomClrObject(a - b);
@@ -98,8 +96,7 @@ namespace OpenRA
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
{
CVec a, b;
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
if (!left.TryGetClrValue(out CVec a) || !right.TryGetClrValue(out CVec b))
return false;
return a == b;

View File

@@ -90,8 +90,7 @@ namespace OpenRA
public void CancelAsync()
{
lock (syncObject)
if (wc != null)
wc.CancelAsync();
wc?.CancelAsync();
}
}
}

View File

@@ -125,8 +125,7 @@ namespace OpenRA
public static V GetOrAdd<K, V>(this Dictionary<K, V> d, K k, Func<K, V> createFn)
{
V ret;
if (!d.TryGetValue(k, out ret))
if (!d.TryGetValue(k, out var ret))
d.Add(k, ret = createFn(k));
return ret;
}
@@ -353,8 +352,7 @@ namespace OpenRA
public static int IntegerDivisionRoundingAwayFromZero(int dividend, int divisor)
{
int remainder;
var quotient = Math.DivRem(dividend, divisor, out remainder);
var quotient = Math.DivRem(dividend, divisor, out var remainder);
if (remainder == 0)
return quotient;
return quotient + (Math.Sign(dividend) == Math.Sign(divisor) ? 1 : -1);
@@ -405,8 +403,7 @@ namespace OpenRA
// Check for a key conflict:
if (d.ContainsKey(key))
{
List<string> dupKeyMessages;
if (!dupKeys.TryGetValue(key, out dupKeyMessages))
if (!dupKeys.TryGetValue(key, out var dupKeyMessages))
{
// Log the initial conflicting value already inserted:
dupKeyMessages = new List<string>();

View File

@@ -18,7 +18,6 @@ using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
using OpenRA.Graphics;
using OpenRA.Primitives;
using OpenRA.Support;
@@ -122,8 +121,7 @@ namespace OpenRA
{
ret = null;
MiniYaml yaml;
if (!md.TryGetValue(yamlName, out yaml))
if (!md.TryGetValue(yamlName, out var yaml))
return false;
ret = GetValue(field.Name, field.FieldType, yaml, field);
@@ -181,42 +179,36 @@ namespace OpenRA
public static object GetValue(string fieldName, Type fieldType, MiniYaml yaml, MemberInfo field)
{
var value = yaml.Value;
if (value != null) value = value.Trim();
var value = yaml.Value?.Trim();
if (fieldType == typeof(int))
{
int res;
if (Exts.TryParseIntegerInvariant(value, out res))
if (Exts.TryParseIntegerInvariant(value, out var res))
return res;
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(ushort))
{
ushort res;
if (ushort.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out res))
if (ushort.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out var res))
return res;
return InvalidValueAction(value, fieldType, fieldName);
}
if (fieldType == typeof(long))
{
long res;
if (long.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out res))
if (long.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out var res))
return res;
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(float))
{
float res;
if (value != null && float.TryParse(value.Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
if (value != null && float.TryParse(value.Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var res))
return res * (value.Contains('%') ? 0.01f : 1f);
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(decimal))
{
decimal res;
if (value != null && decimal.TryParse(value.Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
if (value != null && decimal.TryParse(value.Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var res))
return res * (value.Contains('%') ? 0.01m : 1m);
return InvalidValueAction(value, fieldType, fieldName);
}
@@ -228,16 +220,14 @@ namespace OpenRA
}
else if (fieldType == typeof(Color))
{
Color color;
if (value != null && Color.TryParse(value, out color))
if (value != null && Color.TryParse(value, out var color))
return color;
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(Hotkey))
{
Hotkey res;
if (Hotkey.TryParse(value, out res))
if (Hotkey.TryParse(value, out var res))
return res;
return InvalidValueAction(value, fieldType, fieldName);
@@ -248,8 +238,7 @@ namespace OpenRA
}
else if (fieldType == typeof(WDist))
{
WDist res;
if (WDist.TryParse(value, out res))
if (WDist.TryParse(value, out var res))
return res;
return InvalidValueAction(value, fieldType, fieldName);
@@ -261,8 +250,7 @@ namespace OpenRA
var parts = value.Split(',');
if (parts.Length == 3)
{
WDist rx, ry, rz;
if (WDist.TryParse(parts[0], out rx) && WDist.TryParse(parts[1], out ry) && WDist.TryParse(parts[2], out rz))
if (WDist.TryParse(parts[0], out var rx) && WDist.TryParse(parts[1], out var ry) && WDist.TryParse(parts[2], out var rz))
return new WVec(rx, ry, rz);
}
}
@@ -282,8 +270,7 @@ namespace OpenRA
for (var i = 0; i < vecs.Length; ++i)
{
WDist rx, ry, rz;
if (WDist.TryParse(parts[3 * i], out rx) && WDist.TryParse(parts[3 * i + 1], out ry) && WDist.TryParse(parts[3 * i + 2], out rz))
if (WDist.TryParse(parts[3 * i], out var rx) && WDist.TryParse(parts[3 * i + 1], out var ry) && WDist.TryParse(parts[3 * i + 2], out var rz))
vecs[i] = new WVec(rx, ry, rz);
}
@@ -299,8 +286,7 @@ namespace OpenRA
var parts = value.Split(',');
if (parts.Length == 3)
{
WDist rx, ry, rz;
if (WDist.TryParse(parts[0], out rx) && WDist.TryParse(parts[1], out ry) && WDist.TryParse(parts[2], out rz))
if (WDist.TryParse(parts[0], out var rx) && WDist.TryParse(parts[1], out var ry) && WDist.TryParse(parts[2], out var rz))
return new WPos(rx, ry, rz);
}
}
@@ -309,8 +295,7 @@ namespace OpenRA
}
else if (fieldType == typeof(WAngle))
{
int res;
if (Exts.TryParseIntegerInvariant(value, out res))
if (Exts.TryParseIntegerInvariant(value, out var res))
return new WAngle(res);
return InvalidValueAction(value, fieldType, fieldName);
}
@@ -321,8 +306,7 @@ namespace OpenRA
var parts = value.Split(',');
if (parts.Length == 3)
{
int rr, rp, ry;
if (Exts.TryParseIntegerInvariant(parts[0], out rr) && Exts.TryParseIntegerInvariant(parts[1], out rp) && Exts.TryParseIntegerInvariant(parts[2], out ry))
if (Exts.TryParseIntegerInvariant(parts[0], out var rr) && Exts.TryParseIntegerInvariant(parts[1], out var rp) && Exts.TryParseIntegerInvariant(parts[2], out var ry))
return new WRot(new WAngle(rr), new WAngle(rp), new WAngle(ry));
}
}
@@ -361,8 +345,7 @@ namespace OpenRA
var vecs = new CVec[parts.Length / 2];
for (var i = 0; i < vecs.Length; i++)
{
int rx, ry;
if (int.TryParse(parts[2 * i], out rx) && int.TryParse(parts[2 * i + 1], out ry))
if (int.TryParse(parts[2 * i], out var rx) && int.TryParse(parts[2 * i + 1], out var ry))
vecs[i] = new CVec(rx, ry);
}
@@ -416,8 +399,7 @@ namespace OpenRA
}
else if (fieldType == typeof(bool))
{
bool result;
if (bool.TryParse(value.ToLowerInvariant(), out result))
if (bool.TryParse(value.ToLowerInvariant(), out var result))
return result;
return InvalidValueAction(value, fieldType, fieldName);
@@ -510,8 +492,7 @@ namespace OpenRA
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
float xx = 0;
float yy = 0;
float res;
if (float.TryParse(parts[0].Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
if (float.TryParse(parts[0].Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var res))
xx = res * (parts[0].Contains('%') ? 0.01f : 1f);
if (float.TryParse(parts[1].Replace("%", ""), NumberStyles.Float, NumberFormatInfo.InvariantInfo, out res))
yy = res * (parts[1].Contains('%') ? 0.01f : 1f);
@@ -525,13 +506,11 @@ namespace OpenRA
if (value != null)
{
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
float x = 0;
float y = 0;
float z = 0;
float.TryParse(parts[0], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out x);
float.TryParse(parts[1], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out y);
float.TryParse(parts[0], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var x);
float.TryParse(parts[1], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var y);
// z component is optional for compatibility with older float2 definitions
float z = 0;
if (parts.Length > 2)
float.TryParse(parts[2], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out z);
@@ -567,14 +546,16 @@ namespace OpenRA
}
else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
if (string.IsNullOrEmpty(value))
return null;
var innerType = fieldType.GetGenericArguments().First();
var innerValue = GetValue("Nullable<T>", innerType, value, field);
return fieldType.GetConstructor(new[] { innerType }).Invoke(new[] { innerValue });
}
else if (fieldType == typeof(DateTime))
{
DateTime dt;
if (DateTime.TryParseExact(value, "yyyy-MM-dd HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out dt))
if (DateTime.TryParseExact(value, "yyyy-MM-dd HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var dt))
return dt;
return InvalidValueAction(value, fieldType, fieldName);
}
@@ -724,8 +705,7 @@ namespace OpenRA
if (translations == null)
return key;
string value;
if (!translations.TryGetValue(key, out value))
if (!translations.TryGetValue(key, out var value))
return key;
return value;

View File

@@ -15,7 +15,6 @@ using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA

View File

@@ -67,15 +67,12 @@ namespace OpenRA.FileSystem
return new Folder(resolvedPath);
// Children of another package require special handling
IReadOnlyPackage parent;
string subPath = null;
if (TryGetPackageContaining(filename, out parent, out subPath))
if (TryGetPackageContaining(filename, out var parent, out var subPath))
return parent.OpenPackage(subPath, this);
// Try and open it normally
IReadOnlyPackage package;
var stream = Open(filename);
if (TryParsePackage(stream, filename, out package))
if (TryParsePackage(stream, filename, out var package))
return package;
// No package loaders took ownership of the stream, so clean it up
@@ -97,8 +94,7 @@ namespace OpenRA.FileSystem
{
name = name.Substring(1);
Manifest mod;
if (!installedMods.TryGetValue(name, out mod))
if (!installedMods.TryGetValue(name, out var mod))
throw new InvalidOperationException("Could not load mod '{0}'. Available mods: {1}".F(name, installedMods.Keys.JoinWith(", ")));
package = mod.Package;
@@ -122,8 +118,7 @@ namespace OpenRA.FileSystem
public void Mount(IReadOnlyPackage package, string explicitName = null)
{
var mountCount = 0;
if (mountedPackages.TryGetValue(package, out mountCount))
if (mountedPackages.TryGetValue(package, out var mountCount))
{
// Package is already mounted
// Increment the mount count and bump up the file loading priority
@@ -149,8 +144,7 @@ namespace OpenRA.FileSystem
public bool Unmount(IReadOnlyPackage package)
{
var mountCount = 0;
if (!mountedPackages.TryGetValue(package, out mountCount))
if (!mountedPackages.TryGetValue(package, out var mountCount))
return false;
if (--mountCount <= 0)
@@ -203,16 +197,12 @@ namespace OpenRA.FileSystem
var package = fileIndex[filename]
.LastOrDefault(x => x.Contains(filename));
if (package != null)
return package.GetStream(filename);
return null;
return package?.GetStream(filename);
}
public Stream Open(string filename)
{
Stream s;
if (!TryOpen(filename, out s))
if (!TryOpen(filename, out var s))
throw new FileNotFoundException("File not found: {0}".F(filename), filename);
return s;
@@ -238,8 +228,7 @@ namespace OpenRA.FileSystem
var explicitSplit = filename.IndexOf('|');
if (explicitSplit > 0)
{
IReadOnlyPackage explicitPackage;
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out explicitPackage))
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out var explicitPackage))
{
s = explicitPackage.GetStream(filename.Substring(explicitSplit + 1));
if (s != null)
@@ -274,12 +263,9 @@ namespace OpenRA.FileSystem
{
var explicitSplit = filename.IndexOf('|');
if (explicitSplit > 0)
{
IReadOnlyPackage explicitPackage;
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out explicitPackage))
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out var explicitPackage))
if (explicitPackage.Contains(filename.Substring(explicitSplit + 1)))
return true;
}
return fileIndex.ContainsKey(filename);
}
@@ -293,8 +279,7 @@ namespace OpenRA.FileSystem
if (explicitSplit < 0)
return false;
IReadOnlyPackage explicitPackage;
if (!explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out explicitPackage))
if (!explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out var explicitPackage))
return false;
if (installedMods[modID].Package == explicitPackage)
@@ -321,8 +306,7 @@ namespace OpenRA.FileSystem
if (parentPath.StartsWith("$", StringComparison.Ordinal))
{
Manifest mod;
if (!installedMods.TryGetValue(parentPath.Substring(1), out mod))
if (!installedMods.TryGetValue(parentPath.Substring(1), out var mod))
return null;
if (!(mod.Package is Folder))

View File

@@ -58,17 +58,15 @@ namespace OpenRA.FileSystem
return new Folder(resolvedPath);
// Zip files loaded from Folders (and *only* from Folders) can be read-write
IReadWritePackage readWritePackage;
if (ZipFileLoader.TryParseReadWritePackage(resolvedPath, out readWritePackage))
if (ZipFileLoader.TryParseReadWritePackage(resolvedPath, out var readWritePackage))
return readWritePackage;
// Other package types can be loaded normally
IReadOnlyPackage package;
var s = GetStream(filename);
if (s == null)
return null;
if (context.TryParsePackage(s, filename, out package))
if (context.TryParsePackage(s, filename, out var package))
return package;
s.Dispose();

View File

@@ -14,7 +14,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using ICSharpCode.SharpZipLib.Zip;
using OpenRA.Primitives;
namespace OpenRA.FileSystem
{
@@ -67,8 +66,7 @@ namespace OpenRA.FileSystem
public void Dispose()
{
if (pkg != null)
pkg.Close();
pkg?.Close();
}
public IReadOnlyPackage OpenPackage(string filename, FileSystem context)
@@ -82,12 +80,11 @@ namespace OpenRA.FileSystem
return new ZipFolder(this, filename);
// Other package types can be loaded normally
IReadOnlyPackage package;
var s = GetStream(filename);
if (s == null)
return null;
if (context.TryParsePackage(s, filename, out package))
if (context.TryParsePackage(s, filename, out var package))
return package;
s.Dispose();

View File

@@ -75,12 +75,12 @@ namespace OpenRA
static string TimestampedFilename(bool includemilliseconds = false)
{
var format = includemilliseconds ? "yyyy-MM-ddTHHmmssfffZ" : "yyyy-MM-ddTHHmmssZ";
return "OpenRA-" + DateTime.UtcNow.ToString(format, CultureInfo.InvariantCulture);
return ModData.Manifest.Id + "-" + DateTime.UtcNow.ToString(format, CultureInfo.InvariantCulture);
}
static void JoinInner(OrderManager om)
{
if (OrderManager != null) OrderManager.Dispose();
OrderManager?.Dispose();
OrderManager = om;
lastConnectionState = ConnectionState.PreConnecting;
ConnectionStateChanged(OrderManager);
@@ -154,8 +154,7 @@ namespace OpenRA
internal static void StartGame(string mapUID, WorldType type)
{
// Dispose of the old world before creating a new one.
if (worldRenderer != null)
worldRenderer.Dispose();
worldRenderer?.Dispose();
Cursor.SetCursor(null);
BeforeGameStart();
@@ -330,11 +329,9 @@ namespace OpenRA
Log.Write("graphics", "{0}", e);
Console.WriteLine("Renderer initialization failed. Check graphics.log for details.");
if (Renderer != null)
Renderer.Dispose();
Renderer?.Dispose();
if (Sound != null)
Sound.Dispose();
Sound?.Dispose();
}
}
@@ -355,8 +352,7 @@ namespace OpenRA
ExternalMods = new ExternalMods();
Manifest currentMod;
if (modID != null && Mods.TryGetValue(modID, out currentMod))
if (modID != null && Mods.TryGetValue(modID, out _))
{
var launchPath = args.GetValue("Engine.LaunchPath", Assembly.GetEntryAssembly().Location);
@@ -367,8 +363,7 @@ namespace OpenRA
ExternalMods.Register(Mods[modID], launchPath, ModRegistration.User);
ExternalMod activeMod;
if (ExternalMods.TryGetValue(ExternalMod.MakeKey(Mods[modID]), out activeMod))
if (ExternalMods.TryGetValue(ExternalMod.MakeKey(Mods[modID]), out var activeMod))
ExternalMods.ClearInvalidRegistrations(activeMod, ModRegistration.User);
}
@@ -390,13 +385,10 @@ namespace OpenRA
Ui.ResetAll();
if (worldRenderer != null)
worldRenderer.Dispose();
worldRenderer?.Dispose();
worldRenderer = null;
if (server != null)
server.Shutdown();
if (OrderManager != null)
OrderManager.Dispose();
server?.Shutdown();
OrderManager?.Dispose();
if (ModData != null)
{
@@ -432,8 +424,7 @@ namespace OpenRA
var grid = ModData.Manifest.Contains<MapGrid>() ? ModData.Manifest.Get<MapGrid>() : null;
Renderer.InitializeDepthBuffer(grid);
if (Cursor != null)
Cursor.Dispose();
Cursor?.Dispose();
Cursor = new CursorManager(ModData.CursorProvider);
@@ -442,13 +433,13 @@ namespace OpenRA
PerfHistory.Items["render_world"].HasNormalTick = false;
PerfHistory.Items["render_widgets"].HasNormalTick = false;
PerfHistory.Items["render_flip"].HasNormalTick = false;
PerfHistory.Items["terrain_lighting"].HasNormalTick = false;
JoinLocal();
try
{
if (discoverNat != null)
discoverNat.Wait();
discoverNat?.Wait();
}
catch (Exception e)
{
@@ -611,8 +602,7 @@ namespace OpenRA
Sync.RunUnsynced(Settings.Debug.SyncCheckUnsyncedCode, world, () => world.TickRender(worldRenderer));
}
if (benchmark != null)
benchmark.Tick(LocalTick);
benchmark?.Tick(LocalTick);
}
}
@@ -710,6 +700,7 @@ namespace OpenRA
PerfHistory.Items["render_world"].Tick();
PerfHistory.Items["render_widgets"].Tick();
PerfHistory.Items["render_flip"].Tick();
PerfHistory.Items["terrain_lighting"].Tick();
}
static void Loop()
@@ -836,12 +827,10 @@ namespace OpenRA
finally
{
// Ensure that the active replay is properly saved
if (OrderManager != null)
OrderManager.Dispose();
OrderManager?.Dispose();
}
if (worldRenderer != null)
worldRenderer.Dispose();
worldRenderer?.Dispose();
ModData.Dispose();
ChromeProvider.Deinitialize();
@@ -880,8 +869,7 @@ namespace OpenRA
public static void Disconnect()
{
if (OrderManager.World != null)
OrderManager.World.TraitDict.PrintReport();
OrderManager.World?.TraitDict.PrintReport();
OrderManager.Dispose();
CloseServer();
@@ -890,8 +878,7 @@ namespace OpenRA
public static void CloseServer()
{
if (server != null)
server.Shutdown();
server?.Shutdown();
}
public static T CreateObject<T>(string name)

View File

@@ -133,9 +133,7 @@ namespace OpenRA
/// <summary>Gets the player information for the specified runtime player instance.</summary>
public Player GetPlayer(OpenRA.Player runtimePlayer)
{
Player player;
playersByRuntime.TryGetValue(runtimePlayer, out player);
playersByRuntime.TryGetValue(runtimePlayer, out var player);
return player;
}

View File

@@ -23,6 +23,7 @@ namespace OpenRA
public class ActorInfo
{
public const string AbstractActorPrefix = "^";
public const char TraitInstanceSeparator = '@';
/// <summary>
/// The actor name can be anything, but the sprites used in the Render*: traits default to this one.
@@ -32,7 +33,7 @@ namespace OpenRA
/// </summary>
public readonly string Name;
readonly TypeDictionary traits = new TypeDictionary();
List<ITraitInfo> constructOrderCache = null;
List<TraitInfo> constructOrderCache = null;
public ActorInfo(ObjectCreator creator, string name, MiniYaml node)
{
@@ -46,7 +47,7 @@ namespace OpenRA
{
// HACK: The linter does not want to crash when a trait doesn't exist but only print an error instead
// LoadTraitInfo will only return null to signal us to abort here if the linter is running
var trait = LoadTraitInfo(creator, t.Key.Split('@')[0], t.Value);
var trait = LoadTraitInfo(creator, t.Key, t.Value);
if (trait != null)
traits.Add(trait);
}
@@ -64,7 +65,7 @@ namespace OpenRA
}
}
public ActorInfo(string name, params ITraitInfo[] traitInfos)
public ActorInfo(string name, params TraitInfo[] traitInfos)
{
Name = name;
foreach (var t in traitInfos)
@@ -72,7 +73,7 @@ namespace OpenRA
traits.TrimExcess();
}
static ITraitInfo LoadTraitInfo(ObjectCreator creator, string traitName, MiniYaml my)
static TraitInfo LoadTraitInfo(ObjectCreator creator, string traitName, MiniYaml my)
{
if (!string.IsNullOrEmpty(my.Value))
throw new YamlException("Junk value `{0}` on trait node {1}"
@@ -80,12 +81,16 @@ namespace OpenRA
// HACK: The linter does not want to crash when a trait doesn't exist but only print an error instead
// ObjectCreator will only return null to signal us to abort here if the linter is running
var info = creator.CreateObject<ITraitInfo>(traitName + "Info");
var traitInstance = traitName.Split(TraitInstanceSeparator);
var info = creator.CreateObject<TraitInfo>(traitInstance[0] + "Info");
if (info == null)
return null;
try
{
if (traitInstance.Length > 1)
info.GetType().GetField("InstanceName").SetValue(info, traitInstance[1]);
FieldLoader.Load(info, my);
}
catch (FieldLoader.MissingFieldsException e)
@@ -97,12 +102,12 @@ namespace OpenRA
return info;
}
public IEnumerable<ITraitInfo> TraitsInConstructOrder()
public IEnumerable<TraitInfo> TraitsInConstructOrder()
{
if (constructOrderCache != null)
return constructOrderCache;
var source = traits.WithInterface<ITraitInfo>().Select(i => new
var source = traits.WithInterface<TraitInfo>().Select(i => new
{
Trait = i,
Type = i.GetType(),
@@ -148,7 +153,7 @@ namespace OpenRA
return constructOrderCache;
}
public static IEnumerable<Type> PrerequisitesOf(ITraitInfo info)
public static IEnumerable<Type> PrerequisitesOf(TraitInfo info)
{
return info
.GetType()

View File

@@ -41,8 +41,7 @@ namespace OpenRA.GameRules
public void Load(IReadOnlyFileSystem fileSystem)
{
Stream stream;
if (!fileSystem.TryOpen(Filename, out stream))
if (!fileSystem.TryOpen(Filename, out var stream))
return;
try
@@ -50,8 +49,7 @@ namespace OpenRA.GameRules
Exists = true;
foreach (var loader in Game.ModData.SoundLoaders)
{
ISoundFormat soundFormat;
if (loader.TryParseSound(stream, out soundFormat))
if (loader.TryParseSound(stream, out var soundFormat))
{
Length = (int)soundFormat.LengthInSeconds;
soundFormat.Dispose();

View File

@@ -24,8 +24,8 @@ namespace OpenRA.GameRules
public int[] DamageModifiers;
public int[] InaccuracyModifiers;
public int[] RangeModifiers;
public int Facing;
public Func<int> CurrentMuzzleFacing;
public WAngle Facing;
public Func<WAngle> CurrentMuzzleFacing;
public WPos Source;
public Func<WPos> CurrentSource;
public Actor SourceActor;
@@ -38,6 +38,8 @@ namespace OpenRA.GameRules
public WeaponInfo Weapon;
public int[] DamageModifiers = { };
public WPos? Source;
public WRot ImpactOrientation;
public WPos ImpactPosition;
public Actor SourceActor;
public Target WeaponTarget;
@@ -45,6 +47,7 @@ namespace OpenRA.GameRules
{
Weapon = args.Weapon;
DamageModifiers = args.DamageModifiers;
ImpactPosition = args.PassiveTarget;
Source = args.Source;
SourceActor = args.SourceActor;
WeaponTarget = args.GuidedTarget;
@@ -102,6 +105,12 @@ namespace OpenRA.GameRules
[Desc("What types of targets are unaffected.", "Overrules ValidTargets.")]
public readonly BitSet<TargetableType> InvalidTargets;
static readonly BitSet<TargetableType> TargetTypeAir = new BitSet<TargetableType>("Air");
[Desc("If weapon is not directly targeting an actor and targeted position is above this altitude,",
"the weapon will ignore terrain target types and only check TargetTypeAir for validity.")]
public readonly WDist AirThreshold = new WDist(128);
[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 };
@@ -128,8 +137,7 @@ namespace OpenRA.GameRules
static object LoadProjectile(MiniYaml yaml)
{
MiniYaml proj;
if (!yaml.ToDictionary().TryGetValue("Projectile", out proj))
if (!yaml.ToDictionary().TryGetValue("Projectile", out var proj))
return null;
var ret = Game.CreateObject<IProjectileInfo>(proj.Value + "Info");
FieldLoader.Load(ret, proj);
@@ -165,6 +173,10 @@ namespace OpenRA.GameRules
if (target.Type == TargetType.Terrain)
{
var dat = world.Map.DistanceAboveTerrain(target.CenterPosition);
if (dat > AirThreshold)
return IsValidTarget(TargetTypeAir);
var cell = world.Map.CellContaining(target.CenterPosition);
if (!world.Map.Contains(cell))
return false;

View File

@@ -54,12 +54,12 @@ namespace OpenRA.Graphics
public IRenderable[] Render(WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale)
{
var imageRenderable = new SpriteRenderable(Image, pos, offset, CurrentSequence.ZOffset + zOffset, palette, scale, IsDecoration);
var imageRenderable = new SpriteRenderable(Image, pos, offset, CurrentSequence.ZOffset + zOffset, palette, scale, IsDecoration, CurrentSequence.IgnoreWorldTint);
if (CurrentSequence.ShadowStart >= 0)
{
var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc());
var shadowRenderable = new SpriteRenderable(shadow, pos, offset, CurrentSequence.ShadowZOffset + zOffset, palette, scale, true);
var shadowRenderable = new SpriteRenderable(shadow, pos, offset, CurrentSequence.ShadowZOffset + zOffset, palette, scale, true, CurrentSequence.IgnoreWorldTint);
return new IRenderable[] { shadowRenderable, imageRenderable };
}
@@ -156,7 +156,7 @@ namespace OpenRA.Graphics
{
frame = CurrentSequence.Length - 1;
tickFunc = () => { };
if (after != null) after();
after?.Invoke();
}
};
}

View File

@@ -54,10 +54,10 @@ namespace OpenRA.Graphics
public static IReadOnlyDictionary<string, Collection> Collections { get; private set; }
static Dictionary<string, Collection> collections;
static Dictionary<string, Pair<Sheet, int>> cachedSheets;
static Dictionary<string, (Sheet Sheet, int Density)> cachedSheets;
static Dictionary<string, Dictionary<string, Sprite>> cachedSprites;
static Dictionary<string, Sprite[]> cachedPanelSprites;
static Dictionary<Collection, Pair<Sheet, int>> cachedCollectionSheets;
static Dictionary<Collection, (Sheet Sheet, int)> cachedCollectionSheets;
static IReadOnlyFileSystem fileSystem;
static float dpiScale = 1;
@@ -72,10 +72,10 @@ namespace OpenRA.Graphics
fileSystem = modData.DefaultFileSystem;
collections = new Dictionary<string, Collection>();
cachedSheets = new Dictionary<string, Pair<Sheet, int>>();
cachedSheets = new Dictionary<string, (Sheet, int)>();
cachedSprites = new Dictionary<string, Dictionary<string, Sprite>>();
cachedPanelSprites = new Dictionary<string, Sprite[]>();
cachedCollectionSheets = new Dictionary<Collection, Pair<Sheet, int>>();
cachedCollectionSheets = new Dictionary<Collection, (Sheet, int)>();
Collections = new ReadOnlyDictionary<string, Collection>(collections);
@@ -91,7 +91,7 @@ namespace OpenRA.Graphics
{
if (cachedSheets != null)
foreach (var sheet in cachedSheets.Values)
sheet.First.Dispose();
sheet.Sheet.Dispose();
collections = null;
cachedSheets = null;
@@ -108,12 +108,10 @@ namespace OpenRA.Graphics
collections.Add(name, FieldLoader.Load<Collection>(yaml));
}
static Pair<Sheet, int> SheetForCollection(Collection c)
static (Sheet Sheet, int Density) SheetForCollection(Collection c)
{
Pair<Sheet, int> sheetDensity;
// Outer cache avoids recalculating image names
if (!cachedCollectionSheets.TryGetValue(c, out sheetDensity))
if (!cachedCollectionSheets.TryGetValue(c, out (Sheet, int) sheetDensity))
{
var image = c.Image;
var density = 1;
@@ -137,7 +135,7 @@ namespace OpenRA.Graphics
sheet.GetTexture().ScaleFilter = TextureScaleFilter.Linear;
sheetDensity = Pair.New(sheet, density);
sheetDensity = (sheet, density);
cachedSheets.Add(image, sheetDensity);
}
@@ -153,20 +151,16 @@ namespace OpenRA.Graphics
return null;
// Cached sprite
Dictionary<string, Sprite> cachedCollection;
Sprite sprite;
if (cachedSprites.TryGetValue(collectionName, out cachedCollection) && cachedCollection.TryGetValue(imageName, out sprite))
if (cachedSprites.TryGetValue(collectionName, out var cachedCollection) && cachedCollection.TryGetValue(imageName, out var sprite))
return sprite;
Collection collection;
if (!collections.TryGetValue(collectionName, out collection))
if (!collections.TryGetValue(collectionName, out var collection))
{
Log.Write("debug", "Could not find collection '{0}'", collectionName);
return null;
}
Rectangle mi;
if (!collection.Regions.TryGetValue(imageName, out mi))
if (!collection.Regions.TryGetValue(imageName, out var mi))
return null;
// Cache the sprite
@@ -177,7 +171,7 @@ namespace OpenRA.Graphics
cachedSprites.Add(collectionName, cachedCollection);
}
var image = new Sprite(sheetDensity.First, sheetDensity.Second * mi, TextureChannel.RGBA, 1f / sheetDensity.Second);
var image = new Sprite(sheetDensity.Sheet, sheetDensity.Density * mi, TextureChannel.RGBA, 1f / sheetDensity.Density);
cachedCollection.Add(imageName, image);
return image;
@@ -189,12 +183,10 @@ namespace OpenRA.Graphics
return null;
// Cached sprite
Sprite[] cachedSprites;
if (cachedPanelSprites.TryGetValue(collectionName, out cachedSprites))
if (cachedPanelSprites.TryGetValue(collectionName, out var cachedSprites))
return cachedSprites;
Collection collection;
if (!collections.TryGetValue(collectionName, out collection))
if (!collections.TryGetValue(collectionName, out var collection))
{
Log.Write("debug", "Could not find collection '{0}'", collectionName);
return null;
@@ -214,20 +206,20 @@ namespace OpenRA.Graphics
var pr = collection.PanelRegion;
var ps = collection.PanelSides;
var sides = new[]
var sides = new (PanelSides PanelSides, Rectangle Bounds)[]
{
Pair.New(PanelSides.Top | PanelSides.Left, new Rectangle(pr[0], pr[1], pr[2], pr[3])),
Pair.New(PanelSides.Top, new Rectangle(pr[0] + pr[2], pr[1], pr[4], pr[3])),
Pair.New(PanelSides.Top | PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1], pr[6], pr[3])),
Pair.New(PanelSides.Left, new Rectangle(pr[0], pr[1] + pr[3], pr[2], pr[5])),
Pair.New(PanelSides.Center, new Rectangle(pr[0] + pr[2], pr[1] + pr[3], pr[4], pr[5])),
Pair.New(PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1] + pr[3], pr[6], pr[5])),
Pair.New(PanelSides.Bottom | PanelSides.Left, new Rectangle(pr[0], pr[1] + pr[3] + pr[5], pr[2], pr[7])),
Pair.New(PanelSides.Bottom, new Rectangle(pr[0] + pr[2], pr[1] + pr[3] + pr[5], pr[4], pr[7])),
Pair.New(PanelSides.Bottom | PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1] + pr[3] + pr[5], pr[6], pr[7]))
(PanelSides.Top | PanelSides.Left, new Rectangle(pr[0], pr[1], pr[2], pr[3])),
(PanelSides.Top, new Rectangle(pr[0] + pr[2], pr[1], pr[4], pr[3])),
(PanelSides.Top | PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1], pr[6], pr[3])),
(PanelSides.Left, new Rectangle(pr[0], pr[1] + pr[3], pr[2], pr[5])),
(PanelSides.Center, new Rectangle(pr[0] + pr[2], pr[1] + pr[3], pr[4], pr[5])),
(PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1] + pr[3], pr[6], pr[5])),
(PanelSides.Bottom | PanelSides.Left, new Rectangle(pr[0], pr[1] + pr[3] + pr[5], pr[2], pr[7])),
(PanelSides.Bottom, new Rectangle(pr[0] + pr[2], pr[1] + pr[3] + pr[5], pr[4], pr[7])),
(PanelSides.Bottom | PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1] + pr[3] + pr[5], pr[6], pr[7]))
};
sprites = sides.Select(x => ps.HasSide(x.First) ? new Sprite(sheetDensity.First, sheetDensity.Second * x.Second, TextureChannel.RGBA, 1f / sheetDensity.Second) : null)
sprites = sides.Select(x => ps.HasSide(x.PanelSides) ? new Sprite(sheetDensity.Sheet, sheetDensity.Density * x.Bounds, TextureChannel.RGBA, 1f / sheetDensity.Density) : null)
.ToArray();
}
else
@@ -256,8 +248,7 @@ namespace OpenRA.Graphics
if (string.IsNullOrEmpty(collectionName))
return new Size(0, 0);
Collection collection;
if (!collections.TryGetValue(collectionName, out collection))
if (!collections.TryGetValue(collectionName, out var collection))
{
Log.Write("debug", "Could not find collection '{0}'", collectionName);
return new Size(0, 0);

View File

@@ -47,15 +47,13 @@ namespace OpenRA.Graphics
if (d.ContainsKey("X"))
{
int x;
Exts.TryParseIntegerInvariant(d["X"].Value, out x);
Exts.TryParseIntegerInvariant(d["X"].Value, out var x);
Hotspot = Hotspot.WithX(x);
}
if (d.ContainsKey("Y"))
{
int y;
Exts.TryParseIntegerInvariant(d["Y"].Value, out y);
Exts.TryParseIntegerInvariant(d["Y"].Value, out var y);
Hotspot = Hotspot.WithY(y);
}
}

View File

@@ -38,19 +38,16 @@ namespace OpenRA.Graphics
public IPalette GetPalette(string name)
{
MutablePalette mutable;
if (modifiablePalettes.TryGetValue(name, out mutable))
if (modifiablePalettes.TryGetValue(name, out var mutable))
return mutable.AsReadOnly();
ImmutablePalette immutable;
if (palettes.TryGetValue(name, out immutable))
if (palettes.TryGetValue(name, out var immutable))
return immutable;
throw new InvalidOperationException("Palette `{0}` does not exist".F(name));
}
public int GetPaletteIndex(string name)
{
int ret;
if (!indices.TryGetValue(name, out ret))
if (!indices.TryGetValue(name, out var ret))
throw new InvalidOperationException("Palette `{0}` does not exist".F(name));
return ret;
}

View File

@@ -10,7 +10,6 @@
#endregion
using System;
using System.Collections.Generic;
using OpenRA.Primitives;
namespace OpenRA.Graphics
@@ -19,12 +18,12 @@ namespace OpenRA.Graphics
{
public readonly IModel Model;
public readonly Func<WVec> OffsetFunc;
public readonly Func<IEnumerable<WRot>> RotationFunc;
public readonly Func<WRot> RotationFunc;
public readonly Func<bool> DisableFunc;
public readonly Func<uint> FrameFunc;
public readonly bool ShowShadow;
public ModelAnimation(IModel model, Func<WVec> offset, Func<IEnumerable<WRot>> rotation, Func<bool> disable, Func<uint> frame, bool showshadow)
public ModelAnimation(IModel model, Func<WVec> offset, Func<WRot> rotation, Func<bool> disable, Func<uint> frame, bool showshadow)
{
Model = model;
OffsetFunc = offset;

View File

@@ -48,7 +48,7 @@ namespace OpenRA.Graphics
readonly Dictionary<Sheet, IFrameBuffer> mappedBuffers = new Dictionary<Sheet, IFrameBuffer>();
readonly Stack<KeyValuePair<Sheet, IFrameBuffer>> unmappedBuffers = new Stack<KeyValuePair<Sheet, IFrameBuffer>>();
readonly List<Pair<Sheet, Action>> doRender = new List<Pair<Sheet, Action>>();
readonly List<(Sheet Sheet, Action Func)> doRender = new List<(Sheet, Action)>();
SheetBuilder sheetBuilderForFrame;
bool isInFrame;
@@ -114,8 +114,7 @@ namespace OpenRA.Graphics
var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(m.OffsetFunc()));
var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]);
var worldTransform = m.RotationFunc().Aggregate(Util.IdentityMatrix(),
(x, y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x));
var worldTransform = Util.MakeFloatMatrix(m.RotationFunc().AsMatrix());
worldTransform = Util.MatrixMultiply(scaleTransform, worldTransform);
worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform);
@@ -161,10 +160,8 @@ namespace OpenRA.Graphics
}
// Shadows are rendered at twice the resolution to reduce artifacts
Size spriteSize, shadowSpriteSize;
int2 spriteOffset, shadowSpriteOffset;
CalculateSpriteGeometry(tl, br, 1, out spriteSize, out spriteOffset);
CalculateSpriteGeometry(stl, sbr, 2, out shadowSpriteSize, out shadowSpriteOffset);
CalculateSpriteGeometry(tl, br, 1, out var spriteSize, out var spriteOffset);
CalculateSpriteGeometry(stl, sbr, 2, out var shadowSpriteSize, out var shadowSpriteOffset);
if (sheetBuilderForFrame == null)
sheetBuilderForFrame = new SheetBuilder(SheetType.BGRA, AllocateSheet);
@@ -181,7 +178,7 @@ namespace OpenRA.Graphics
var correctionTransform = Util.MatrixMultiply(translateMtx, FlipMtx);
var shadowCorrectionTransform = Util.MatrixMultiply(shadowTranslateMtx, ShadowScaleFlipMtx);
doRender.Add(Pair.New<Sheet, Action>(sprite.Sheet, () =>
doRender.Add((sprite.Sheet, () =>
{
foreach (var m in models)
{
@@ -189,8 +186,7 @@ namespace OpenRA.Graphics
var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(m.OffsetFunc()));
var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]);
var rotations = m.RotationFunc().Aggregate(Util.IdentityMatrix(),
(x, y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x));
var rotations = Util.MakeFloatMatrix(m.RotationFunc().AsMatrix());
var worldTransform = Util.MatrixMultiply(scaleTransform, rotations);
worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform);
@@ -326,16 +322,16 @@ namespace OpenRA.Graphics
foreach (var v in doRender)
{
// Change sheet
if (v.First != currentSheet)
if (v.Sheet != currentSheet)
{
if (fbo != null)
DisableFrameBuffer(fbo);
currentSheet = v.First;
currentSheet = v.Sheet;
fbo = EnableFrameBuffer(currentSheet);
}
v.Second();
v.Func();
}
if (fbo != null)

View File

@@ -47,14 +47,13 @@ namespace OpenRA.Graphics
remapRamp = ramp.Select(r => r - ramp[rampMaxIndex]);
}
remapColors = remapRamp.Select((x, i) => Pair.New(baseIndex + i, Exts.ColorLerp(x / (float)ramp.Length, c1, c2)))
.ToDictionary(u => u.First, u => u.Second);
remapColors = remapRamp.Select((x, i) => (baseIndex + i, Exts.ColorLerp(x / (float)ramp.Length, c1, c2)))
.ToDictionary(u => u.Item1, u => u.Item2);
}
public Color GetRemappedColor(Color original, int index)
{
Color c;
return remapColors.TryGetValue(index, out c)
return remapColors.TryGetValue(index, out var c)
? c : original;
}
}

View File

@@ -28,6 +28,11 @@ namespace OpenRA.Graphics
IFinalizedRenderable PrepareRender(WorldRenderer wr);
}
public interface ITintableRenderable
{
IRenderable WithTint(float3 newTint);
}
public interface IFinalizedRenderable
{
void Render(WorldRenderer wr);

View File

@@ -45,5 +45,21 @@ namespace OpenRA.Graphics
parent.DrawSprite(s, a, b, c, d);
}
public void DrawSpriteWithTint(Sprite s, float3 location, float3 size, float3 tint)
{
if (s.Channel != TextureChannel.RGBA)
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");
parent.DrawSpriteWithTint(s, location, 0, size, tint);
}
public void DrawSpriteWithTint(Sprite s, float3 a, float3 b, float3 c, float3 d, float3 tint)
{
if (s.Channel != TextureChannel.RGBA)
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");
parent.DrawSpriteWithTint(s, a, b, c, d, tint);
}
}
}

View File

@@ -32,6 +32,7 @@ namespace OpenRA.Graphics
int ShadowZOffset { get; }
int[] Frames { get; }
Rectangle Bounds { get; }
bool IgnoreWorldTint { get; }
Sprite GetSprite(int frame);
Sprite GetSprite(int frame, WAngle facing);
@@ -69,12 +70,10 @@ namespace OpenRA.Graphics
public ISpriteSequence GetSequence(string unitName, string sequenceName)
{
UnitSequences unitSeq;
if (!sequences.Value.TryGetValue(unitName, out unitSeq))
if (!sequences.Value.TryGetValue(unitName, out var unitSeq))
throw new InvalidOperationException("Unit `{0}` does not have any sequences defined.".F(unitName));
ISpriteSequence seq;
if (!unitSeq.Value.TryGetValue(sequenceName, out seq))
if (!unitSeq.Value.TryGetValue(sequenceName, out var seq))
throw new InvalidOperationException("Unit `{0}` does not have a sequence named `{1}`".F(unitName, sequenceName));
return seq;
@@ -87,8 +86,7 @@ namespace OpenRA.Graphics
public bool HasSequence(string unitName, string sequenceName)
{
UnitSequences unitSeq;
if (!sequences.Value.TryGetValue(unitName, out unitSeq))
if (!sequences.Value.TryGetValue(unitName, out var unitSeq))
throw new InvalidOperationException("Unit `{0}` does not have any sequences defined.".F(unitName));
return unitSeq.Value.ContainsKey(sequenceName);
@@ -96,8 +94,7 @@ namespace OpenRA.Graphics
public IEnumerable<string> Sequences(string unitName)
{
UnitSequences unitSeq;
if (!sequences.Value.TryGetValue(unitName, out unitSeq))
if (!sequences.Value.TryGetValue(unitName, out var unitSeq))
throw new InvalidOperationException("Unit `{0}` does not have any sequences defined.".F(unitName));
return unitSeq.Value.Keys;
@@ -114,8 +111,7 @@ namespace OpenRA.Graphics
var key = node.Value.ToLines(node.Key).JoinWith("|");
UnitSequences t;
if (sequenceCache.TryGetValue(key, out t))
if (sequenceCache.TryGetValue(key, out var t))
items.Add(node.Key, t);
else
{

View File

@@ -146,8 +146,7 @@ namespace OpenRA.Graphics
public void Dispose()
{
if (texture != null)
texture.Dispose();
texture?.Dispose();
}
}
}

View File

@@ -23,8 +23,8 @@ namespace OpenRA.Graphics
readonly SheetBuilder builder;
readonly Func<string, float> lineWidth;
readonly IFont font;
readonly Cache<Pair<char, Color>, GlyphInfo> glyphs;
readonly Cache<Tuple<char, Color, int>, Sprite> contrastGlyphs;
readonly Cache<char, GlyphInfo> glyphs;
readonly Cache<(char C, int Radius), Sprite> contrastGlyphs;
readonly Cache<int, float[]> dilationElements;
float deviceScale;
@@ -39,17 +39,20 @@ namespace OpenRA.Graphics
this.builder = builder;
font = Game.Renderer.CreateFont(data);
glyphs = new Cache<Pair<char, Color>, GlyphInfo>(CreateGlyph, Pair<char, Color>.EqualityComparer);
contrastGlyphs = new Cache<Tuple<char, Color, int>, Sprite>(CreateContrastGlyph);
glyphs = new Cache<char, GlyphInfo>(CreateGlyph);
contrastGlyphs = new Cache<(char, int), Sprite>(CreateContrastGlyph);
dilationElements = new Cache<int, float[]>(CreateCircularWeightMap);
// PERF: Cache these delegates for Measure calls.
Func<char, float> characterWidth = character => glyphs[Pair.New(character, Color.White)].Advance;
Func<char, float> characterWidth = character => glyphs[character].Advance;
lineWidth = line => line.Sum(characterWidth) / deviceScale;
// Pre-cache small font sizes so glyphs are immediately available when we need them
if (size <= 24)
PrecacheColor(Color.White, name);
using (new PerfTimer("Precache {0} {1}px".F(name, size)))
for (var n = (char)0x20; n < (char)0x7f; n++)
if (glyphs[n] == null)
throw new InvalidOperationException();
TopOffset = size - ascender;
}
@@ -61,14 +64,6 @@ namespace OpenRA.Graphics
contrastGlyphs.Clear();
}
void PrecacheColor(Color c, string name)
{
using (new PerfTimer("PrecacheColor {0} {1}px {2}".F(name, size, c)))
for (var n = (char)0x20; n < (char)0x7f; n++)
if (glyphs[Pair.New(n, c)] == null)
throw new InvalidOperationException();
}
void DrawTextContrast(string text, float2 location, Color contrastColor, int contrastOffset)
{
// Offset from the baseline position to the top-left of the glyph for rendering
@@ -78,6 +73,7 @@ namespace OpenRA.Graphics
var screenContrast = (int)(contrastOffset * deviceScale);
var screen = new int2((int)(location.X * deviceScale + 0.5f), (int)(location.Y * deviceScale + 0.5f));
var contrastVector = new float2(screenContrast, screenContrast);
var tint = new float3(contrastColor.R / 255f, contrastColor.G / 255f, contrastColor.B / 255f);
foreach (var s in text)
{
if (s == '\n')
@@ -87,15 +83,16 @@ namespace OpenRA.Graphics
continue;
}
var g = glyphs[Pair.New(s, Color.Black)];
var g = glyphs[s];
// Convert screen coordinates back to UI coordinates for drawing
if (g.Sprite != null)
{
var contrastSprite = contrastGlyphs[Tuple.Create(s, contrastColor, screenContrast)];
Game.Renderer.RgbaSpriteRenderer.DrawSprite(contrastSprite,
var contrastSprite = contrastGlyphs[(s, screenContrast)];
Game.Renderer.RgbaSpriteRenderer.DrawSpriteWithTint(contrastSprite,
(screen + g.Offset - contrastVector) / deviceScale,
contrastSprite.Size / deviceScale);
contrastSprite.Size / deviceScale,
tint);
}
screen += new int2((int)(g.Advance + 0.5f), 0);
@@ -109,6 +106,7 @@ namespace OpenRA.Graphics
// Calculate positions in screen pixel coordinates
var screen = new int2((int)(location.X * deviceScale + 0.5f), (int)(location.Y * deviceScale + 0.5f));
var tint = new float3(c.R / 255f, c.G / 255f, c.B / 255f);
foreach (var s in text)
{
if (s == '\n')
@@ -118,13 +116,14 @@ namespace OpenRA.Graphics
continue;
}
var g = glyphs[Pair.New(s, c)];
var g = glyphs[s];
// Convert screen coordinates back to UI coordinates for drawing
if (g.Sprite != null)
Game.Renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite,
Game.Renderer.RgbaSpriteRenderer.DrawSpriteWithTint(g.Sprite,
(screen + g.Offset).ToFloat2() / deviceScale,
g.Sprite.Size / deviceScale);
g.Sprite.Size / deviceScale,
tint);
screen += new int2((int)(g.Advance + 0.5f), 0);
}
@@ -144,6 +143,7 @@ namespace OpenRA.Graphics
var offset = new float2(0, size);
var cosa = (float)Math.Cos(-angle);
var sina = (float)Math.Sin(-angle);
var tint = new float3(c.R / 255f, c.G / 255f, c.B / 255f);
var p = offset;
foreach (var s in text)
@@ -155,7 +155,7 @@ namespace OpenRA.Graphics
continue;
}
var g = glyphs[Pair.New(s, c)];
var g = glyphs[s];
if (g.Sprite != null)
{
var tl = new float2(
@@ -172,11 +172,12 @@ namespace OpenRA.Graphics
// Offset rotated glyph to align the top-left corner with the screen pixel grid
var screenOffset = new float2((int)(ra.X * deviceScale + 0.5f), (int)(ra.Y * deviceScale + 0.5f)) / deviceScale - ra;
Game.Renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite,
Game.Renderer.RgbaSpriteRenderer.DrawSpriteWithTint(g.Sprite,
ra + screenOffset,
rb + screenOffset,
rc + screenOffset,
rd + screenOffset);
rd + screenOffset,
tint);
}
p += new float2(g.Advance / deviceScale, 0);
@@ -241,10 +242,9 @@ namespace OpenRA.Graphics
return new int2((int)Math.Ceiling(lines.Max(lineWidth)), lines.Length * size);
}
GlyphInfo CreateGlyph(Pair<char, Color> c)
GlyphInfo CreateGlyph(char c)
{
var glyph = font.CreateGlyph(c.First, size, deviceScale);
var glyph = font.CreateGlyph(c, size, deviceScale);
if (glyph.Data == null)
{
return new GlyphInfo
@@ -274,12 +274,10 @@ namespace OpenRA.Graphics
if (p != 0)
{
var q = destStride * (j + s.Bounds.Top) + 4 * (i + s.Bounds.Left);
var pmc = Util.PremultiplyAlpha(Color.FromArgb(p, c.Second));
dest[q] = pmc.B;
dest[q + 1] = pmc.G;
dest[q + 2] = pmc.R;
dest[q + 3] = pmc.A;
dest[q] = p;
dest[q + 1] = p;
dest[q + 2] = p;
dest[q + 3] = p;
}
}
}
@@ -347,16 +345,12 @@ namespace OpenRA.Graphics
return elem;
}
Sprite CreateContrastGlyph(Tuple<char, Color, int> c)
Sprite CreateContrastGlyph((char C, int Radius) c)
{
// Source glyph color doesn't matter, so use black
var glyph = glyphs[Pair.New(c.Item1, Color.Black)];
var color = c.Item2;
var r = c.Item3;
var glyph = glyphs[c.C];
var r = c.Radius;
var size = new Size(glyph.Sprite.Bounds.Width + 2 * r, glyph.Sprite.Bounds.Height + 2 * r);
var s = builder.Allocate(size);
var s = builder.Allocate(new Size(glyph.Sprite.Bounds.Width + 2 * r, glyph.Sprite.Bounds.Height + 2 * r));
var dest = s.Sheet.GetData();
var destStride = s.Sheet.Size.Width * 4;
@@ -398,11 +392,10 @@ namespace OpenRA.Graphics
if (alpha > 0)
{
var q = destStride * (j + s.Bounds.Top) + 4 * (i + s.Bounds.Left);
var pmc = Util.PremultiplyAlpha(Color.FromArgb(alpha, color));
dest[q] = pmc.B;
dest[q + 1] = pmc.G;
dest[q + 2] = pmc.R;
dest[q + 3] = pmc.A;
dest[q] = alpha;
dest[q + 1] = alpha;
dest[q + 2] = alpha;
dest[q + 3] = alpha;
}
}
}

View File

@@ -76,8 +76,7 @@ namespace OpenRA.Graphics
var allSprites = sprites.GetOrAdd(filename);
var sprite = allSprites.FirstOrDefault();
ISpriteFrame[] unloaded;
if (!unloadedFrames.TryGetValue(filename, out unloaded))
if (!unloadedFrames.TryGetValue(filename, out var unloaded))
unloaded = null;
// This is the first time that the file has been requested
@@ -85,8 +84,7 @@ namespace OpenRA.Graphics
// the loaded cache (initially empty)
if (sprite == null)
{
TypeDictionary fileMetadata = null;
unloaded = FrameLoader.GetFrames(fileSystem, filename, loaders, out fileMetadata);
unloaded = FrameLoader.GetFrames(fileSystem, filename, loaders, out var fileMetadata);
unloadedFrames[filename] = unloaded;
metadata[filename] = fileMetadata;
@@ -125,8 +123,7 @@ namespace OpenRA.Graphics
/// </summary>
public TypeDictionary FrameMetadata(string filename)
{
TypeDictionary fileMetadata;
if (!metadata.TryGetValue(filename, out fileMetadata))
if (!metadata.TryGetValue(filename, out var fileMetadata))
{
FrameLoader.GetFrames(fileSystem, filename, loaders, out fileMetadata);
metadata[filename] = fileMetadata;
@@ -142,8 +139,7 @@ namespace OpenRA.Graphics
public FrameCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders)
{
TypeDictionary metadata;
frames = new Cache<string, ISpriteFrame[]>(filename => FrameLoader.GetFrames(fileSystem, filename, loaders, out metadata));
frames = new Cache<string, ISpriteFrame[]>(filename => FrameLoader.GetFrames(fileSystem, filename, loaders, out _));
}
public ISpriteFrame[] this[string filename] { get { return frames[filename]; } }
@@ -165,11 +161,10 @@ namespace OpenRA.Graphics
public static ISpriteFrame[] GetFrames(Stream stream, ISpriteLoader[] loaders, out TypeDictionary metadata)
{
ISpriteFrame[] frames;
metadata = null;
foreach (var loader in loaders)
if (loader.TryParseSprite(stream, out frames, out metadata))
if (loader.TryParseSprite(stream, out var frames, out metadata))
return frames;
return null;

View File

@@ -14,7 +14,7 @@ using OpenRA.Primitives;
namespace OpenRA.Graphics
{
public struct SpriteRenderable : IRenderable, IFinalizedRenderable
public struct SpriteRenderable : IRenderable, ITintableRenderable, IFinalizedRenderable
{
public static readonly IEnumerable<IRenderable> None = new IRenderable[0];
@@ -24,9 +24,17 @@ namespace OpenRA.Graphics
readonly int zOffset;
readonly PaletteReference palette;
readonly float scale;
readonly float3 tint;
readonly bool isDecoration;
readonly bool ignoreWorldTint;
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, bool isDecoration)
: this(sprite, pos, offset, zOffset, palette, scale, float3.Ones, isDecoration, false) { }
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, bool isDecoration, bool ignoreWorldTint)
: this(sprite, pos, offset, zOffset, palette, scale, float3.Ones, isDecoration, ignoreWorldTint) { }
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, float3 tint, bool isDecoration, bool ignoreWorldTint)
{
this.sprite = sprite;
this.pos = pos;
@@ -34,7 +42,9 @@ namespace OpenRA.Graphics
this.zOffset = zOffset;
this.palette = palette;
this.scale = scale;
this.tint = tint;
this.isDecoration = isDecoration;
this.ignoreWorldTint = ignoreWorldTint;
}
public WPos Pos { get { return pos + offset; } }
@@ -43,10 +53,12 @@ namespace OpenRA.Graphics
public int ZOffset { get { return zOffset; } }
public bool IsDecoration { get { return isDecoration; } }
public IRenderable WithPalette(PaletteReference newPalette) { return new SpriteRenderable(sprite, pos, offset, zOffset, newPalette, scale, isDecoration); }
public IRenderable WithZOffset(int newOffset) { return new SpriteRenderable(sprite, pos, offset, newOffset, palette, scale, isDecoration); }
public IRenderable OffsetBy(WVec vec) { return new SpriteRenderable(sprite, pos + vec, offset, zOffset, palette, scale, isDecoration); }
public IRenderable AsDecoration() { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, true); }
public IRenderable WithPalette(PaletteReference newPalette) { return new SpriteRenderable(sprite, pos, offset, zOffset, newPalette, scale, tint, isDecoration, ignoreWorldTint); }
public IRenderable WithZOffset(int newOffset) { return new SpriteRenderable(sprite, pos, offset, newOffset, palette, scale, tint, isDecoration, ignoreWorldTint); }
public IRenderable OffsetBy(WVec vec) { return new SpriteRenderable(sprite, pos + vec, offset, zOffset, palette, scale, tint, isDecoration, ignoreWorldTint); }
public IRenderable AsDecoration() { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, tint, true, ignoreWorldTint); }
public IRenderable WithTint(float3 newTint) { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, newTint, isDecoration, ignoreWorldTint); }
float3 ScreenPosition(WorldRenderer wr)
{
@@ -59,7 +71,17 @@ namespace OpenRA.Graphics
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
public void Render(WorldRenderer wr)
{
Game.Renderer.WorldSpriteRenderer.DrawSprite(sprite, ScreenPosition(wr), palette, scale * sprite.Size);
var wsr = Game.Renderer.WorldSpriteRenderer;
if (ignoreWorldTint)
wsr.DrawSprite(sprite, ScreenPosition(wr), palette, scale * sprite.Size);
else
{
var t = tint;
if (wr.TerrainLighting != null)
t *= wr.TerrainLighting.TintAt(pos);
wsr.DrawSpriteWithTint(sprite, ScreenPosition(wr), palette, scale * sprite.Size, t);
}
}
public void RenderDebugGeometry(WorldRenderer wr)

View File

@@ -107,7 +107,7 @@ namespace OpenRA.Graphics
internal void DrawSprite(Sprite s, float3 location, float paletteTextureIndex, float3 size)
{
var samplers = SetRenderStateForSprite(s);
Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, samplers, paletteTextureIndex, nv, size);
Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, samplers, paletteTextureIndex, nv, size, float3.Ones);
nv += 6;
}
@@ -124,7 +124,26 @@ namespace OpenRA.Graphics
public void DrawSprite(Sprite s, float3 a, float3 b, float3 c, float3 d)
{
var samplers = SetRenderStateForSprite(s);
Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, 0, nv);
Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, 0, float3.Ones, nv);
nv += 6;
}
internal void DrawSpriteWithTint(Sprite s, float3 location, float paletteTextureIndex, float3 size, float3 tint)
{
var samplers = SetRenderStateForSprite(s);
Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, samplers, paletteTextureIndex, nv, size, tint);
nv += 6;
}
public void DrawSpriteWithTint(Sprite s, float3 location, PaletteReference pal, float3 size, float3 tint)
{
DrawSpriteWithTint(s, location, pal.TextureIndex, size, tint);
}
public void DrawSpriteWithTint(Sprite s, float3 a, float3 b, float3 c, float3 d, float3 tint)
{
var samplers = SetRenderStateForSprite(s);
Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, 0, tint, nv);
nv += 6;
}

View File

@@ -18,6 +18,8 @@ namespace OpenRA.Graphics
{
public sealed class TerrainSpriteLayer : IDisposable
{
static readonly int[] CornerVertexMap = { 0, 1, 2, 2, 3, 0 };
public readonly Sheet Sheet;
public readonly BlendMode BlendMode;
@@ -25,6 +27,7 @@ namespace OpenRA.Graphics
readonly IVertexBuffer<Vertex> vertexBuffer;
readonly Vertex[] vertices;
readonly bool[] ignoreTint;
readonly HashSet<int> dirtyRows = new HashSet<int>();
readonly int rowStride;
readonly bool restrictToBounds;
@@ -50,6 +53,12 @@ namespace OpenRA.Graphics
emptySprite = new Sprite(sheet, Rectangle.Empty, TextureChannel.Alpha);
wr.PaletteInvalidated += UpdatePaletteIndices;
if (wr.TerrainLighting != null)
{
ignoreTint = new bool[rowStride * map.MapSize.Y];
wr.TerrainLighting.CellChanged += UpdateTint;
}
}
void UpdatePaletteIndices()
@@ -59,22 +68,76 @@ namespace OpenRA.Graphics
for (var i = 0; i < vertices.Length; i++)
{
var v = vertices[i];
vertices[i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C);
vertices[i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C, v.R, v.G, v.B);
}
for (var row = 0; row < map.MapSize.Y; row++)
dirtyRows.Add(row);
}
public void Update(CPos cell, Sprite sprite)
public void Clear(CPos cell)
{
var xyz = sprite == null ? float3.Zero :
worldRenderer.Screen3DPosition(map.CenterOfCell(cell)) + sprite.Offset - 0.5f * sprite.Size;
Update(cell.ToMPos(map.Grid.Type), sprite, xyz);
Update(cell, null, true);
}
public void Update(MPos uv, Sprite sprite, float3 pos)
public void Update(CPos cell, ISpriteSequence sequence, int frame)
{
Update(cell, sequence.GetSprite(frame), sequence.IgnoreWorldTint);
}
public void Update(CPos cell, Sprite sprite, bool ignoreTint)
{
var xyz = float3.Zero;
if (sprite != null)
{
var cellOrigin = map.CenterOfCell(cell) - new WVec(0, 0, map.Grid.Ramps[map.Ramp[cell]].CenterHeightOffset);
xyz = worldRenderer.Screen3DPosition(cellOrigin) + sprite.Offset - 0.5f * sprite.Size;
}
Update(cell.ToMPos(map.Grid.Type), sprite, xyz, ignoreTint);
}
void UpdateTint(MPos uv)
{
var offset = rowStride * uv.V + 6 * uv.U;
if (ignoreTint[offset])
{
var noTint = float3.Ones;
for (var i = 0; i < 6; i++)
{
var v = vertices[offset + i];
vertices[offset + i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C, noTint);
}
return;
}
// Allow the terrain tint to vary linearly across the cell to smooth out the staircase effect
// This is done by sampling the lighting the corners of the sprite, even though those pixels are
// transparent for isometric tiles
var tl = worldRenderer.TerrainLighting;
var pos = map.CenterOfCell(uv.ToCPos(map));
var step = map.Grid.Type == MapGridType.RectangularIsometric ? 724 : 512;
var weights = new[]
{
tl.TintAt(pos + new WVec(-step, -step, 0)),
tl.TintAt(pos + new WVec(step, -step, 0)),
tl.TintAt(pos + new WVec(step, step, 0)),
tl.TintAt(pos + new WVec(-step, step, 0))
};
// Apply tint directly to the underlying vertices
// This saves us from having to re-query the sprite information, which has not changed
for (var i = 0; i < 6; i++)
{
var v = vertices[offset + i];
vertices[offset + i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C, weights[CornerVertexMap[i]]);
}
dirtyRows.Add(uv.V);
}
public void Update(MPos uv, Sprite sprite, float3 pos, bool ignoreTint)
{
if (sprite != null)
{
@@ -92,7 +155,13 @@ namespace OpenRA.Graphics
return;
var offset = rowStride * uv.V + 6 * uv.U;
Util.FastCreateQuad(vertices, pos, sprite, int2.Zero, palette.TextureIndex, offset, sprite.Size);
Util.FastCreateQuad(vertices, pos, sprite, int2.Zero, palette.TextureIndex, offset, sprite.Size, float3.Ones);
if (worldRenderer.TerrainLighting != null)
{
this.ignoreTint[offset] = ignoreTint;
UpdateTint(uv);
}
dirtyRows.Add(uv.V);
}
@@ -135,6 +204,9 @@ namespace OpenRA.Graphics
public void Dispose()
{
worldRenderer.PaletteInvalidated -= UpdatePaletteIndices;
if (worldRenderer.TerrainLighting != null)
worldRenderer.TerrainLighting.CellChanged -= UpdateTint;
vertexBuffer.Dispose();
}
}

View File

@@ -118,8 +118,7 @@ namespace OpenRA.Graphics
public Sprite TileSprite(TerrainTile r, int? variant = null)
{
TheaterTemplate template;
if (!templates.TryGetValue(r.Type, out template))
if (!templates.TryGetValue(r.Type, out var template))
return missingTile;
if (r.Index >= template.Stride)

View File

@@ -20,15 +20,18 @@ namespace OpenRA.Graphics
// yes, our channel order is nuts.
static readonly int[] ChannelMasks = { 2, 1, 0, 3 };
public static void FastCreateQuad(Vertex[] vertices, float3 o, Sprite r, int2 samplers, float paletteTextureIndex, int nv, float3 size)
public static void FastCreateQuad(Vertex[] vertices, float3 o, Sprite r, int2 samplers, float paletteTextureIndex, int nv, float3 size, float3 tint)
{
var b = new float3(o.X + size.X, o.Y, o.Z);
var c = new float3(o.X + size.X, o.Y + size.Y, o.Z + size.Z);
var d = new float3(o.X, o.Y + size.Y, o.Z + size.Z);
FastCreateQuad(vertices, o, b, c, d, r, samplers, paletteTextureIndex, nv);
FastCreateQuad(vertices, o, b, c, d, r, samplers, paletteTextureIndex, tint, nv);
}
public static void FastCreateQuad(Vertex[] vertices, float3 a, float3 b, float3 c, float3 d, Sprite r, int2 samplers, float paletteTextureIndex, int nv)
public static void FastCreateQuad(Vertex[] vertices,
float3 a, float3 b, float3 c, float3 d,
Sprite r, int2 samplers, float paletteTextureIndex,
float3 tint, int nv)
{
float sl = 0;
float st = 0;
@@ -51,12 +54,12 @@ namespace OpenRA.Graphics
}
var fAttribC = (float)attribC;
vertices[nv] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC);
vertices[nv + 1] = new Vertex(b, r.Right, r.Top, sr, st, paletteTextureIndex, fAttribC);
vertices[nv + 2] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC);
vertices[nv + 3] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC);
vertices[nv + 4] = new Vertex(d, r.Left, r.Bottom, sl, sb, paletteTextureIndex, fAttribC);
vertices[nv + 5] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC);
vertices[nv] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC, tint);
vertices[nv + 1] = new Vertex(b, r.Right, r.Top, sr, st, paletteTextureIndex, fAttribC, tint);
vertices[nv + 2] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC, tint);
vertices[nv + 3] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC, tint);
vertices[nv + 4] = new Vertex(d, r.Left, r.Bottom, sl, sb, paletteTextureIndex, fAttribC, tint);
vertices[nv + 5] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC, tint);
}
public static void FastCopyIntoChannel(Sprite dest, byte[] src)

View File

@@ -16,17 +16,34 @@ namespace OpenRA.Graphics
[StructLayout(LayoutKind.Sequential)]
public struct Vertex
{
public readonly float X, Y, Z, S, T, U, V, P, C;
// 3d position
public readonly float X, Y, Z;
// Primary and secondary texture coordinates or RGBA color
public readonly float S, T, U, V;
// Palette and channel flags
public readonly float P, C;
// Color tint
public readonly float R, G, B;
public Vertex(float3 xyz, float s, float t, float u, float v, float p, float c)
: this(xyz.X, xyz.Y, xyz.Z, s, t, u, v, p, c) { }
: this(xyz.X, xyz.Y, xyz.Z, s, t, u, v, p, c, float3.Ones) { }
public Vertex(float x, float y, float z, float s, float t, float u, float v, float p, float c)
public Vertex(float3 xyz, float s, float t, float u, float v, float p, float c, float3 tint)
: this(xyz.X, xyz.Y, xyz.Z, s, t, u, v, p, c, tint.X, tint.Y, tint.Z) { }
public Vertex(float x, float y, float z, float s, float t, float u, float v, float p, float c, float3 tint)
: this(x, y, z, s, t, u, v, p, c, tint.X, tint.Y, tint.Z) { }
public Vertex(float x, float y, float z, float s, float t, float u, float v, float p, float c, float r, float g, float b)
{
X = x; Y = y; Z = z;
S = s; T = t;
U = u; V = v;
P = p; C = c;
R = r; G = g; B = b;
}
}
}

View File

@@ -97,6 +97,14 @@ namespace OpenRA.Graphics
Zoom = (zoom * (float)Math.Exp(dz)).Clamp(unlockMinZoom ? unlockedMinZoom : minZoom, maxZoom);
}
public void AdjustZoom(float dz, int2 center)
{
var oldCenter = worldRenderer.Viewport.ViewToWorldPx(center);
AdjustZoom(dz);
var newCenter = worldRenderer.Viewport.ViewToWorldPx(center);
CenterLocation += oldCenter - newCenter;
}
public void ToggleZoom()
{
// Unlocked zooms always reset to the default zoom
@@ -254,7 +262,6 @@ namespace OpenRA.Graphics
var world = worldRenderer.Viewport.ViewToWorldPx(view);
var map = worldRenderer.World.Map;
var candidates = CandidateMouseoverCells(world).ToList();
var tileSet = worldRenderer.World.Map.Rules.TileSet;
foreach (var uv in candidates)
{
@@ -263,18 +270,9 @@ namespace OpenRA.Graphics
var s = worldRenderer.ScreenPxPosition(p);
if (Math.Abs(s.X - world.X) <= tileSize.Width && Math.Abs(s.Y - world.Y) <= tileSize.Height)
{
var ramp = 0;
if (map.Contains(uv))
{
var ti = tileSet.GetTileInfo(map.Tiles[uv]);
if (ti != null)
ramp = ti.RampType;
}
var corners = map.Grid.CellCorners[ramp];
var pos = map.CenterOfCell(uv.ToCPos(map));
var screen = corners.Select(c => worldRenderer.ScreenPxPosition(pos + c)).ToArray();
var ramp = map.Grid.Ramps[map.Ramp.Contains(uv) ? map.Ramp[uv] : 0];
var pos = map.CenterOfCell(uv.ToCPos(map)) - new WVec(0, 0, ramp.CenterHeightOffset);
var screen = ramp.Corners.Select(c => worldRenderer.ScreenPxPosition(pos + c)).ToArray();
if (screen.PolygonContains(world))
return uv.ToCPos(map);
}

View File

@@ -28,6 +28,7 @@ namespace OpenRA.Graphics
public readonly World World;
public readonly Theater Theater;
public Viewport Viewport { get; private set; }
public readonly ITerrainLighting TerrainLighting;
public event Action PaletteInvalidated = null;
@@ -43,6 +44,8 @@ namespace OpenRA.Graphics
readonly List<IFinalizedRenderable> preparedOverlayRenderables = new List<IFinalizedRenderable>();
readonly List<IFinalizedRenderable> preparedAnnotationRenderables = new List<IFinalizedRenderable>();
readonly List<IRenderable> renderablesBuffer = new List<IRenderable>();
bool lastDepthPreviewEnabled;
internal WorldRenderer(ModData modData, World world)
@@ -66,6 +69,7 @@ namespace OpenRA.Graphics
palette.Initialize();
Theater = new Theater(world.Map.Rules.TileSet);
TerrainLighting = world.WorldActor.TraitOrDefault<ITerrainLighting>();
terrainRenderer = world.WorldActor.TraitOrDefault<IRenderTerrain>();
debugVis = Exts.Lazy(() => world.WorldActor.TraitOrDefault<DebugVisualizations>());
@@ -93,8 +97,8 @@ namespace OpenRA.Graphics
var oldHeight = palette.Height;
palette.AddPalette(name, pal, allowModifiers);
if (oldHeight != palette.Height && PaletteInvalidated != null)
PaletteInvalidated();
if (oldHeight != palette.Height)
PaletteInvalidated?.Invoke();
}
}
@@ -107,78 +111,117 @@ namespace OpenRA.Graphics
palettes[name].Palette = pal;
}
IEnumerable<IFinalizedRenderable> GenerateRenderables()
// PERF: Avoid LINQ.
void GenerateRenderables()
{
var actors = onScreenActors.Append(World.WorldActor);
if (World.RenderPlayer != null)
actors = actors.Append(World.RenderPlayer.PlayerActor);
foreach (var actor in onScreenActors)
renderablesBuffer.AddRange(actor.Render(this));
renderablesBuffer.AddRange(World.WorldActor.Render(this));
if (World.RenderPlayer != null)
renderablesBuffer.AddRange(World.RenderPlayer.PlayerActor.Render(this));
var worldRenderables = actors.SelectMany(a => a.Render(this));
if (World.OrderGenerator != null)
worldRenderables = worldRenderables.Concat(World.OrderGenerator.Render(this, World));
renderablesBuffer.AddRange(World.OrderGenerator.Render(this, World));
// Unpartitioned effects
worldRenderables = worldRenderables.Concat(World.UnpartitionedEffects.SelectMany(e => e.Render(this)));
foreach (var e in World.UnpartitionedEffects)
renderablesBuffer.AddRange(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)));
foreach (var e in World.ScreenMap.RenderableEffectsInBox(Viewport.TopLeft, Viewport.BottomRight))
renderablesBuffer.AddRange(e.Render(this));
worldRenderables = worldRenderables.OrderBy(RenderableScreenZPositionComparisonKey);
foreach (var renderable in renderablesBuffer.OrderBy(RenderableScreenZPositionComparisonKey))
preparedRenderables.Add(renderable.PrepareRender(this));
return worldRenderables.Select(r => r.PrepareRender(this));
// PERF: Reuse collection to avoid allocations.
renderablesBuffer.Clear();
}
IEnumerable<IFinalizedRenderable> GenerateOverlayRenderables()
// PERF: Avoid LINQ.
void GenerateOverlayRenderables()
{
var actors = World.ActorsWithTrait<IRenderAboveShroud>()
.Where(a => a.Actor.IsInWorld && !a.Actor.Disposed && (!a.Trait.SpatiallyPartitionable || onScreenActors.Contains(a.Actor)))
.SelectMany(a => a.Trait.RenderAboveShroud(a.Actor, this));
foreach (var a in World.ActorsWithTrait<IRenderAboveShroud>())
{
if (!a.Actor.IsInWorld || a.Actor.Disposed || (a.Trait.SpatiallyPartitionable && !onScreenActors.Contains(a.Actor)))
continue;
var selected = World.Selection.Actors.Where(a => a.IsInWorld && !a.Disposed)
.SelectMany(a => a.TraitsImplementing<IRenderAboveShroudWhenSelected>()
.Where(t => !t.SpatiallyPartitionable || onScreenActors.Contains(a))
.SelectMany(t => t.RenderAboveShroud(a, this)));
foreach (var renderable in a.Trait.RenderAboveShroud(a.Actor, this))
preparedOverlayRenderables.Add(renderable.PrepareRender(this));
}
var effects = World.Effects.Select(e => e as IEffectAboveShroud)
.Where(e => e != null)
.SelectMany(e => e.RenderAboveShroud(this));
foreach (var a in World.Selection.Actors)
{
if (!a.IsInWorld || a.Disposed)
continue;
foreach (var t in a.TraitsImplementing<IRenderAboveShroudWhenSelected>())
{
if (t.SpatiallyPartitionable && !onScreenActors.Contains(a))
continue;
foreach (var renderable in t.RenderAboveShroud(a, this))
preparedOverlayRenderables.Add(renderable.PrepareRender(this));
}
}
foreach (var e in World.Effects)
{
var ea = e as IEffectAboveShroud;
if (ea == null)
continue;
foreach (var renderable in ea.RenderAboveShroud(this))
preparedOverlayRenderables.Add(renderable.PrepareRender(this));
}
var orderGenerator = SpriteRenderable.None;
if (World.OrderGenerator != null)
orderGenerator = World.OrderGenerator.RenderAboveShroud(this, World);
return actors
.Concat(selected)
.Concat(effects)
.Concat(orderGenerator)
.Select(r => r.PrepareRender(this));
foreach (var renderable in World.OrderGenerator.RenderAboveShroud(this, World))
preparedOverlayRenderables.Add(renderable.PrepareRender(this));
}
IEnumerable<IFinalizedRenderable> GenerateAnnotationRenderables()
// PERF: Avoid LINQ.
void GenerateAnnotationRenderables()
{
var actors = World.ActorsWithTrait<IRenderAnnotations>()
.Where(a => a.Actor.IsInWorld && !a.Actor.Disposed && (!a.Trait.SpatiallyPartitionable || onScreenActors.Contains(a.Actor)))
.SelectMany(a => a.Trait.RenderAnnotations(a.Actor, this));
foreach (var a in World.ActorsWithTrait<IRenderAnnotations>())
{
if (!a.Actor.IsInWorld || a.Actor.Disposed || (a.Trait.SpatiallyPartitionable && !onScreenActors.Contains(a.Actor)))
continue;
var selected = World.Selection.Actors.Where(a => a.IsInWorld && !a.Disposed)
.SelectMany(a => a.TraitsImplementing<IRenderAnnotationsWhenSelected>()
.Where(t => !t.SpatiallyPartitionable || onScreenActors.Contains(a))
.SelectMany(t => t.RenderAnnotations(a, this)));
foreach (var renderAnnotation in a.Trait.RenderAnnotations(a.Actor, this))
preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this));
}
var effects = World.Effects.Select(e => e as IEffectAnnotation)
.Where(e => e != null)
.SelectMany(e => e.RenderAnnotation(this));
foreach (var a in World.Selection.Actors)
{
if (!a.IsInWorld || a.Disposed)
continue;
foreach (var t in a.TraitsImplementing<IRenderAnnotationsWhenSelected>())
{
if (t.SpatiallyPartitionable && !onScreenActors.Contains(a))
continue;
foreach (var renderAnnotation in t.RenderAnnotations(a, this))
preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this));
}
}
foreach (var e in World.Effects)
{
var ea = e as IEffectAnnotation;
if (ea == null)
continue;
foreach (var renderAnnotation in ea.RenderAnnotation(this))
preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this));
}
var orderGenerator = SpriteRenderable.None;
if (World.OrderGenerator != null)
orderGenerator = World.OrderGenerator.RenderAnnotations(this, World);
return actors
.Concat(selected)
.Concat(effects)
.Concat(orderGenerator)
.Select(r => r.PrepareRender(this));
foreach (var renderAnnotation in World.OrderGenerator.RenderAnnotations(this, World))
preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this));
}
public void PrepareRenderables()
@@ -190,9 +233,11 @@ namespace OpenRA.Graphics
// PERF: Reuse collection to avoid allocations.
onScreenActors.UnionWith(World.ScreenMap.RenderableActorsInBox(Viewport.TopLeft, Viewport.BottomRight));
preparedRenderables.AddRange(GenerateRenderables());
preparedOverlayRenderables.AddRange(GenerateOverlayRenderables());
preparedAnnotationRenderables.AddRange(GenerateAnnotationRenderables());
GenerateRenderables();
GenerateOverlayRenderables();
GenerateAnnotationRenderables();
onScreenActors.Clear();
}
@@ -213,8 +258,7 @@ namespace OpenRA.Graphics
if (enableDepthBuffer)
Game.Renderer.Context.EnableDepthBuffer();
if (terrainRenderer != null)
terrainRenderer.RenderTerrain(this, Viewport);
terrainRenderer?.RenderTerrain(this, Viewport);
Game.Renderer.Flush();
@@ -279,9 +323,12 @@ namespace OpenRA.Graphics
foreach (var b in World.ScreenMap.MouseBounds(World.RenderPlayer))
{
var points = b.Vertices
.Select(p => Viewport.WorldToViewPx(p).ToFloat2())
.ToArray();
var points = new float2[b.Vertices.Length];
for (var index = 0; index < b.Vertices.Length; index++)
{
var vertex = b.Vertices[index];
points[index] = Viewport.WorldToViewPx(vertex).ToFloat2();
}
Game.Renderer.RgbaColorRenderer.DrawPolygon(points, 1, Color.OrangeRed);
}

View File

@@ -50,8 +50,7 @@ namespace OpenRA
return () => keys[name];
// Try and parse as a hardcoded definition
Hotkey key;
if (!Hotkey.TryParse(name, out key))
if (!Hotkey.TryParse(name, out var key))
key = Hotkey.Invalid;
return () => key;
@@ -59,8 +58,7 @@ namespace OpenRA
public void Set(string name, Hotkey value)
{
HotkeyDefinition definition;
if (!definitions.TryGetValue(name, out definition))
if (!definitions.TryGetValue(name, out var definition))
return;
keys[name] = value;

View File

@@ -32,11 +32,9 @@ namespace OpenRA
var parts = s.Split(' ');
Keycode key;
if (!Enum<Keycode>.TryParse(parts[0], true, out key))
if (!Enum<Keycode>.TryParse(parts[0], true, out var key))
{
int c;
if (!int.TryParse(parts[0], out c))
if (!int.TryParse(parts[0], out var c))
return false;
key = (Keycode)c;
}

View File

@@ -498,8 +498,7 @@ namespace OpenRA
public static string DisplayString(Keycode k)
{
string ret;
if (!KeyNames.TryGetValue(k, out ret))
if (!KeyNames.TryGetValue(k, out var ret))
return k.ToString();
return ret;

View File

@@ -14,7 +14,6 @@ using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.FileSystem;
using OpenRA.Graphics;
using OpenRA.Primitives;
@@ -35,9 +34,9 @@ namespace OpenRA
mods = GetInstalledMods(searchPaths, explicitPaths);
}
static IEnumerable<Pair<string, string>> GetCandidateMods(IEnumerable<string> searchPaths)
static IEnumerable<(string Id, string Path)> GetCandidateMods(IEnumerable<string> searchPaths)
{
var mods = new List<Pair<string, string>>();
var mods = new List<(string, string)>();
foreach (var path in searchPaths)
{
try
@@ -48,7 +47,7 @@ namespace OpenRA
var directory = new DirectoryInfo(resolved);
foreach (var subdir in directory.EnumerateDirectories())
mods.Add(Pair.New(subdir.Name, subdir.FullName));
mods.Add((subdir.Name, subdir.FullName));
}
catch (Exception e)
{
@@ -79,8 +78,7 @@ namespace OpenRA
Log.Write("debug", "Load mod '{0}': {1}".F(path, e));
}
if (package != null)
package.Dispose();
package?.Dispose();
return null;
}
@@ -89,13 +87,13 @@ namespace OpenRA
{
var ret = new Dictionary<string, Manifest>();
var candidates = GetCandidateMods(searchPaths)
.Concat(explicitPaths.Select(p => Pair.New(Path.GetFileNameWithoutExtension(p), p)));
.Concat(explicitPaths.Select(p => (Id: Path.GetFileNameWithoutExtension(p), Path: p)));
foreach (var pair in candidates)
{
var mod = LoadMod(pair.First, pair.Second);
var mod = LoadMod(pair.Id, pair.Path);
if (mod != null)
ret[pair.First] = mod;
ret[pair.Id] = mod;
}
return ret;

View File

@@ -80,8 +80,6 @@ namespace OpenRA
{
try
{
innerState = LinkState.Unlinked;
if (i.Error != null)
{
innerState = LinkState.ConnectionFailed;
@@ -100,6 +98,8 @@ namespace OpenRA
else
innerState = LinkState.Linked;
}
else
innerState = LinkState.Unlinked;
}
catch (Exception e)
{
@@ -108,8 +108,7 @@ namespace OpenRA
}
finally
{
if (onComplete != null)
onComplete();
onComplete?.Invoke();
}
};

View File

@@ -52,7 +52,7 @@ namespace OpenRA
}
/// <summary> Describes what is to be loaded in order to run a mod. </summary>
public class Manifest
public class Manifest : IDisposable
{
public readonly string Id;
public readonly IReadOnlyPackage Package;
@@ -96,8 +96,7 @@ namespace OpenRA
// TODO: Use fieldloader
MapFolders = YamlDictionary(yaml, "MapFolders");
MiniYaml packages;
if (yaml.TryGetValue("Packages", out packages))
if (yaml.TryGetValue("Packages", out var packages))
Packages = packages.ToDictionary(x => x.Value).AsReadOnly();
Rules = YamlList(yaml, "Rules");
@@ -217,9 +216,8 @@ namespace OpenRA
/// </summary>
public T Get<T>(ObjectCreator oc) where T : IGlobalModData
{
MiniYaml data;
var t = typeof(T);
if (!yaml.TryGetValue(t.Name, out data))
if (!yaml.TryGetValue(t.Name, out var data))
{
// Lazily create the default values if not explicitly defined.
return (T)oc.CreateBasic(t);
@@ -241,5 +239,14 @@ namespace OpenRA
return (T)module;
}
public void Dispose()
{
foreach (var module in modules)
{
var disposableModule = module as IDisposable;
disposableModule?.Dispose();
}
}
}
}

View File

@@ -9,17 +9,29 @@
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA
{
public interface IActorInitializer
{
World World { get; }
T Get<T>() where T : IActorInit;
U Get<T, U>() where T : IActorInit<U>;
bool Contains<T>() where T : IActorInit;
T GetOrDefault<T>(TraitInfo info) where T : ActorInit;
T Get<T>(TraitInfo info) where T : ActorInit;
U GetValue<T, U>(TraitInfo info) where T : ValueActorInit<U>;
U GetValue<T, U>(TraitInfo info, U fallback) where T : ValueActorInit<U>;
bool Contains<T>(TraitInfo info) where T : ActorInit;
T GetOrDefault<T>() where T : ActorInit, ISingleInstanceInit;
T Get<T>() where T : ActorInit, ISingleInstanceInit;
U GetValue<T, U>() where T : ValueActorInit<U>, ISingleInstanceInit;
U GetValue<T, U>(U fallback) where T : ValueActorInit<U>, ISingleInstanceInit;
bool Contains<T>() where T : ActorInit, ISingleInstanceInit;
}
public class ActorInitializer : IActorInitializer
@@ -35,50 +47,228 @@ namespace OpenRA
Dict = dict;
}
public T Get<T>() where T : IActorInit { return Dict.Get<T>(); }
public U Get<T, U>() where T : IActorInit<U> { return Dict.Get<T>().Value(World); }
public bool Contains<T>() where T : IActorInit { return Dict.Contains<T>(); }
}
public interface IActorInit { }
public interface IActorInit<T> : IActorInit
{
T Value(World world);
}
public class LocationInit : IActorInit<CPos>
{
[FieldFromYamlKey]
readonly CPos value = CPos.Zero;
public LocationInit() { }
public LocationInit(CPos init) { value = init; }
public CPos Value(World world) { return value; }
}
public class OwnerInit : IActorInit<Player>
{
[FieldFromYamlKey]
public readonly string PlayerName = "Neutral";
Player player;
public OwnerInit() { }
public OwnerInit(string playerName) { PlayerName = playerName; }
public OwnerInit(Player player)
public T GetOrDefault<T>(TraitInfo info) where T : ActorInit
{
this.player = player;
PlayerName = player.InternalName;
var inits = Dict.WithInterface<T>();
// Traits tagged with an instance name prefer inits with the same name.
// If a more specific init is not available, fall back to an unnamed init.
// If duplicate inits are defined, take the last to match standard yaml override expectations
if (info != null && !string.IsNullOrEmpty(info.InstanceName))
return inits.LastOrDefault(i => i.InstanceName == info.InstanceName) ??
inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
// Untagged traits will only use untagged inits
return inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
}
public T Get<T>(TraitInfo info) where T : ActorInit
{
var init = GetOrDefault<T>(info);
if (init == null)
throw new InvalidOperationException("TypeDictionary does not contain instance of type `{0}`".F(typeof(T)));
return init;
}
public U GetValue<T, U>(TraitInfo info) where T : ValueActorInit<U>
{
return Get<T>(info).Value;
}
public U GetValue<T, U>(TraitInfo info, U fallback) where T : ValueActorInit<U>
{
var init = GetOrDefault<T>(info);
return init != null ? init.Value : fallback;
}
public bool Contains<T>(TraitInfo info) where T : ActorInit { return GetOrDefault<T>(info) != null; }
public T GetOrDefault<T>() where T : ActorInit, ISingleInstanceInit
{
return Dict.GetOrDefault<T>();
}
public T Get<T>() where T : ActorInit, ISingleInstanceInit
{
return Dict.Get<T>();
}
public U GetValue<T, U>() where T : ValueActorInit<U>, ISingleInstanceInit
{
return Get<T>().Value;
}
public U GetValue<T, U>(U fallback) where T : ValueActorInit<U>, ISingleInstanceInit
{
var init = GetOrDefault<T>();
return init != null ? init.Value : fallback;
}
public bool Contains<T>() where T : ActorInit, ISingleInstanceInit { return GetOrDefault<T>() != null; }
}
/*
* Things to be aware of when writing ActorInits:
*
* - ActorReference and ActorGlobal can dynamically create objects without calling a constructor.
* The object will be allocated directly then the best matching Initialize() method will be called to set valid state.
* - ActorReference will always attempt to call Initialize(MiniYaml). ActorGlobal will use whichever one it first
* finds with an argument type that matches the given LuaValue.
* - Most ActorInits will want to inherit either ValueActorInit<T> or CompositeActorInit which hide the low-level plumbing.
* - Inits that reference actors should use ActorInitActorReference which allows actors to be referenced by name in map.yaml
* - Inits that should only have a single instance defined on an actor should implement ISingleInstanceInit to allow
* direct queries and runtime enforcement.
* - Inits that aren't ISingleInstanceInit should expose a ctor that accepts a TraitInfo to allow per-trait targeting.
*/
public abstract class ActorInit
{
[FieldLoader.Ignore]
public readonly string InstanceName;
protected ActorInit(string instanceName)
{
InstanceName = instanceName;
}
protected ActorInit() { }
public abstract MiniYaml Save();
}
public interface ISingleInstanceInit { }
public abstract class ValueActorInit<T> : ActorInit
{
protected readonly T value;
protected ValueActorInit(TraitInfo info, T value)
: base(info.InstanceName) { this.value = value; }
protected ValueActorInit(string instanceName, T value)
: base(instanceName) { this.value = value; }
protected ValueActorInit(T value) { this.value = value; }
public virtual T Value { get { return value; } }
public virtual void Initialize(MiniYaml yaml)
{
Initialize((T)FieldLoader.GetValue("value", typeof(T), yaml.Value));
}
public virtual void Initialize(T value)
{
var field = GetType().GetField("value", BindingFlags.NonPublic | BindingFlags.Instance);
if (field != null)
field.SetValue(this, value);
}
public override MiniYaml Save()
{
return new MiniYaml(FieldSaver.FormatValue(value));
}
}
public abstract class CompositeActorInit : ActorInit
{
protected CompositeActorInit(TraitInfo info)
: base(info.InstanceName) { }
protected CompositeActorInit()
: base() { }
public virtual void Initialize(MiniYaml yaml)
{
FieldLoader.Load(this, yaml);
}
public virtual void Initialize(Dictionary<string, object> values)
{
foreach (var field in GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
var sa = field.GetCustomAttributes<FieldLoader.SerializeAttribute>(false).DefaultIfEmpty(FieldLoader.SerializeAttribute.Default).First();
if (!sa.Serialize)
continue;
if (values.TryGetValue(field.Name, out var value))
field.SetValue(this, value);
}
}
public virtual Dictionary<string, Type> InitializeArgs()
{
var dict = new Dictionary<string, Type>();
foreach (var field in GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
var sa = field.GetCustomAttributes<FieldLoader.SerializeAttribute>(false).DefaultIfEmpty(FieldLoader.SerializeAttribute.Default).First();
if (!sa.Serialize)
continue;
dict[field.Name] = field.FieldType;
}
return dict;
}
public override MiniYaml Save()
{
return FieldSaver.Save(this);
}
}
public class LocationInit : ValueActorInit<CPos>, ISingleInstanceInit
{
public LocationInit(CPos value)
: base(value) { }
}
public class OwnerInit : ActorInit, ISingleInstanceInit
{
public readonly string InternalName;
protected readonly Player value;
public OwnerInit(Player value)
{
this.value = value;
InternalName = value.InternalName;
}
public OwnerInit(string value)
{
InternalName = value;
}
public Player Value(World world)
{
if (player != null)
return player;
return value ?? world.Players.First(x => x.InternalName == InternalName);
}
return world.Players.First(x => x.InternalName == PlayerName);
public void Initialize(MiniYaml yaml)
{
var field = GetType().GetField("InternalName", BindingFlags.Public | BindingFlags.Instance);
if (field != null)
field.SetValue(this, yaml.Value);
}
public void Initialize(Player player)
{
var field = GetType().GetField("value", BindingFlags.NonPublic | BindingFlags.Instance);
if (field != null)
field.SetValue(this, player);
}
public override MiniYaml Save()
{
return new MiniYaml(InternalName);
}
}
public abstract class RuntimeFlagInit : ActorInit, ISuppressInitExport
{
public override MiniYaml Save()
{
throw new NotImplementedException("RuntimeFlagInit cannot be saved");
}
}
}

View File

@@ -12,7 +12,11 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA
{
@@ -21,13 +25,10 @@ namespace OpenRA
public class ActorReference : IEnumerable
{
public string Type;
public TypeDictionary InitDict
{
get { return initDict.Value; }
}
Lazy<TypeDictionary> initDict;
internal TypeDictionary InitDict { get { return initDict.Value; } }
public ActorReference(string type)
: this(type, new Dictionary<string, MiniYaml>()) { }
@@ -38,47 +39,168 @@ namespace OpenRA
{
var dict = new TypeDictionary();
foreach (var i in inits)
dict.Add(LoadInit(i.Key, i.Value));
{
var init = LoadInit(i.Key, i.Value);
if (init is ISingleInstanceInit && dict.Contains(init.GetType()))
throw new InvalidDataException("Duplicate initializer '{0}'".F(init.GetType().Name));
dict.Add(init);
}
return dict;
});
}
static IActorInit LoadInit(string traitName, MiniYaml my)
public ActorReference(string type, TypeDictionary inits)
{
var info = Game.CreateObject<IActorInit>(traitName + "Init");
FieldLoader.Load(info, my);
return info;
Type = type;
initDict = new Lazy<TypeDictionary>(() =>
{
var dict = new TypeDictionary();
foreach (var i in inits)
dict.Add(i);
return dict;
});
}
public MiniYaml Save(Func<object, bool> initFilter = null)
static ActorInit LoadInit(string initName, MiniYaml initYaml)
{
var initInstance = initName.Split(ActorInfo.TraitInstanceSeparator);
var type = Game.ModData.ObjectCreator.FindType(initInstance[0] + "Init");
if (type == null)
throw new InvalidDataException("Unknown initializer type '{0}Init'".F(initInstance[0]));
var init = (ActorInit)FormatterServices.GetUninitializedObject(type);
if (initInstance.Length > 1)
type.GetField("InstanceName").SetValue(init, initInstance[1]);
var loader = type.GetMethod("Initialize", new[] { typeof(MiniYaml) });
if (loader == null)
throw new InvalidDataException("{0}Init does not define a yaml-assignable type.".F(initInstance[0]));
loader.Invoke(init, new[] { initYaml });
return init;
}
public MiniYaml Save(Func<ActorInit, bool> initFilter = null)
{
var ret = new MiniYaml(Type);
foreach (var init in InitDict)
foreach (var o in initDict.Value)
{
if (init is ISuppressInitExport)
var init = o as ActorInit;
if (init == null || o is ISuppressInitExport)
continue;
if (initFilter != null && !initFilter(init))
continue;
var initName = init.GetType().Name;
ret.Nodes.Add(new MiniYamlNode(initName.Substring(0, initName.Length - 4), FieldSaver.Save(init)));
var initTypeName = init.GetType().Name;
var initName = initTypeName.Substring(0, initTypeName.Length - 4);
if (!string.IsNullOrEmpty(init.InstanceName))
initName += ActorInfo.TraitInstanceSeparator + init.InstanceName;
ret.Nodes.Add(new MiniYamlNode(initName, init.Save()));
}
return ret;
}
// for initialization syntax
public void Add(object o) { InitDict.Add(o); }
public IEnumerator GetEnumerator() { return InitDict.GetEnumerator(); }
public IEnumerator GetEnumerator() { return initDict.Value.GetEnumerator(); }
public ActorReference Clone()
{
var clone = new ActorReference(Type);
foreach (var init in InitDict)
clone.InitDict.Add(init);
foreach (var init in initDict.Value)
clone.initDict.Value.Add(init);
return clone;
}
public void Add(ActorInit init)
{
if (init is ISingleInstanceInit && InitDict.Contains(init.GetType()))
throw new InvalidDataException("Duplicate initializer '{0}'".F(init.GetType().Name));
InitDict.Add(init);
}
public void Remove(ActorInit o) { initDict.Value.Remove(o); }
public int RemoveAll<T>() where T : ActorInit
{
var removed = 0;
foreach (var o in initDict.Value.WithInterface<T>().ToList())
{
removed++;
initDict.Value.Remove(o);
}
return removed;
}
public IEnumerable<T> GetAll<T>() where T : ActorInit
{
return initDict.Value.WithInterface<T>();
}
public T GetOrDefault<T>(TraitInfo info) where T : ActorInit
{
var inits = initDict.Value.WithInterface<T>();
// Traits tagged with an instance name prefer inits with the same name.
// If a more specific init is not available, fall back to an unnamed init.
// If duplicate inits are defined, take the last to match standard yaml override expectations
if (info != null && !string.IsNullOrEmpty(info.InstanceName))
return inits.LastOrDefault(i => i.InstanceName == info.InstanceName) ??
inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
// Untagged traits will only use untagged inits
return inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
}
public T Get<T>(TraitInfo info) where T : ActorInit
{
var init = GetOrDefault<T>(info);
if (init == null)
throw new InvalidOperationException("TypeDictionary does not contain instance of type `{0}`".F(typeof(T)));
return init;
}
public U GetValue<T, U>(TraitInfo info) where T : ValueActorInit<U>
{
return Get<T>(info).Value;
}
public U GetValue<T, U>(TraitInfo info, U fallback) where T : ValueActorInit<U>
{
var init = GetOrDefault<T>(info);
return init != null ? init.Value : fallback;
}
public bool Contains<T>(TraitInfo info) where T : ActorInit { return GetOrDefault<T>(info) != null; }
public T GetOrDefault<T>() where T : ActorInit, ISingleInstanceInit
{
return initDict.Value.GetOrDefault<T>();
}
public T Get<T>() where T : ActorInit, ISingleInstanceInit
{
return initDict.Value.Get<T>();
}
public U GetValue<T, U>() where T : ValueActorInit<U>, ISingleInstanceInit
{
return Get<T>().Value;
}
public U GetValue<T, U>(U fallback) where T : ValueActorInit<U>, ISingleInstanceInit
{
var init = GetOrDefault<T>();
return init != null ? init.Value : fallback;
}
public bool Contains<T>() where T : ActorInit, ISingleInstanceInit { return GetOrDefault<T>() != null; }
}
}

View File

@@ -10,42 +10,28 @@
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using OpenRA.Primitives;
namespace OpenRA
{
// Represents a layer of "something" that covers the map
public class CellLayer<T> : IEnumerable<T>
public sealed class CellLayer<T> : CellLayerBase<T>
{
public readonly Size Size;
readonly Rectangle bounds;
public readonly MapGridType GridType;
public event Action<CPos> CellEntryChanged = null;
readonly T[] entries;
public CellLayer(Map map)
: this(map.Grid.Type, new Size(map.MapSize.X, map.MapSize.Y)) { }
: base(map) { }
public CellLayer(MapGridType gridType, Size size)
{
Size = size;
bounds = new Rectangle(0, 0, Size.Width, Size.Height);
GridType = gridType;
entries = new T[size.Width * size.Height];
}
: base(gridType, size) { }
public void CopyValuesFrom(CellLayer<T> anotherLayer)
public override void CopyValuesFrom(CellLayerBase<T> anotherLayer)
{
if (Size != anotherLayer.Size || GridType != anotherLayer.GridType)
throw new ArgumentException(
"layers must have a matching size and shape (grid type).", "anotherLayer");
if (CellEntryChanged != null)
throw new InvalidOperationException(
"Cannot copy values when there are listeners attached to the CellEntryChanged event.");
Array.Copy(anotherLayer.entries, entries, entries.Length);
base.CopyValuesFrom(anotherLayer);
}
public static CellLayer<T> CreateInstance(Func<MPos, T> initialCellValueFactory, Size size, MapGridType mapGridType)
@@ -87,8 +73,7 @@ namespace OpenRA
{
entries[Index(cell)] = value;
if (CellEntryChanged != null)
CellEntryChanged(cell);
CellEntryChanged?.Invoke(cell);
}
}
@@ -104,28 +89,10 @@ namespace OpenRA
{
entries[Index(uv)] = value;
if (CellEntryChanged != null)
CellEntryChanged(uv.ToCPos(GridType));
CellEntryChanged?.Invoke(uv.ToCPos(GridType));
}
}
/// <summary>Clears the layer contents with a known value</summary>
public void Clear(T clearValue)
{
for (var i = 0; i < entries.Length; i++)
entries[i] = clearValue;
}
public IEnumerator<T> GetEnumerator()
{
return ((IEnumerable<T>)entries).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public bool Contains(CPos cell)
{
// .ToMPos() returns the same result if the X and Y coordinates

View File

@@ -0,0 +1,63 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 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.Collections.Generic;
using OpenRA.Primitives;
namespace OpenRA
{
public abstract class CellLayerBase<T> : IEnumerable<T>
{
public readonly Size Size;
public readonly MapGridType GridType;
protected readonly T[] entries;
protected readonly Rectangle bounds;
public CellLayerBase(Map map)
: this(map.Grid.Type, new Size(map.MapSize.X, map.MapSize.Y)) { }
public CellLayerBase(MapGridType gridType, Size size)
{
Size = size;
bounds = new Rectangle(0, 0, Size.Width, Size.Height);
GridType = gridType;
entries = new T[size.Width * size.Height];
}
public virtual void CopyValuesFrom(CellLayerBase<T> anotherLayer)
{
if (Size != anotherLayer.Size || GridType != anotherLayer.GridType)
throw new ArgumentException("Layers must have a matching size and shape (grid type).", "anotherLayer");
Array.Copy(anotherLayer.entries, entries, entries.Length);
}
/// <summary>Clears the layer contents with a known value</summary>
public void Clear(T clearValue)
{
for (var i = 0; i < entries.Length; i++)
entries[i] = clearValue;
}
public IEnumerator<T> GetEnumerator()
{
return ((IEnumerable<T>)entries).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@@ -117,7 +117,7 @@ namespace OpenRA
return GetEnumerator();
}
public sealed class CellRegionEnumerator : IEnumerator<CPos>
public struct CellRegionEnumerator : IEnumerator<CPos>
{
readonly CellRegion r;
@@ -128,9 +128,11 @@ namespace OpenRA
CPos current;
public CellRegionEnumerator(CellRegion region)
: this()
{
r = region;
Reset();
current = new MPos(u, v).ToCPos(r.gridType);
}
public bool MoveNext()

View File

@@ -230,9 +230,10 @@ namespace OpenRA
public CellLayer<TerrainTile> Tiles { get; private set; }
public CellLayer<ResourceTile> Resources { get; private set; }
public CellLayer<byte> Height { get; private set; }
public CellLayer<byte> Ramp { get; private set; }
public CellLayer<byte> CustomTerrain { get; private set; }
public ProjectedCellRegion ProjectedCellBounds { get; private set; }
public PPos[] ProjectedCells { get; private set; }
public CellRegion AllCells { get; private set; }
public List<CPos> AllEdgeCells { get; private set; }
@@ -301,10 +302,12 @@ namespace OpenRA
Tiles = new CellLayer<TerrainTile>(Grid.Type, size);
Resources = new CellLayer<ResourceTile>(Grid.Type, size);
Height = new CellLayer<byte>(Grid.Type, size);
Ramp = new CellLayer<byte>(Grid.Type, size);
if (Grid.MaximumTerrainHeight > 0)
{
Height.CellEntryChanged += UpdateProjection;
Tiles.CellEntryChanged += UpdateProjection;
Tiles.CellEntryChanged += UpdateRamp;
}
Tiles.Clear(tileRef);
@@ -336,6 +339,7 @@ namespace OpenRA
Tiles = new CellLayer<TerrainTile>(Grid.Type, size);
Resources = new CellLayer<ResourceTile>(Grid.Type, size);
Height = new CellLayer<byte>(Grid.Type, size);
Ramp = new CellLayer<byte>(Grid.Type, size);
using (var s = Package.GetStream("map.bin"))
{
@@ -384,6 +388,7 @@ namespace OpenRA
if (Grid.MaximumTerrainHeight > 0)
{
Tiles.CellEntryChanged += UpdateRamp;
Tiles.CellEntryChanged += UpdateProjection;
Height.CellEntryChanged += UpdateProjection;
}
@@ -422,9 +427,23 @@ namespace OpenRA
foreach (var uv in AllCells.MapCoords)
CustomTerrain[uv] = byte.MaxValue;
// Cache initial ramp state
var tileset = Rules.TileSet;
foreach (var uv in AllCells)
{
var tile = tileset.GetTileInfo(Tiles[uv]);
Ramp[uv] = tile != null ? tile.RampType : (byte)0;
}
AllEdgeCells = UpdateEdgeCells();
}
void UpdateRamp(CPos cell)
{
var tile = Rules.TileSet.GetTileInfo(Tiles[cell]);
Ramp[cell] = tile != null ? tile.RampType : (byte)0;
}
void InitializeCellProjection()
{
if (initializedCellProjection)
@@ -531,12 +550,8 @@ namespace OpenRA
return new[] { (PPos)uv };
// Odd-height ramps get bumped up a level to the next even height layer
if ((height & 1) == 1)
{
var ti = Rules.TileSet.GetTileInfo(Tiles[uv]);
if (ti != null && ti.RampType != 0)
height += 1;
}
if ((height & 1) == 1 && Ramp[uv] != 0)
height += 1;
var candidates = new List<PPos>();
@@ -647,12 +662,40 @@ namespace OpenRA
return dataStream.ToArray();
}
public (Color Left, Color Right) GetTerrainColorPair(MPos uv)
{
Color left, right;
var tileset = Rules.TileSet;
var type = tileset.GetTileInfo(Tiles[uv]);
if (type != null)
{
if (type.MinColor != type.MaxColor)
{
left = Exts.ColorLerp(Game.CosmeticRandom.NextFloat(), type.MinColor, type.MaxColor);
right = Exts.ColorLerp(Game.CosmeticRandom.NextFloat(), type.MinColor, type.MaxColor);
}
else
left = right = type.MinColor;
if (tileset.MinHeightColorBrightness != 1.0f || tileset.MaxHeightColorBrightness != 1.0f)
{
var scale = float2.Lerp(tileset.MinHeightColorBrightness, tileset.MaxHeightColorBrightness, Height[uv] * 1f / Grid.MaximumTerrainHeight);
left = Color.FromArgb((int)(scale * left.R).Clamp(0, 255), (int)(scale * left.G).Clamp(0, 255), (int)(scale * left.B).Clamp(0, 255));
right = Color.FromArgb((int)(scale * right.R).Clamp(0, 255), (int)(scale * right.G).Clamp(0, 255), (int)(scale * right.B).Clamp(0, 255));
}
}
else
left = right = Color.Black;
return (left, right);
}
public byte[] SavePreview()
{
var tileset = Rules.TileSet;
var actorTypes = Rules.Actors.Values.Where(a => a.HasTraitInfo<IMapPreviewSignatureInfo>());
var actors = ActorDefinitions.Where(a => actorTypes.Where(ai => ai.Name == a.Value.Value).Any());
var positions = new List<Pair<MPos, Color>>();
var positions = new List<(MPos Position, Color Color)>();
foreach (var actor in actors)
{
var s = new ActorReference(actor.Value.Value, actor.Value.ToDictionary());
@@ -686,24 +729,18 @@ namespace OpenRA
var stride = bitmapWidth * 4;
var pxStride = 4;
var minimapData = new byte[stride * height];
Color leftColor, rightColor;
(Color Left, Color Right) terrainColor = default((Color, Color));
for (var y = 0; y < height; y++)
{
for (var x = 0; x < width; x++)
{
var uv = new MPos(x + Bounds.Left, y + Bounds.Top);
var actorsThere = positions.Where(ap => ap.First == uv);
if (actorsThere.Any())
{
leftColor = rightColor = actorsThere.First().Second;
}
else
{
// Cell contains terrain
var type = tileset.GetTileInfo(Tiles[uv]);
leftColor = type != null ? type.LeftColor : Color.Black;
rightColor = type != null ? type.RightColor : Color.Black;
}
// FirstOrDefault will return a (MPos.Zero, Color.Transparent) if positions is empty
var actorColor = positions.FirstOrDefault(ap => ap.Position == uv).Color;
if (actorColor.A == 0)
terrainColor = GetTerrainColorPair(uv);
if (isRectangularIsometric)
{
@@ -713,28 +750,31 @@ namespace OpenRA
if (x + dx > 0)
{
var z = y * stride + xOffset - pxStride;
minimapData[z++] = leftColor.R;
minimapData[z++] = leftColor.G;
minimapData[z++] = leftColor.B;
minimapData[z++] = leftColor.A;
var c = actorColor.A == 0 ? terrainColor.Left : actorColor;
minimapData[z++] = c.R;
minimapData[z++] = c.G;
minimapData[z++] = c.B;
minimapData[z] = c.A;
}
if (xOffset < stride)
{
var z = y * stride + xOffset;
minimapData[z++] = rightColor.R;
minimapData[z++] = rightColor.G;
minimapData[z++] = rightColor.B;
minimapData[z++] = rightColor.A;
var c = actorColor.A == 0 ? terrainColor.Right : actorColor;
minimapData[z++] = c.R;
minimapData[z++] = c.G;
minimapData[z++] = c.B;
minimapData[z] = c.A;
}
}
else
{
var z = y * stride + pxStride * x;
minimapData[z++] = leftColor.R;
minimapData[z++] = leftColor.G;
minimapData[z++] = leftColor.B;
minimapData[z++] = leftColor.A;
var c = actorColor.A == 0 ? terrainColor.Left : actorColor;
minimapData[z++] = c.R;
minimapData[z++] = c.G;
minimapData[z++] = c.B;
minimapData[z] = c.A;
}
}
}
@@ -770,8 +810,9 @@ namespace OpenRA
bool ContainsAllProjectedCellsCovering(MPos uv)
{
// PERF: Checking the bounds directly here is the same as calling Contains((PPos)uv) but saves an allocation
if (Grid.MaximumTerrainHeight == 0)
return Contains((PPos)uv);
return Bounds.Contains(uv.U, uv.V);
// If the cell has no valid projection, then we're off the map.
var projectedCells = ProjectedCellsCovering(uv);
@@ -781,6 +822,7 @@ namespace OpenRA
foreach (var puv in projectedCells)
if (!Contains(puv))
return false;
return true;
}
@@ -805,7 +847,7 @@ namespace OpenRA
// (c) u, v coordinates run diagonally to the cell axes, and we define
// 1024 as the length projected onto the primary cell axis
// - 512 * sqrt(2) = 724
var z = Height.Contains(cell) ? 724 * Height[cell] : 0;
var z = Height.Contains(cell) ? 724 * Height[cell] + Grid.Ramps[Ramp[cell]].CenterHeightOffset : 0;
return new WPos(724 * (cell.X - cell.Y + 1), 724 * (cell.X + cell.Y + 1), z);
}
@@ -813,15 +855,42 @@ namespace OpenRA
{
var index = (int)subCell;
if (index >= 0 && index < Grid.SubCellOffsets.Length)
return CenterOfCell(cell) + Grid.SubCellOffsets[index];
{
var center = CenterOfCell(cell);
var offset = Grid.SubCellOffsets[index];
var ramp = Ramp.Contains(cell) ? Ramp[cell] : 0;
if (ramp != 0)
{
var r = Grid.Ramps[ramp];
offset += new WVec(0, 0, r.HeightOffset(offset.X, offset.Y) - r.CenterHeightOffset);
}
return center + offset;
}
return CenterOfCell(cell);
}
public WDist DistanceAboveTerrain(WPos pos)
{
if (Grid.Type == MapGridType.Rectangular)
return new WDist(pos.Z);
// Apply ramp offset
var cell = CellContaining(pos);
var delta = pos - CenterOfCell(cell);
return new WDist(delta.Z);
var offset = pos - CenterOfCell(cell);
if (!Ramp.Contains(cell))
return new WDist(offset.Z);
var ramp = Ramp[cell];
if (ramp != 0)
{
var r = Grid.Ramps[ramp];
return new WDist(offset.Z + r.CenterHeightOffset - r.HeightOffset(offset.X, offset.Y));
}
return new WDist(offset.Z);
}
public WVec Offset(CVec delta, int dz)
@@ -899,13 +968,13 @@ namespace OpenRA
return projectedHeight[(MPos)puv];
}
public int FacingBetween(CPos cell, CPos towards, int fallbackfacing)
public WAngle FacingBetween(CPos cell, CPos towards, WAngle fallbackfacing)
{
var delta = CenterOfCell(towards) - CenterOfCell(cell);
if (delta.HorizontalLengthSquared == 0)
return fallbackfacing;
return delta.Yaw.Facing;
return delta.Yaw;
}
public void Resize(int width, int height)
@@ -913,11 +982,13 @@ namespace OpenRA
var oldMapTiles = Tiles;
var oldMapResources = Resources;
var oldMapHeight = Height;
var oldMapRamp = Ramp;
var newSize = new Size(width, height);
Tiles = CellLayer.Resize(oldMapTiles, newSize, oldMapTiles[MPos.Zero]);
Resources = CellLayer.Resize(oldMapResources, newSize, oldMapResources[MPos.Zero]);
Height = CellLayer.Resize(oldMapHeight, newSize, oldMapHeight[MPos.Zero]);
Ramp = CellLayer.Resize(oldMapRamp, newSize, oldMapRamp[MPos.Zero]);
MapSize = new int2(newSize);
var tl = new MPos(0, 0);
@@ -947,7 +1018,8 @@ namespace OpenRA
ProjectedBottomRight = new WPos(br.U * 1024 - 1, (br.V + 1) * 1024 - 1, 0);
}
ProjectedCellBounds = new ProjectedCellRegion(this, tl, br);
// PERF: This enumeration isn't going to change during the game
ProjectedCells = new ProjectedCellRegion(this, tl, br).ToArray();
}
public void FixOpenAreas()

View File

@@ -111,8 +111,7 @@ namespace OpenRA
}
catch (Exception e)
{
if (mapPackage != null)
mapPackage.Dispose();
mapPackage?.Dispose();
Console.WriteLine("Failed to load map: {0}", map);
Console.WriteLine("Details: {0}", e);
Log.Write("debug", "Failed to load map: {0}", map);
@@ -131,8 +130,7 @@ namespace OpenRA
// Enumerate map directories
foreach (var kv in modData.Manifest.MapFolders)
{
MapClassification packageClassification;
if (!Enum.TryParse(kv.Value, out packageClassification))
if (!Enum.TryParse(kv.Value, out MapClassification packageClassification))
continue;
if (!classification.HasFlag(packageClassification))
@@ -193,8 +191,7 @@ namespace OpenRA
foreach (var p in maps.Values)
p.UpdateRemoteSearch(MapStatus.Unavailable, null);
if (queryFailed != null)
queryFailed();
queryFailed?.Invoke();
return;
}
@@ -214,8 +211,7 @@ namespace OpenRA
{
Log.Write("debug", "Can't parse remote map search data:\n{0}", data);
Log.Write("debug", "Exception: {0}", e);
if (queryFailed != null)
queryFailed();
queryFailed?.Invoke();
}
};
@@ -299,8 +295,7 @@ namespace OpenRA
Game.RunAfterTick(() =>
{
// Wait for any existing thread to exit before starting a new one.
if (previewLoaderThread != null)
previewLoaderThread.Join();
previewLoaderThread?.Join();
previewLoaderThread = new Thread(LoadAsyncInternal)
{

View File

@@ -9,7 +9,6 @@
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -20,6 +19,85 @@ namespace OpenRA
{
public enum MapGridType { Rectangular, RectangularIsometric }
public enum RampSplit { Flat, X, Y }
public enum RampCornerHeight { Low = 0, Half = 1, Full = 2 }
public struct CellRamp
{
public readonly int CenterHeightOffset;
public readonly WVec[] Corners;
public readonly WVec[][] Polygons;
public CellRamp(MapGridType type, RampCornerHeight tl = RampCornerHeight.Low, RampCornerHeight tr = RampCornerHeight.Low, RampCornerHeight br = RampCornerHeight.Low, RampCornerHeight bl = RampCornerHeight.Low, RampSplit split = RampSplit.Flat)
{
if (type == MapGridType.RectangularIsometric)
{
Corners = new[]
{
new WVec(0, -724, 724 * (int)tl),
new WVec(724, 0, 724 * (int)tr),
new WVec(0, 724, 724 * (int)br),
new WVec(-724, 0, 724 * (int)bl),
};
}
else
{
Corners = new[]
{
new WVec(-512, -512, 512 * (int)tl),
new WVec(512, -512, 512 * (int)tr),
new WVec(512, 512, 512 * (int)br),
new WVec(-512, 512, 512 * (int)bl)
};
}
if (split == RampSplit.X)
{
Polygons = new[]
{
new[] { Corners[0], Corners[1], Corners[3] },
new[] { Corners[1], Corners[2], Corners[3] }
};
}
else if (split == RampSplit.Y)
{
Polygons = new[]
{
new[] { Corners[0], Corners[1], Corners[2] },
new[] { Corners[0], Corners[2], Corners[3] }
};
}
else
Polygons = new[] { Corners };
// Initial value must be asigned before HeightOffset can be called
CenterHeightOffset = 0;
CenterHeightOffset = HeightOffset(0, 0);
}
public int HeightOffset(int dX, int dY)
{
// Enumerate over the polygons, assuming that they are triangles
// If the ramp is not split we will take the first three vertices of the corners as a valid triangle
WVec[] p = null;
var u = 0;
var v = 0;
for (var i = 0; i < Polygons.Length; i++)
{
p = Polygons[i];
u = ((p[1].Y - p[2].Y) * (dX - p[2].X) - (p[1].X - p[2].X) * (dY - p[2].Y)) / 1024;
v = ((p[0].X - p[2].X) * (dY - p[2].Y) - (p[0].Y - p[2].Y) * (dX - p[2].X)) / 1024;
// Point is within the triangle if 0 <= u,v <= 1024
if (u >= 0 && u <= 1024 && v >= 0 && v <= 1024)
break;
}
// Calculate w from u,v and interpolate height
return (u * p[0].Z + v * p[1].Z + (1024 - u - v) * p[2].Z) / 1024;
}
}
public class MapGrid : IGlobalModData
{
public readonly MapGridType Type = MapGridType.Rectangular;
@@ -41,43 +119,7 @@ namespace OpenRA
new WVec(256, 256, 0), // bottom right - index 5
};
public WVec[][] CellCorners { get; private set; }
readonly int[][] cellCornerHalfHeights = new int[][]
{
// Flat
new[] { 0, 0, 0, 0 },
// Slopes (two corners high)
new[] { 0, 0, 1, 1 },
new[] { 1, 0, 0, 1 },
new[] { 1, 1, 0, 0 },
new[] { 0, 1, 1, 0 },
// Slopes (one corner high)
new[] { 0, 0, 0, 1 },
new[] { 1, 0, 0, 0 },
new[] { 0, 1, 0, 0 },
new[] { 0, 0, 1, 0 },
// Slopes (three corners high)
new[] { 1, 0, 1, 1 },
new[] { 1, 1, 0, 1 },
new[] { 1, 1, 1, 0 },
new[] { 0, 1, 1, 1 },
// Slopes (two corners high, one corner double high)
new[] { 1, 0, 1, 2 },
new[] { 2, 1, 0, 1 },
new[] { 1, 2, 1, 0 },
new[] { 0, 1, 2, 1 },
// Slopes (two corners high, alternating)
new[] { 1, 0, 1, 0 },
new[] { 0, 1, 0, 1 },
new[] { 1, 0, 1, 0 },
new[] { 0, 1, 0, 1 }
};
public CellRamp[] Ramps { get; private set; }
internal readonly CVec[][] TilesByDistance;
@@ -96,34 +138,46 @@ namespace OpenRA
throw new InvalidDataException("Subcell default index must be a valid index into the offset triples and must be greater than 0 for mods with subcells");
}
var makeCorners = Type == MapGridType.RectangularIsometric ?
(Func<int[], WVec[]>)IsometricCellCorners : RectangularCellCorners;
CellCorners = cellCornerHalfHeights.Select(makeCorners).ToArray();
// Slope types are hardcoded following the convention from the TS and RA2 map format
Ramps = new[]
{
// Flat
new CellRamp(Type),
// Two adjacent corners raised by half a cell
new CellRamp(Type, tr: RampCornerHeight.Half, br: RampCornerHeight.Half),
new CellRamp(Type, br: RampCornerHeight.Half, bl: RampCornerHeight.Half),
new CellRamp(Type, tl: RampCornerHeight.Half, bl: RampCornerHeight.Half),
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Half),
// One corner raised by half a cell
new CellRamp(Type, br: RampCornerHeight.Half, split: RampSplit.X),
new CellRamp(Type, bl: RampCornerHeight.Half, split: RampSplit.Y),
new CellRamp(Type, tl: RampCornerHeight.Half, split: RampSplit.X),
new CellRamp(Type, tr: RampCornerHeight.Half, split: RampSplit.Y),
// Three corners raised by half a cell
new CellRamp(Type, tr: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X),
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.Y),
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X),
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.Y),
// Full tile sloped (mid corners raised by half cell, far corner by full cell)
new CellRamp(Type, tr: RampCornerHeight.Half, br: RampCornerHeight.Full, bl: RampCornerHeight.Half),
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, bl: RampCornerHeight.Full),
new CellRamp(Type, tl: RampCornerHeight.Full, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half),
new CellRamp(Type, tl: RampCornerHeight.Half, tr: RampCornerHeight.Full, br: RampCornerHeight.Half),
// Two opposite corners raised by half a cell
new CellRamp(Type, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.Y),
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.Y),
new CellRamp(Type, tr: RampCornerHeight.Half, bl: RampCornerHeight.Half, split: RampSplit.X),
new CellRamp(Type, tl: RampCornerHeight.Half, br: RampCornerHeight.Half, split: RampSplit.X),
};
TilesByDistance = CreateTilesByDistance();
}
static WVec[] IsometricCellCorners(int[] cornerHeight)
{
return new WVec[]
{
new WVec(-724, 0, 724 * cornerHeight[0]),
new WVec(0, -724, 724 * cornerHeight[1]),
new WVec(724, 0, 724 * cornerHeight[2]),
new WVec(0, 724, 724 * cornerHeight[3])
};
}
static WVec[] RectangularCellCorners(int[] cornerHeight)
{
return new WVec[]
{
new WVec(-512, -512, 512 * cornerHeight[0]),
new WVec(512, -512, 512 * cornerHeight[1]),
new WVec(512, 512, 512 * cornerHeight[2]),
new WVec(-512, 512, 512 * cornerHeight[3])
};
}
CVec[][] CreateTilesByDistance()
{
var ts = new List<CVec>[MaximumTileSearchRange + 1];

View File

@@ -89,7 +89,7 @@ namespace OpenRA
public bool DefinesUnsafeCustomRules { get; private set; }
public bool RulesLoaded { get; private set; }
public void SetRulesetGenerator(ModData modData, Func<Pair<Ruleset, bool>> generator)
public void SetRulesetGenerator(ModData modData, Func<(Ruleset Ruleset, bool DefinesUnsafeCustomRules)> generator)
{
InvalidCustomRules = false;
RulesLoaded = false;
@@ -106,8 +106,8 @@ namespace OpenRA
try
{
var ret = generator();
DefinesUnsafeCustomRules = ret.Second;
return ret.First;
DefinesUnsafeCustomRules = ret.DefinesUnsafeCustomRules;
return ret.Ruleset;
}
catch (Exception e)
{
@@ -233,8 +233,7 @@ namespace OpenRA
newData.GridType = gridType;
newData.Class = classification;
MiniYaml temp;
if (yaml.TryGetValue("MapFormat", out temp))
if (yaml.TryGetValue("MapFormat", out var temp))
{
var format = FieldLoader.GetValue<int>("MapFormat", temp.Value);
if (format != Map.SupportedMapFormat)
@@ -269,14 +268,13 @@ namespace OpenRA
try
{
// Actor definitions may change if the map format changes
MiniYaml actorDefinitions;
if (yaml.TryGetValue("Actors", out actorDefinitions))
if (yaml.TryGetValue("Actors", out var actorDefinitions))
{
var spawns = new List<CPos>();
foreach (var kv in actorDefinitions.Nodes.Where(d => d.Value.Value == "mpspawn"))
{
var s = new ActorReference(kv.Value.Value, kv.Value.ToDictionary());
spawns.Add(s.InitDict.Get<LocationInit>().Value(null));
spawns.Add(s.Get<LocationInit>().Value);
}
newData.SpawnPoints = spawns.ToArray();
@@ -293,8 +291,7 @@ namespace OpenRA
try
{
// Player definitions may change if the map format changes
MiniYaml playerDefinitions;
if (yaml.TryGetValue("Players", out playerDefinitions))
if (yaml.TryGetValue("Players", out var playerDefinitions))
{
newData.Players = new MapPlayers(playerDefinitions.Nodes);
newData.PlayerCount = newData.Players.Players.Count(x => x.Value.Playable);
@@ -318,7 +315,7 @@ namespace OpenRA
voiceDefinitions, notificationDefinitions, musicDefinitions, sequenceDefinitions, modelSequenceDefinitions);
var flagged = Ruleset.DefinesUnsafeCustomRules(modData, this, ruleDefinitions,
weaponDefinitions, voiceDefinitions, notificationDefinitions, sequenceDefinitions);
return Pair.New(rules, flagged);
return (rules, flagged);
});
if (p.Contains("map.png"))
@@ -331,8 +328,7 @@ namespace OpenRA
MiniYaml LoadRuleSection(Dictionary<string, MiniYaml> yaml, string section)
{
MiniYaml node;
if (!yaml.TryGetValue(section, out node))
if (!yaml.TryGetValue(section, out var node))
return null;
return node;
@@ -402,7 +398,7 @@ namespace OpenRA
voiceDefinitions, notificationDefinitions, musicDefinitions, sequenceDefinitions, modelSequenceDefinitions);
var flagged = Ruleset.DefinesUnsafeCustomRules(modData, this, ruleDefinitions,
weaponDefinitions, voiceDefinitions, notificationDefinitions, sequenceDefinitions);
return Pair.New(rules, flagged);
return (rules, flagged);
});
}
catch (Exception e)
@@ -416,8 +412,7 @@ namespace OpenRA
if (innerData.Preview != null)
cache.CacheMinimap(this);
if (parseMetadata != null)
parseMetadata(this);
parseMetadata?.Invoke(this);
}
// Update the status and class unconditionally
@@ -526,9 +521,7 @@ namespace OpenRA
public void Delete()
{
Invalidate();
var deleteFromPackage = parentPackage as IReadWritePackage;
if (deleteFromPackage != null)
deleteFromPackage.Delete(Package.Name);
(parentPackage as IReadWritePackage)?.Delete(Package.Name);
}
Stream IReadOnlyFileSystem.Open(string filename)

View File

@@ -0,0 +1,49 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 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.Primitives;
namespace OpenRA
{
public sealed class ProjectedCellLayer<T> : CellLayerBase<T>
{
public ProjectedCellLayer(Map map)
: base(map) { }
public ProjectedCellLayer(MapGridType gridType, Size size)
: base(gridType, size) { }
// Resolve an array index from map coordinates.
int Index(PPos uv)
{
return uv.V * Size.Width + uv.U;
}
/// <summary>Gets or sets the layer contents using projected map coordinates.</summary>
public T this[PPos uv]
{
get
{
return entries[Index(uv)];
}
set
{
entries[Index(uv)] = value;
}
}
public bool Contains(PPos uv)
{
return bounds.Contains(uv.U, uv.V);
}
}
}

View File

@@ -76,7 +76,7 @@ namespace OpenRA
return GetEnumerator();
}
public sealed class ProjectedCellRegionEnumerator : IEnumerator<PPos>
public struct ProjectedCellRegionEnumerator : IEnumerator<PPos>
{
readonly ProjectedCellRegion r;
@@ -86,9 +86,11 @@ namespace OpenRA
PPos current;
public ProjectedCellRegionEnumerator(ProjectedCellRegion region)
: this()
{
r = region;
Reset();
current = new PPos(u, v);
}
public bool MoveNext()

View File

@@ -24,9 +24,8 @@ namespace OpenRA
public readonly byte TerrainType = byte.MaxValue;
public readonly byte Height;
public readonly byte RampType;
public readonly Color LeftColor;
public readonly Color RightColor;
public readonly Color MinColor;
public readonly Color MaxColor;
public readonly float ZOffset = 0.0f;
public readonly float ZRamp = 1.0f;
}
@@ -73,8 +72,7 @@ namespace OpenRA
tileInfo = new TerrainTileInfo[Size.X * Size.Y];
foreach (var node in nodes)
{
int key;
if (!int.TryParse(node.Key, out key) || key < 0 || key >= tileInfo.Length)
if (!int.TryParse(node.Key, out var key) || key < 0 || key >= tileInfo.Length)
throw new InvalidDataException("Invalid tile key '{0}' on template '{1}' of tileset '{2}'.".F(node.Key, Id, tileSet.Id));
tileInfo[key] = LoadTileInfo(tileSet, node.Value);
@@ -87,8 +85,7 @@ namespace OpenRA
var i = 0;
foreach (var node in nodes)
{
int key;
if (!int.TryParse(node.Key, out key) || key != i++)
if (!int.TryParse(node.Key, out var key) || key != i++)
throw new InvalidDataException("Invalid tile key '{0}' on template '{1}' of tileset '{2}'.".F(node.Key, Id, tileSet.Id));
tileInfo[key] = LoadTileInfo(tileSet, node.Value);
@@ -106,11 +103,11 @@ namespace OpenRA
// Fall back to the terrain-type color if necessary
var overrideColor = tileSet.TerrainInfo[tile.TerrainType].Color;
if (tile.LeftColor == default(Color))
tile.GetType().GetField("LeftColor").SetValue(tile, overrideColor);
if (tile.MinColor == default(Color))
tile.GetType().GetField("MinColor").SetValue(tile, overrideColor);
if (tile.RightColor == default(Color))
tile.GetType().GetField("RightColor").SetValue(tile, overrideColor);
if (tile.MaxColor == default(Color))
tile.GetType().GetField("MaxColor").SetValue(tile, overrideColor);
return tile;
}
@@ -139,6 +136,8 @@ namespace OpenRA
public readonly string[] EditorTemplateOrder;
public readonly bool IgnoreTileSpriteOffsets;
public readonly bool EnableDepth = false;
public readonly float MinHeightColorBrightness = 1.0f;
public readonly float MaxHeightColorBrightness = 1.0f;
[FieldLoader.Ignore]
public readonly IReadOnlyDictionary<ushort, TerrainTemplateInfo> Templates;
@@ -215,8 +214,7 @@ namespace OpenRA
public byte GetTerrainIndex(string type)
{
byte index;
if (terrainIndexByType.TryGetValue(type, out index))
if (terrainIndexByType.TryGetValue(type, out var index))
return index;
throw new InvalidDataException("Tileset '{0}' lacks terrain type '{1}'".F(Id, type));
@@ -224,8 +222,7 @@ namespace OpenRA
public byte GetTerrainIndex(TerrainTile r)
{
TerrainTemplateInfo tpl;
if (!Templates.TryGetValue(r.Type, out tpl))
if (!Templates.TryGetValue(r.Type, out var tpl))
return defaultWalkableTerrainIndex;
if (tpl.Contains(r.Index))
@@ -240,8 +237,7 @@ namespace OpenRA
public TerrainTileInfo GetTileInfo(TerrainTile r)
{
TerrainTemplateInfo tpl;
if (!Templates.TryGetValue(r.Type, out tpl))
if (!Templates.TryGetValue(r.Type, out var tpl))
return null;
return tpl.Contains(r.Index) ? tpl[r.Index] : null;

View File

@@ -339,8 +339,7 @@ namespace OpenRA
{
if (n.Key == "Inherits" || n.Key.StartsWith("Inherits@", StringComparison.Ordinal))
{
MiniYaml parent;
if (!tree.TryGetValue(n.Value.Value, out parent))
if (!tree.TryGetValue(n.Value.Value, out var parent))
throw new YamlException(
"{0}: Parent type `{1}` not found".F(n.Location, n.Value.Value));
@@ -428,9 +427,8 @@ namespace OpenRA
foreach (var key in allKeys)
{
MiniYamlNode existingNode, overrideNode;
existingDict.TryGetValue(key, out existingNode);
overrideDict.TryGetValue(key, out overrideNode);
existingDict.TryGetValue(key, out var existingNode);
overrideDict.TryGetValue(key, out var overrideNode);
var loc = overrideNode == null ? default(MiniYamlNode.SourceLocation) : overrideNode.Location;
var comment = (overrideNode ?? existingNode).Comment;

View File

@@ -177,8 +177,7 @@ namespace OpenRA
public Map PrepareMap(string uid)
{
if (LoadScreen != null)
LoadScreen.Display();
LoadScreen?.Display();
if (MapCache[uid].Status != MapStatus.Available)
throw new InvalidDataException("Invalid map uid: {0}".F(uid));
@@ -202,12 +201,12 @@ namespace OpenRA
public void Dispose()
{
if (LoadScreen != null)
LoadScreen.Dispose();
LoadScreen?.Dispose();
MapCache.Dispose();
if (ObjectCreator != null)
ObjectCreator.Dispose();
ObjectCreator?.Dispose();
Manifest.Dispose();
}
}

View File

@@ -174,23 +174,21 @@ namespace OpenRA.Network
foreach (var p in packets)
{
packetFn(p.FromClient, p.Data);
if (Recorder != null)
Recorder.Receive(p.FromClient, p.Data);
Recorder?.Receive(p.FromClient, p.Data);
}
}
public void StartRecording(Func<string> chooseFilename)
{
// If we have a previous recording then save/dispose it and start a new one.
if (Recorder != null)
Recorder.Dispose();
Recorder?.Dispose();
Recorder = new ReplayRecorder(chooseFilename);
}
protected virtual void Dispose(bool disposing)
{
if (disposing && Recorder != null)
Recorder.Dispose();
if (disposing)
Recorder?.Dispose();
}
public void Dispose()
@@ -372,8 +370,7 @@ namespace OpenRA.Network
// Closing the stream will cause any reads on the receiving thread to throw.
// This will mark the connection as no longer connected and the thread will terminate cleanly.
if (tcp != null)
tcp.Close();
tcp?.Close();
base.Dispose(disposing);
}

View File

@@ -167,28 +167,22 @@ namespace OpenRA.Network
// Games advertised using the old API calculated the play time locally
if (State == 2 && PlayTime < 0)
{
DateTime startTime;
if (DateTime.TryParse(Started, out startTime))
if (DateTime.TryParse(Started, out var startTime))
PlayTime = (int)(DateTime.UtcNow - startTime).TotalSeconds;
}
ExternalMod external;
var externalKey = ExternalMod.MakeKey(Mod, Version);
if (Game.ExternalMods.TryGetValue(externalKey, out external) && external.Version == Version)
if (Game.ExternalMods.TryGetValue(externalKey, out var external) && external.Version == Version)
IsCompatible = true;
// Games advertised using the old API used local mod metadata
if (string.IsNullOrEmpty(ModTitle))
{
Manifest mod;
if (external != null && external.Version == Version)
{
// Use external mod registration to populate the section header
ModTitle = external.Title;
}
else if (Game.Mods.TryGetValue(Mod, out mod))
else if (Game.Mods.TryGetValue(Mod, out var mod))
{
// Use internal mod data to populate the section header, but
// on-connect switching must use the external mod plumbing.

View File

@@ -107,8 +107,7 @@ namespace OpenRA
{
case TargetType.Actor:
{
Actor targetActor;
if (world != null && TryGetActorFromUInt(world, r.ReadUInt32(), out targetActor))
if (world != null && TryGetActorFromUInt(world, r.ReadUInt32(), out var targetActor))
target = Target.FromActor(targetActor);
break;
}
@@ -118,8 +117,7 @@ namespace OpenRA
var playerActorID = r.ReadUInt32();
var frozenActorID = r.ReadUInt32();
Actor playerActor;
if (world == null || !TryGetActorFromUInt(world, playerActorID, out playerActor))
if (world == null || !TryGetActorFromUInt(world, playerActorID, out var playerActor))
break;
if (playerActor.Owner.FrozenActorLayer == null)

View File

@@ -115,7 +115,7 @@ namespace OpenRA.Network
Connection.SendImmediate(localImmediateOrders.Select(o => o.Serialize()));
localImmediateOrders.Clear();
var immediatePackets = new List<Pair<int, byte[]>>();
var immediatePackets = new List<(int ClientId, byte[] Packet)>();
Connection.Receive(
(clientId, packet) =>
@@ -126,16 +126,16 @@ namespace OpenRA.Network
else if (packet.Length >= 5 && packet[4] == (byte)OrderType.SyncHash)
CheckSync(packet);
else if (frame == 0)
immediatePackets.Add(Pair.New(clientId, packet));
immediatePackets.Add((clientId, packet));
else
frameData.AddFrameOrders(clientId, frame, packet);
});
foreach (var p in immediatePackets)
{
foreach (var o in p.Second.ToOrderList(World))
foreach (var o in p.Packet.ToOrderList(World))
{
UnitOrders.ProcessOrder(this, World, p.First, o);
UnitOrders.ProcessOrder(this, World, p.ClientId, o);
// A mod switch or other event has pulled the ground from beneath us
if (disposed)
@@ -149,8 +149,7 @@ namespace OpenRA.Network
void CheckSync(byte[] packet)
{
var frame = BitConverter.ToInt32(packet, 0);
byte[] existingSync;
if (syncForFrame.TryGetValue(frame, out existingSync))
if (syncForFrame.TryGetValue(frame, out var existingSync))
{
if (packet.Length != existingSync.Length)
OutOfSync(frame);
@@ -165,14 +164,14 @@ namespace OpenRA.Network
public bool IsReadyForNextFrame
{
get { return NetFrameNumber >= 1 && frameData.IsReadyForFrame(NetFrameNumber); }
get { return GameStarted && frameData.IsReadyForFrame(NetFrameNumber); }
}
public IEnumerable<Session.Client> GetClientsNotReadyForNextFrame
{
get
{
return NetFrameNumber >= 1
return GameStarted
? frameData.ClientsNotReadyForFrame(NetFrameNumber)
.Select(a => LobbyInfo.ClientWithIndex(a))
: NoClients;
@@ -207,8 +206,7 @@ namespace OpenRA.Network
public void Dispose()
{
disposed = true;
if (Connection != null)
Connection.Dispose();
Connection?.Dispose();
}
}

View File

@@ -14,7 +14,6 @@ using System.Collections.Generic;
using System.IO;
using System.Net;
using OpenRA.FileFormats;
using OpenRA.Primitives;
namespace OpenRA.Network
{
@@ -23,7 +22,7 @@ namespace OpenRA.Network
class Chunk
{
public int Frame;
public Pair<int, byte[]>[] Packets;
public (int ClientId, byte[] Packet)[] Packets;
}
Queue<Chunk> chunks = new Queue<Chunk>();
@@ -55,7 +54,7 @@ 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 packets = new List<(int ClientId, byte[] Packet)>();
var chunk = new Chunk();
@@ -67,7 +66,7 @@ namespace OpenRA.Network
var packetLen = rs.ReadInt32();
var packet = rs.ReadBytes(packetLen);
var frame = BitConverter.ToInt32(packet, 0);
packets.Add(Pair.New(client, packet));
packets.Add((client, packet));
if (frame != int.MaxValue &&
(!lastClientsFrame.ContainsKey(client) || frame > lastClientsFrame[client]))
@@ -111,13 +110,13 @@ namespace OpenRA.Network
{
foreach (var tmpPacketPair in tmpChunk.Packets)
{
var client = tmpPacketPair.First;
var client = tmpPacketPair.ClientId;
// Don't replace the final disconnection packet - we still want this to end the replay.
if (client == lastClientToDisconnect)
continue;
var packet = tmpPacketPair.Second;
var packet = tmpPacketPair.Packet;
if (packet.Length == 5 && packet[4] == (byte)OrderType.Disconnect)
{
var lastClientFrame = lastClientsFrame[client];
@@ -156,7 +155,7 @@ namespace OpenRA.Network
while (chunks.Count != 0 && chunks.Peek().Frame <= ordersFrame)
foreach (var o in chunks.Dequeue().Packets)
packetFn(o.First, o.Second);
packetFn(o.ClientId, o.Packet);
}
public void Dispose() { }

View File

@@ -100,8 +100,7 @@ namespace OpenRA.Network
Metadata.Write(writer);
}
if (preStartBuffer != null)
preStartBuffer.Dispose();
preStartBuffer?.Dispose();
writer.Close();
}
}

View File

@@ -250,8 +250,7 @@ namespace OpenRA.Network
public bool OptionOrDefault(string id, bool def)
{
LobbyOptionState option;
if (LobbyOptions.TryGetValue(id, out option))
if (LobbyOptions.TryGetValue(id, out var option))
return option.IsEnabled;
return def;
@@ -259,8 +258,7 @@ namespace OpenRA.Network
public string OptionOrDefault(string id, string def)
{
LobbyOptionState option;
if (LobbyOptions.TryGetValue(id, out option))
if (LobbyOptions.TryGetValue(id, out var option))
return option.Value;
return def;

View File

@@ -29,7 +29,7 @@ namespace OpenRA.Network
readonly Report[] syncReports = new Report[NumSyncReports];
int curIndex = 0;
static Pair<string[], Values> DumpSyncTrait(ISync sync)
static (string[] Names, Values Values) DumpSyncTrait(ISync sync)
{
var type = sync.GetType();
TypeInfo typeInfo;
@@ -41,7 +41,7 @@ namespace OpenRA.Network
foreach (var func in typeInfo.SerializableCopyOfMemberFunctions)
values[index++] = func(sync);
return Pair.New(typeInfo.Names, values);
return (typeInfo.Names, values);
}
public SyncReport(OrderManager orderManager)
@@ -120,9 +120,9 @@ namespace OpenRA.Network
Log.Write("sync", "\t {0} {1} {2} {3} ({4})".F(a.ActorID, a.Type, a.Owner, a.Trait, a.Hash));
var nvp = a.NamesValues;
for (int i = 0; i < nvp.First.Length; i++)
if (nvp.Second[i] != null)
Log.Write("sync", "\t\t {0}: {1}".F(nvp.First[i], nvp.Second[i]));
for (int i = 0; i < nvp.Names.Length; i++)
if (nvp.Values[i] != null)
Log.Write("sync", "\t\t {0}: {1}".F(nvp.Names[i], nvp.Values[i]));
}
Log.Write("sync", "Synced Effects:");
@@ -131,9 +131,9 @@ namespace OpenRA.Network
Log.Write("sync", "\t {0} ({1})", e.Name, e.Hash);
var nvp = e.NamesValues;
for (int i = 0; i < nvp.First.Length; i++)
if (nvp.Second[i] != null)
Log.Write("sync", "\t\t {0}: {1}".F(nvp.First[i], nvp.Second[i]));
for (int i = 0; i < nvp.Names.Length; i++)
if (nvp.Values[i] != null)
Log.Write("sync", "\t\t {0}: {1}".F(nvp.Names[i], nvp.Values[i]));
}
Log.Write("sync", "Orders Issued:");
@@ -163,14 +163,14 @@ namespace OpenRA.Network
public string Owner;
public string Trait;
public int Hash;
public Pair<string[], Values> NamesValues;
public (string[] Names, Values Values) NamesValues;
}
struct EffectReport
{
public string Name;
public int Hash;
public Pair<string[], Values> NamesValues;
public (string[] Names, Values Values) NamesValues;
}
struct TypeInfo

View File

@@ -9,10 +9,8 @@
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Primitives;
using OpenRA.Server;
using OpenRA.Traits;
@@ -147,8 +145,7 @@ namespace OpenRA.Network
var data = MiniYaml.FromString(order.TargetString)[0];
var traitIndex = int.Parse(data.Key);
if (world != null)
world.AddGameSaveTraitData(traitIndex, data.Value);
world?.AddGameSaveTraitData(traitIndex, data.Value);
break;
}
@@ -192,9 +189,8 @@ namespace OpenRA.Network
var request = HandshakeRequest.Deserialize(order.TargetString);
var externalKey = ExternalMod.MakeKey(request.Mod, request.Version);
ExternalMod external;
if ((request.Mod != mod.Id || request.Version != mod.Metadata.Version)
&& Game.ExternalMods.TryGetValue(externalKey, out external))
if ((request.Mod != mod.Id || request.Version != mod.Metadata.Version) &&
Game.ExternalMods.TryGetValue(externalKey, out var external))
{
// The ConnectionFailedLogic will prompt the user to switch mods
orderManager.ServerExternalMod = external;
@@ -306,7 +302,10 @@ namespace OpenRA.Network
{
var strings = node.Key.Split('@');
if (strings[0] == "GlobalSettings")
{
orderManager.LobbyInfo.GlobalSettings = Session.Global.Deserialize(node.Value);
orderManager.IssueOrder(Order.Command("state {0}".F(Session.ClientState.NotReady)));
}
}
SetOrderLag(orderManager);

View File

@@ -26,7 +26,7 @@ namespace OpenRA
readonly Cache<string, Type> typeCache;
readonly Cache<Type, ConstructorInfo> ctorCache;
readonly Pair<Assembly, string>[] assemblies;
readonly (Assembly Assembly, string Namespace)[] assemblies;
public ObjectCreator(Manifest manifest, InstalledMods mods)
{
@@ -48,8 +48,7 @@ namespace OpenRA
// We can't check the internal name of the assembly, so we'll work off the data instead
var hash = CryptoUtil.SHA1Hash(File.ReadAllBytes(resolvedPath));
Assembly assembly;
if (!ResolvedAssemblies.TryGetValue(hash, out assembly))
if (!ResolvedAssemblies.TryGetValue(hash, out var assembly))
{
assembly = Assembly.LoadFile(resolvedPath);
ResolvedAssemblies.Add(hash, assembly);
@@ -59,7 +58,7 @@ namespace OpenRA
}
AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
assemblies = assemblyList.SelectMany(asm => asm.GetNamespaces().Select(ns => Pair.New(asm, ns))).ToArray();
assemblies = assemblyList.SelectMany(asm => asm.GetNamespaces().Select(ns => (asm, ns))).ToArray();
}
Assembly ResolveAssembly(object sender, ResolveEventArgs e)
@@ -68,10 +67,7 @@ namespace OpenRA
if (a.FullName == e.Name)
return a;
if (assemblies == null)
return null;
return assemblies.Select(a => a.First).FirstOrDefault(a => a.FullName == e.Name);
return assemblies?.Select(a => a.Assembly).FirstOrDefault(a => a.FullName == e.Name);
}
// Only used by the linter to prevent exceptions from being thrown during a lint run
@@ -106,7 +102,7 @@ namespace OpenRA
public Type FindType(string className)
{
return assemblies
.Select(pair => pair.First.GetType(pair.Second + "." + className, false))
.Select(pair => pair.Assembly.GetType(pair.Namespace + "." + className, false))
.FirstOrDefault(t => t != null);
}
@@ -146,7 +142,7 @@ namespace OpenRA
public IEnumerable<Type> GetTypes()
{
return assemblies.Select(ma => ma.First).Distinct()
return assemblies.Select(ma => ma.Assembly).Distinct()
.SelectMany(ma => ma.GetTypes());
}

View File

@@ -5,7 +5,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<UseVSHostingProcess>false</UseVSHostingProcess>
<LangVersion>5</LangVersion>
<LangVersion>7.3</LangVersion>
<DebugSymbols>true</DebugSymbols>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
@@ -55,7 +55,7 @@
<PackageReference Include="OpenRA-Eluant" Version="1.0.17" />
<PackageReference Include="OpenRA-Open.NAT" Version="1.0.0" />
<PackageReference Include="SharpZipLib" Version="1.2.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
<AdditionalFiles Include="../stylecop.json" />
<AdditionalFiles Include="Properties/launchSettings.json" />

View File

@@ -18,13 +18,14 @@ namespace OpenRA.Orders
public class GenericSelectTarget : UnitOrderGenerator
{
public readonly string OrderName;
protected readonly IEnumerable<Actor> Subjects;
protected readonly string Cursor;
protected readonly MouseButton ExpectedButton;
protected IEnumerable<Actor> subjects;
public GenericSelectTarget(IEnumerable<Actor> subjects, string order, string cursor, MouseButton button)
{
Subjects = subjects;
this.subjects = subjects;
OrderName = order;
Cursor = cursor;
ExpectedButton = button;
@@ -53,7 +54,7 @@ namespace OpenRA.Orders
world.CancelInputMode();
var queued = mi.Modifiers.HasModifier(Modifiers.Shift);
yield return new Order(OrderName, null, Target.FromCell(world, cell), queued, null, Subjects.ToArray());
yield return new Order(OrderName, null, Target.FromCell(world, cell), queued, null, subjects.ToArray());
}
}
@@ -68,6 +69,11 @@ namespace OpenRA.Orders
return true;
}
public override void SelectionChanged(World world, IEnumerable<Actor> selected)
{
subjects = selected;
}
public override bool ClearSelectionOnLeftClick { get { return false; } }
}
}

View File

@@ -24,5 +24,6 @@ namespace OpenRA
string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi);
void Deactivate();
bool HandleKeyPress(KeyInput e);
void SelectionChanged(World world, IEnumerable<Actor> selected);
}
}

View File

@@ -124,6 +124,8 @@ namespace OpenRA.Orders
return false;
}
public virtual void SelectionChanged(World world, IEnumerable<Actor> selected) { }
/// <summary>
/// Returns the most appropriate order for a given actor and target.
/// First priority is given to orders that interact with the given actors.

View File

@@ -14,7 +14,6 @@ using System.Collections.Generic;
using System.Linq;
using Eluant;
using Eluant.ObjectBinding;
using OpenRA.Graphics;
using OpenRA.Network;
using OpenRA.Primitives;
using OpenRA.Scripting;
@@ -37,6 +36,9 @@ namespace OpenRA
public class Player : IScriptBindable, IScriptNotifyBind, ILuaTableBinding, ILuaEqualityBinding, ILuaToStringBinding
{
public const string PlayerActorType = "Player";
public const string EditorPlayerActorType = "EditorPlayer";
struct StanceColors
{
public Color Self;
@@ -164,13 +166,22 @@ namespace OpenRA
if (!Spectating)
PlayerMask = new LongBitSet<PlayerBitMask>(InternalName);
var playerActorType = world.Type == WorldType.Editor ? "EditorPlayer" : "Player";
PlayerActor = world.CreateActor(playerActorType, new TypeDictionary { new OwnerInit(this) });
// Set this property before running any Created callbacks on the player actor
IsBot = BotType != null;
// Special case handling is required for the Player actor:
// Since Actor.Created would be called before PlayerActor is assigned here
// querying player traits in INotifyCreated.Created would crash.
// Therefore assign the uninitialized actor and run the Created callbacks
// by calling Initialize ourselves.
var playerActorType = world.Type == WorldType.Editor ? EditorPlayerActorType : PlayerActorType;
PlayerActor = new Actor(world, playerActorType, new TypeDictionary { new OwnerInit(this) });
PlayerActor.Initialize(true);
Shroud = PlayerActor.Trait<Shroud>();
FrozenActorLayer = PlayerActor.TraitOrDefault<FrozenActorLayer>();
// Enable the bot logic on the host
IsBot = BotType != null;
if (IsBot && Game.IsHost)
{
var logic = PlayerActor.TraitsImplementing<IBot>().FirstOrDefault(b => b.Info.Type == BotType);
@@ -242,8 +253,7 @@ namespace OpenRA
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
{
Player a, b;
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
if (!left.TryGetClrValue(out Player a) || !right.TryGetClrValue(out Player b))
return false;
return a == b;

View File

@@ -10,7 +10,6 @@
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
@@ -33,7 +32,7 @@ namespace OpenRA
SheetBuilder sheetBuilder;
[FieldLoader.Ignore]
Cache<Pair<PlayerBadge, int>, Sprite> iconCache;
Cache<(PlayerBadge, int), Sprite> iconCache;
Sprite LoadSprite(string url, int density)
{
@@ -84,15 +83,15 @@ namespace OpenRA
{
sheetBuilder = new SheetBuilder(SheetType.BGRA, CreateSheet);
iconCache = new Cache<Pair<PlayerBadge, int>, Sprite>(p =>
iconCache = new Cache<(PlayerBadge Badge, int Density), Sprite>(p =>
{
if (p.Second > 2 && !string.IsNullOrEmpty(p.First.Icon3x))
return LoadSprite(p.First.Icon3x, 3);
if (p.Density > 2 && !string.IsNullOrEmpty(p.Badge.Icon3x))
return LoadSprite(p.Badge.Icon3x, 3);
if (p.Second > 1 && !string.IsNullOrEmpty(p.First.Icon2x))
return LoadSprite(p.First.Icon2x, 2);
if (p.Density > 1 && !string.IsNullOrEmpty(p.Badge.Icon2x))
return LoadSprite(p.Badge.Icon2x, 2);
return LoadSprite(p.First.Icon, 1);
return LoadSprite(p.Badge.Icon, 1);
});
}
@@ -114,7 +113,7 @@ namespace OpenRA
{
var ws = Game.Renderer.WindowScale;
var density = ws > 2 ? 3 : ws > 1 ? 2 : 1;
return iconCache[Pair.New(badge, density)];
return iconCache[(badge, density)];
}
}
}

View File

@@ -47,14 +47,9 @@ namespace OpenRA.Primitives
BitSetIndex bits = 0;
lock (Bits)
{
foreach (var value in values)
{
BitSetIndex valueBit;
if (Bits.TryGetValue(value, out valueBit))
if (Bits.TryGetValue(value, out var valueBit))
bits |= valueBit;
}
}
return bits;
}

View File

@@ -124,10 +124,10 @@ namespace OpenRA.Primitives
if (value.Length != 6 && value.Length != 8)
return false;
byte red, green, blue, alpha = 255;
if (!byte.TryParse(value.Substring(0, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out red)
|| !byte.TryParse(value.Substring(2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out green)
|| !byte.TryParse(value.Substring(4, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out blue))
byte alpha = 255;
if (!byte.TryParse(value.Substring(0, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var red)
|| !byte.TryParse(value.Substring(2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var green)
|| !byte.TryParse(value.Substring(4, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var blue))
return false;
if (value.Length == 8

View File

@@ -50,14 +50,9 @@ namespace OpenRA.Primitives
long bits = 0;
lock (Bits)
{
foreach (var value in values)
{
long valueBit;
if (Bits.TryGetValue(value, out valueBit))
if (Bits.TryGetValue(value, out var valueBit))
bits |= valueBit;
}
}
return bits;
}

View File

@@ -1,70 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 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;
namespace OpenRA.Primitives
{
public struct Pair<T, U> : IEquatable<Pair<T, U>>
{
public T First;
public U Second;
public Pair(T first, U second)
{
First = first;
Second = second;
}
internal static IEqualityComparer<T> Tcomparer = EqualityComparer<T>.Default;
internal static IEqualityComparer<U> Ucomparer = EqualityComparer<U>.Default;
public static bool operator ==(Pair<T, U> a, Pair<T, U> b)
{
return Tcomparer.Equals(a.First, b.First) && Ucomparer.Equals(a.Second, b.Second);
}
public static bool operator !=(Pair<T, U> a, Pair<T, U> b)
{
return !(a == b);
}
public override int GetHashCode() { return First.GetHashCode() ^ Second.GetHashCode(); }
public bool Equals(Pair<T, U> other) { return this == other; }
public override bool Equals(object obj) { return obj is Pair<T, U> && Equals((Pair<T, U>)obj); }
public Pair<T, U> WithFirst(T t) { return new Pair<T, U>(t, Second); }
public Pair<T, U> WithSecond(U u) { return new Pair<T, U>(First, u); }
public static T AsFirst(Pair<T, U> p) { return p.First; }
public static U AsSecond(Pair<T, U> p) { return p.Second; }
public override string ToString()
{
return "({0},{1})".F(First, Second);
}
class PairEqualityComparer : IEqualityComparer<Pair<T, U>>
{
public bool Equals(Pair<T, U> x, Pair<T, U> y) { return x == y; }
public int GetHashCode(Pair<T, U> obj) { return obj.GetHashCode(); }
}
public static IEqualityComparer<Pair<T, U>> EqualityComparer { get { return new PairEqualityComparer(); } }
}
public static class Pair
{
public static Pair<T, U> New<T, U>(T t, U u) { return new Pair<T, U>(t, u); }
}
}

View File

@@ -120,8 +120,7 @@ namespace OpenRA.Primitives
/// <param name="count">The length of the segment.</param>
public static Stream CreateWithoutOwningStream(Stream stream, long offset, int count)
{
Stream parentStream;
var nestedOffset = offset + GetOverallNestedOffset(stream, out parentStream);
var nestedOffset = offset + GetOverallNestedOffset(stream, out var parentStream);
// Special case FileStream - instead of creating an in-memory copy,
// just reference the portion of the on-disk file that we need to save memory.

View File

@@ -52,8 +52,7 @@ namespace OpenRA.Primitives
public bool Remove(T item)
{
Rectangle bounds;
if (!itemBounds.TryGetValue(item, out bounds))
if (!itemBounds.TryGetValue(item, out var bounds))
return false;
MutateBins(item, bounds, removeItem);
@@ -91,8 +90,7 @@ namespace OpenRA.Primitives
void MutateBins(T actor, Rectangle bounds, Action<Dictionary<T, Rectangle>, T, Rectangle> action)
{
int minRow, maxRow, minCol, maxCol;
BoundsToBinRowsAndCols(bounds, out minRow, out maxRow, out minCol, out maxCol);
BoundsToBinRowsAndCols(bounds, out var minRow, out var maxRow, out var minCol, out var maxCol);
for (var row = minRow; row < maxRow; row++)
for (var col = minCol; col < maxCol; col++)
@@ -110,8 +108,7 @@ namespace OpenRA.Primitives
public IEnumerable<T> InBox(Rectangle box)
{
int minRow, maxRow, minCol, maxCol;
BoundsToBinRowsAndCols(box, out minRow, out maxRow, out minCol, out maxCol);
BoundsToBinRowsAndCols(box, out var minRow, out var maxRow, out var minCol, out var maxCol);
// We want to return any items intersecting the box.
// If the box covers multiple bins, we must handle items that are contained in multiple bins and avoid

View File

@@ -41,6 +41,11 @@ namespace OpenRA.Primitives
return data.ContainsKey(typeof(T));
}
public bool Contains(Type t)
{
return data.ContainsKey(t);
}
public T Get<T>()
{
return (T)Get(typeof(T), true);
@@ -56,8 +61,7 @@ namespace OpenRA.Primitives
object Get(Type t, bool throwsIfMissing)
{
List<object> ret;
if (!data.TryGetValue(t, out ret))
if (!data.TryGetValue(t, out var ret))
{
if (throwsIfMissing)
throw new InvalidOperationException("TypeDictionary does not contain instance of type `{0}`".F(t));
@@ -71,8 +75,7 @@ namespace OpenRA.Primitives
public IEnumerable<T> WithInterface<T>()
{
List<object> objs;
if (data.TryGetValue(typeof(T), out objs))
if (data.TryGetValue(typeof(T), out var objs))
return objs.Cast<T>();
return new T[0];
}
@@ -89,8 +92,7 @@ namespace OpenRA.Primitives
void InnerRemove(Type t, object val)
{
List<object> objs;
if (!data.TryGetValue(t, out objs))
if (!data.TryGetValue(t, out var objs))
return;
objs.Remove(val);
if (objs.Count == 0)

View File

@@ -62,5 +62,6 @@ namespace OpenRA
public override string ToString() { return "{0},{1},{2}".F(X, Y, Z); }
public static readonly float3 Zero = new float3(0, 0, 0);
public static readonly float3 Ones = new float3(1, 1, 1);
}
}

View File

@@ -115,8 +115,7 @@ namespace OpenRA
font.Dispose();
using (new PerfTimer("SpriteFonts"))
{
if (fontSheetBuilder != null)
fontSheetBuilder.Dispose();
fontSheetBuilder?.Dispose();
fontSheetBuilder = new SheetBuilder(SheetType.BGRA, 512);
Fonts = modData.Manifest.Get<Fonts>().FontList.ToDictionary(x => x.Key,
x => new SpriteFont(x.Value.Font, modData.DefaultFileSystem.Open(x.Value.Font).ReadAllBytes(),
@@ -156,8 +155,7 @@ namespace OpenRA
if (screenSprite == null || screenSprite.Sheet.Size != surfaceBufferSize)
{
if (screenBuffer != null)
screenBuffer.Dispose();
screenBuffer?.Dispose();
// Render the screen into a frame buffer to simplify reading back screenshots
screenBuffer = Context.CreateFrameBuffer(surfaceBufferSize, Color.FromArgb(0xFF, 0, 0, 0));
@@ -195,8 +193,7 @@ namespace OpenRA
var worldBufferSize = worldViewport.Size.NextPowerOf2();
if (worldSprite == null || worldSprite.Sheet.Size != worldBufferSize)
{
if (worldBuffer != null)
worldBuffer.Dispose();
worldBuffer?.Dispose();
// Render the world into a framebuffer at 1:1 scaling to allow the depth buffer to match the artwork at all zoom levels
worldBuffer = Context.CreateFrameBuffer(worldBufferSize);
@@ -329,8 +326,7 @@ namespace OpenRA
{
if (currentBatchRenderer == value)
return;
if (currentBatchRenderer != null)
currentBatchRenderer.Flush();
currentBatchRenderer?.Flush();
currentBatchRenderer = value;
}
}
@@ -460,8 +456,7 @@ namespace OpenRA
{
WorldModelRenderer.Dispose();
tempBuffer.Dispose();
if (fontSheetBuilder != null)
fontSheetBuilder.Dispose();
fontSheetBuilder?.Dispose();
if (Fonts != null)
foreach (var font in Fonts.Values)
font.Dispose();

View File

@@ -267,8 +267,7 @@ namespace OpenRA.Scripting
return;
disposed = true;
if (runtime != null)
runtime.Dispose();
runtime?.Dispose();
}
static IEnumerable<Type> ExtractRequiredTypes(Type t)

View File

@@ -28,8 +28,7 @@ namespace OpenRA.Scripting
public static string LuaDocString(this Type t)
{
string ret;
if (!LuaTypeNameReplacements.TryGetValue(t.Name, out ret))
if (!LuaTypeNameReplacements.TryGetValue(t.Name, out var ret))
ret = t.Name;
return ret;
}

View File

@@ -111,8 +111,7 @@ namespace OpenRA.Scripting
if (IsSetProperty)
{
var pi = (PropertyInfo)Member;
object clrValue;
if (!value.TryGetClrValue(pi.PropertyType, out clrValue))
if (!value.TryGetClrValue(pi.PropertyType, out var clrValue))
throw new LuaException("Unable to convert '{0}' to Clr type '{1}'".F(value.WrappedClrType().Name, pi.PropertyType));
pi.SetValue(Target, clrValue, null);

View File

@@ -51,8 +51,7 @@ namespace OpenRA.Scripting
get
{
var name = keyValue.ToString();
ScriptMemberWrapper wrapper;
if (!members.TryGetValue(name, out wrapper))
if (!members.TryGetValue(name, out var wrapper))
throw new LuaException(MemberNotFoundError(name));
return wrapper.Get(runtime);
@@ -61,8 +60,7 @@ namespace OpenRA.Scripting
set
{
var name = keyValue.ToString();
ScriptMemberWrapper wrapper;
if (!members.TryGetValue(name, out wrapper))
if (!members.TryGetValue(name, out var wrapper))
throw new LuaException(MemberNotFoundError(name));
wrapper.Set(runtime, value);

View File

@@ -18,8 +18,7 @@ namespace OpenRA.Scripting
{
public static Type WrappedClrType(this LuaValue value)
{
object inner;
if (value.TryGetClrObject(out inner))
if (value.TryGetClrObject(out var inner))
return inner.GetType();
return value.GetType();
@@ -27,16 +26,13 @@ namespace OpenRA.Scripting
public static bool TryGetClrValue<T>(this LuaValue value, out T clrObject)
{
object temp;
var ret = value.TryGetClrValue(typeof(T), out temp);
var ret = value.TryGetClrValue(typeof(T), out object temp);
clrObject = ret ? (T)temp : default(T);
return ret;
}
public static bool TryGetClrValue(this LuaValue value, Type t, out object clrObject)
{
object temp;
// Is t a nullable?
// If yes, get the underlying type
var nullable = Nullable.GetUnderlyingType(t);
@@ -44,7 +40,7 @@ namespace OpenRA.Scripting
t = nullable;
// Value wraps a CLR object
if (value.TryGetClrObject(out temp))
if (value.TryGetClrObject(out var temp))
{
if (temp.GetType() == t)
{
@@ -172,8 +168,7 @@ namespace OpenRA.Scripting
{
// Object needs additional notification / context
var notify = obj as IScriptNotifyBind;
if (notify != null)
notify.OnScriptBind(context);
notify?.OnScriptBind(context);
return new LuaCustomClrObject(obj);
}

View File

@@ -99,8 +99,7 @@ namespace OpenRA.Server
// Non-blocking sends are free to send only part of the data
while (start < length)
{
SocketError error;
var sent = s.Send(data, start, length - start, SocketFlags.None, out error);
var sent = s.Send(data, start, length - start, SocketFlags.None, out var error);
if (error == SocketError.WouldBlock)
{
Log.Write("server", "Non-blocking send of {0} bytes failed. Falling back to blocking send.", length - start);
@@ -245,8 +244,7 @@ namespace OpenRA.Server
}
var conn = Conns.SingleOrDefault(c => c.Socket == s);
if (conn != null)
conn.ReadData(this);
conn?.ReadData(this);
}
delayedActions.PerformActions(0);
@@ -716,8 +714,7 @@ namespace OpenRA.Server
break;
case "Pong":
{
long pingSent;
if (!OpenRA.Exts.TryParseInt64Invariant(o.TargetString, out pingSent))
if (!OpenRA.Exts.TryParseInt64Invariant(o.TargetString, out var pingSent))
{
Log.Write("server", "Invalid order pong payload: {0}", o.TargetString);
break;
@@ -1016,7 +1013,7 @@ namespace OpenRA.Server
foreach (var c in Conns)
foreach (var d in Conns)
DispatchOrdersToClient(c, d.PlayerIndex, 0x7FFFFFFF, new[] { (byte)OrderType.Disconnect });
DispatchOrdersToClient(c, d.PlayerIndex, int.MaxValue, new[] { (byte)OrderType.Disconnect });
if (GameSave == null && LobbyInfo.GlobalSettings.GameSavesEnabled)
GameSave = new GameSave();

View File

@@ -53,7 +53,7 @@ namespace OpenRA
public bool DiscoverNatDevices = false;
[Desc("Time in milliseconds to search for UPnP enabled NAT devices.")]
public int NatDiscoveryTimeout = 1000;
public int NatDiscoveryTimeout = 5000;
[Desc("Starts the game with a default map. Input as hash that can be obtained by the utility.")]
public string Map = null;
@@ -295,8 +295,7 @@ namespace OpenRA
yamlCache = MiniYaml.FromFile(settingsFile, false);
foreach (var yamlSection in yamlCache)
{
object settingsSection;
if (yamlSection.Key != null && Sections.TryGetValue(yamlSection.Key, out settingsSection))
if (yamlSection.Key != null && Sections.TryGetValue(yamlSection.Key, out var settingsSection))
LoadSectionYaml(yamlSection.Value, settingsSection);
}

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