Compare commits

...

1717 Commits

Author SHA1 Message Date
abcdefg30
82acdbc32a Fix RA assets installation from the Steam C&C:R version 2023-08-01 22:29:52 +03:00
Gustas
462a3ef3c0 Make yaml node resolving optional 2023-07-30 20:39:21 +02:00
Gustas
d4e6815f64 Fix AutoTarget ignoring frozen actor bot targeting hack
Mirrors check of the function Recalculate which is found it TargetExtensions class
2023-07-30 20:32:18 +02:00
Gustas
723ffdf33d Revert Hunt to move within 2 cells of the target
Otherwise infantry get stuck within weapon range but outside of vision range
2023-07-30 20:26:41 +02:00
Gustas
8376b09129 Normalise depth charges 2023-07-29 14:22:30 -05:00
Gustas
fa9ce3e2ac Normalise TD anti-air values 2023-07-29 14:22:30 -05:00
Gustas
605681b252 Normalise RA anti-air values 2023-07-29 14:22:30 -05:00
dnqbob
a3c5945f2a Set BackwardDuration to -1 means ignore the time and set MaxBackwardCells to -1 means ignore the distance. 2023-07-29 18:01:40 +03:00
dnqbob
d7ef22d64f Add MaxBackwardCells for moving backward control 2023-07-29 18:01:40 +03:00
michaeldgg2
d907192be0 Added GrantConditionOnMinelaying trait
Uses INotifyMineLaying callbacks
2023-07-28 12:55:06 +03:00
michaeldgg2
74f8db0578 LayMines: render minefield cells only if the planned minefield has more than 1 cell 2023-07-28 12:55:06 +03:00
Smittytron
9dca6ef1c3 Add Soviet13a 2023-07-27 22:09:30 +03:00
Gustas
c093e7c90b Fix hunt incorrectly pathing to uncrushable targets 2023-07-27 16:14:29 +03:00
michaeldgg2
66cf912da0 LayMines: fixed occasional incorrect mine position when using BeginMinefield order
When laying mine with PreLayDelay > 0, end activity's tick immediately. That means don't try to immediately move to next cell.

This change unifies the behavior with scenario when a mine is laid without any PreLayDelay.
2023-07-26 22:05:42 +03:00
Gustas
42baa0c42f Rename update rule folder to 20230225 2023-07-26 20:00:54 +02:00
Gustas
305ba1c567 Add missing rule to UpdatePath 2023-07-26 20:00:54 +02:00
Gustas
a84b7591f6 Fix invalid color adjuster not considering saturation and V as mutable 2023-07-26 20:00:54 +02:00
Gustas
9d8f0634b1 Revert color validator 2023-07-26 20:00:54 +02:00
Gustas
4cd4e1f8ea Move PlayerExperience from Infiltrates to InfiltrateFor 2023-07-25 21:15:14 +02:00
Gustas
3207d01cf2 Consider AutoTarget ScanRadius when attack moving 2023-07-25 19:48:57 +02:00
Gustas
9fc0f79703 Add a description for AttackBomber 2023-07-25 13:11:17 +02:00
michaeldgg2
1a2d43fc99 WorldRenderer: use string.IsNullOrEmpty for check in Palette method
Unified usage of WorldRenderer.Palette method when it comes to appending player name (in case of player palette)
2023-07-25 13:33:11 +03:00
EoralMilk
b944b21325 Aircraft won't take off on terrain height change when idle 2023-07-25 12:48:50 +03:00
Gustas
c7e0bc4c08 Add missing carryall checks 2023-07-25 10:02:46 +03:00
Gustas
a69417f0a6 Fix caryall not removing influence when cargo dies 2023-07-25 10:02:46 +03:00
Gustas
c36609cc9f Don't call DetachCarryable every tick 2023-07-25 10:02:46 +03:00
Gustas
1edf313090 Don't calculate range when it is unused 2023-07-25 10:02:46 +03:00
RoosterDragon
813d48dd70 Ensure save file is closed after saving.
If you attempt to load after saving, this prevents an exception from the file being "in use" due to the unclosed file handle.
2023-07-18 23:44:40 +02:00
Gustas
9caf12d133 Add an option to order maps by size 2023-07-17 23:34:38 +02:00
Gustas
de5bcbbca5 Add an option to order maps by title 2023-07-17 23:34:38 +02:00
michaeldgg2
8aa548f70c Minelayer: extract creating BeginMinefield order so it can be triggered from outside 2023-07-17 20:18:52 +02:00
michaeldgg2
ce6e73dc92 Minelayer: supports specifying both mine laying and post laying delays 2023-07-17 20:18:52 +02:00
RoosterDragon
4a02e6c6cc Improve Exts.GetOrAdd method to avoid multiple lookups.
Use CollectionsMarshal to hold a ref to the dictionary entry. When the value needs to be added this allows us to set the value directly into it without having to locate the entry a second time.
2023-07-17 20:12:48 +02:00
RoosterDragon
d6a31bb0cc MiniYaml performance tweaks.
- Seal the classes, and make SourceLocation a readonly struct.
- In ToDictionary, use TryAdd to avoid a try-catch.
- In Merge, use ToList to ensure sources is only enumerated once.
2023-07-17 20:12:48 +02:00
RoosterDragon
f5daa19a1c Improve MiniYaml MergePartial performance.
- Track plain keys in a set, to avoid quadratic searches for plain node keys.
- Avoid the Concat iterator by looping twice instead.
2023-07-17 20:12:48 +02:00
RoosterDragon
a96e445e4d Handle duplicate nodes key checks in MiniYaml in a better place.
Moving the key duplication check allows a redundant check on top-level nodes to be avoided. Add tests to ensure key checks are functioning as expected.
2023-07-17 20:12:48 +02:00
RoosterDragon
30b1f926f2 Improve performance of MiniYaml inheritance tree tracking.
Use an ImmutableDictionary to avoid having to clone the inheritance tree in ResolveInherits. This avoids a lot of dictionary clones.
2023-07-17 20:12:48 +02:00
RoosterDragon
58e8b123db Avoid some allocations during loading.
- In FieldLoader, cache boxed bools and some boxed ints.
- In FieldLoader, presize collections when parsing a List, HashSet or Dictionary.
- In FieldLoader, don't allocate a list of missing items until required.
- In FieldLoader, when a string value is passed, avoid wrapping this in a MiniYaml object by allowing both strings and yaml to be passed in the GetValue overload that does the real work.
- In Animation, avoid allocating no-op actions.
- In VxlReader, use EnsureCapcity to better size the Dictionary.
- In VxlReader change VxlElement to a struct.
- In Locomotor, presize TerrainSpeeds dictionary.
2023-07-16 23:21:20 +02:00
RoosterDragon
be04d232c0 Avoid some allocations on the large object heap during loading.
- In MixFile, the Distinct call doesn't presize the HashSet it uses internally. As we know we will enumerate all results, create the HashSet ourselves so that is it presized correctly.
- In ObjectCreator, stream the assembly when hashing rather than reading all bytes into memory.

These changes avoid some allocations on the large object heap, in turn this means the GC can avoid performing unnecessary Gen 2 collections just to clear down the LOH.
2023-07-16 23:21:20 +02:00
Gustas
659ec5e335 Make phase transport uncloak on loading cargo 2023-07-14 17:25:30 +02:00
Paul Chote
7f37454666 Include Linux DE in OS sysinfo string. 2023-07-13 14:34:38 +03:00
michaeldgg2
433d69af7a Make Voiced trait conditional 2023-07-10 18:05:56 +03:00
Matthias Mailänder
21c21e4963 Update OpenAL. 2023-07-07 23:47:32 +02:00
RoosterDragon
1c0885c636 Improve loading performance for loader/compression classes.
- In RLEZerosCompression use dedicated Array.Clear method instead of open-coded loop.
- In VoxelLoader.GenerateSlicePlanes.Get use TryGetValue to avoid repeated array and dictionary lookups.
- In TmpTSLoader.UnpackTileData use ReadBytes to populate array with less overhead compared to repeated one byte reads.
- Resolve TODO in VqaVideo.
2023-07-07 22:36:54 +03:00
michaeldgg2
dccb3ce9ce Added extensibility points to LayMines activity:
Provides two callbacks using INotifyMineLaying interface: MineLaying (just before laying a mine), MineLaid after mine actor has been created (in FrameEndTask)
2023-07-04 23:52:25 +02:00
michaeldgg2
22b39f35aa Mine + related classes are public 2023-07-04 23:52:25 +02:00
michaeldgg2
5ab3276a2d Moved Minelaying related traits and activity to Common 2023-07-04 23:52:25 +02:00
penev92
36420114e0 Fixed packaging for Windows missing assembly info
Apparently building/publishing/packaging for Windows on Linux using .NET 6 sets some of the assembly / Portable Executable information for the generated DLL, but nothing for the generated EXE (which isn't the case when building on Windows).
2023-07-04 20:53:49 +01:00
penev92
7cda031888 Added product version to assembly info 2023-07-04 20:53:49 +01:00
penev92
fc85a4864d Added project information in Directory.Build.props 2023-07-04 20:53:49 +01:00
Gustas
99226c3df5 Always have ActorReference string on optional arrays instantiated 2023-07-01 18:29:59 +02:00
Gustas
ed395c8ace Fix linter crashing on null actor array references 2023-07-01 18:29:59 +02:00
Vapre
edbded8f0a PerfTickLogger, reduce overhead of logging long ticks. 2023-07-01 18:07:27 +02:00
abcdefg30
c095690619 Fix combined sequences using frames being broken 2023-07-01 12:56:50 +02:00
dnqbob
628cc837ef Fix a crash when RallyPoint creates RallyPointIndicator 2023-07-01 12:51:54 +02:00
RoosterDragon
0c32fca6c0 Fix slow saving of map previews.
Avoid quadratic behaviour when searching through actors by creating a dictionary lookup outside the loop.
2023-07-01 12:48:32 +02:00
IceReaper
56b5ace109 WSA use a Framerate of 15, source: FFMPEG. 2023-06-26 19:49:51 +02:00
IceReaper
5572650da2 Video FrameRate is now taken into account when video has no audio. 2023-06-26 19:49:51 +02:00
IceReaper
41669d246f Videos no longer use more vram then required. 2023-06-26 19:49:51 +02:00
abcdefg30
49c837e7d0 Fix \r\n-style line endings not being properly handled for script errors 2023-06-26 19:36:47 +02:00
RoosterDragon
bc5e7d1497 Compress all pngs within oramap files.
Reduces size used for oramap files from 2,774,405 bytes to 2,614,332 bytes in total. The smaller files also improve loading times as the zlib decoder has less bytes to schlep through.

The PNG decoder shipped with OpenRA only supports a bit depth of 8 for images with a palette, so must must ensure that other depths (1,2 or 4) are not used.

zopfilpng is used for compression with the following command line:
'zopflipng.exe --keepcolortype -y -m image.png image.png'

The keepcolortype flag ensures the bit depth is not changed.

The oramap files were updated by:
- Saving as a folder, unpacking the data in loose files.
- Compressing the png files using the command above.
- Saving back as an oramap, except instead of regenerating the map preview on save we save the existing version on disk.
2023-06-26 19:28:06 +02:00
RoosterDragon
78bef8a98f Compress all pngs.
Reduces size used for png files from 19,034,446 bytes to 13,305,798 bytes in total. The smaller files also improve loading times as the zlib decoder has less bytes to schlep through.

The PNG decoder shipped with OpenRA only supports a bit depth of 8 for images with a palette, so must must ensure that other depths (1,2 or 4) are not used.

zopfilpng is used for compression with the following command line:
'zopflipng.exe --keepcolortype -y -m image.png image.png'

The keepcolortype flag ensures the bit depth is not changed.
2023-06-26 19:28:06 +02:00
RoosterDragon
4f3d8f4caa Fix Png parsing.
A regression from 06df75ffee causes this parsing to fail depending on how the image was compressed.
2023-06-26 19:28:06 +02:00
Paul Chote
0369f7516d Work around Gnome 44 titlebar bug. 2023-06-26 19:21:51 +02:00
Gustas
3b0415678c RA Naval balance 2023-06-26 19:13:41 +02:00
Gustas
69867b6c06 Fix EMP'ed units turning 2023-06-21 20:55:52 +02:00
RoosterDragon
231bf01f18 Fix CA1854 2023-06-20 17:57:40 +02:00
RoosterDragon
56fe08cb00 Disable CA1845 2023-06-20 17:57:40 +02:00
RoosterDragon
f752e04b03 Fix CA1816 2023-06-20 17:57:40 +02:00
RoosterDragon
a50e72f68d Fix CA1802 2023-06-20 17:57:40 +02:00
RoosterDragon
0958197df2 Fix CA1052 2023-06-20 17:57:40 +02:00
RoosterDragon
f336a956cf Fix CA1012 2023-06-20 17:57:40 +02:00
darkademic
19fa03435b Only consider non-paused armaments (if any exist) when determining min/max range. 2023-06-20 16:46:21 +02:00
RoosterDragon
fa65e7fd3f Bump Linguini.Bundle to 0.5.0.
This version contains performance improvements for the parser, improving the loading time of translations.
2023-06-15 17:48:37 +02:00
RoosterDragon
f794cf69f9 In TypeDictionary.TrimExcess, also TrimExcess on the internal data dictionary size.
As TypeDictionary instances tend to live a long time without edits after being initially populated, this will reduce their long term memory footprint.
2023-06-13 23:52:44 +02:00
RoosterDragon
366dc5383c In HierarchicalPathFinder.BuildGrid, presize and reuse accessible cell set.
Most cells are accessible, so presizing to the full size of the grid is sensible to avoid allocations to resize as it is filled up. The set can also be reused across all layers to avoid allocating it many times.
2023-06-13 23:52:44 +02:00
michaeldgg2
fd2b14f464 DrawLineToTarget: made palette for rendering sprites customizable (and thus optional too) 2023-06-12 21:10:52 +03:00
Matthias Mailänder
590976a8e7 Add support for Wayland. 2023-06-12 19:57:35 +02:00
Paul Chote
bdbb651b98 Remove unused sequences and an awkward Combine. 2023-06-11 22:04:43 +02:00
Paul Chote
703618be19 Remove obsolete x64 and x64process sysinfo columns. 2023-06-11 13:18:49 +02:00
Paul Chote
1f37728ecf Return proper sysinfo OS names for Linux/macOS. 2023-06-11 13:18:49 +02:00
penev92
d955efff14 Updated configure-system-libraries for new OpenAL 2023-06-11 10:41:02 +02:00
penev92
3fdee06dc7 Updated other referenced NuGet packages 2023-06-11 10:41:02 +02:00
penev92
c0cd7259b3 Updated native dependencies NuGet packages 2023-06-11 10:41:02 +02:00
Matthias Mailänder
c31f2abfc9 Add sanity checks to the Lua script trait. 2023-06-11 11:03:38 +03:00
RoosterDragon
06df75ffee Improve PNG parsing performance.
Switch on the filter once per row rather than once per byte. This allows each row to be processed with a much tighter loop.
2023-06-10 16:20:02 +02:00
Matthias Mailänder
855e839b77 Try to fix chocolatey. 2023-06-09 19:25:36 +02:00
Gustas
06437df9b0 Fix CA1852 2023-06-06 14:13:04 +03:00
RoosterDragon
f4af5c1764 Fix CA1852 2023-06-06 11:51:47 +03:00
RoosterDragon
277699cbd5 Fix CA1822 2023-06-06 11:51:47 +03:00
Gustas
e4cac1fffc Add more varied pre-selected colors to player colors palette 2023-06-05 13:16:26 +02:00
Matthias Mailänder
e164e48aae Fix invalid integer expression syntax. 2023-06-04 21:29:15 +03:00
Matthias Mailänder
5eec9d29cb Add a lint check for invalid integer expression syntax. 2023-06-04 21:29:15 +03:00
Matthias Mailänder
94abd8a928 Revert "Revert "Replace legacy Evaluator with IntegerExpressions.""
This reverts commit 4f16b0d464.
2023-06-04 21:29:15 +03:00
JovialFeline
b18c2fe855 Add Mousetrap (scu33ea). 2023-06-03 21:45:18 +02:00
Gustas
02a7ff87db Fix MoveAdjacent activities cancelling queued activities 2023-06-03 13:43:44 +02:00
Vapre
7c0f6ead3a ActorMap, avoid IPositionable trait lookup. 2023-06-03 13:41:14 +02:00
Vapre
d72b1ffd49 ActorMap, do not look up influence node up to three times in cell layer. 2023-06-03 13:06:38 +02:00
Gustas
c82be175e1 Add RemoveNegativeSequenceLength update rule 2023-06-02 11:59:22 +02:00
michaeldgg2
dac35a60ad WithDecoration: fixed crash when Palette is null 2023-06-02 11:49:59 +02:00
abcdefg30
ee02af3605 Don't play low power notifications in the beginning of twist-of-fate 2023-06-01 20:42:39 +03:00
abcdefg30
74ed202b29 Support enabling and disabling the low power notification via Lua 2023-06-01 20:42:39 +03:00
RoosterDragon
300281695a Deserialize mod rules only once when loading all maps.
This avoids loading, parsing and merging YAML rules for the mod during loading of each individual map. This saves significant time resolving custom rules on each map loaded.
2023-06-01 12:59:53 +02:00
Matthias Mailänder
de22556153 Move unit testing into the make script. 2023-06-01 12:43:10 +02:00
Matthias Mailänder
5bcb1a678c Fix fluent plural forms. 2023-06-01 12:43:10 +02:00
Matthias Mailänder
8a9426a0d4 Add a test case for Fluent plural forms. 2023-06-01 12:43:10 +02:00
Matthias Mailänder
dd9ab16401 Run unit tests on Linux. 2023-06-01 12:43:10 +02:00
abcdefg30
12e6932930 Fix dedicated servers crashing on startup due to missing translations 2023-06-01 10:04:06 +03:00
abcdefg30
95f18d4bc3 Fix the ExplicitSequenceFilenames updating sequences twice 2023-05-30 16:31:49 +03:00
abcdefg30
8c9cc93185 Fix the ExplicitSequenceFilenames rule breaking when updating single maps 2023-05-30 16:31:49 +03:00
Matthias Mailänder
b58a8aaa0f Fix benchmark CSV export on non-US systems. 2023-05-28 23:11:25 +03:00
Gustas
c5b7728ac9 RA Naval balance 2023-05-25 23:35:34 +02:00
Matthias Mailänder
c9dddc342c Extract editor brush texts. 2023-05-23 19:45:15 +02:00
Matthias Mailänder
8433bc0948 Throw early when Lua function parameters are null. 2023-05-23 17:30:03 +03:00
abcdefg30
52a916012f Remove references to the deleted Lua directory from Windows packaging 2023-05-21 18:23:04 +03:00
RoosterDragon
2fe7e1bff9 Fix HierarchicalPathFinder pathing from inaccessible source locations.
When a search is initiated from an inaccessible source location, a path is still allowed if there is an adjacent, accessible location the unit can move into. The local pathfinder and HierarchicalPathFinder already account for this logic, but HPF has some bugs.

Firstly, when the HierarchicalPathFinder heuristic is being evaluated, it assumes all cells being explored are accessible - this is important for performance as it avoids constantly rechecking the accessibility of cells. Although this fact holds true for cells explored by the path search, it does not hold true for cells being added as the initial starting points of the search.

Secondly, when checking for adjacent locations to an inaccessible source cell, we checked only if these were still on the map. This is insufficient - we need to know if movement between the source cell and the adjacent cell is possible.

The fixes resolve this by:
- Teaching the heuristic an extra parameter to know if the location is known to be accessible. This allow an accessibility check to be performed for starting locations which stops HPF mistakenly assuming the abstract node for that location is the one we need to consider, and to correctly check the adjacent locations and their abstract nodes instead. The parameter means will can still skip the accessibility check in the typical case where the path search is being expanded, preserving performance.
- When adjacent cells are considered we now check if movement to them is possible. This stops HPF from allowing jumps over height discontinuities (i.e. no magically jumping up or down cliffs) and thinking a path is possible when it is not.
2023-05-21 16:56:16 +02:00
abcdefg30
0788e5ff3e Adjust the ValidTargets of DuplicateUnitCrateAction in RA 2023-05-21 16:34:58 +02:00
Matthias Mailänder
0fb5853b7a Fallback to normal difficulty when none is set. 2023-05-21 15:46:20 +03:00
Matthias Mailänder
b30285e38d Officially deprecate this function. 2023-05-21 15:46:20 +03:00
Brenton Horne
02e4bfba95 Remove reference to deleted lua directory
Lua directory was deleted a few commits ago and this just updates packaging/functions.sh to reflect this change.
2023-05-21 13:13:13 +02:00
abcdefg30
b5f5d5f9d5 Fix ScriptContext crashing without a WorldLoaded function 2023-05-21 13:11:17 +02:00
abcdefg30
f2b3a9f837 Fix ScriptContext crashing without a Tick function 2023-05-21 13:11:17 +02:00
dnqbob
6af14c16c9 Fix a rare crash when actor in IBotRespondToAttack is dead 2023-05-21 10:40:21 +02:00
Gustas
211f7160dc Remove \r
we use \n everywhere else in the engine
2023-05-20 20:47:56 +03:00
Smittytron
8a1463a471 Add Allies10b 2023-05-20 20:36:54 +03:00
abcdefg30
b623214e04 Fix IdlingUnits definitions for multiple reinforcements in D2k missions 2023-05-20 18:56:58 +02:00
abcdefg30
6b536ca88a Add a Lua function to concat two tables 2023-05-20 18:56:58 +02:00
Matthias Mailänder
445b736885 Replace sandbox wrapper scripts. 2023-05-20 13:19:48 +02:00
dnqbob
e8dd85419f add Autocrusher for baby visc 2023-05-20 12:53:29 +02:00
yamismo
b1fd392486 Add GDI Covert Operations - Twist of Fate - scg41ea 2023-05-19 18:55:17 +02:00
Gustas
e487c3366d Allow EMP cannon to EMP itself 2023-05-19 17:29:17 +02:00
Gustas
ade27ad8b9 Fail CI on lint warnings 2023-05-19 17:25:03 +02:00
Gustas
34bcae9abb Translation keys should not be required 2023-05-19 17:25:03 +02:00
Gustas
8894fdeaf9 Add map translation parse error linting 2023-05-19 17:25:03 +02:00
Gustas
ff14b75e1b Fix duplicate translations 2023-05-19 17:25:03 +02:00
Gustas
b5ef9c29cf Improve spawnpoint lint 2023-05-19 17:25:03 +02:00
Gustas
f344ccb714 Improve visibility lint 2023-05-19 17:25:03 +02:00
Gustas
9b71317280 Improve lint error wording 2023-05-19 17:25:03 +02:00
Gustas
1ac6912c2a Fix lint error formatting 2023-05-19 17:25:03 +02:00
Gustas
3188532e59 Add punctuation to lint comments 2023-05-19 17:25:03 +02:00
abcdefg30
d9d8c23c63 Enable the restart button when we encounter a script error 2023-05-19 17:02:06 +02:00
abcdefg30
dfe0c15399 Remove wrong definitions from TD's notifications.yaml 2023-05-19 16:39:42 +02:00
IceReaper
0751b30d33 Fixed duplicate asset selection in asset browser. 2023-05-19 10:26:32 +03:00
Gustas
f4dc29f9db Add missing balance changes 2023-05-18 18:30:53 -05:00
abcdefg30
ce7f9e71c1 DefaultSpriteSequence: Avoid an extra allocation and LINQ when adding shadow frames 2023-05-16 22:57:33 +03:00
abcdefg30
05f21fcbe2 DefaultSpriteSequence: Merge if statements for readability 2023-05-16 22:57:33 +03:00
abcdefg30
00f2ba1a53 DefaultSpriteSequence: Remove redundant modulo operations
frame is already bounded by length.Value
2023-05-16 22:57:33 +03:00
abcdefg30
991e0a4c9a DefaultSpriteSequence: Use .Count == 0 over !.Any() 2023-05-16 22:57:33 +03:00
abcdefg30
aa28881726 DefaultSpriteSequence: Use List.Find instead of LINQ's FirstOrDefault 2023-05-16 22:57:33 +03:00
RoosterDragon
6d288aba2f Track keys during MiniYaml Merge.
This improves performance by avoiding repeated linear scans over the nodes to match the keys.
2023-05-15 23:48:39 +02:00
dnqbob
0980856072 Set up Hover MRLS moving style 2023-05-11 17:27:18 +02:00
dnqbob
0d98405bdc Make ActorFacingModifier privately set 2023-05-11 17:27:18 +02:00
dnqbob
69441a4fee Add TurnsWhileMoving to Mobile 2023-05-11 17:27:18 +02:00
dnqbob
a65bb17d68 Cache the notifyAttacks in AttackGarrisoned 2023-05-09 22:49:41 +02:00
Matthias Mailänder
6e6bf1ca81 Translate labels with parameters. 2023-05-09 20:14:52 +03:00
Matthias Mailänder
474463111f Remove unused CamelCase UI string. 2023-05-09 20:14:52 +03:00
RoosterDragon
0b4a54ab54 TechTree.GatherOwnedPrerequisites performance improvements.
- Consuming methods cared only about the count and not the actual actors, so only counts the actors rather that creating lists.
- ProvidesPrerequisites implementations return cached objects rather then allocating new enumerables on each call.
2023-05-06 20:07:35 +03:00
Matthias Mailänder
65c0cf1065 Deprecate string format shorthand. 2023-05-05 19:03:09 +02:00
Matthias Mailänder
1c2ce0dcc0 Deprecate string format log shorthand. 2023-05-05 19:03:09 +02:00
Matthias Mailänder
e251126dd4 Remove unused log proxy. 2023-05-05 19:03:09 +02:00
Matthias Mailänder
f2a4e7b984 Remove null sprite workaround from Tiberian Sun. 2023-05-04 22:13:59 +03:00
Matthias Mailänder
9e659cacf2 Fix rally point sprites not being truely optional. 2023-05-04 22:13:59 +03:00
Matthias Mailänder
c5e9567875 Fix a null reference exception upon empty Fluent strings. 2023-05-02 22:25:51 +02:00
Gustas
44f1af7059 Move TileScale to MapGrid 2023-05-02 16:37:30 +03:00
Nate Nichols
8f511a3bb6 Added ability to use Mouse 4 and Mouse 5 as hotkeys 2023-04-28 15:22:25 +03:00
Matthias Mailänder
4f7a01a291 Localize difficulty settings. 2023-04-25 21:33:02 +03:00
Matthias Mailänder
af6330b1bd Allow localisation of dictionary values. 2023-04-25 21:33:02 +03:00
Matthias Mailänder
55ff0ac1f4 Inline variables. 2023-04-25 21:33:02 +03:00
abcdefg30
51bbfc39b0 Fix DischargeableSupportPowerInstance crashing without instances 2023-04-25 21:15:06 +03:00
JovialFeline
90bb2db349 Fix OnAllKilled crash for allies05b and allies05c 2023-04-23 23:49:10 +02:00
Matthias Mailänder
0d36bc19c6 Document radar appear trait and fields. 2023-04-22 23:24:24 +02:00
Gustas
a9a7777293 Report linguini parse errors
Reports duplicate keys
2023-04-22 19:23:41 +02:00
Gustas
bf66068557 Add per map linting 2023-04-22 19:23:41 +02:00
Matthias Mailänder
68eec52cef Add TranslationProvider 2023-04-22 19:23:41 +02:00
Gustas
a065e6a47c Fix map level lobby options not being translated 2023-04-22 19:23:41 +02:00
Gustas
efe135e38b Add Translations to MapPreview 2023-04-22 19:23:41 +02:00
Gustas
07e47b6a28 Extract common rules translations 2023-04-22 19:23:41 +02:00
Gustas
8f5d8de1c2 Allow empty translation keys 2023-04-22 19:23:41 +02:00
Matthias Mailänder
2867334c00 Less verbose logging for untranslated strings. 2023-04-22 19:23:41 +02:00
Gustas
dc390a7301 Add IMove.MoveOntoTarget interface
In `TraitsInterfaces` we expose offset as WPos instead of CPos. In an upcoming PR we'll translate the same change to yaml.
2023-04-21 18:29:43 +02:00
Gustas
ad683d9226 Add MoveOnto Activity
No functional changes to `MoveWithinRange` nor `MoveAdjacentTo`. I've just
moved around code to for allow better overwriting.
2023-04-21 18:29:43 +02:00
Gustas
1c2eaa2654 Use nameof for ContrailEndColor description 2023-04-21 17:58:42 +02:00
Gustas
7ef1dccdcf Add ContrailEndWidth 2023-04-21 17:58:42 +02:00
Gustas
ff488b77b5 Implement elite radar invisibility 2023-04-21 16:59:11 +02:00
Gustas
92cceea2b8 Enemy veterancy should always be visible 2023-04-21 16:59:11 +02:00
yamismo
8b522680e3 Added Nod Covert Operations - Eviction Notice - scb30ea 2023-04-19 21:52:30 -05:00
RoosterDragon
7507333cd3 Fix missing Flags attribute on CABFlags enum. 2023-04-17 00:05:12 +02:00
RoosterDragon
f470f9ab91 Fix CA2216 2023-04-17 00:05:12 +02:00
RoosterDragon
a120b9d37e Fix CA2208 2023-04-17 00:05:12 +02:00
RoosterDragon
1b1b9dc29b Fix CA2215 2023-04-17 00:05:12 +02:00
RoosterDragon
ef04e2e1e8 Fix CA2019 2023-04-17 00:05:12 +02:00
RoosterDragon
321d4b8afd Fix CA1850 2023-04-17 00:05:12 +02:00
RoosterDragon
c3e6c4685f Fix CA1849 2023-04-17 00:05:12 +02:00
RoosterDragon
6362bbd176 Fix CA1846 2023-04-17 00:05:12 +02:00
RoosterDragon
07fb5e8027 Fix CA1841 2023-04-17 00:05:12 +02:00
RoosterDragon
8fe82ed976 Fix CA1839 2023-04-17 00:05:12 +02:00
RoosterDragon
25b8e7fefc Fix CA1834 2023-04-17 00:05:12 +02:00
RoosterDragon
ad4a443fc2 Fix CA1066 2023-04-17 00:05:12 +02:00
RoosterDragon
01eaa6b228 Fix CA1064 2023-04-17 00:05:12 +02:00
RoosterDragon
c442bd83f8 Fix CA1036 2023-04-17 00:05:12 +02:00
RoosterDragon
ff799303b0 Fix CA1018 2023-04-17 00:05:12 +02:00
RoosterDragon
f09241d263 Fix CA1010 2023-04-17 00:05:12 +02:00
Gustas
1db982276a Fix actors being added to world while world actors are being iterated 2023-04-15 17:26:02 +02:00
Paul Chote
718c6d03cc Downgrade SDL to 2.0.22. 2023-04-15 16:14:12 +02:00
Thomas Christlieb
57fba4e18e Rename JoinChatDelay 2023-04-15 12:50:06 +02:00
ThomasChr
b219731173 Allow overriding of Server.Map via the dedicated server scripts 2023-04-15 12:50:06 +02:00
ThomasChr
a0eea7bcc0 fix path to wiki page 2023-04-15 12:50:06 +02:00
RoosterDragon
0c3071b9c6 Adjust MiniYaml node merging when removals are present.
# Example Scenario
MockString:
	CollectionOfStrings:
		StringA: A

MockString:
	CollectionOfStrings:
		StringB: B

MockString:
	-CollectionOfStrings:

MockString:
	CollectionOfStrings:
		StringC: C

MockString:
	CollectionOfStrings:
		StringD: D

# Previous MergePartial result
# The CollectionOfStrings is merged into a single unit, so the C and D items are dragged upwards and jump ahead of the Removal
# When this is processed, the final result removes CollectionOfStrings entirely

MockString:
	CollectionOfStrings:
		StringA: A
		StringB: B
		StringC: C
		StringD: D
	-CollectionOfStrings:

# New MergePartial result
# When merging nodes, we no longer allow merges to jump an intervening removal node.
# This means we can have multiple of a certain key (CollectionOfStrings in this example) which was not the case previously.
# When this is processed, the final result includes C/D but not A/B.

MockString:
	CollectionOfStrings:
		StringA: A
		StringB: B
	-CollectionOfStrings:
	CollectionOfStrings:
		StringC: C
		StringD: D
2023-04-14 23:57:26 +03:00
RoosterDragon
0066010792 Added MiniYAML merging unit tests
MockString:
	CollectionOfStrings:
		StringA: A

MockString:
	CollectionOfStrings:
		StringB: B

MockString:
	-CollectionOfStrings:

MockString:
	CollectionOfStrings:
		StringC: C

MockString:
	CollectionOfStrings:
		StringD: D

MockString:
	CollectionOfStrings:
		StringA: A
		StringB: B
		StringC: C
		StringD: D
	-CollectionOfStrings:

MockString:
	CollectionOfStrings:
		StringA: A
		StringB: B
	-CollectionOfStrings:
	CollectionOfStrings:
		StringC: C
		StringD: D
2023-04-14 23:57:26 +03:00
penev92
0b61954e39 Reimported some TS maps
Could not find the source for most of the maps, so reimported only those that were part of the original game's "official map pool" + 3 custom ones.
2023-04-14 20:50:08 +03:00
Paul Chote
a0cd008da6 Fixed gen2 map importer Bounds calculations 2023-04-14 20:50:08 +03:00
penev92
3d2ba9d5bf Added abstract base class ImportGen2MapCommand
Split out ImportGen2MapCommand from ImportTSMapCommand, to also serve as a base for a RA2 map importer.
Also renamed TS importer to ImportTiberianSunMapCommand.
2023-04-14 20:50:08 +03:00
penev92
01e6babd54 Moved ImportLegacyMapCommand to OpenRA.Mods.Cnc
Also renamed to `ImportGen1MapCommand`.
Also moved Extensions.DistinctBy().
2023-04-14 20:50:08 +03:00
Ivaylo Draganov
96d023de87 Don't change cursor when mousing over label widgets
Labels don't handle input so the cursor should not change over them.
2023-04-12 12:09:50 +03:00
Gustas
3ca2bb1d23 Fix IDE0074, SA1316 and followup CS8141 2023-04-09 10:26:04 +01:00
RoosterDragon
595717fff0 Enable Code Quality Rules
Enforces a variety of CAxxxx rules that do not have existing violations.

For the benefit of dotnet_code_quality.CA2241.try_determine_additional_string_formatting_methods_automatically = true, rename parameters of methods that forward to string.Format so format issues will get detected automatically.
2023-04-08 23:15:40 +02:00
Gustas
d838d08570 Add IColorPickerManagerInfo interface 2023-04-08 18:05:20 +03:00
darkademic
265f915442 Resurrected old colour picker. 2023-04-08 18:05:20 +03:00
RoosterDragon
dcac966d49 Don't enable IDE0063, IDE0078. 2023-04-08 16:51:51 +03:00
RoosterDragon
4110c199fb Enable IDE0150, CA1845 2023-04-08 16:51:51 +03:00
RoosterDragon
14c0d011ea Fix SA1414 2023-04-08 16:51:51 +03:00
RoosterDragon
a167f9680f Fix SA1316 2023-04-08 16:51:51 +03:00
RoosterDragon
062dc2bd40 Fix SA1141 2023-04-08 16:51:51 +03:00
RoosterDragon
1ce9acd442 Fix IDE0110 2023-04-08 16:51:51 +03:00
RoosterDragon
8a285f9b19 Fix IDE0090 2023-04-08 16:51:51 +03:00
RoosterDragon
164abfdae1 Fix IDE0083 2023-04-08 16:51:51 +03:00
RoosterDragon
bd2b3d9793 Fix IDE0074 2023-04-08 16:51:51 +03:00
RoosterDragon
cbd0583289 Fix IDE0062 2023-04-08 16:51:51 +03:00
RoosterDragon
023d80b94d Fix IDE0057 2023-04-08 16:51:51 +03:00
RoosterDragon
5254348819 Fix IDE0056 2023-04-08 16:51:51 +03:00
RoosterDragon
4ec5a4b34a Fix reversed path searches from inaccessible locations.
The Harvester trait and MoveAdjacentTo activity called the pathfinder but had a single source and multiple targets. The pathfinder interface only allows for the opposite: multiple sources and a single target. To work around this they would swap the inputs. This works in most cases but not all cases. One aspect of asymmetry is that an actor may move out of an inaccessible source cell, but not onto an inaccessible target cell.

Searches that involved an inaccessible source cell and that applied this swapping method would therefore fail to return a path, when a valid path was possible. Although a rare case, once good way to reproduce is to use a production building that spawns actors on inaccessible cells around it, such as the RA naval yard. A move order uses the pathfinder correctly and the unit will move out. Using a force attack causes the unit to use the broken "swapped" mechanism in MoveAdjacentTo and it will be stuck.

This asymmetry has been longstanding but the pathfinding infrastructure only sporadically accounted for it. It is now documented and applied consistently. Create a new overload on the pathfinder trait that allows a single source and multiple targets, so callers have an overload that does what they need and won't be tempted to swap the positions and run into this issue.

Internally, this requires us to teach Locomotor to ignore the self actor when performing movement cost checks for these "in reverse" searches so the unit doesn't consider the cell blocked by itself.
2023-04-07 16:38:37 +01:00
Gustas
e4ba9733fe Add sequences linting to ingame lobby 2023-04-07 16:23:30 +01:00
IceReaper
a332fba702 IDFiles should be optional. 2023-04-05 21:13:11 +02:00
RoosterDragon
83561d639d Update LangVersion to C# 9.
mono was the bottleneck restricting our ability to use a newer C# version. mono 6.12 is currently available. Although poorly documented on their website, this supports C# 9. https://www.mono-project.com/docs/about-mono/versioning/#mono-source-versioning indicates mono 6.12 uses Roslyn 3.9.0. https://github.com/dotnet/roslyn/blob/main/docs/wiki/NuGet-packages.md#versioning indicates Roslyn 3.9.0 supports C# 9.

This unlocks C# 8 and C# 9 features previously unavailable to us.
- https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-80
- https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-9

A newer version of StyleCop is required to avoid rules tripping up on the new syntax. Currently only prerelease versions are available but their use is encouraged https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3420#issuecomment-994899135

Fix style rule violations on existing rules where the newer language version makes some existing casts redundant or allows use of the null coalescing assignment operator.
2023-04-05 15:27:41 +03:00
RoosterDragon
9dd4f938da Cache reflection calls when running utility lints and commands.
Reduces runtime of --check-yaml command to 70% of original.
2023-04-05 00:25:32 +02:00
Gustas
1a2aafa17c Fix backwards movement not canceling when BackwardDuration times out 2023-04-04 20:25:04 +02:00
Gustas
a4e80e1153 Fix backwards movement not working correctly when turning in an arc 2023-04-04 20:25:04 +02:00
dnqbob
a261abbc3f Using support power uncloak TS superweapon 2023-04-04 18:26:11 +03:00
dnqbob
27f780c5d1 Using support power can uncloak actor 2023-04-04 18:26:11 +03:00
IceReaper
11ab4a7935 Support dos paths in registry. 2023-04-04 16:20:05 +02:00
IceReaper
4dd532f60e Implemented MP3 detection. 2023-04-03 20:05:52 +02:00
IceReaper
138715b509 Added Zip support to installer source actions. 2023-04-03 17:57:16 +02:00
IceReaper
d97df78f4f Steam and Gog resolvers now take IDFiles into account. 2023-04-03 17:57:16 +02:00
IceReaper
56c0680685 Fixed a typo. 2023-04-03 17:57:16 +02:00
RoosterDragon
95f675becd Improve performance of ActorInfo.TraitsInConstructOrder.
Avoid redundant enumerations of the more and unresolved enumerables. This gives an approx 3x speedup for this function.
2023-04-03 17:29:08 +03:00
ThomasChr
a5ef6801c9 fix two crashes in hotkey menu, trying to select not visible widgets 2023-03-31 16:52:54 +03:00
abcdefg30
75a47aabb5 Don't always display the overwrite dialogue
When a save was selected but the filename text was changed,
we don't overwrite the existing save, so we don't need a dialogue
2023-03-31 16:32:09 +03:00
abcdefg30
73f29005bd Allow saving games by pressing enter 2023-03-31 16:32:09 +03:00
abcdefg30
f1d439a07f Make the save game title field take focus 2023-03-31 16:32:09 +03:00
penev92
9ce4ef0bff Updated ExtractEmmyLuaAPI command to fix issues
Issues:
 - The generated API file caused a ton of diagnostics warnings.
 - Perhaps due to EmmyLua moving forward since this was created, we were missing out on some features.

Changes:
 - Disabled diagnostics about missing return values for functions.
 - Added add operator definitions because by default CPos+CVec would be assumed to return a number and assignments would cause warnings about type mismatches.
 - Added explicit @enum annotations. This also fixed warnings in the generated API file about missing types.
 - Changed default type of properties from an empty table to nil. This fixed warnings in the generated API file about type mismatch.
 - Renamed local variable "required" -> "requiredTraits" for readability.
 - Disabled duplicate function/property  name warnings
 - Updated trait docs URL.
2023-03-31 11:46:28 +02:00
Thomas Christlieb
9e081763ad fix stance icon not updating when selecting units 2023-03-28 22:09:07 +03:00
Matthias Mailänder
378f66a1ff Resolve Lua language server diagnosed problems. 2023-03-28 11:45:15 +02:00
Matthias Mailänder
3d0efa1cbe Add EmmyLua notation to resolve the type. 2023-03-28 11:45:15 +02:00
Matthias Mailänder
cae07bb408 Remove uninitialized variable. 2023-03-28 11:45:15 +02:00
Matthias Mailänder
c94c1f8a6c Remove missing actors. 2023-03-28 11:45:15 +02:00
Matthias Mailänder
0847cd33bd Fix typos in mission scripts. 2023-03-28 11:45:15 +02:00
RoosterDragon
dc40442118 Fix doc comment on CanKickClient. 2023-03-27 20:55:44 +02:00
Gustas
2959a2c137 Fix warnings in LobbyCommand.cs 2023-03-27 20:15:17 +02:00
Gustas
bf00577d33 Allow kicking dead players 2023-03-27 20:15:17 +02:00
Gustas
c4bd9fb7aa Add quit button to connection lost panel 2023-03-27 20:02:54 +02:00
Gustas
482f2fc335 Fix inconsistent abort naming 2023-03-27 20:02:54 +02:00
Gustas
925e042455 Add Interactable lint test
Since # 19174 bounds are calculated from WDist instead of pixels. This causes many of the older maps to crash games, And this lint test makes it easier for map makers to detect these issues.

As calculating the bounds requires MapGrid, we need to write a separate lint test for Interactable instead of defaulting to RulesetLoaded checks.
2023-03-27 19:55:19 +02:00
Gustas
c525c48d25 Make sure submarines don't smoke underwater 2023-03-26 20:24:30 -05:00
Gustas
f90dba2c6b Add ^Submarine and make submarines inherit it 2023-03-26 20:24:30 -05:00
Gustas
e28f45f785 Use interpolated facings for submarines 2023-03-26 20:24:30 -05:00
Mustafa Alperen Seki
87eca8f2c0 Fix colors of paratroopers in the missions. 2023-03-26 17:49:02 -05:00
Mustafa Alperen Seki
960f829221 Move Soviets palette index definition to campaign-palettes.yaml. 2023-03-26 17:49:02 -05:00
RoosterDragon
8a4303cc94 Rework PriorityQueue for performance.
- Providing the comparer as a type argument that is a struct allows the calls to be devirtualised, leading to approx a 3x performance improvement.
- Use a single backing array, rather than a list of arrays.
2023-03-25 18:50:09 +01:00
penev92
7a4ac01348 Changed SequenceReferenceAttributes to use nameof 2023-03-24 18:51:36 +01:00
penev92
6dda4fa9f7 Fixed BuildableTerrainOverlay ignoring scale 2023-03-24 18:51:36 +01:00
Matthias Mailänder
af2b32e7ba Add particle smoke effects. 2023-03-23 12:15:16 +02:00
michaeldgg2
069b7c5500 RepairableBuilding: play/display notification when repair process is aborted. 2023-03-23 10:28:12 +01:00
michaeldgg2
ae1983faba ProductionQueue: make PauseProduction, CancelProduction virtual, CancelProductionInner protected 2023-03-20 12:17:55 +02:00
RoosterDragon
bcfa0c9ae9 Review StyleCop rules.
- Enforce SA1604 ElementDocumentationShouldHaveSummary.
- Enforce SA1629 DocumentationTextShouldEndWithAPeriod.
- Turn off some rules covered by IDExxxx rules.
- Remaining rules are treated as part of OpenRA style.
2023-03-18 12:46:10 +02:00
RoosterDragon
88ba974ea5 Reformat editorconfig
- Group style rules with their associated options in a way that matches the documentation. This makes it easier to pair rules and their options.
- Remove OpenRA.ruleset and move all rules into .editorconfig file.
- Centralise IDE0005 workaround in Directory.Build.props file.
2023-03-17 22:31:10 +01:00
RoosterDragon
721c03d9af Enforce additional style rules. 2023-03-17 22:31:10 +01:00
Gustas
384435f8eb Remove annoying autocomplete suffix 2023-03-16 21:07:21 +01:00
penev92
e808549637 Removed a redundant cast - IDE0004 2023-03-14 17:43:32 +01:00
penev92
bde13a8572 Fixed a bad merge 2023-03-14 17:43:32 +01:00
RoosterDragon
98c4eaca83 Fix IDE0032 2023-03-14 13:41:25 +02:00
michaeldgg2
e64c0a35c5 ProductionPaletteWidget: offset and align for queued count is now customizable 2023-03-12 15:10:26 +01:00
penev92
6bedc4697b Fix modcontent.chrome-3x.png
After it was changed to a version without padding.
2023-03-12 13:24:39 +02:00
Gustas
2dbefaf375 Cache font 2023-03-12 10:53:45 +01:00
Gustas
2050d55b21 Add player's name to super weapon timers 2023-03-12 10:53:45 +01:00
penev92
d61178de41 Added tooltips to ContentPackages 2023-03-11 21:43:18 +01:00
penev92
9cb2d19654 Added checkbox tick artwork to modcontent's chrome 2023-03-11 21:43:18 +01:00
penev92
8a59982420 Made Advanced installation offer optional packages
Previously all detected content would be installed. Now the user can choose whether they want to install the optional packages like music and videos.
2023-03-11 21:43:18 +01:00
penev92
c5aee7b2cf Added Title and Identifier as fields of ModPackage 2023-03-11 21:43:18 +01:00
penev92
d0285b058b Grouped installer SourceActions by ContentPackage
ContentPackages are defined in mod.yaml and list Installers that support them, but then the Installers and their SourceActions knew nothing about ContentPackages.
Also added BeforeInstall and AfterInstall sections for SourceActions in the Installers.
2023-03-11 21:43:18 +01:00
abcdefg30
64e84554d3 Fix empty mission objectives getting translated 2023-03-11 21:07:02 +01:00
Gustas
8a18c2e4b6 Add a CanDeploy check to Transform at FrameEndTask 2023-03-11 21:00:51 +01:00
abcdefg30
43bb9e4302 Fix the map.yaml formatting of some new D2k maps 2023-03-11 20:56:32 +01:00
abcdefg30
cb8921dc22 Fix the sequence filename update rule node placement 2023-03-11 20:56:32 +01:00
abcdefg30
64933ed04b Fix sequence inheritance defined in yaml
Nodes need to be placed after "Inherits" nodes so that they take
precedence over the inherited nodes.
2023-03-11 20:56:32 +01:00
abcdefg30
040fbf9694 Make Wanders and AttackWander queue activities instead of resolving orders 2023-03-11 20:47:34 +01:00
Wylli
4500d964b3 Update AUTHORS
Update AUTHORS
2023-03-10 20:35:56 +01:00
IceReaper
fceab4f388 Allow mods to override production widgets text colors. 2023-03-10 20:31:15 +01:00
Guillermoqnk
b3d468aca1 Fix return fire logic ignoring AutoAttack priotities
Added brand new feature

Unnecessary assignmet removed
2023-03-10 20:18:10 +02:00
Paul Chote
3b0b15abb9 Reduce duplication and allocations around CalculateFrameIndices. 2023-03-10 20:11:33 +02:00
Paul Chote
f0cf728825 Dispose SequenceSet when we're done with it.
Utility rules that do something on a map and exit
are left without explicit disposing, as they will
be cleaned up immediately anyway.
2023-03-10 20:11:33 +02:00
Paul Chote
c35ab081ff Rewrite sequence loading logic.
Multiple layers of Lazy<T>ness are replaced with
an explicit two-part loading scheme.

Sequences are parsed immediately, without the need
for the sprite assets, and tell the SpriteCache
which frames they need. Use-cases that want the
actual sprites can then tell the SpriteCache to
load the frames and the sequences to resolve the
sprites.
2023-03-10 20:11:33 +02:00
Paul Chote
1f3403717b Fix negative sequence lengths. 2023-03-10 20:11:33 +02:00
Paul Chote
a6f3db0a45 Allow all sprites to use interpolated facings. 2023-03-10 20:11:33 +02:00
Paul Chote
b051211842 Remove internal state from ISpriteSequence API. 2023-03-10 20:11:33 +02:00
Paul Chote
279869b4c5 Fix --dump-sequence-sheets utility command.
Also formats output filenames to specify
indexed sprite channels.
2023-03-10 20:11:33 +02:00
Paul Chote
b69adb518a Remove asset browser dependency on sequences. 2023-03-10 20:11:33 +02:00
Paul Chote
b7cdcf419f Add ILintSequencesPass. 2023-03-10 20:11:33 +02:00
Paul Chote
7cd4272350 Access sequences from Map. 2023-03-10 20:11:33 +02:00
Paul Chote
992ba1a9a2 Remove HasEmbeddedPalette from sequences. 2023-03-10 20:11:33 +02:00
Gustas
dab3ca0025 Add support for dark player colors 2023-03-10 15:43:24 +02:00
RoosterDragon
939f715e3c Fix IDE0053 2023-03-07 13:18:13 +02:00
Matthias Mailänder
37afd6094e Rename GiveMcvCrateAction to GiveBaseBuilderCrateAction. 2023-03-07 12:53:31 +02:00
RoosterDragon
c916a00624 Remove workaround for old versions of mono. 2023-03-07 12:51:06 +02:00
Matthias Mailänder
f6e5bee334 Move JumpJet traits to the C&C library. 2023-03-07 12:24:05 +02:00
Matthias Mailänder
63b9f18d05 Also document the launch parameters. 2023-03-05 12:15:32 +02:00
Matthias Mailänder
7b9dafcd19 Code cleanup. 2023-03-05 12:15:32 +02:00
RoosterDragon
53e9f44972 Spelling fixes 2023-03-02 20:11:54 +02:00
RoosterDragon
52fd564eac Fix some whitespace formatting issues: stray tabs or spaces.
Wrap some long lines on affected code.
2023-03-02 20:02:45 +02:00
Matthias Mailänder
edaf11cb89 Update thirdparty dependencies. 2023-03-01 23:52:59 +01:00
RoosterDragon
ad122c8e32 When running a server, don't load minimap previews into memory. 2023-03-02 00:13:46 +02:00
RoosterDragon
8ee6957e6a Fix IDE0048 2023-03-01 21:56:28 +02:00
abcdefg30
65e28d5562 Retire the release-20200202 update path 2023-03-01 20:02:01 +01:00
Gustas
422a228cea Fix PlayerColorRemap expecting colors in linear space
PlayerColorRemap expected colors in linear space yet we provided them in gamma. We fix this by instead expecting gamma space colors and then converting them into linear space ourselves.
2023-02-28 23:26:37 +02:00
RoosterDragon
0b01b73111 Fix IDE0060 2023-02-28 21:21:40 +02:00
RoosterDragon
555aac3f64 Fix IDE0042 2023-02-28 21:21:40 +02:00
RoosterDragon
5b70d344cc Fix IDE0038 2023-02-28 21:21:40 +02:00
RoosterDragon
71ce515d6d Fix IDE0004 2023-02-28 21:21:40 +02:00
Eonfge
a4f9ceaf09 Add PrefersNonDefaultGPU flag to Desktop Entry
This means that the application will automatically start with the dedicated GPU of laptops, instead of the iGPU. Based on the Linux's FreeDesktop spec: https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html
2023-02-27 10:42:07 +01:00
RoosterDragon
d4135d608e Fix IDE0039 2023-02-27 10:09:11 +01:00
Gustas
4b3f7034b2 Silence rule "Use span-based 'string.Concat'" 2023-02-27 08:36:47 +02:00
Gustas
e64c77fdde Use pattern matching to avoid is check followed by a cast (with variable) 2023-02-27 08:36:47 +02:00
Gustas
8d0fe52dd8 Remove unnecessary parentheses 2023-02-27 08:36:47 +02:00
Gustas
157d1b32dc Use null propagation 2023-02-27 08:36:47 +02:00
Gustas
b06cbd7a95 Add missing punctuation to editorconfig 2023-02-27 08:36:47 +02:00
Gustas
2cccae96fe Change click InterruptType to Overlap 2023-02-25 14:46:53 +02:00
Orb370
e758678140 update TD map pool 2023 2023-02-24 22:01:15 +01:00
RoosterDragon
6c96405ab2 Fix CS1573 2023-02-24 22:00:25 +02:00
RoosterDragon
63aa34cb35 Fix CS1570 2023-02-24 22:00:25 +02:00
RoosterDragon
5a2a448c32 Fix IDE0250 2023-02-24 22:00:25 +02:00
RoosterDragon
5e52d067c8 Fix IDE0180 2023-02-24 22:00:25 +02:00
RoosterDragon
bf960b6eae Fix IDE0120 2023-02-24 22:00:25 +02:00
RoosterDragon
ede5412526 Fix IDE0100 2023-02-24 22:00:25 +02:00
RoosterDragon
78c41b84a1 Fix IDE0082 2023-02-24 22:00:25 +02:00
RoosterDragon
99c289e063 Fix IDE0075 2023-02-24 22:00:25 +02:00
RoosterDragon
3402031399 Fix IDE0071 2023-02-24 22:00:25 +02:00
RoosterDragon
837c70f857 Fix IDE0061 2023-02-24 22:00:25 +02:00
RoosterDragon
4f6095c3d4 Fix IDE0054 2023-02-24 22:00:25 +02:00
RoosterDragon
2d4119e88d Fix IDE0051 2023-02-24 22:00:25 +02:00
RoosterDragon
4991f2f892 Fix IDE0041 2023-02-24 22:00:25 +02:00
RoosterDragon
10def52ad9 Fix IDE0033 2023-02-24 22:00:25 +02:00
RoosterDragon
99c1a4448b Fix IDE0030 2023-02-24 22:00:25 +02:00
RoosterDragon
8223161959 Fix IDE0029 2023-02-24 22:00:25 +02:00
RoosterDragon
67ba3e55de Fix IDE0020 2023-02-24 22:00:25 +02:00
RoosterDragon
6d7c73d498 Fix IDE0019 2023-02-24 22:00:25 +02:00
RoosterDragon
80bb828fe5 Fix IDE0018 2023-02-24 22:00:25 +02:00
RoosterDragon
b0dca05e50 Fix IDE0002 2023-02-24 22:00:25 +02:00
RoosterDragon
8b4500146f Fix IDE0001 2023-02-24 22:00:25 +02:00
Matthias Mailänder
ee35cbc0d0 Let bot Deviators prioritize vehicles. 2023-02-22 23:09:08 +01:00
Matthias Mailänder
0b6f335c9f Reference trait name in description. 2023-02-22 23:09:08 +01:00
Matthias Mailänder
40e8061797 Removed unused water terrain type from Dune 2000. 2023-02-22 23:09:08 +01:00
penev92
bb8e6ab03c Fix sprites with no frames crashing AssetBrowser 2023-02-22 11:25:17 +02:00
penev92
807bd4d06a Make AssetBrowser not case sensitive to file types 2023-02-22 11:25:17 +02:00
penev92
e1a7fb1cc8 Fixed displayed audio length in the AssetBrowser 2023-02-20 13:07:32 +01:00
Matthias Mailänder
5032b2b872 Extract translation strings. 2023-02-19 23:46:41 +01:00
abcdefg30
640e9d68b7 Fix not all ammo pools getting rearmed during RearmTick 2023-02-19 15:11:05 +02:00
Gustas
049d0283f9 Remove TiberianSunRefinery
Also add IDockClientBody interface,
move WithDockingOverlay cnc -> common,
remove HarvesterDockSequence implementing classes
2023-02-18 16:35:15 +01:00
Andre Mohren
3f0c3a8b9c Allow mods to control how looped sounds are used. 2023-02-18 16:18:20 +01:00
Gustas
68fddf818c Polish encyclopedia descriptions 2023-02-18 16:18:01 +01:00
Gustas
6997c442c6 Add MacOS Utility executable 2023-02-18 17:14:22 +02:00
Gustas
78677fd8ab Fix replays continuing after desync 2023-02-18 15:53:17 +01:00
Gustas
20a16ad5f8 Move force pausing to EndGame method 2023-02-18 15:53:17 +01:00
penev92
19613ed833 Minor D2k encyclopedia UI polish
- Make the text area taller so all the current text fits without a scroll.
- Limit label width so text doesn't press against the scrollbar.
2023-02-18 15:39:19 +01:00
penev92
63bc73d9d2 Change the way the output directory is set during packaging 2023-02-18 15:18:35 +02:00
penev92
5c7b8955dc Added video player background for D2k 2023-02-18 14:10:24 +01:00
penev92
2c51e791ad Added VideoPlayerWidget.LoadAndPlayAsync method
Previous commits removed the async loading of videos, which can be a problem for videos played in the radar widget mid-game because it can cause a lag spike. This loads the video on a separate thread and runs it on the main thread whenever it is loaded, not blocking the main thread meanwhile and allowing the game to continue while the video loads.
Also add back cancelling of already playing video and add a check to not try to run onComplete if it is null.
2023-02-18 14:10:24 +01:00
penev92
c3fcbf77ed Rename some VideoPlayerWidget methods for clarity 2023-02-18 14:10:24 +01:00
penev92
6321432d97 Fix crash caused by VideoPlayerWidget.DrawOverlay
Somehow this would divide by 0 on the next line, not crashing but resulting in an integer overflow on `overlayHeight`, which would crash when trying to render.
2023-02-18 14:10:24 +01:00
penev92
4135079290 Unify MediaGlobal/Scripting.Media PlayFMV* methods
- Improve consistency
- Remove dead code
- Removed AsyncAction!
- Delegate.BeginInvoke is unsupported since .NET Core and throws an "Operation is not supported on this platform." exception.
2023-02-18 14:10:24 +01:00
penev92
287428b487 Did a slight refactor pass on MediaGlobal
- Unified onPlayComplete callback handling to avoid code duplication.
- Fixed code style issues.
2023-02-18 14:10:24 +01:00
Paul Chote
af23888b95 Remove dependency on VCRUNTIME140.dll. 2023-02-18 14:30:56 +02:00
abcdefg30
4a554431ff Remove redundant jumps and other style nits from the update rules 2023-02-18 00:41:07 +02:00
Paul Chote
947f53a991 Disable max order length check for local servers. 2023-02-14 10:15:21 +02:00
abcdefg30
835537fcc2 Adjust the encyclopedia descriptions of combat tanks 2023-02-14 10:09:11 +02:00
abcdefg30
08ba35763c Adjust the encyclopedia descriptions of light armored units 2023-02-14 10:09:11 +02:00
oldnaari
14ad4e168e Update DownloadPackageLogic.cs, to log missing files in package 2023-02-05 20:19:49 +02:00
penev92
a614375982 Fixed Makefile check of MacOS architecture 2023-02-05 00:19:05 +01:00
abcdefg30
e4bb13ea07 Throw an ArgumentException when trying to translate null keys 2023-02-03 08:31:28 +02:00
abcdefg30
84add8a03d Fix an NRE in ConnectionLogic 2023-02-03 08:31:28 +02:00
Gustas
f5aa2f153a Use Refinery reference in HarvesterDockSequence 2023-01-31 23:49:10 +01:00
Gustas
f56b0ea0d0 Moved HarvesterDockSequence notifications to the base class
And fix a minor oversight in VoxelHarvesterDockSequence.cs
2023-01-31 23:49:10 +01:00
Azarus
77b06ac9f7 Add an option to check if an actor can be captured via Lua 2023-01-30 13:46:29 +02:00
Gustas
3f2007cd2c Optimise TakeCover to have clear states 2023-01-28 20:18:48 +01:00
michaeldgg2
6f87c565ac AddFactionSuffixLogic: add suffix to ProductionTabsWidget.Decorations 2023-01-27 12:30:38 +02:00
michaeldgg2
949b993a4a ProductionTabsWidget: center left/right arrow image on left/right button background 2023-01-27 12:30:38 +02:00
michaeldgg2
ca7e7c2304 ProductionTabsWidget: allow specifying different panels for left/right scroll button and tab buttons. 2023-01-27 12:30:38 +02:00
michaeldgg2
38e52c062e ProductionTabsWidget: allow customizing left/right arrow buttons.
This moves initialization of Initialize getters for left/right arrow image from constructor Initialize method making it possible to override fields Decorations, DecorationScrollLeft and DecorationScrollRight after widget creation.
2023-01-27 12:30:38 +02:00
Paul Chote
8be3ac863b Only update changed files when saving maps. 2023-01-27 07:43:25 +01:00
Dean Simmons
e1b78c4821 Only return file paths when returning zip contents 2023-01-26 19:25:58 +02:00
penev92
c1da198f5d Fixed RA parabomb/paradrop support power tooltips
The AFLD's AirstrikePower@parabombs' description wasn't updated to the latest balance patch (3 Badgers -> 1 Badger)
2023-01-26 09:32:34 +01:00
penev92
474de014f8 Move RenameEngineerRepair to its proper folder 2023-01-25 12:00:57 +01:00
Matthias Mailänder
c3752d1c18 Remove engineer repairing completely. 2023-01-24 23:26:14 +01:00
Matthias Mailänder
f67b6f6cad Give EngineerRepair(able) more generic names 2023-01-24 23:26:14 +01:00
Matthias Mailänder
9b2e291a46 Extract translation strings. 2023-01-23 20:51:45 +01:00
Matthias Mailänder
867efcc6e8 Add Media.DisplayMessageToPlayer 2023-01-23 20:51:45 +01:00
Matthias Mailänder
847bbf5710 Use out parameter modifier code style. 2023-01-23 20:51:45 +01:00
Matthias Mailänder
f013a003a0 Don't translate enemy objectives. 2023-01-23 20:51:45 +01:00
Matthias Mailänder
7cdc8c4ec5 Add a quick save button to the map editor. 2023-01-23 14:13:19 +02:00
Paul Chote
3dd1fd6b00 Add support for Flatpak Steam installs. 2023-01-22 22:39:36 +01:00
Paul Chote
e13a7aed90 Manual yaml cleanup. 2023-01-22 22:10:48 +02:00
Paul Chote
5b8f148c50 Simplify tileset-specific sequence definitions.
All magic behaviour for constructing sprite filenames
has been removed in favour of an explicit Filename
(and TilesetFilenames for tileset-specific sequences)
property.
2023-01-22 22:10:48 +02:00
Paul Chote
04c3cd6ec5 Reimplement sequence Defaults parsing:
The previous MiniYaml.Merge implementation interacted
poorly with yaml inheritance, making it complicated
(or impossible) to override certain keys from Defaults.

The new implementation is simpler: If a key is defined
it will be used. If it isn't, the default (if defined)
will be used. Defaults can be masked by making sure
the same key is defined (even with an empty value)
in the sequence.

This also fixes naming within the sequence code to
distinguish between images (a group of sequences),
sequences (defining a specific sprite/animation),
and filenames for a specific sprite/animation.
2023-01-22 22:10:48 +02:00
Paul Chote
05c83a9dbb Fix ExtractSpriteSequenceDocsCommand crash on non-generic static fields. 2023-01-22 22:10:48 +02:00
abcdefg30
3ef0b3be95 Add the expected parameter(s) to the ExtractFilesCommand description 2023-01-22 18:31:32 +00:00
Gustas
84e7eb144b Move RearmTick to Rearmable 2023-01-20 11:43:12 +02:00
Gustas
75d65b3d20 Remove redundant INotifyRearm 2023-01-20 10:43:18 +02:00
Gustas
12af8506f8 Fix Rearmable using the wrong interface 2023-01-20 10:43:18 +02:00
Gustas
0d24f2c08b Merge INotifyBeingResupplied into INotifyDockClient 2023-01-20 10:43:18 +02:00
Gustas
dca07d240c Add INotifyDockHost and INotifyDockClient interfaces
Rename INotifyDocking to INotifyDockHost and extract INotifyDockClient from INotifyHarvesterAction
2023-01-20 10:43:18 +02:00
N.N
ba011ffc5f Fix deviator GrantExternalCondition Warhead
Warhead now applies only to enemy and neutral units.
2023-01-18 23:41:18 +01:00
RoosterDragon
faf12f93a4 Fix Locomotor cache coherency for disabled or paused mobile actors.
The cache in Locomotor that is populated via the UpdateCellBlocking method disagreed with the non-cached logic of IsBlockedBy when dealing with Mobile actors. The cache determined an actor to be moving if it was both movable and had horizontal movement types. IsBlockedBy determined an actor to be moving if it had horizontal movement types, but did not check if it was movable. This difference in checks could allow a mobile trait that was disabled or paused and which had horizontal movement to be treated differently be the two methods. UpdateCellBlocking would consider it not moving due to the disabled/paused trait. IsBlockedBy would consider it moving as it didn't care about the disabled/paused state of the trait.

Now, we unify the two methods to consider a mobile trait that is disabled/paused as not moving. This prevents HierarchicalPathFinder from crashing on the inconsistent state, i.e. when asked to path search through a cell of a mobile unit which has disabled or paused movement, but which has horizontal movement types from prior movement.
2023-01-17 18:58:22 +01:00
Andre Mohren
1dc44c4047 Looped sounds can now be unlooped to avoid cut-offs. 2023-01-14 13:56:20 +01:00
David Wilson
9cd3981b94 Fix to ensure Windows uninstaller removes reg entries from the 64 bit registry 2023-01-14 13:47:37 +01:00
Moath Altarawneh
44be6cea94 Update Program.cs
a small change to line 174 to use an easier way to format the string 👍
2023-01-14 13:02:28 +02:00
abcdefg30
5bf7fe852c Remove the copyright year numbers 2023-01-11 11:58:54 +02:00
penev92
5f80e93aee Added ITilesetSpecificPaletteInfo for linting 2023-01-10 16:36:36 +00:00
Gustas
80b92fb667 Fixed UnhardcodeBaseBuilderBotModule update rule
Update rules should should not read `modData.DefaultRules`
2023-01-10 18:02:24 +02:00
Gustas
29d21545a6 Fixed UnhardcodeSquadManager update rule
Update rules should should not read `modData.DefaultRules`
2023-01-10 18:02:24 +02:00
Gustas
326f8115a0 Exclude Plugs from TS protection types 2023-01-10 18:02:24 +02:00
Paul Chote
129db98a2f Add BeforeUpdate* methods for update rules.
These make it possible to write more advanced update
rules that query state across multiple actors, or
based on resolved state.
2023-01-10 18:02:24 +02:00
penev92
15fe2d5594 Added an update rule for adding ControlGroups 2023-01-10 00:15:00 +01:00
penev92
e3e012a9ed Added an update rule for DomainIndex removal
Also for adding the new HPF-related PathFinderOverlay and HierarchicalPathFinderOverlay that were added at the same time.
2023-01-10 00:15:00 +01:00
Ivaylo Draganov
04648a66e6 Clarify the wording and explain the meaning of some labels in settings 2023-01-09 21:47:11 +02:00
abcdefg30
edd0068d88 Remove an unuseful comment from mods/ra/installer/origin.yaml 2023-01-09 19:27:50 +01:00
Matthias Mailänder
79d786708b Fix URL button blocking the party button. 2023-01-08 23:07:19 +01:00
Matthias Mailänder
921da2f19e Bump DiscordRichPresence library. 2023-01-08 23:07:19 +01:00
Andre Mohren
0b94a0639e Implemented WestwoodCompression for AUD files. 2023-01-08 12:40:57 +00:00
Gustas
fb93281beb Fixed a spelling error in Discovery ant mission 2023-01-07 17:02:12 +01:00
Gustas
b75976b6d8 Remove bogus bridge tiles from temperate tileset 2023-01-07 17:02:12 +01:00
Matthias Mailänder
b0aea7b810 Bump checkout action. 2023-01-04 21:54:55 +00:00
Matthias Mailänder
f65c3a09db Bump setup .NET action. 2023-01-04 21:54:55 +00:00
abcdefg30
d4d6d5b7c0 Remove custom network frame counting from ReplayConnection 2023-01-04 09:33:55 +00:00
Matthias Mailänder
56ff98a2a3 Throw specific cursor sequence errors
instead of dividing by zero because length is 0.
2023-01-03 13:25:50 +01:00
Paul Chote
6d438a9d61 Allow mods to customise the default rendering scale. 2022-12-31 17:11:03 +01:00
Gustas
e21f94f36a Guarantee 1 riflemen on sell in mods
And engineer on RA and D2K construction yards
2022-12-31 16:51:26 +01:00
Gustas
34543e2952 Add an option to guarantee actors on sell 2022-12-31 16:51:26 +01:00
Unrud
b1ffe0edd5 Don't use parameter expansion for paths
It fails when the script is started with `bash package-all.sh`.
2022-12-31 16:20:55 +01:00
Matthias Mailänder
19ecddcd86 Enforce use of 'var' instead of explicit type. 2022-12-28 23:02:04 +01:00
abcdefg30
982c97dc6c Specify targetable offsets for the repair pad in Dune 2000 2022-12-28 17:11:31 +02:00
Paul Chote
27c602fc30 Downgrade OpenAL-soft to 1.10.1.
1.12.2 introduces noticeable behaviour changes
to source positioning and cash ticks. This also
fixes missing sound backends on Linux.
2022-12-28 00:19:32 +01:00
Matthias Mailänder
943751547e Don't hardcode enemy aircraft ignorance. 2022-12-25 22:24:57 +01:00
Matthias Mailänder
24ed5f7a1a Add linting to AI squad manager types. 2022-12-25 22:24:57 +01:00
abcdefg30
0d654d5e53 Change into the correct directory before trying to push
Each workflow step starts back in the default directory,
so we need to checkout the repository folder again
2022-12-24 16:02:46 +01:00
abcdefg30
ffa015dc21 Let the documentation workflow fetch the required git branch 2022-12-24 16:02:46 +01:00
Paul Chote
a6eb00f326 Fix pseudo-fullscreen window size on macs with a notch. 2022-12-24 12:10:26 +01:00
abcdefg30
5a12f44a25 Fix map actors not being spawned with the correct owner 2022-12-24 11:20:35 +02:00
Matthias Mailänder
5ffb564376 Fix invalid actors not spawning in-game. 2022-12-23 18:33:55 +01:00
Matthias Mailänder
6bcf2f718c In map editor replace invalid actor owners with neutral. 2022-12-23 18:33:55 +01:00
Paul Chote
7c2be4ce3c Implement state prediction for lobby ready checkbox. 2022-12-23 16:33:21 +02:00
Paul Chote
25935bbe99 Implement state prediction for debug menu checkboxes. 2022-12-23 16:33:21 +02:00
Paul Chote
2ba52f1b94 Implement state prediction for lobby checkboxes. 2022-12-23 16:33:21 +02:00
Paul Chote
e2e541a251 Package macOS releases as a universal binary.
* Minimum macOS version is raised to 10.11.
* App bundles ship 3 versions of the runtime and engine binaries,
  and a fat launcher that selects the appropriate runtime/apphost.
* Mono is used for macOS 10.11 - 10.14, or if OPENRA_PREFER_MONO
  environment variable has been set.
2022-12-23 12:54:06 +02:00
Paul Chote
363a0e1d1e Update native deps to include macOS and Linux arm64 binaries. 2022-12-23 12:54:06 +02:00
Paul Chote
80945cd08a Report CPU arch in logs and sysinfo. 2022-12-23 12:54:06 +02:00
Paul Chote
a17e1671f0 Fix MiniYaml source locations being lost when merging. 2022-12-23 10:44:54 +02:00
Gustas
b619dd14c5 Remove sniper 2022-12-21 21:56:24 -06:00
Gustas
55cf40ec52 Added carryalls to spectator Economy statistics 2022-12-21 21:06:38 +01:00
Gustas
f3e44094a1 Fix contrail using the wrong colors 2022-12-21 19:35:52 +02:00
N.N
43094742fb Change tile 9 to Rough TerrainType 2022-12-21 19:31:15 +02:00
abcdefg30
70549fce14 Run the CI workflow on PRs targeting prep 2022-12-21 13:28:14 +02:00
Paul Chote
360a5b293d Bubble unhandled double-click events to OnClick. 2022-12-20 23:01:41 +01:00
RoosterDragon
f4965915ee Fix PathFinderOverlay crash when deselecting actor moving along waypoints.
Set an actor moving along several waypoints whilst the /path-debug command is active, then deselect the actor. Each waypoint will add more information to the debugging overlay until it crashes with "Maximum two records permitted." This resolves the crash by no longer adding new debugging information once the actor is deselected.
2022-12-20 13:23:32 +13:00
RoosterDragon
39e48d9e8d HPF is aware of map projection changes.
An event is added to Map to indicate when the cell projection is changed. This is important as this can mean Map.Contains(CPos) could now return different results for the cell. The HierarchicalPathFinder is made aware of these changes so it can rebuild any out-of-date information. This fixes prevent a crash if a cell that was previously outside the map changes height and becomes inside the map. The local path search will explore the cell as it is inside the map - but if the HPF was unaware if had been updated, it will still consider the cell to be outside the map and unreachable, resulting in a crash.
2022-12-20 09:47:24 +13:00
RoosterDragon
d8ebb96077 HPF handles searches from unreachable source cells into cut off areas.
If a path search is performed by the HierarchicalPathFinder when the source cell is unreachable location, a path is still allowed and starts from one of the cells adjacent to the location. If moving into one of these cells results in the actor moving into an isolated area that cannot reach the target this would previously crash as no abstract path could be found. Now we handle such locations by giving them a unreachable cost so the path search will not attempt to explore them.

Imagine a map split into two by a one tile wide line of impassable cliffs. If an actor is on top of these cliffs it is allowed to path because it can "jump off" the cliff and move into the cell immediately either side of it. However it is important which side it chooses to jump into, as one it has moved off the cliff it loses access to the other side. The previous error came about because the path might try and search on the side that couldn't reach the target location. Now we avoid that being considered.
2022-12-20 09:47:24 +13:00
Paul Chote
11e5d19f32 Prioritise primary buildings in CycleBasesHotkeyLogic. 2022-12-19 11:55:45 +02:00
Alex
82d0546d16 build: harden workflow permissions
Signed-off-by: Alex <aleksandrosansan@gmail.com>
2022-12-19 22:40:19 +13:00
Ivaylo Draganov
a0f17b15ec Refactor translation files
- Add prefixes to all message keys to provide context
- Use messages with attributes for some UI elements (dropdowns, dialogs, checkboxes, menus)
- Rename some class fields for consistency with translation keys
2022-12-19 22:04:54 +13:00
Matthias Mailänder
46caa2d889 Make RenderShroud/JammerCircle conditional. 2022-12-19 20:03:40 +13:00
Gustas
475468ccc7 Fix Devil's Tongue attacking while submerged 2022-12-19 19:25:59 +13:00
Gustas
f3dc168dbd Don't cancel guard cursor when holding shift 2022-12-18 15:39:12 +01:00
Matthias Mailänder
4ffd81bd41 Fix documentation deployment. 2022-12-18 12:57:08 +13:00
abcdefg30
0f149f1143 Fix the actor edit panel not always getting closed properly 2022-12-18 12:24:36 +13:00
Ivaylo Draganov
8513de0b47 Fix error in PlayerResources trait documentation
This was missed in #19295
2022-12-16 12:14:54 +02:00
Ivaylo Draganov
614603089e Define and measure duration for text notifications in milliseconds
During a game notification duration should be the same regardless of
game speed. Switch to using wall-clock time defined in milliseconds 
instead of game ticks. Also use the opportunity to rename the field 
to "Duration" because "RemoveTime" is not so clear.
2022-12-15 23:28:46 +01:00
Ivaylo Draganov
e280e0f31c Use an overload that already passes the second argument as true 2022-12-15 23:28:46 +01:00
Gustas
c739447598 Fix map editor sliders stealing focus 2022-12-14 23:53:28 +01:00
Ivaylo Draganov
e60f7bb125 Remove unused common chrome yaml file 2022-12-12 23:51:46 +01:00
Ivaylo Draganov
18e36b96db Add HPF overlay controls to observer chrome 2022-12-12 23:51:46 +01:00
Gustas
2b57b6be1d Don't cancel attack move cursor when holding shift 2022-12-12 23:20:45 +01:00
dnqbob
45d4a2c7e2 Fix syncreport crash caused by getting LocalClient index from ReplayConnection 2022-12-11 23:32:59 +02:00
Gustas
6fccd6be84 Fix attack move lines not showing up for undeploy on force move units
DeployForGrantedCondition is wrapped around the Move activity, so the AttackMoveActivity thinks that DeployForGrantedCondition is the Move activity.

All it means is that we need to forward the target line request to the Move activity
2022-12-11 22:06:47 +01:00
Smittytron
767ac1c4e2 Remove MustDestroy from longbows in Soviet 11 2022-12-10 16:16:00 +02:00
Smittytron
b14caf004d Fix Siberian Conflict 3 win trigger 2022-12-10 16:16:00 +02:00
N.N
d72d25c369 Update D2k map pool 2022-12-07 23:39:35 +01:00
Matthias Mailänder
640e52d4b4 Report missing translation keys only once. 2022-12-07 18:40:26 +02:00
Matthias Mailänder
9ba51c6b51 Manually add game speeds to the linter. 2022-12-07 18:40:26 +02:00
Matthias Mailänder
8297fcff30 Expose lobby options to localisation. 2022-12-07 18:40:26 +02:00
Matthias Mailänder
aefa49a831 Add support for translating trait rules. 2022-12-07 18:40:26 +02:00
Matthias Mailänder
e251377f7c Add support for translation attributes. 2022-12-07 18:40:26 +02:00
Matthias Mailänder
760a1245c5 Mark non-moddable translation strings as constant. 2022-12-07 18:40:26 +02:00
Matthias Mailänder
4f016f149f Install libfuse2 in Ubuntu 22.04 2022-12-06 09:46:48 +02:00
abcdefg30
c14b585433 Remove a stray $ from infront of the version string when writing logs 2022-12-03 23:49:15 +02:00
Matthias Mailänder
2d78dae01a Execute self-contained binaries with runtime as backfall 2022-12-02 23:03:40 +01:00
Gustas
b0036a2d3e Add cell reference HPF crash messages 2022-12-02 17:46:46 +01:00
Matthias Mailänder
3091504c7e Unify file extensions. 2022-12-01 19:52:56 +02:00
Matthias Mailänder
bd678659a2 Update Ubuntu 2022-12-01 19:52:56 +02:00
Paul Chote
06edc3dff1 Remove redundant order copies. 2022-11-27 22:58:23 +01:00
Paul Chote
c2208ce8fe Always serialize orders. 2022-11-27 22:58:23 +01:00
Paul Chote
1add57e5ad Fix frame number not being included in pre-serialized order packets. 2022-11-27 22:58:23 +01:00
Matthias Mailänder
435c999abf Update notarisation to XCode 13 tooling. 2022-11-27 20:31:10 +01:00
Paul Chote
c82b8244e8 Fix display bounds when running on macbooks with a notch. 2022-11-26 20:43:57 +01:00
dnqbob
8703cfc4f4 Incease NumSyncReports to 7 2022-11-25 23:57:39 +01:00
dnqbob
63411f9938 Add Player is host infomation for a better debug of desync related with AI 2022-11-25 23:57:39 +01:00
dnqbob
60f35f779d SyncReport filename contains index of local client 2022-11-25 23:57:39 +01:00
dnqbob
1a2ef49100 SyncReport: Output the frames recorded for better debug. 2022-11-25 23:57:39 +01:00
dnqbob
6fbdc2c221 HarvesterBotModule should not command harvesters that cannot be ordered 2022-11-24 23:09:22 +01:00
Matthias Mailänder
14b5504ea7 Show the host in the download failed error message. 2022-11-23 23:35:53 +01:00
Matthias Mailänder
6f4f0c4e8f Fix unknown host not getting translated. 2022-11-23 23:35:53 +01:00
dnqbob
ac92162825 Weapon checks valid frozen actor 2022-11-22 21:49:12 +01:00
dnqbob
a25558b550 Weapon doesn't check warheads in IsValidAgainst 2022-11-22 21:49:12 +01:00
dnqbob
e8d9e2dfa9 Make weapon can target attacker itself an option. 2022-11-22 21:49:12 +01:00
Vapre
aa878c9dc8 ExceptionHandler, fix npe. 2022-11-22 12:35:28 +01:00
abcdefg30
7aa50d412c Remove the unused MONO_TAG variable from the MacOS buildpackage script 2022-11-20 22:08:38 +01:00
Matthias Mailänder
69949f9d53 Revert "Scripts: Check exit status of background process"
This reverts commit 3f106bef72.
2022-11-20 20:36:29 +02:00
Andre Mohren
0c8ae195ae Removed offset and length utility commands, obsolete now. 2022-11-19 12:27:13 +01:00
Andre Mohren
e6682d2108 Added C&C Remastered installer support (Origin and Steam) 2022-11-19 12:27:13 +01:00
IceReaper
9fa6c47dc5 Added GoG and Steam SourceResolver. 2022-11-19 12:27:13 +01:00
Andre Mohren
f5b169ab54 Added ExtractMixSourceAction. 2022-11-19 12:27:13 +01:00
IceReaper
09b32f7f98 Fixed the blast decompression. Fixes #13689 2022-11-19 12:27:13 +01:00
Matthias Mailänder
621c85059e Extract Tiberian Dawn campaign translations. 2022-11-17 22:28:07 +02:00
Matthias Mailänder
b62cf7ee9a Add translation arguments to the Lua API. 2022-11-17 22:28:07 +02:00
Matthias Mailänder
5d118e2634 Inline variables. 2022-11-17 22:28:07 +02:00
Matthias Mailänder
807e9b5496 Don't crash Lua when the translation attempt fails. 2022-11-17 22:28:07 +02:00
IceReaper
0c47a0a710 Bugfix: mod.yaml was listing downloads as source too! 2022-11-17 21:19:57 +01:00
IceReaper
35eb246080 Replaced hardcoded SourceType with custom defined ISourceResolver. 2022-11-17 21:19:57 +01:00
IceReaper
7188f88ba1 Replaced hardcoded source actions by user defined ISourceAction. 2022-11-17 21:19:57 +01:00
IceReaper
fcc8f53b59 Installer downloads now using specified IPackageLoader. 2022-11-17 21:19:57 +01:00
Gustas
a9da6bb2d8 Introduce MinDistance to AreaBeam projectile 2022-11-17 20:48:37 +01:00
Gustas
f7286b525c Move AbortOnResupply from AttackAircraft to AttackFollow 2022-11-17 20:42:47 +01:00
Julius Vitkauskas
4517734fbe Dispose DependencyContextJsonReader after using 2022-11-17 20:36:13 +02:00
ThomasChr
bd882c98c7 fix Issue #20373 - Aircraft hanging midair 2022-11-17 20:27:14 +02:00
Thomas Christlieb
ed9880f801 fix misclicks through sidebar 2022-11-16 23:20:50 +01:00
IceReaper
8ae5383698 Made installer asset resolving case insensitive. 2022-11-17 00:16:59 +02:00
IceReaper
98e7058486 ZipFile is now determined by a zip header, not the file extensions, as it may be a temp file. 2022-11-17 00:16:59 +02:00
IceReaper
0aced08204 Support for InstallShield CAB version <= 5. 2022-11-17 00:16:59 +02:00
abcdefg30
e54b88a6cb Remove dead code inside SupportPower.cs 2022-11-16 20:45:33 +02:00
RoosterDragon
b8a71215eb Fix Locomotor IsMoving checks.
The Locomotor IsMoving check was allowing us to consider another actor that moving as not a blocker. However for some reason it also considered the actor trying to path being mobile as sufficient for this check to pass which did not make sense. We remove that extra check and inline the method.

This was a regression from 4a609bbee8 which changed the method from IsMovingInMyDirection (which required the lookup of the mobile trait) to just IsMoving. It should have removed the lookup as not required.

This fixes a crash in HPF which was considered the location as blocked when Locomotor considered it unblocked because the logic was not aligned. Removing this check aligns the logic and resolves the crash.
2022-11-15 15:42:02 +02:00
Vapre
7005da3592 SpriteRenderer, do not copy vertex array data each flush. 2022-11-14 23:33:24 +01:00
Gustas
243e2b2a2a Fix cloned widgets not having default cursor set 2022-11-13 15:05:48 -06:00
RoosterDragon
a85ac26367 Pathing considers reachability of source cells consistently.
Using the local pathfinder, you could not find a path to an unreachable destination cell, but it was possible to find a path from an unreachable source cell if there was a reachable cells adjacent to it.

The hierarchical pathfinder did not have this behaviour and considering an unreachable source cell to block attempts to find a path.

Now, we unify the pathfinders to use a consistent behaviour, allowing paths from unreachable source cells to be found.
2022-11-13 19:59:36 +01:00
abcdefg30
bedfa622d7 Update AUTHORS 2022-11-12 07:57:12 +01:00
Gustas
0b86936dcd Nerf player experience 2022-11-10 23:33:52 +01:00
ThomasChr
6c348620f3 Use Stop button on (production) buildings to reset rally point fixes #20414 2022-11-09 08:44:09 +02:00
Gustas
ad269555d9 Don't let the player interrupt takeoff after a succesful caryall pickup 2022-11-08 21:51:12 +01:00
Gustas
5db07097e8 Fix carryall not removing influence when cancelling land activity 2022-11-08 21:51:12 +01:00
Gustas
b401f601de Refresh ChatDisabledUntil when entering a new server 2022-11-06 22:28:52 +01:00
Gustas
33a1bb8e6b Don't disable chat in replays 2022-11-06 22:28:52 +01:00
Ivaylo Draganov
4ecf4f9f3f Add a .zsync suffix to appimagecheck URL to pass appimaged checks
Depends on changes in the master server that rewrite the appimagecheck URL.
2022-11-06 14:00:25 +01:00
Matthias Mailänder
81020e70fa Fix actors with immobile actors that don't occupy space
to be placed everywhere way outside the map boundaries.
2022-11-05 21:41:39 +01:00
Matthias Mailänder
74c35edbd9 Fix a crash when flood filling tiles outside of the map. 2022-11-04 00:10:46 +01:00
Gustas
64908c8e70 Fix RenameContrailProperties UpdateRule 2022-11-03 23:35:57 +01:00
Matthias Mailänder
889be47b23 Remove dubFinder style sheet. 2022-11-02 21:33:32 +01:00
Vapre
4fc232f2a6 Authors, anvilvapre. 2022-11-02 14:05:53 +02:00
ThomasChr
a3f8b41e25 Add dropdown for sorting maps in map chooser dialog 2022-11-01 12:08:01 +02:00
abcdefg30
c664af4fe2 Seal the Server.Connection class
Solves CA1816
2022-10-31 12:19:34 +01:00
abcdefg30
a00348dac1 Dispose all connections when shutting down a server 2022-10-31 12:19:34 +01:00
abcdefg30
111d9e4230 Fix replay player disconnect handling 2022-10-30 14:55:55 +01:00
Gustas
7cdc98c8fa FIx anti-ground missiles damaging air units and vice versa 2022-10-30 14:40:39 +01:00
dnqbob
583d85cc2e Allow weapons with no damage warheads to be fired 2022-10-30 14:38:16 +01:00
N.N
08de346e31 fix worm spawn in campaing 2022-10-30 14:32:25 +01:00
notsinned
5242716887 Fix for turret following invisible unit
Fixed turret tracking invisible units after being attacked

Fix for turret following invisible unit

Fix for turrent following invisible unit
2022-10-30 14:08:29 +01:00
Gustas
57143087d7 Fix Aircraft not updating influence when changing height
Occupied cells was defined by height yet we didn't update actor map on changing height. This in some scenarios could have caused the aircraft to forget to remove its influence from actor map
2022-10-30 14:01:32 +01:00
darkademic
f612d82797 Use MacOS 11 for release packaging. 2022-10-29 18:30:58 +02:00
Matthias Mailänder
c22b3f30bb Log OpenRA and Operating System language on crash. 2022-10-29 13:36:36 +03:00
Matthias Mailänder
147804ac30 Inline variables. 2022-10-29 13:36:36 +03:00
EoralMilk
949ef1662d Add mouse scroll to ProductionPaletteWidget 2022-10-28 19:33:57 +02:00
Matthias Mailänder
efe65701e4 Expose game speeds to localisation. 2022-10-27 23:30:19 +02:00
penev92
425c678cd9 Fix TD missions file not using package syntax 2022-10-24 11:45:36 +02:00
Matthias Mailänder
71956cb2a2 Documentation split release/playtest into different branches. 2022-10-21 18:54:58 +03:00
Matthias Mailänder
34ccaf6e9d Fix a crash in Fall of Greece 1: Personal War 2022-10-21 10:42:48 -05:00
Matthias Mailänder
5c8a537efd Fix a crash in Allies 06a. 2022-10-21 10:42:48 -05:00
abcdefg30
c100e64c8e Update Tiberium Forest 2022-10-21 10:35:52 -05:00
Matthias Mailänder
8f415bc7af Localize developer debug cheats. 2022-10-21 17:08:16 +02:00
Matthias Mailänder
44aaf4dd07 Localise text notifications. 2022-10-21 17:08:16 +02:00
Gustas
c041ea7d39 Crash on image/panel not found and add TryGet functions for searching 2022-10-20 19:28:02 +02:00
Gustas
e743e6dd61 Simplify boolean expressions in ProductionPaletteWidget 2022-10-20 19:20:45 +02:00
Gustas
af3d6792b8 Fix ProductionPalette ClockAnimation and NotBuildableAnimation being ignored. 2022-10-20 19:20:45 +02:00
Matthias Mailänder
bd138db9e2 Don't use header tags for non-headers. 2022-10-20 11:02:58 +03:00
Orb370
d9a8a0619c TD-Fall-2022-Balance-Commit 2022-10-19 20:23:19 +03:00
Gustas
686f158117 Refactor BindButtonIcon 2022-10-19 18:26:24 +02:00
abcdefg30
3de0b7982e Adjust the ZOffset of pyle to avoid clipping through the flagpole 2022-10-19 12:23:35 +02:00
abcdefg30
3f5e5e43b3 Make the exit cells of the GDI Barracks transient 2022-10-19 12:23:35 +02:00
Matthias Mailänder
5c3d4a7fe4 Return an error message for invalid cash amounts. 2022-10-18 23:16:20 +02:00
Gustas
347148e02f Allow cash ticking sound to overlap 2022-10-18 22:36:44 +02:00
abcdefg30
fde4f8d0e5 Throw an exception when map.yaml cannot be read 2022-10-18 22:27:51 +02:00
abcdefg30
95c0846ced Remove the filename from the undefined MapFormat exception 2022-10-18 22:27:51 +02:00
abcdefg30
40e207200a Fix a typo in the undefined MapFormat exception message 2022-10-18 22:27:51 +02:00
Matthias Mailänder
acc2c11e69 Fix SupportPowers ClockAnimation being ignored. 2022-10-17 14:20:11 +03:00
abcdefg30
df484633f7 Update the TD map pool 2022-10-14 21:22:42 -05:00
abcdefg30
9498f067bc Remove crate crushing from Visceroids 2022-10-14 20:56:47 -05:00
abcdefg30
625dca6435 Fix dinos not being able to crush crates 2022-10-14 20:56:47 -05:00
Gustas
3181102415 Fix default expressions in commit 75f642bd09 2022-10-14 14:15:57 +03:00
abcdefg30
6e6c828c85 Fix SquadManagerBotModule caching in RenderDebugState 2022-10-14 13:50:57 +03:00
abcdefg30
d8349a429a Remove unnecessary uses of Exts.IsTraitEnabled 2022-10-14 13:50:57 +03:00
abcdefg30
75f642bd09 Introduce FirstEnabledConditionalTraitOrDefault 2022-10-14 13:50:57 +03:00
Gustas
858d782af1 Simplify 'default' expression (IDE0034) 2022-10-11 17:40:05 +02:00
abcdefg30
02d9ba020d Add a workaround for unbinding script members on Mono 2022-10-10 20:27:07 +03:00
Unrud
a2a34dafde MacOS buildpackage: Fix deduplication
The variable `${MOD}` was not enclosed in quotes, but the value contained a space. This caused the argument to be split into two parts.
Using **find** isn't necessary due to globbing.
2022-10-09 00:39:28 +02:00
Gustas
ab26878033 Fix MapCache mapUpdates 2022-10-08 23:08:53 +02:00
Gustas
7f677f1842 Allow driving under crates and crushable units 2022-10-07 21:06:04 +02:00
Gustas
5abbdc37cb Revert "Fix crushables and crates causing HPF to crash."
This reverts commit 5765e51c56.
2022-10-07 21:06:04 +02:00
Gustas
625dc1dd35 Fix subterranean units teleporting to surface and jump jets to ground 2022-10-06 23:03:33 +02:00
Matthias Mailänder
ee0d37f2b1 Add back the man page install 2022-10-05 22:01:04 +02:00
Matthias Mailänder
38a22ebd55 These are not Linux exclusive. 2022-10-05 22:01:04 +02:00
Gustas
043e6f662f Make demolition conditional 2022-10-04 21:55:17 +02:00
Matthias Mailänder
eb897d755e Add observer vision stats. 2022-10-03 20:48:18 +02:00
Ivaylo Draganov
e7dcbb3c2d Improve translation of power/silo usage tooltip
- Fix an instance where "silo-usage" translation was used without
arguments
- Use the same translation reference for the "Power usage"
- Make the ResourceBarWidget accept a cached transform with the tooltip
text
so it won't have to build the string itself
- Display an infinity symbol when the infinite power cheat is used
- Removes a magic number that is no longer used (>1000000 to check for
unlimited power)
2022-10-03 12:00:42 +02:00
Gustas
ba763ac0f0 Add highlighted state to GetCachedStatefulImage 2022-10-01 16:52:04 +03:00
abcdefg30
a75818026a Use SDL.SDL_OpenURL instead of Process.Start to open FAQ and Logs 2022-10-01 14:19:30 +03:00
Matthias Mailänder
6bd631618c Remove unnecessary value assignment (IDE0059) 2022-10-01 14:15:33 +03:00
abcdefg30
757c4d84c7 Change the incompatible replay dialogue text from "Cancel" to "OK" 2022-09-29 23:34:59 +03:00
abcdefg30
b2498fec7d Fix a crash when version or mod of a replay are null 2022-09-29 23:34:59 +03:00
abcdefg30
0d6a7b3c52 Add line breaks to the incompatible replay translations 2022-09-29 23:34:59 +03:00
abcdefg30
a691112c54 Update Linguini to 0.3.1 2022-09-29 23:34:59 +03:00
RoosterDragon
3c66ca709a Fix some bugs in LongBitSet
- Use LongBitSetAllocator and not BitSetAllocator. Using the wrong allocator means all string based checks and displays would provide incorrect results.
- Remove LongBitSetAllocator.Mask which wasn't being calculated or Reset correctly. We can use world.AllPlayersMask to provide the same effect at use sites.
2022-09-29 21:58:17 +02:00
Matthias Mailänder
0080e98390 Fix No Players No Bots No Spectators label overlapping. 2022-09-26 22:47:33 +03:00
tomas
7b7ccf4128 Fix crash in OrderBuffer 2022-09-26 22:44:13 +03:00
Gustas
899298442a Rename the remaining properties in ScrollItemWidget
BaseName: to BackGround:
2022-09-26 13:33:21 +02:00
Matthias Mailänder
f6d13baf4b Use inline strings. 2022-09-26 11:05:15 +03:00
Matthias Mailänder
90a2b677f1 Exit with error code on invalid commands and arguments. 2022-09-26 11:05:15 +03:00
Gustas
fe72dd4140 Remove a ScrollItem header hack
with the merge of #20218 headers no longer need to be selected
2022-09-24 16:23:59 +02:00
Gustas
6b63114aaa Rename ScrollWidget BaseName to Background
To match button
2022-09-24 16:23:59 +02:00
Gustas
5e6f14c9ee Polish TD ScrollItemWidget 2022-09-24 16:23:59 +02:00
RoosterDragon
5765e51c56 Fix crushables and crates causing HPF to crash.
When crushables and crates change their Location/TopLeft, their crushability is cached, but when their CenterPosition is changed, their cached crushability is not refreshed. Since their CrushableBy functions depends on IsAtGroundLevel, which depends on the CenterPosition, this means that when the crushability is cached it will depend on the current height of the object. If the height of the object changes, the cache is not refreshed and now contains out of date information.

The Locomotor cache and the HPF both cache this same information, but at different times. HPF caches immediately, but Locomotor caches on demand which means there can be a delay. This means they can have inconsistent, differing views of the crushability information. This eventually surfaces in a "The abstract path should never be searched for an unreachable point." error from HPF when it detects the inconsistency.

The bug is that Locomotor was caching information without refreshing it when required. Fixing this to refresh the cache when the CenterPosition changes is likely to have negative performance impacts. As would removing crushability from the cache. These would both be fixes that address the underlying bug.

The high impacts of a proper fix lead us to a workaround instead. If we set the CenterPosition before setting the Location, then when the Location is set and the caches are refreshed, the new CenterPosition is available when caching the crushability information. This means logic depending on IsAtGroundLevel will get the new information and cache a more up-to-date view of things. This means when changing both the CenterPosition and Location together we now cache correct information. However calls that set only the CenterPosition and not the Location can still result in a bad cache state. Although this is imperfect it is an improvement over current affairs, and has less impact.
2022-09-24 15:15:53 +02:00
Gustas
1809817b3f Add ContrailEndColor and Contrail transparency control 2022-09-24 13:19:22 +02:00
Gustas
d8f45714a7 Add contrail property descriptions to Bullet and Missile 2022-09-24 13:19:22 +02:00
Gustas
56b665f243 Add TrailDelay to Contrail 2022-09-24 13:19:22 +02:00
Gustas
c781eb0cab Add pressed state to RA and TS ScrollItemWidget 2022-09-22 17:11:33 +02:00
abcdefg30
28adb915f5 Make CheckPalettes report duplicate palettes 2022-09-21 20:06:11 +02:00
abcdefg30
97c96c46f4 Only search for palette definitions on the world actor 2022-09-21 20:06:11 +02:00
abcdefg30
0bbcff973f Only query the palette attribute once in CheckPalettes 2022-09-21 20:06:11 +02:00
N.N
ae7fc11472 Minor adjustment into the D2k
Minor adjustment into the D2k

- Add rally point into the Palace
- removed harvester MustBeDestroyed in campaing
- Players can see they carryalls and ornothopers under the fog.
- Increased CameraRemoveDelay on Superweapons so player can see superweapon impact.
2022-09-21 18:06:18 +02:00
RoosterDragon
70c2ec15d3 Change spaces to tabs.
To follow code style.
2022-09-19 23:36:30 +02:00
Unrud
3f106bef72 Scripts: Check exit status of background process 2022-09-18 10:30:58 +02:00
Unrud
5b085b6c15 Scripts: Remove obsolete comment 2022-09-18 10:30:58 +02:00
Unrud
c29f1590c9 Scripts: Remove exit stmts covered by errexit 2022-09-18 10:30:58 +02:00
Unrud
e2fd7ce7ed Scripts: Remove some subshells that ignore errors 2022-09-18 10:30:58 +02:00
Unrud
1b9a86c0a0 Sciprts: Set pipefail or remove pipes
Errors in pipes are ignored otherwise
2022-09-18 10:30:58 +02:00
Unrud
946cd8f322 Scripts: Set errexit to catch errors 2022-09-18 10:30:58 +02:00
Unrud
7cd0d8c079 Scripts: Prevent paths interpreted as args 2022-09-18 10:30:58 +02:00
Unrud
ea02b90636 Scripts: Quote path variables 2022-09-18 10:30:58 +02:00
Gustas
bb2ee37cc0 Add pressed state to D2K ScrollItemWidget 2022-09-18 10:07:46 +02:00
Shrooblord
70771da45a don't notify players a Harvester is under attack when it's actually healing
Co-authored-by: Gustas <37534529+PunkPun@users.noreply.github.com>
2022-09-17 18:57:59 +02:00
penev92
49ac9079a2 Renamed SpriteSequence documentation command and output file 2022-09-17 15:15:42 +02:00
penev92
04afa4a72c Added some sorting to documentation output 2022-09-17 15:15:42 +02:00
penev92
6b98a75658 Added referencing types to enum definitions 2022-09-17 15:15:42 +02:00
penev92
07b9c941b4 Added documentation page title 2022-09-17 15:15:42 +02:00
penev92
eda3dfa50f Added enum export to documentation generation 2022-09-17 15:15:42 +02:00
Ivaylo Draganov
a985452907 Add myself to AUTHORS 2022-09-16 09:46:20 +01:00
Zachary Schirm
1339faa5f4 Disallowed Save Map without Title and Author Input 2022-09-13 18:48:21 +03:00
Gustas
d95c6e821c Use CachedTransform in ImageWidget 2022-09-13 16:11:18 +02:00
Matthias Mailänder
1536530f78 Add a fake .zsync suffix parameter to pass linting. 2022-09-13 13:20:05 +03:00
Matthias Mailänder
d3a8b07f05 Remove unread private member (IDE0052) 2022-09-13 10:36:57 +03:00
Gustas
43e0cca663 Prevent game from starting with unavailable map 2022-09-12 20:13:42 +02:00
Vapre
7f404f64a6 Selection, cache DeveloperMode trait. 2022-09-11 22:00:18 +03:00
Matthias Mailänder
3be0e9e8a5 Add an in-game encyclopedia to Dune 2000. 2022-09-11 20:19:58 +03:00
RoosterDragon
d2a3659078 Fix landed aircraft above ground level not removing influence on take off.
When the Land activity is run, the aircraft adds influence to the cell so it cannot be used by other actors. When the TakeOff activity runs, it removes the influence so the cell can be used by other actors.

However, when a Carryall picks up a unit, it is told to Land with a vertical offset - it never reaches ground level. When the TakeOff activity runs, it saw the aircraft was above ground level and bailed out. The means the influence is never removed. The cell is now unusable despite the fact the Carryall has left.

To fix this, TakeOff now checks if influence was applied instead of checking if the aircraft is above ground level. If so, we know the Land activity had decided that influence was required, even if the aircraft has not made it to ground level. When TakeOff runs, it will treat it as a proper take off event even though the aircraft is already above ground level. This means influence will be removed and the cell will become accessible as intended.

In ActorMap, we also fix a design flaw where disposed actors where excluded from queries. This caused cache inconsistencies with clients using ActorMap.CellUpdated event to rely on updates. This event will not get called when the actor was disposed, so the downsteam client may have cached the actors at that location, only for them to "change" when the actor is later disposed. This could cause the Locomotor and HierarchicalPathFInder to have inconsistent views of the actors on the map, causing crashes if the inconsistent state broken some internal invariants. The only reason to exclude disposed actors would be to cover up for the actors not being removed properly from the map, which is fixed now aircraft are handled correctly. If ever an actor isn't removed from the actor map, then the caller needs fixing rather than having the actor map exclude it.
2022-09-11 20:04:12 +03:00
Matthias Mailänder
b8e261ff2f Code cleanup in the Tiberian Dawn Lua scripts.
Uppercase global variables.
Declare local variables.
Remove unused variable.
2022-09-11 19:44:38 +03:00
Matthias Mailänder
ca45e02265 Fix untranslated strings in skirmish objectives. 2022-09-10 22:58:28 +03:00
Ivaylo Draganov
3453d13188 Adjust faction label width in lobby to fit better when truncated 2022-09-10 18:26:40 +02:00
Vapre
e3aa2dc6c0 HitShape, query trait via actor cached targetable positions. 2022-09-10 18:23:12 +02:00
abcdefg30
f88b6d78ff Move update rules from 20201213 to 20210321 2022-09-10 17:52:19 +02:00
Vapre
7754e486ee GpsDotEffect, ShouldRender, optimization. 2022-09-10 17:49:58 +02:00
RoosterDragon
7e67889294 Fix a crash when trying to pathfind from unusable custom movement layers.
If a path search is attempted from a location outside the map, then PathSearch will filter these out to prevent any crashes. The path search will result in no path. However if the location is within the map but on a custom movement layer that the locomotor cannot use, this currently crashes. To fix this we apply a similar filtering logic to ignore any source locations that cannot be used, and so the path search will result in no path for these as well.
2022-09-10 15:24:25 +02:00
penev92
c52913716c Added SpriteSequence documentation generation 2022-09-09 21:18:58 +02:00
penev92
dc8c0221e7 Fixed documentation Python script handling of null
The script would serialize null values as "None" in the Markdown files it produces, which is not great. It's better to leave empty strings.
2022-09-09 21:18:58 +02:00
penev92
99b27bbe7b Added missing file extensions to asset browser
These are all file extensions used by the respective mods ingame, so they should be visible in the asset browser as well.
2022-09-09 16:57:22 +03:00
penev92
60b85c933e Reorder asset browser UI's asset list and filters 2022-09-09 16:57:22 +03:00
penev92
8a38ac0d24 Added an asset type filter to AssetBrowserLogic 2022-09-09 16:57:22 +03:00
Gustas
4901de24b1 Select LastModifiedMap when entering ServerCreation 2022-09-09 14:31:25 +03:00
Gustas
81561778a2 Select LastModifiedMap when entering MissionBrowser
And add auto-updating to the play button
2022-09-09 14:31:25 +03:00
Gustas
63b76d1b53 Select LastModifiedMap when entering LobbyLogic 2022-09-09 14:31:25 +03:00
Gustas
02b6a260af Make IngameMenuLogic use ShowMenu 2022-09-09 14:31:25 +03:00
Zachary Schirm
9a5d352e41 Disallow saving the game with an empty name field 2022-09-09 11:39:31 +03:00
Zachary Schirm
125a7b8c88 Disallow Join button without IP address input
Disallow join button without IP address Input

Closes #20234

Trying to join a server with an empty address crashes the game. This fix disallows pressing join button without ip address field input.  Updated to reuse search for joinButton vs 2 separate calls to search.
2022-09-09 11:22:52 +03:00
Gustas
ee0d958cd1 Fixed incorect fluent name in MissionBrowserLogic 2022-09-08 23:45:11 +02:00
Gustas
9ae27b8e60 Fix team 0 being translated in SpawnSelectorTooltipLogic 2022-09-08 23:45:11 +02:00
Gustas
1b00cef30f Fix a few tooltip translations being called every frame
in WorldTooltipLogic and IngamePowerBarLogic
2022-09-08 23:45:11 +02:00
Ivaylo Draganov
56a9acd035 Extract a couple of untranslated strings 2022-09-08 23:45:11 +02:00
dnqbob
6ccd000257 Make building and bridge repair traits public 2022-09-07 14:04:23 +02:00
Matthias Mailänder
fb5624880b Fix sorting. 2022-09-06 20:52:04 +03:00
Matthias Mailänder
67aa0cdede Avoid Count() when Any() could be used (CA1827) 2022-09-06 20:52:04 +03:00
Matthias Mailänder
7ba6a49378 Avoid using LINQ on indexable collections (CA1826) 2022-09-06 18:02:39 +02:00
Matthias Mailänder
68ca09e896 Fixed "bots" not getting translated in Lobby dropdowns 2022-09-05 22:35:50 +02:00
reaperrr
145f6abc09 Add option to limit render fps to game tick rate
This helps slow systems that struggle to render 2 frames per game tick.
2022-09-04 17:00:37 +03:00
penev92
216029dc27 Fix a crash with BlockingCollection in Connection
The BlockingCollection would have `IsAddingCompleted` to true, but `IsComplete` to false, slipping through the cracks and causing an InvalidOperationException ("The collection has been marked as complete with regards to additions.") when trying to add to it.
We now add a check on `(Try)SendData` to only try to add if we can. The collection is still viable for reading until empty/`IsComplete`.
2022-09-04 13:04:17 +03:00
RoosterDragon
2a681d3791 Fix HierarchicalPathFinder considering some unreachable cells as reachable.
When using the internal AbstractCellForLocalCell method to check if a local cell is reachable, this should return null when the cell is unreachable. If multiple abstract cells were required for that grid, this worked as intended. Only reachable cells are stored in the localCellToAbstractCell mapping. For a grid that required only a single abstract cell, which is the common case, we optimize this to store only the single abstract cell rather than the whole mapping for potentially 100 cells in that grid. However this makes no distinction between the reachable and unreachable cells, so when we check later we get incorrect results. If a cell is unreachable but belongs to the same grid as a single group of reachable cells then we incorrectly report it as reachable. The easiest way to see this incorrect behaviour is when the PathExists is called and can sometimes indicate a path exists when it does not.

To fix this, we now ensure we perform a check to see if the cell is reachable in this single layer case, this allows us to retain the optimization where we don't need to store the whole mapping, but allows us to correctly indicate when cells are unreachable.
2022-09-03 19:28:47 +02:00
Vapre
63499c6334 ShroudRenderer, fix, no shroud or fog in editor. 2022-09-03 19:20:25 +02:00
Vapre
57ce88cc9a ShroudRenderer, fix, render Shroud if fog disabled. 2022-09-02 20:13:56 -05:00
Matthias Mailänder
e2284f660c Add documentation to TeslaZap. 2022-09-02 19:58:48 -05:00
Matthias Mailänder
3513d37702 Fix a line break. 2022-09-02 19:58:48 -05:00
penev92
737cdd7851 Reworked trait documentation generation
Switched the Utility's ExtractTraitDocsCommand output to JSON.
Updated documentation generation to use that and the new Python script to generate the Markdown file, same as the Weapon documentation.
2022-09-02 20:56:55 +02:00
penev92
a522457bb6 Reworked weapon documentation generation
Switched the Utility's ExtractWeaponDocsCommand output to JSON.
Added a Python script to generate documentation Markdown from JSON.
2022-09-02 20:56:55 +02:00
penev92
c21bf31ebc Fixed weapon docs not including WeaponInfo
Also made some code cleanups in the weapon docs export code.
2022-09-02 20:56:55 +02:00
penev92
a73d710bec Added Util.InternalTypeName() 2022-09-02 20:56:55 +02:00
penev92
9ed2e699c6 Fix tiny error in output string 2022-09-02 20:56:55 +02:00
penev92
2f0f5f4cda Fixed ScrollPanelWidget not resetting on Clear 2022-09-02 16:16:45 +02:00
Gustas
8402d7d476 Improved Widget.RemoveChildren performance
Modifying the list potentially several thousand times is really slow, so notify the child elements that they are being removed and then clear the list in one go.
2022-09-02 16:16:45 +02:00
penev92
378c447ded Misc fixes in AssetBrowserLogic and MapOverlaysLogic 2022-09-02 16:16:45 +02:00
penev92
df836620dc Added missing spacing to map editor dropdowns 2022-09-02 16:16:45 +02:00
Ivaylo Draganov
c1e1765c2f Move game speed lobby dropdown before time limit dropdown
Game speed is the more frequently changed option and with the current
layout it was buried below the fold.
2022-09-02 16:09:05 +02:00
Matthias Mailänder
0b67b5bfae Extract translation strings. 2022-09-02 14:41:24 +03:00
Gustas
dfd5a960ed Fix RepairableBuilding never stopping repair 2022-09-01 18:30:47 +03:00
RoosterDragon
2d45e67bca Teach HierarchicalPathFinder about Immovable actors.
By tracking updates on the ActorMap the HierarchicalPathFinder can be aware of actors moving around the map. We track a subset of immovable actors that always block. These actors can be treated as impassable obstacles just like terrain. When a path needs to be found the abstract path will guide the search around this subset of immovable actors just like it can guide the search around impassable terrain. For path searches that were previously imperformant because some immovable actors created a bottleneck that needed to be routed around, these will now be performant instead. Path searches with bottlenecks created by items such as trees, walls and buildings should see a performance improvement. Bottlenecks created by other units will not benefit.

We now maintain two sets of HPFs. One is aware of immovable actors and will be used for path searches that request BlockedByActor.Immovable, BlockedByActor.Stationary and BlockedByActor.All to guide that around the immovable obstacles. The other is aware of terrain only and will be used for searches that request BlockedByActor.None, or if an ignoreActor is provided. A new UI dropdown when using the `/hpf` command will allow switching between the visuals of the two sets.
2022-08-31 23:12:42 +02:00
RoosterDragon
7e7d94ca89 Fix Locomotor CellCache to not consider transit only cells as crushable.
When the UpdateCellBlocking encountered a transit-only cell (the bibs around a building) it would bail from the loop. This would leave the cellCrushablePlayers set to all players. It would update the cell cache and mark that cell as a crushable location.

When CanMoveFreelyInto would later evaluate a cell, it would consider it passable because the crushable check would pass (cellCache.Crushable.Overlaps(actor.Owner.PlayerMask)) rather than because the transit check (otherActor.OccupiesSpace is Building building && building.TransitOnlyCells().Contains(cell)) would pass.

Although this meant the cell was treated as passable in either scenario, it means the cache contained incorrect data. The cell does not contain any crushable actors but the cache would indicate it did. Correcting this means we can rely on the crushability information stored in the cache to be accurate.
2022-08-31 23:12:42 +02:00
RoosterDragon
77779023d5 Notify of shroud state changes when using DevVisibility cheat.
When this cheat is used by notifying of shroud changes we invoke the usual logic that would occur if the visibility had been granted by units. Without this change any cached information about the visibility is not refreshed. Without this refresh actors with different visibility may not act correctly.

One aspect this improves is frozen actors. Using the visibility cheat will show up all actors on the map. If the cheat is then disabled than frozen actors will appear in their place. Prior to this change a frozen actor would fail to appear if the cheat had caused it to be revealed. Healthbars and selection boxes are also made consistent for similar reasons.
2022-08-31 23:31:48 +03:00
RoosterDragon
1fc1bdc849 Fix frozen actors lacking tooltips if they have the cloak ability.
Since bbf5970bc1 we update frozen actors only when required.

In 8339c6843e a regression was fixed where actors created in line of sight would be invisible.

Here, we fix a related regression where cloaked units that are revealed, and then frozen when you move out of line of sight would lack tooltips.

The fix centers around the setting of the Hidden flag. In the old code this used CanBeViewedByPlayer which checks for visibility modifiers and then uses the default visibility. The bug with this code is that when a visibility modifier was not hiding the actor, then we would report the default visibility state instead. However the frozen visibility state applies here which means if the frozen actor is visible, then we consider the actor to be hidden and therefore tooltips will not appear. In the fixed version we only consider the modifiers. This means a visibility modifier such as Cloak can hide the frozen actor tooltips. But otherwise we do not consider the frozen actor to be hidden. This prevents a frozen actor from hiding its own tooltips in some unintended circular logic. Hidden now becomes just a flag to indicate if the visibility modifiers are overriding things, as intended.
2022-08-31 23:31:48 +03:00
Mustafa Alperen Seki
16babc1975 Fix Fog color on Radar. 2022-08-31 16:04:40 +03:00
Vapre
215898c7ec ScriptActorInterfaces, unbind on actor destroy. 2022-08-30 21:44:39 +02:00
Gustas
fc1d8d2355 Update RA maps to format 12 2022-08-30 20:03:40 +02:00
Gustas
f98a74f70d Update TD maps to format 12 2022-08-30 20:03:40 +02:00
Gustas
89bb800dbf Update D2K maps to format 12 2022-08-30 20:03:40 +02:00
Gustas
09cb38bc6e Update TS maps to format 12 2022-08-30 20:03:40 +02:00
Gustas
c40675cfba Include map.png into uid generation 2022-08-30 20:03:40 +02:00
RoosterDragon
bcf4ff3b7c Prevent radar crash when dealing with map height. 2022-08-30 19:58:48 +02:00
Gustas
539bb09d50 Fix shroud selector dropdown 2022-08-29 21:13:27 +03:00
Gustas
51c09ddae9 Fix Mission Group having all headers selected 2022-08-29 13:45:38 +03:00
Gustas
08dbfe0cbd Refactor ScrollItemWidget to use stateful image names 2022-08-29 13:45:38 +03:00
Gustas
11a2e6e19b Add more confirmation dialogue to the map editor
When saving on top of another map, or when saving on a map that has been edited outside the map editor
2022-08-29 12:31:01 +03:00
Gustas
d3589c051d Add descriptions to all projectiles and warheads 2022-08-29 12:26:33 +03:00
Vapre
6e547469d6 Shroud, combine IsVisible and IsExplored into a single function. 2022-08-28 18:50:51 +02:00
Ivaylo Draganov
cc1f10dd35 Truncate faction name in lobby dropdown button 2022-08-25 10:44:17 +03:00
dnqbob
29fc2b80d9 WithMuzzleOverlay is decoration 2022-08-21 23:59:15 +02:00
Gustas
ce254f8b46 Add per player mutes 2022-08-20 14:52:49 +02:00
Gustas
81da717f19 Add chat on/off icon to glyphs 2022-08-20 14:52:49 +02:00
Gustas
16198c121c Increase the size of ingame-info panel
And add an Actions section for Kick
2022-08-20 14:52:49 +02:00
Gustas
dde10249d5 Fixup faction and score info UI
And increase spectator name length to match regular players
2022-08-20 14:52:49 +02:00
Gustas
58fcffa429 Add anti-flood protection 2022-08-20 10:24:25 +02:00
tomas
ac623d784a Remove Do() and replace with foreach() 2022-08-19 22:38:38 +02:00
tomas
92478a219e Fix crash when selecting the same map 2022-08-18 00:03:50 +02:00
Gustas
3ab6d3f00a Remove map editor debug message 2022-08-17 23:29:11 +02:00
RoosterDragon
32aaac1dc2 HierarchicalPathFinder.PathExists checks the locations are in map bounds.
Without this, passing locations outside the map could cause a crash instead of reporting no path.
2022-08-17 10:13:27 +03:00
Gustas
ae3a1c2561 Add truncation to FactionLabel 2022-08-16 18:58:45 +03:00
Mustafa Alperen Seki
1b1868fca6 Render every available ProductionIconOverlay. 2022-08-16 15:08:51 +03:00
Mustafa Alperen Seki
54340591e3 Unhardcode VeteranProductionIconOverlay. 2022-08-16 15:08:51 +03:00
Gustas
d438508994 Added try/catch for TypeDictionary errors in Lint code
TypeDictionary errors are very hard for modders to debug as they don't mention which actor is causing the error
2022-08-15 23:19:18 +02:00
Matthias Mailänder
cc58fe1a0f Extract translation strings. 2022-08-14 16:11:51 +02:00
Matthias Mailänder
8201a57b10 Don't complain about re-usable terms not getting referenced. 2022-08-14 16:11:51 +02:00
Matthias Mailänder
2c8c6e50da Code cleanup 2022-08-14 16:11:51 +02:00
Gustas
10ac07bf9f Fix ChooseInitialMap 2022-08-13 17:14:21 +02:00
RoosterDragon
8339c6843e Fix actors not being visible when created within sight range of an enemy.
Since bbf5970bc1 we only update frozen actor state on demand rather than every tick. However when the actor was initially created we were failing to set the initial visibility state if the frozen actor was invisible.

With this fix, we now set the visibility states on creation correctly. This fixes an issue where enemy actors created within line of sight would not appear.
2022-08-13 12:05:03 +03:00
RoosterDragon
2599cb26d8 Allow custom cost to exclude source locations in path searches.
During the refactoring to introduce HierarchicalPathFinder, custom costs were no longer applied to source locations. The logic being that if we are already in the source location, then there should be no cost added to it to get there - we are already there!

Path searches support the ability to go from multiple sources to a single target, but the reverse is not supported. Some callers that require a search from a single source to one of multiple targets perform their search in reverse, swapping the source and targets so they can run the search, then reversing the path they are given so things are the correct way around again. For callers that also use a custom cost like the harvester code that do this in order to find free refineries, they might want the custom cost to be applied to the source location. The harvester code uses this to filter out overloaded refineries. It wants to search from a harvester to multiple refineries, and thus reverses this in order to perform the path search. Without the custom cost being applied to the "source" locations, this filtering logic never gets applied.

To fix this, we now apply the custom cost to source locations. If the custom cost provides an invalid path, then the source location is excluded entirely. Although this seems unintuitive on its own, this allows searches done "in reverse" to work again.
2022-08-13 11:58:45 +03:00
RoosterDragon
df858e06d6 Fix HierarchicalPathFinder failing to consider multiple source locations.
When asked to find a path from multiple source locations, the abstract search is used to determine which source locations are viable. Source locations that cannot be reached on the abstract graph are excluded from the local path search. As we know the locations are unreachable, this prevents the local path search from expanding over the entire search space in an attempt to find these unreachable locations, preventing wasted effort.

In order to determine these reachable locations, the abstract search is expanded successively trying to reach each source location each time. However, this failed to account for a property of the ExpandToTarget for which a comment is now added. If the location was found previously, then expanding to try and find it again will fail. If the source locations were close together, it was likely that the initial expansions of the search space would have included them, and thus they would not be found on a later expansion. This would mean these locations would incorrectly be thought unreachable.

To fix this, we check if the location has already been explored (has CellStatus.Closed in the graph). If so we can check the cost to determine if it is reachable.
2022-08-13 11:58:45 +03:00
abcdefg30
f49536ea12 Use Attribute.IsDefined over GetCustomAttributes 2022-08-12 20:14:54 +02:00
Matthias Mailänder
aa14c9c570 Add VTOL landing exhaust animation. 2022-08-12 00:54:44 +03:00
Matthias Mailänder
1073a7124f Remove an unused field from TakeOff.cs 2022-08-12 00:54:44 +03:00
Gustas
542c5dcfc3 Cleanup directional cursor yaml 2022-08-08 23:28:51 +02:00
Gustas
88e2314776 Add directional support powers to D2K ornithopters 2022-08-08 23:28:51 +02:00
Gustas
fd9758dcbf Make game timer only blink on pause 2022-08-08 10:47:05 +02:00
RoosterDragon
bbf5970bc1 Update frozen actors only when required.
Previously, actors that were visible would refresh their frozen actor state every tick in preparation for the actor becoming hidden, and the frozen actor appearing as a placeholder instead.

By using ICreatesFrozenActors.OnVisibilityChanged when can avoid refreshing the state constantly, and instead just refresh it the moment the frozen actor needs to appear. This provides a nice performance improvement on the cost on managing frozen actors.
2022-08-07 16:50:53 +02:00
darkademic
e827e9952e Additional performance graph colors so same color is not used multiple times. 2022-08-06 22:33:27 +02:00
abcdefg30
dc6be0fd77 Change the IRC link from Freenode to Libera in the issue template config 2022-08-06 16:12:25 +02:00
abcdefg30
47b6f564e3 Add a link to Discord to the PR template and change Freenode to Libera 2022-08-06 16:12:25 +02:00
abcdefg30
d830bca706 Fix force fire opportunity targets not being persisted properly 2022-08-06 15:38:46 +02:00
abcdefg30
5f86f56bed Reduce code duplication in AttackFollow 2022-08-06 15:38:46 +02:00
abcdefg30
0134f63f4d Fix actors with AttackFollow moving away from their targets on amove 2022-08-06 15:38:46 +02:00
Unrud
b88ebd8499 Make Red Alert SVG artwork square
Fix typo in height (128 instead of 138).
2022-08-04 20:02:44 +02:00
RoosterDragon
93998dc4a7 Add a PathFinderOverlay to visualize path searches.
Activated with the '/path-debug' chat command, this displays the explored search space and costs when searching for paths. It supports custom movement layers, bi-directional searches as well as visualizing searches over the abstract graph of the HierarchicalPathFinder. The most recent search among selected units is shown.
2022-08-03 23:12:42 +02:00
RoosterDragon
aef65d353d Replace DomainIndex internals with a lookup from HierarchicalPathFinder instead
Teach HierarchicalPathFinder to keep a cache of domain indices, refreshing them only on demand and when invalidated by terrain changes. This provides an accurate and quick determination for checking if paths exist between given locations.

By exposing PathExistsForLocomotor on the IPathFinder interface, we can remove the DomainIndex trait entirely.
2022-08-03 23:12:42 +02:00
RoosterDragon
5a8f91aa21 Add a hierarchical path finder to improve pathfinding performance.
Replaces the existing bi-directional search between points used by the pathfinder with a guided hierarchical search. The old search was a standard A* search with a heuristic of advancing in straight line towards the target. This heuristic performs well if a mostly direct path to the target exists, it performs poorly it the path has to navigate around blockages in the terrain. The hierarchical path finder maintains a simplified, abstract graph. When a path search is performed it uses this abstract graph to inform the heuristic. Instead of moving blindly towards the target, it will instead steer around major obstacles, almost as if it had been provided a map which ensures it can move in roughly the right direction. This allows it to explore less of the area overall, improving performance.

When a path needs to steer around terrain on the map, the hierarchical path finder is able to greatly improve on the previous performance. When a path is able to proceed in a straight line, no performance benefit will be seen. If the path needs to steer around actors on the map instead of terrain (e.g. trees, buildings, units) then the same poor pathfinding performance as before will be observed.
2022-08-03 23:12:42 +02:00
abcdefg30
cea9ceb72e Support multiple With(Turret)AimAnimation traits 2022-08-03 21:11:03 +02:00
abcdefg30
f2eb42a4b2 Make With(Turret)AimAnimation support multiple AttackBase traits 2022-08-03 21:11:03 +02:00
abcdefg30
ee3c54b572 Disallow starting a game without players 2022-08-03 21:03:46 +02:00
abcdefg30
ea72c50fb4 Fix GrantConditionOnPowerState not being usable on player actors 2022-08-03 20:58:21 +02:00
abcdefg30
1628ce64db Fix lobby error messages from the server being untranslated 2022-08-03 20:54:13 +02:00
dnqbob
8d3ff9d2fc UnhardcodeBaseBuilderBotModule rule update 2022-08-03 11:22:59 +02:00
dnqbob
013ec52108 Unhardcode defenses in BaseBuilderBotModule 2022-08-03 11:22:59 +02:00
Vapre
e8748200f7 Demolishable, trivial optimization. 2022-08-02 00:29:59 +03:00
Vapre
3f3687f71d Widget, avoid copying child list when reverse iterating. 2022-07-31 22:10:49 +02:00
N.N
a9d1b771a0 Align ORA d2k to original D2k
Align ORA D2k weapons to better match original d2k.
2022-07-28 23:42:00 +02:00
RoosterDragon
8dec998d8f Fix tab completion to work for all available commands.
Commands are registered in WorldLoaded event handlers, and IngameChatLogic takes all registered commands and provides tab completion. However IngameChatLogic is also created during WorldLoaded via LoadWidgetAtGameStart. No initialization order is enforced between commands and LoadWidgetAtGameStart, so they can appear in any order.

If a command gets registered before LoadWidgetAtGameStart runs, then it will get tab completion. If it gets registered after then no tab completion is available, even though the command can still be used and appears when using '/help'.

To fix this, we allow the tab completion to check for available commands lazily, meaning it will check for available commands every time the tab key is pressed. This means it will always have the full list of commands available regardless of the initialization order.
2022-07-26 16:42:18 +02:00
teinarss
999af0c05b Add OrderBuffer and time synchronisation. 2022-07-26 15:09:08 +03:00
abcdefg30
4435bdec3c Fix a crash when there is no briefing text 2022-07-25 20:19:36 -05:00
Gustas
e00887e4e1 Make support power fakes infiltratable 2022-07-25 20:07:02 -05:00
N.N
75554123f6 make ornithopter targetable
make ornithopter targetable by anti-air units

update to use just one Targetable type
2022-07-24 21:17:28 +02:00
Gustas
bf5bd63635 Refactor checkbox 2022-07-23 18:32:43 +02:00
Vapre
804bff1b0e TooltipWidgetContainer, load tooltip ui only when visible. 2022-07-18 17:54:57 +02:00
Andre Mohren
0e5f33ef93 PlayerColorPalette now using the full palette if no RemapIndex is set. 2022-07-18 12:23:39 +03:00
Andre Mohren
df72d303b8 Added PaletteFromGrayscale. 2022-07-18 12:23:39 +03:00
Gustas
b597c000d6 Update LobbyLogic to use the new MapCache map tracking 2022-07-17 22:23:13 +02:00
Gustas
6bcf194874 Add map update tracking to MapCache and fix crash when restarting a game 2022-07-17 22:23:13 +02:00
AspectInteractive2
a1a50d6c98 Added rotation logic to the renderer to enable the use of Interpolated Facings. 2022-07-17 17:03:53 +02:00
Sieds Aalberts
e060d6eb05 CellLayer TryGetValue. Return a value if within cell layer bounds. 2022-07-15 20:41:26 +03:00
Gustas
a5ea98ae35 Add RAGL S12 balance 2022-07-15 19:05:41 +02:00
penev92
4f34029556 Added a missing SequenceReferenceAttribute 2022-07-15 19:23:41 +03:00
tomas
5f4ed5f16b Update TextNotificationsManager to use Ui.Send 2022-07-15 19:19:09 +03:00
tomas
b0329aad35 Add Mediator for UI notifications 2022-07-15 19:19:09 +03:00
Matthias Mailänder
91fbd618ce Fix a crash when encountering 0 byte .vqa placeholders. 2022-07-12 23:23:28 +02:00
Leo512bit
9e34299085 Changed anypower tag name to A Power Plant
Updated wording and changed cnc

Capitlazied and added TS
2022-07-11 23:31:41 +02:00
Unrud
0a36d6f995 Use python3 for gtk-dialog.py
As mentioned in https://github.com/OpenRA/OpenRA/pull/20059#issuecomment-1166288560
2022-07-10 18:55:25 +02:00
Unrud
678f249c63 Scripts: Disable markup in error messages
Error messages are displayed using the following methods:

* **zenity** parses pango markup and replaces escaped characters
* **kdialog** replaces (some) escaped characters
* **gtk-dialog.py** replaces `\n`
* **printf** interprets format strings and replaces escaped characters
* **echo** just displays the text

The error messages themself contain escaped characters and paths from variables.

This PR unifies the behavior by:

* Use **printf** to format error messages and replace escaped characters
* Setting `--no-markup` for **zenity** to disable pango markup and escaped characters
* Remove `\n` replacement from **gtk-dialog.py**.
* Use plain **echo** instead of **printf**
2022-07-10 18:55:25 +02:00
Gustas
a03e794140 Add an option to disable chat in replays 2022-07-08 19:40:04 +02:00
abcdefg30
8a98ad51fd Fix sequences only being checked on actors with RenderSprites 2022-07-08 10:28:39 +03:00
abcdefg30
0ded8f8080 Allow null images for SmokeImage on SmudgeLayer 2022-07-08 10:28:39 +03:00
Gustas
36a86c2cd8 Nerf supply truck XP gain 2022-07-06 19:55:49 -05:00
abcdefg30
90ea611cee Rename the 20201213 update rules directory to 20210321 2022-07-05 16:44:17 +02:00
Unrud
bd6d69c5a1 Makefile: More robust check-scripts
Use **xargs** to pass results of **find** instead of word splitting. Word splitting fails when filenames contain white spaces (or if no files are found).
2022-07-05 16:07:01 +02:00
abcdefg30
8e1dce4bbe Add a lint check for maps without playable player 2022-07-05 16:01:22 +02:00
Gustas
7439f8b20a Update AUTHORS 2022-07-05 16:12:09 +03:00
Vapre
8c042a243e PathSearch, make TargetPredicate a readonly private field. 2022-07-04 20:34:23 +02:00
Matthias Mailänder
834de4efbe Port to Linguini 2022-07-02 22:32:37 +01:00
Matthias Mailänder
9d8c2bb4c4 Recommend .ftl syntax highlighting. 2022-07-02 22:32:37 +01:00
Sieds Aalberts
f5de8be3f0 Rectangle equals, trivial optimization. 2022-07-02 23:40:01 +03:00
Paul Chote
c8df1e864c Rework sequence docs plumbing. 2022-07-02 14:10:52 +03:00
Paul Chote
2037e37d4e Replace Sequence EmbeddedPalette with HasEmbeddedPalette. 2022-07-02 14:10:52 +03:00
abcdefg30
c1822d1cef Fix NREs in CheckUnknownWeaponFields 2022-06-26 23:46:51 +01:00
abcdefg30
82692b9d7f Fix crashes in WeaponInfo when warheads or projectiles cannot be created 2022-06-26 23:46:51 +01:00
abcdefg30
185bef39b0 Fix "Inherits" nodes being resolved as objects during linting 2022-06-26 23:46:51 +01:00
abcdefg30
5fe166dfd3 Fix an NRE in CheckUnknownTraitFields 2022-06-26 23:46:51 +01:00
penev92
07ec2d03fb Added an option to run utility.cmd programatically
And still pass arguments to Utility.exe without having to manually input them.
2022-06-26 16:14:22 +01:00
penev92
c3c5dbfa35 Unhardcoded SpriteSequence properties
To prepare them for documentation generation.
Also added descriptions to SpriteSequence implementations and their properties.
Also made a few code style fixes.
2022-06-26 15:41:19 +01:00
abcdefg30
d1f7fb8fb8 Serialize the actor generation for network orders 2022-06-25 18:18:49 +02:00
abcdefg30
13145557c8 Fix the switch-case formatting in Order.Deserialize 2022-06-25 18:18:49 +02:00
Mustafa Alperen Seki
c15af9f68a Ignore nonexistent actors in D2k importer code instead of crashing. 2022-06-24 22:26:15 +01:00
Mustafa Alperen Seki
6083eb4ac8 Remove D2k CheckImportActors 2022-06-24 22:26:15 +01:00
penev92
07db77fb8d Fix RemoveTurnToDock update rule 2022-06-24 21:16:26 +01:00
Ivaylo Draganov
5f42c7c8df Add support for readonly hotkeys and expose chat input hotkeys 2022-06-24 19:42:53 +01:00
Matthias Mailänder
1969ae361c Add missing ISync 2022-06-12 18:40:35 +02:00
Paul Chote
d050fe9f26 Move UnitOrderGenerator to Mods.Common. 2022-06-12 11:57:38 +02:00
Paul Chote
6eb8a4568b Allow mods to replace UnitOrderGenerator with their own default. 2022-06-12 11:57:38 +02:00
Paul Chote
bbe068f6cb Move IOrderGenerator to OpenRA.Orders namespace. 2022-06-12 11:57:38 +02:00
Ivaylo Draganov
320228f9d9 Add hotkeys for editor map overlays 2022-06-10 18:21:57 +02:00
Ivaylo Draganov
13ceda3259 Add hotkeys for map editor tabs 2022-06-10 18:21:57 +02:00
Ivaylo Draganov
b02a3d0f8f Remove unused yaml node in editor chrome 2022-06-10 18:21:57 +02:00
RoosterDragon
c9ee902510 Fix issues preventing suboptimal path searches.
Two different issues were causing a path search to not explore cells in order of the cheapest estimated route first. This meant the search could sometimes miss a cheaper route and return a suboptimal path.

- PriorityQueue had a bug which would cause it to not check some elements when restoring the heap property of its internal data structure. Failing to do this would invalidate the heap property, meaning it would not longer return the items in correct priority order. Additional tests ensure this is covered.
- When a path search encountered the same cell again with a lower cost, it would not update the priority queue with the new cost. This meant the cell was not explored early enough as it was in the queue with its original, higher cost. Exploring other paths might close off surrounding cells, preventing the cell with the lower cost from progressing. Instead we now add a duplicate with the lower cost to ensure it gets explored at the right time. We remove the duplicate with the higher cost in CanExpand by checking for already Closed cells.
2022-06-07 15:47:02 +02:00
penev92
c1cb9ea6be Removed an using, redundant when building on Mono 2022-06-03 21:43:43 +02:00
abcdefg30
6a31b1f9f3 Update the copyright header year 2022-05-28 00:35:10 -05:00
Matthias Mailänder
3f328a14be Hack removal 2022-05-24 21:07:54 -05:00
Matthias Mailänder
709512b166 Add Lua API export in EmmyLua syntax. 2022-05-24 21:07:54 -05:00
RoosterDragon
550db7e958 Ensure SpawnStartingUnits initializes after Locomotor.
Prior to ef44c31a72, Locomotor would be earlier in the trait initialization sequence than SpawnStartingUnits. After this commit, the initialization sequence was perturbed and SpawnStartingUnits would initialize first. When SpawnStartingUnits would query CanEnterCell this would generate a null reference as Locomotor had not yet initialized.

SpawnStartingUnitsInfo is made to initialize NotBefore LocomotorInfo to enforce the required trait ordering.
2022-05-24 12:45:03 -05:00
darkademic
1fc3785f79 Make range modifiers apply to AreaBeam projectile. 2022-05-22 19:42:23 -05:00
RoosterDragon
89042014bd Gracefully handle trying to find paths outside the map.
Rather than crashing, return no path instead.
2022-05-22 17:39:44 -05:00
abcdefg30
4ec19b3486 Remove deprecated airstrike and paratrooper methods for Lua 2022-05-22 10:57:30 -05:00
abcdefg30
7ec74749be Remove the deprecated WAngle.Range accessor for Lua 2022-05-22 10:57:30 -05:00
abcdefg30
c827d1a4ab Remove deprecated integer facing handling for Lua 2022-05-22 10:57:30 -05:00
abcdefg30
660130653c Use TryGetValue instead of ContainsKey and a lookup for cached sounds 2022-05-22 10:39:27 -05:00
abcdefg30
ea04a7fec5 Clear cached notifications when initializing a new map/mod 2022-05-22 10:39:27 -05:00
abcdefg30
9d481854f3 Make currentSounds readonly 2022-05-22 10:39:27 -05:00
atlimit8
85b9cf0a69 Fix fatal radar sound bug
The following error occurs on a map where the radar activation sound plays:
Exception has occurred: CLR/System.ArgumentException
An exception of type 'System.ArgumentException' occurred in System.Private.CoreLib.dll but was not handled in user code: 'Property set method not found.'
   at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
   at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index)
   at OpenRA.FieldLoader.LoadField(Object target, String key, String value) in /home/jason/git/OpenRA/OpenRA.Game/FieldLoader.cs:line 609
   at OpenRA.WidgetLoader.LoadWidget(WidgetArgs args, Widget parent, MiniYamlNode node) in /home/jason/git/OpenRA/OpenRA.Game/Widgets/WidgetLoader.cs:line 60
   at OpenRA.WidgetLoader.LoadWidget(WidgetArgs args, Widget parent, MiniYamlNode node) in /home/jason/git/OpenRA/OpenRA.Game/Widgets/WidgetLoader.cs:line 67
   at OpenRA.WidgetLoader.LoadWidget(WidgetArgs args, Widget parent, MiniYamlNode node) in /home/jason/git/OpenRA/OpenRA.Game/Widgets/WidgetLoader.cs:line 67
   at OpenRA.WidgetLoader.LoadWidget(WidgetArgs args, Widget parent, MiniYamlNode node) in /home/jason/git/OpenRA/OpenRA.Game/Widgets/WidgetLoader.cs:line 67
   at OpenRA.WidgetLoader.LoadWidget(WidgetArgs args, Widget parent, String w) in /home/jason/git/OpenRA/OpenRA.Game/Widgets/WidgetLoader.cs:line 43
   at OpenRA.Game.LoadWidget(World world, String id, Widget parent, WidgetArgs args) in /home/jason/git/OpenRA/OpenRA.Game/Game.cs:line 160
   at OpenRA.Mods.Common.Widgets.Logic.LoadIngamePlayerOrObserverUILogic..ctor(Widget widget, World world) in /home/jason/git/OpenRA/OpenRA.Mods.Common/Widgets/Logic/Ingame/LoadIngamePlayerOrObserverUILogic.cs:line 34
2022-05-21 16:10:08 +02:00
Eduardo Cáceres
a1811b4b04 Add fixed issues as warnings to .editorconfig 2022-05-18 11:42:36 -05:00
Eduardo Cáceres
c224bfdc0d Include name in authors 2022-05-18 11:42:36 -05:00
Eduardo Cáceres
8ced155ca3 Simplify 'always true' expressions
Co-authored-by: atlimit8 <atlimit8-vcs@gmx.com>
2022-05-18 11:42:36 -05:00
Eduardo Cáceres
aa998a46d9 Simplify collection initialization 2022-05-18 11:42:36 -05:00
Eduardo Cáceres
cae43808d9 Optimize string comparison 2022-05-18 11:42:36 -05:00
Eduardo Cáceres
c0d270b87d Abstract class with public constructor 2022-05-18 11:42:36 -05:00
Eduardo Cáceres
1b69ff017d Dictionary optimization 2022-05-18 11:42:36 -05:00
Eduardo Cáceres
b71402f64d Convert extension in real extension 2022-05-18 11:42:36 -05:00
Eduardo Cáceres
2677e9c013 Use pattern matching 2022-05-18 11:42:36 -05:00
Eduardo Cáceres
aed2b8afae Remove unnecessarily interpolated strings 2022-05-18 11:42:36 -05:00
Eduardo Cáceres
7eb64ea6fc Use read-only autoimplemented property when possible 2022-05-18 11:42:36 -05:00
Eduardo Cáceres
79f321cb44 .Any(), .Count() -> .Count or .Length 2022-05-18 11:42:36 -05:00
penev92
6eb4fe8980 Added a VSCode task to run the utility 2022-05-15 23:06:25 -05:00
penev92
9a30d260a1 Added launch profiles for the Utility project 2022-05-15 23:06:25 -05:00
abcdefg30
581b5cfacf Resolve an IDE0017 warning in ImportLegacyMapCommand 2022-05-11 15:33:23 +02:00
Paul Chote
c6dc0b58be Change macOS control group defaults back to Cmd. 2022-05-09 14:58:01 +02:00
Ivaylo Draganov
6a1a6b6397 Add platform override support for hotkeys and unhardcode editor hotkeys 2022-05-07 20:55:49 +01:00
Ivaylo Draganov
1cf4838b08 Remove unneeded yaml nodes from hotkey settings 2022-05-03 22:22:55 +02:00
Ivaylo Draganov
f0e69c3f64 Add hotkey filtering functionality (by name and by context) 2022-05-03 22:22:55 +02:00
Paul Chote
56153aac9f Search x86_64 before other multiarch dirs. 2022-05-01 11:55:28 +02:00
Leo512bit
ddef76e833 Added more thief voice lines 2022-04-30 17:19:48 -05:00
Ivaylo Draganov
0522576f73 Allow scrolling of settings panels while hovering a section header 2022-04-30 21:46:55 +02:00
abcdefg30
512eaf2746 Use Building instead of D2kBuilding for the Sietch actor 2022-04-30 12:28:28 -05:00
RoosterDragon
714b38c97c Add CellCostChanged event to Locomotor.
This event allows subscribers to be made aware when the terrain costs for a cell change.
2022-04-30 15:56:04 +02:00
Ivaylo Draganov
bc676fbf78 Add text notifications to many in-game events 2022-04-30 12:39:29 +01:00
Ivaylo Draganov
ea0bcbd1cc Adjust position of transients panel in TS 2022-04-30 12:39:29 +01:00
Ivaylo Draganov
24b9482cc1 Add support for transient text notifications matching speech notifications 2022-04-30 12:39:29 +01:00
Ivaylo Draganov
9f723be65a Add checkbox to settings for toggling transient notifications 2022-04-30 12:39:29 +01:00
Ivaylo Draganov
a1e6ac85dc Add transient notifications pool 2022-04-30 12:39:29 +01:00
RoosterDragon
e22b6de4e8 Rename PathGraph to MapPathGraph.
Move PathCostForInvalidPath and MovementCostForUnreachableCell constants into a new static class, PathGraph.
2022-04-27 23:19:59 +02:00
RoosterDragon
a1a583ea0a Split out a base class for PathGraph.
Move the domain logic involved into a base class named DensePathGraph. The base class contains all the domain logic necessary to traverse a graph including concepts such as custom movement layer.

PathGraph becomes responsible for proving a backing array for the pathfinding information, and is where the pooling logic lives instead, helping split the two concepts out.
2022-04-27 23:19:59 +02:00
penev92
86515610a5 Made all projectile and warhead fields readonly
This came up while working on the new documentation generation and comparing the results to ORAIDE's own code parser.
2022-04-26 22:37:12 +02:00
penev92
2bac492a65 Made all traitInfo fields readonly
This came up while working on the new documentation generation and comparing the results to ORAIDE's own code parser.
2022-04-26 22:37:12 +02:00
Gustas
135823fced fix crash when non-host exits map chooser 2022-04-25 23:03:27 +02:00
Paul Chote
a152bf7324 Replace custom mono defines with toolchain-provided defines. 2022-04-24 20:31:47 +02:00
RoosterDragon
3e5666ca53 Return an empty path when a search with no locations is made.
The restores the previous behaviour before FindUnitPathToTargetCell was introduced. This prevents callers such as the harvester code crashing when a harvester tries to route home to a refinery, but there are no refineries.
2022-04-24 12:52:18 +02:00
RoosterDragon
7df39f3522 On Locomotor initialization, update blocked cells.
Prior to ef44c31a72, Locomotor would be earlier in the trait initialization sequence than SpawnMapActors. Locomotor would assume no actors on the map, and register to update blocked cells when new ones were added. When SpawnMapActors created actors, Locomotor was made aware and kept up-to-date.

After this commit, the initialization sequence was perturbed and SpawnMapActors would initialize first. Locomotor would assume no actors on the map and thus be unaware of these starting units, meaning those starting units would not cause blocking, allowing units to pass through them.

There are two possible fixes. SpawnMapActorsInfo can initialize NotBefore<LocomotorInfo>, enforcing that actors are spawned after locomotor is ready. Or we can remove the assumption in Locomotor that the map starts empty, and have it update blocked cells on startup. The latter seems cleaner, so any other traits that may want to spawn actors don't have to be aware sequencing their initialization with the Locomotor trait, instead things would "just work".
2022-04-24 12:48:52 +02:00
Matthias Mailänder
4c08e449e0 Fix master server respond not being localized. 2022-04-23 22:29:40 +01:00
Paul Chote
0dbd8264b8 Handle exceptions thrown by HttpClient.GetAsync. 2022-04-23 22:44:05 +02:00
Matthias Mailänder
6c81590b20 Extract translation strings. 2022-04-21 13:26:55 +03:00
Matthias Mailänder
99033ab016 Localize dedicated server messages. 2022-04-21 13:26:55 +03:00
Matthias Mailänder
fe15748cc0 Wrap console logging with time stamp. 2022-04-21 13:26:55 +03:00
Matthias Mailänder
aaa3b49496 Update Mono.NAT 2022-04-21 13:11:45 +03:00
RoosterDragon
ecb7c16751 Tweak handling of nullable types in LUA doc generation.
The types for Int32 and Boolean are currently replaced with friendly names of int and bool for the docs. Ensure we apply the same handling when these are nullable types, changing the output from Int32? and Boolean? to int? and bool?
2022-04-18 13:02:05 +02:00
RoosterDragon
d2935672ca Fix the shape of the IPathFinder interface, ensure all path searches use it.
Some path searches, using PathSearch, were created directly at the callsite rather than using the pathfinder trait. This means some searches did not not benefit from the performance checks done in the pathfinder trait. It also means the pathfinder trait was not responsible for all pathing done in the game. Fix this with the following changes:
- Create a sensible shape for the IPathFinder interface and promote it to a trait interface, allowing theoretical replacements of the implementation. Ensure none of the concrete classes in OpenRA.Mods.Common.Pathfinder are exposed in the interface to ensure this is possible.
- Update the PathFinder class to implement the interface, and update several callsites manually running pathfinding code to instead call the IPathFinder interface.
- Overall, this allows any implementation of the IPathFinder interface to intercept and control all path searching performed by the game. Previously some searches would not have used it, and no alternate implementations were possible as the existing implementation was hardcoded into the interface shape.

Additionally:
- Move the responsibility of finding paths on completed path searches from pathfinder to path search, which is a more sensible location.
- Clean up the pathfinder pre-search optimizations.
2022-04-18 11:18:43 +01:00
RoosterDragon
2583a7af31 After NotBefore<> support to control initialization order.
Requires<T> means that trait of type T will be initialized first, and asserts that at least one exists. The new NotBefore<T> means that trait of type T will be initialized first, but allows no traits.

This allows traits to control initialization order for optional dependencies. They want to be initialized second so they can rely on the dependencies having been initialized. But if the dependencies are optional then to not throw if none are present.

We apply this to Locomotor which was previously using AddFrameEndTask to work around trait order initialization. This improves the user experience as the initialization is applied whilst the loading screen is still visible, rather than the game starting and creating jank by performing initialization on the first tick.
2022-04-18 10:31:47 +01:00
Mustafa Alperen Seki
62e7c7a318 Make PortableChrono PausableConditional. 2022-04-17 12:14:50 +02:00
Mustafa Alperen Seki
9de8d8854d Pass PortableChrono instead of PortableChronoInfo to PortableChronoOrderGenerator. 2022-04-17 12:14:50 +02:00
Matthias Mailänder
648c56bca1 Don't crash when joining a game after asset installation. 2022-04-17 00:27:36 +03:00
Matthias Mailänder
0f90713aba Added addition error logging to graphics.log when SDL fails 2022-04-16 22:37:05 +02:00
Mustafa Alperen Seki
30f14dcc4c Fix Army Spectator tab not using FactionImages. 2022-04-15 18:17:49 +02:00
RoosterDragon
ac0969d688 Use nameof instead of hardcoded strings in reflection calls.
This helps improve the safety of code the uses reflection when methods may get renamed, and helps navigating code as the nameof will show up when searching for references to members.
2022-04-14 19:58:15 +02:00
Gustas
b254eb0f3d Add dynamic map refresh 2022-04-14 16:16:38 +02:00
Gustas
61df7974b0 Make the delete button not call MapCache every frame 2022-04-14 16:16:38 +02:00
tomas
515aba0ee7 Update SDL to 2.0.20 2022-04-13 20:13:01 +02:00
Matthias Mailänder
af3362c62f Use string.Contains(char) instead of string.Contains(string)
with single characters
2022-04-13 20:09:57 +02:00
Matthias Mailänder
3bc28ba6e2 Use inline string replacement. 2022-04-13 20:09:57 +02:00
dnqbob
4f43b157a8 Add place variant building for BaseBuilderBotModule.
1. If it follow the refinery placing logic, then we can use Facings in PlaceBuildingVariants to help BaseBuilderBotModule "rotates" it to minefield.

2. If it is a normal building, BaseBuilderBotModule will place a random variant actor.
2022-04-12 22:28:03 +02:00
penev92
f74d1c3cf8 Adjusted D2k mission player colors
PlayerReference colors in D2k missions only affect chat text and minimap colors because actors use specific palette colors.
So using the colors from the original game's minimap.
2022-04-12 22:15:02 +02:00
penev92
2866342522 Add missing PlayerReferences in D2k map import 2022-04-12 22:15:02 +02:00
tomas
8e19463450 Fix desync handling 2022-04-12 21:56:46 +02:00
abcdefg30
dab8ee4f94 Load campaign and utils Lua scripts before map scripts 2022-04-12 21:54:11 +02:00
Mustafa Alperen Seki
c71af0e613 Make NukePower MissileImage optional. 2022-04-12 21:52:29 +02:00
Mustafa Alperen Seki
60b123c641 Split NukePower MissileImage from MissileWeapon. 2022-04-12 21:52:29 +02:00
Matthias Mailänder
0260884369 Added translation support for server orders. 2022-04-03 19:23:26 +02:00
Matthias Mailänder
ee95d2591f Code cleanup. 2022-04-03 19:23:26 +02:00
Matthias Mailänder
7735107deb Add a script trigger overlay. 2022-04-02 18:01:00 +02:00
Matthias Mailänder
058fb51f4c Reduce the menu button size to save space. 2022-04-02 18:01:00 +02:00
Matthias Mailänder
0e7ad43425 Remove unused parameters. 2022-04-01 23:30:26 +02:00
RoosterDragon
ea243b8558 Fix crash in TSEditorResourceLayer when adding resources.
Ensure cells are within map bounds when checking if adjacent cells should be cleared during resource placement.
2022-03-26 23:07:41 +01:00
abcdefg30
e685731b33 Add an update rule for the removal of AttackFrontal's FacingTolerance 2022-03-13 11:16:47 +01:00
abcdefg30
889425ab0f Remove AttackFrontal's FacingTolerance and define it explicitly in rules 2022-03-13 11:16:47 +01:00
dnqbob
9049ae6f20 Add a backawrd moving option for mobile 2022-03-13 10:46:04 +01:00
Vapre
83357af14c WorldRenderer, replace foreach ActorsWithTraits with ApplyToActorsWithTrait. #18798. 2022-03-13 10:45:01 +01:00
Matthias Mailänder
00356b8bbd Setup Tiberian Sun forest fires. 2022-03-12 17:16:43 +01:00
Mustafa Alperen Seki
b54a724aea Add ability to override CannotPlaceNotification per queue. 2022-03-12 12:55:41 +01:00
Matthias Mailänder
a6cb20a4ec Fix overlapping tracks when vehicle rotates.
Also optimize the function slightly.
2022-03-12 12:53:59 +01:00
Matthias Mailänder
5220da1bae This can never be null. 2022-03-12 12:53:59 +01:00
Mustafa Alperen Seki
2f1edd4516 Remove Replacable actors for LineBuild too. 2022-03-11 22:32:08 +01:00
Mustafa Alperen Seki
a7004b2db7 Check for placeablilty of LineBuild Segment instead of the Post. 2022-03-11 22:32:08 +01:00
Mustafa Alperen Seki
153bd14f9e Add Force-Move undeploy to D2k Thumper Infantry. 2022-03-11 22:29:45 +01:00
Leo512bit
d2611ebfb4 Added fsmap to music.ymal and set the lose music to it.
Signed-off-by: Leo512bit <leonardmatthewteyssier@gmail.com>
2022-03-11 21:36:43 +01:00
Ivaylo Draganov
eadc8ad689 Change color of labels that correspond to disabled inputs
- Add a new widget type for input and extend it from other input widgets
- Add a new label type that can be linked to an input widget
- Change the label color when the input's disabled state changes
2022-03-07 21:01:33 +01:00
Mustafa Alperen Seki
0203476da9 Update variables for Flash lua function. 2022-03-05 15:19:01 +01:00
Mustafa Alperen Seki
7e4c3acda3 Fix ZRamp only allowing integer values. 2022-03-02 19:41:49 +01:00
darkademic
e082497a1a Corrected StartBurstReport sound synchronisation when used with FireDelay. 2022-02-26 10:40:19 -06:00
Matthias Mailänder
9605e5ad9c Use the new MiniYAML GitHub syntax. 2022-02-26 17:04:50 +01:00
Matthias Mailänder
da4fb27fca Use modern string syntax. 2022-02-26 17:04:50 +01:00
penev92
57d3321d0f Make WithDockingAnimation optional
The `Refinery` trait has a hardcoded usage of `SpriteHarvesterDockSequence`, which requires the harvester to have `WithDockingAnimation`, making it inconvenient-at-best to NOT have a docking/unloading animation.
2022-02-23 22:06:07 +01:00
IceReaper
fa8bfc6ca0 Allow mods to implement new building placement conditions and cursors. 2022-02-22 01:20:38 +02:00
penev92
0f1ff3f2fc Recommend the MiniYAML language server extension
Recommends  https://marketplace.visualstudio.com/items?itemName=openra.oraide-vscode  as a VSCode extension for the OpenRA workspace.
2022-02-20 16:30:38 +01:00
Mustafa Alperen Seki
91f626c42a Make WithVoxelWalkerBody PausableConditional. 2022-02-18 12:24:23 +01:00
dnqbob
6c33d47ef3 Update rule on renaming 'CloakTypes' to 'DetectionTypes' 2022-02-12 19:30:21 +01:00
dnqbob
831bed2c4d Add enter-cloak & exit-cloak effect for Cloak 2022-02-12 19:30:21 +01:00
RoosterDragon
d67f696bd0 Move BlockedByActor, IPositionableInfo, IPositionable to Mods.Common.
Actor previously cached targetable locations for static actors as an optimization. As we can no longer reference the IPositionable interface, move this optimization to HitShape instead. Although we lose some of the efficiency of caching the final result on the actor, we gain some by allowing HitShape to cache the results as long as they have not changed. So instead of being limited to static actors, we can extend the caching to currently stationary actor.
2022-02-11 23:35:08 +01:00
RoosterDragon
d8a4d7fd1d Cache the global mix database in MixLoader
We can reuse this global database as it doesn't change, rather than loading a new copy each time a new mix file is parsed.
2022-02-11 14:05:55 +01:00
RoosterDragon
f5d1fe4bc4 Use more efficient search in MergeIntoResolved
Switch Enumerable.FirstOrDefault to List.Find. The latter can avoid some allocations because the concrete collection type is known.
2022-02-11 14:01:16 +01:00
RoosterDragon
ed72e61f8f Add detailed documentation for BlockedByActor enum. 2022-02-08 23:34:07 +01:00
RoosterDragon
9cd55df584 Ensure editorconfig naming styles align with StyleCop SA13XX style rules.
Aligns the naming conventions defined in editorconfig (dotnet_naming_style, dotnet_naming_symbols, dotnet_naming_rule) which are reported under the IDE1006 rule with the existing StyleCop rules from the SA13XX range.

This ensures the two rulesets agree when rejecting and accepting naming conventions within the IDE, with a few edges cases where only one ruleset can enforce the convention. IDE1006 allows use to specify a naming convention for type parameters, const locals and protected readonly fields which SA13XX cannot enforce. Some StyleCop SA13XX rules such as SA1309 'Field names should not begin with underscore' are not possible to enforce with the naming rules of IDE1006.

Therefore we enable the IDE1006 as a build time warning to enforce conventions and extend them. We disable SA13XX rules that can now be covered by IDE1006 to avoid double-reporting but leave the remaining SA13XX rules that cover additional cases enabled.

We also re-enable the SA1311 rule convention but enforce it via IDE1006, requiring some violations to be fixed or duplication of existing suppressions. Most violations fixes are trivial renames with the following exception. In ActorInitializer.cs, we prefer to make the fields private instead. ValueActorInit provides a publicly accessible property for access and OwnerInit provides a publicly accessible method. Health.cs is adjusted to access the property base instead when overriding. The reflection calls must be adjusted to target the base class specifically, as searching for a private field from the derived class will fail to locate it on the base class.

Unused suppressions were removed.
2022-02-07 19:14:45 +01:00
Gustas
13ee62c181 Make overlay dropdown optional 2022-02-06 19:21:43 +01:00
Gustas
addfdf50fa Crash when EditorWorld does not have BuildableTerrainOverlay 2022-02-06 19:21:43 +01:00
reaperrr
a17af87a5e Fix aircraft with TakeOffOnCreation=false not freeing exit
...when another actor gets produced.
2022-02-06 19:20:29 +01:00
reaperrr
44fc4a1d0f Fix random placement of AssociateWithAirfieldActivity
It doesn't belong to IMove, so it doesn't belong into that region.
Put it near ICreationActivity where it belongs.
2022-02-06 19:20:29 +01:00
darkademic
257ef95963 Suffix projectile shadow palettes with player name if IsPlayerPalette = true. 2022-02-06 19:18:59 +01:00
Matthias Mailänder
9bb41630e7 Update documentation action to .NET 6 2022-02-06 18:06:44 +01:00
Ivaylo Draganov
f5ab9d95fe Make remove from control group hotkey available only to players 2022-02-06 09:09:33 -06:00
Mustafa Alperen Seki
c096934db8 Fix projectile shadow alpha calculation. 2022-02-04 23:35:44 +01:00
Ivaylo Draganov
1813edc74b Add contexts for hotkey validation 2022-02-04 18:49:05 +01:00
RoosterDragon
1bc95a290f Preallocate dictionary size in ToDictionaryWithConflictLog 2022-02-02 15:10:37 +01:00
Smittytron
74cced319c Change terrain type of clear straight bridge tiles 2022-01-31 14:19:47 +01:00
RoosterDragon
bd30c66f95 Reuse object arrays in FieldLoader Parse methods 2022-01-30 19:47:06 +01:00
RoosterDragon
2ab3917f29 Clean up usage of DomainIndex
- When a path search is being performed the path search will not attempt route to inaccessible cells, so domain index checks to avoid inaccessible cells in the search predicate are redundant and can be removed.
- DomainIndex is a required world trait, so we don't need to use TraitOrDefault and therefore can avoid dealing with the null case.
2022-01-30 16:22:26 +01:00
Matthias Mailänder
4b4b0125a2 Make UnitsInRange accessible to mod code. 2022-01-30 11:56:43 +01:00
Matthias Mailänder
240c96b781 Expose sprite sequence to linting and documentation. 2022-01-30 11:56:43 +01:00
RoosterDragon
6dc189b7d1 Rearrange various API surfaces related to pathfinding.
The existing APIs surfaces for pathfinding are in a wonky shape. We rearrange various responsibilities to better locations and simplify some abstractions that aren't providing value.

- IPathSearch, BasePathSearch and PathSearch are combined into only PathSearch. Its role is now to run a search space over a graph, maintaining the open queue and evaluating the provided heuristic function. The builder-like methods (WithHeuristic, Reverse, FromPoint, etc) are removed in favour of optional parameters in static creation methods. This removes confusion between the builder-aspect and the search function itself. It also becomes responsible for applying the heuristic weight to the heuristic. This fixes an issue where an externally provided heuristic ignored the weighting adjustment, as previously the weight was baked into the default heuristic only.
- Reduce the IGraph interface to the concepts of nodes and edges. Make it non-generic as it is specifically for pathfinding, and rename to IPathGraph accordingly. This is sufficient for a PathSearch to perform a search over any given IGraph. The various customization options are concrete properties of PathGraph only.
- PathFinder does not need to deal with disposal of the search/graph, that is the caller's responsibility.
- Remove CustomBlock from PathGraph as it was unused.
- Remove FindUnitPathToRange as it was unused.
- Use PathFinder.NoPath as the single helper to represent no/empty paths.
2022-01-30 11:47:52 +01:00
Matthias Mailänder
cd1fe2d23b Fix saving into writable system directories. 2022-01-30 11:46:42 +01:00
abcdefg30
2af8296f48 Add the possibility to always show detection circles 2022-01-30 11:41:02 +01:00
abcdefg30
7c085e49c7 Cache DetectCloaked traits in RenderDetectionCircle 2022-01-30 11:41:02 +01:00
xan2622
13fbc412d2 removal 2022-01-30 11:28:16 +01:00
abcdefg30
a9cd2d41c7 Fix ToAhsv using the wrong type for alpha 2022-01-29 13:44:02 +01:00
Ivaylo Draganov
7a93b9ea8c Make control group hotkeys configurable
- Split control groups management to its own interface
- Add hotkeys for selecting, creating, adding to and combining with control groups
- Add a ControlGroups widget to manage the player interaction
2022-01-28 18:38:18 +01:00
Gustas
04b456d6c2 Create overlays dropdown 2022-01-26 23:09:18 +01:00
Gustas
2f130b17ba Fix buildable overlay not updating and make ramps unbuildable 2022-01-23 22:37:02 +01:00
penev92
9cc631ca7e Added a rule about using object initializers 2022-01-23 13:14:57 +01:00
penev92
ab09ce21b4 Changed code to use object initializers everywhere 2022-01-23 13:14:57 +01:00
penev92
70e2769a85 Added a rule about the readonly modifier
Also added a rule to silence StyleCop complaining about StaticReadonlyFieldsMustBeginWithUpperCaseLetter to match what we already have configured for the IDE.
2022-01-22 18:47:06 +00:00
penev92
bf332b6619 Fixed fields missing the readonly modifier 2022-01-22 18:47:06 +00:00
Piotr Usewicz
f83e27d647 Point towards arm libraries
When compiling for macOS using `unix-generic` on Apple M1 processors, point sarchdirs towards the correct location in Homebrew for arm64.
2022-01-22 18:21:36 +00:00
penev92
a67cfabd1e Reordered code style rules
To order them by their number.
2022-01-20 22:10:28 +01:00
penev92
8dbaa0c49f Added more explanations to .editorconfig 2022-01-20 22:10:28 +01:00
penev92
92d1d64dce Changed one-liner braces rule to silent
Having this set to "none" disabled the IDE's option to add braces, whereas "silent" lets it do it on the user's request while still not suggesting it on its own.
2022-01-20 22:10:28 +01:00
penev92
31b3647c09 Added a rule about unordered modifiers 2022-01-20 22:10:28 +01:00
penev92
d37336456d Fixed inconsistent declaration modifier order 2022-01-20 22:10:28 +01:00
penev92
3bacd81b8b Added a rule about zero-length array allocations 2022-01-20 22:10:28 +01:00
penev92
0d24ccc47a Fixed unnecessary zero-length array allocations
Changed all currently present zero-length array allocations in the codebase to use `Array.Empty` instead.
2022-01-20 22:10:28 +01:00
Paul Chote
1312c1aa72 Rename macOS compat packages to mono. 2022-01-20 19:31:43 +01:00
penev92
19dd23e349 Beautified the .editorconfig file 2022-01-19 14:37:14 +01:00
dnqbob
242d589c45 Make AI airstrike aircraft spawns randomly 2022-01-17 15:43:20 +01:00
Mustafa Alperen Seki
d149624b84 Add Lua Scripting for Carryall. 2022-01-12 14:24:13 +01:00
penev92
860ec642b8 Addressed review comments
- Renamed `IVideo.CurrentFrameNumber` to `CurrentFrameIndex`
 - Improved logged error message in VideoPlayerWidget
 - Renumbered fields in ThreadedGraphicsContext
2022-01-11 18:16:31 +01:00
penev92
248b8d1102 Renamed IVideo implementations
To match the interface they are implementing.
2022-01-11 18:16:31 +01:00
penev92
6f0509d235 Removed now-unused ITexture.SetData() overload 2022-01-11 18:16:31 +01:00
penev92
cb8530fbae Reworked internal palettes in video reader classes
This removes the need to pack & unpack color bytes as uints for no gain.
2022-01-11 18:16:31 +01:00
penev92
c4ab7041b8 Updated VideoPlayerWidget to play new IVideo data
Added optional padding to video frames because that's what VideoPlayerWidget expects.
Keeping the option to not use padding for other use-cases like converting frames to PNG.
2022-01-11 18:16:31 +01:00
penev92
ee29d0f9c7 Changed IVideo.CurrentFrameData uint[,] -> byte[] 2022-01-11 18:16:31 +01:00
penev92
1b5f2f1b39 Removed caching properties from video readers
Those seem redundant since the frame number is guaranteed to match the loaded data inside CurrentFrameData.
2022-01-11 18:16:31 +01:00
penev92
0df3b34c52 Did a beautification pass on IVideo and family
Removed property backing fields where applicable, introduced C#7 syntax for properties.
Renamed a bunch of interface properties and class private members with more descriptive names.
Did some inconsequential reordering.
2022-01-11 18:16:31 +01:00
abcdefg30
556413c91d Add whitespace fixes from the automatic update rule run 2022-01-11 18:09:05 +01:00
abcdefg30
f3bc450e20 Fix an oversight in the update rule for AttackBomber's FacingTolerance 2022-01-11 18:09:05 +01:00
abcdefg30
6556b33cef Move update rules to the correct subfolders and paths 2022-01-11 18:09:05 +01:00
abcdefg30
15c2800601 Retire the release-20191117 UpdatePath 2022-01-11 18:09:05 +01:00
abcdefg30
d660ce9c47 Change the UpdatePath targets from playtest-20201213 to release-20210321 2022-01-11 18:09:05 +01:00
Matthias Mailänder
6770c08bf9 Fix scrollitem-nohover-hover not found. 2022-01-11 17:40:58 +01:00
Ivaylo Draganov
99ac128820 Add selected actors count to feedback notification text 2022-01-09 19:07:45 +01:00
Mustafa Alperen Seki
fe05382b24 Change "actor id" to "actor name" in some descs. 2022-01-09 19:02:05 +01:00
Mustafa Alperen Seki
3c60a515f7 Add CarryableConditions to Carryall. 2022-01-09 19:02:05 +01:00
penev92
b67954451a Removed obsolete file OpenRA.sln.DotSettings
- The DEFAULT_PRIVATE_MODIFIER behaviour is now handled by the .editorconfig file via `dotnet_style_require_accessibility_modifiers = omit_if_default:warning`.
 Also added `dotnet_diagnostic.IDE0040.severity = warning` there to raise compile-time errors in the CI.
 - The field naming conventions seem to already be covered by (some) analyzer rules (checked in both VS and VSCode) - IDE1006/SA1306 and SA1307.
2022-01-09 18:58:37 +01:00
penev92
2f6f214bac Removed a bunch of explicit access modifiers 2022-01-09 18:58:37 +01:00
penev92
413d564f1d Removed obsolete file stylecop.json
It currently has two functions, both of which are covered by our .editorconfig file.
2022-01-09 18:58:37 +01:00
penev92
1326bca65c Updated .gitignore file
Removed some obsolete entries now that there is a new build directory.
2022-01-09 18:58:37 +01:00
Mustafa Alperen Seki
721210eafe Fix RevealsMap's effect not being removed for non-owners. 2022-01-09 18:53:16 +01:00
penev92
c6dacb50e8 Added hiding of the palette picker in the AssetBrowser 2022-01-09 18:40:32 +01:00
penev92
495faea96b Updated scale sliders to accout for the new AssetBrowser layout 2022-01-09 18:40:32 +01:00
penev92
b6b417d42f Fixed AssetBrowserLogic disposing audio streams prematurely 2022-01-09 18:40:32 +01:00
penev92
6fb228ddd1 Fixed Mp3Loader and OggLoader not resetting stream position 2022-01-09 18:40:32 +01:00
penev92
87b92b53a4 Reworked ISoundFormat.LengthInSeconds implementations 2022-01-09 18:40:32 +01:00
penev92
631297417c Fixed background music playing momentarily in AssetBrowser when switching to a video asset 2022-01-09 18:40:32 +01:00
penev92
001efc9409 Added containing package name to asset tooltip 2022-01-09 18:40:32 +01:00
penev92
8d20487cb6 Added manual playing and stopping of audio in the AssetBrowserLogic
Also added visualisation of audio file length and progress and improved asset cleanup.
2022-01-09 18:40:32 +01:00
penev92
abea3a0f74 Fixed AudFormat and WavFormat implementations of ISoundFormat.LengthInSeconds
- Fixed a rounding issue in `WavReader.WaveLength()`.
- Fixed `AudReader.SoundLength()` not resetting the stream position.
- Fixed crashes caused by disposed streams because `LengthInSeconds` would try and calculate the length on the fly. It is now precalculated and cached (making it consistent across all 5 current `ISoundFormat` implementations).
- Fixed a crash in `AudReader.LoadSound()`'s `out Func<Stream> result` because that func would try and access the disposed stream's `Length` property. That works for `SegmentStream`, but not for `FileStream`.
- Fixed frameCount/soundLength label positioning in the AssetBrowser window to avoid text clipping .
2022-01-09 18:40:32 +01:00
penev92
8b944e9c82 Added audio playback to the AssetBrowser 2022-01-09 18:40:32 +01:00
penev92
7a9e0863d6 Added a scale slider for sprites in the AssetBrowser 2022-01-09 18:40:32 +01:00
penev92
40c728269c Added a scale slider for models in the AssetBrowser 2022-01-09 18:40:32 +01:00
penev92
6907081c2b Paused shellmap music when opening videos in the AssetBrowser 2022-01-09 18:40:32 +01:00
penev92
a058b1f5bd Split AssetBrowser supported formats
Splitting them from one array into separate allows us to then reliably pick how each asset should be presented. Also lets us unhardcode some checks like "if file is .vxl ... else is sprite".
2022-01-09 18:40:32 +01:00
abcdefg30
8ac2815c9e Fix the first Land ctor not passing targetLineColor on 2022-01-07 01:22:16 +01:00
abcdefg30
942a0c8712 Update an outdated comment in Land.cs 2022-01-07 01:22:16 +01:00
Matthias Mailänder
cdac14b92b Avoid duplicate field. 2022-01-06 16:10:49 +02:00
Matthias Mailänder
718bf88b9a Remove superflous null checks
and cache a dictionary lookup.
2022-01-06 01:54:27 +01:00
Ivaylo Draganov
eb4de47362 Add support for disabled click sound in scrollbar widget 2022-01-05 21:15:19 +01:00
Ivaylo Draganov
c3dfac7ade Remove redundant default hotkey UI notice 2022-01-04 18:35:41 +01:00
Ivaylo Draganov
5aeae694be Use disabled button state for slider thumb 2022-01-04 18:35:41 +01:00
Ivaylo Draganov
bdfe025059 Update introductory prompts to match settings layout 2022-01-04 18:35:41 +01:00
Ivaylo Draganov
8a2b63c944 Update lobby options tab layout to match settings layout 2022-01-04 18:35:41 +01:00
Ivaylo Draganov
9f96d4159a Update common game ingame info tabs to match settings layout 2022-01-04 18:35:41 +01:00
Ivaylo Draganov
955464ee1d Update TD game info panel to match the settings panel 2022-01-04 18:35:41 +01:00
Ivaylo Draganov
3ecaf76804 Overhaul settings panel layout
- make the panel larger
- place settings widgets in a scroll panel
- arrange settings widgets in two columns
- make tabs in TD vertical
2022-01-04 18:35:41 +01:00
Smittytron
a36eb585d3 Fix HiveGassed regression in Ant03 2022-01-04 12:50:00 +01:00
abcdefg30
bf7ec4aec1 Update nuget packages 2022-01-04 12:34:30 +01:00
abcdefg30
b448f2d324 Disable trimmed publishing 2022-01-04 12:34:30 +01:00
abcdefg30
b12c15ea9d Update to .NET 6 2022-01-04 12:34:30 +01:00
abcdefg30
eae6d33cd9 Fix highlighting of the credits tabs 2021-12-30 12:42:56 +00:00
Smittytron
3c24a3f9c7 Update Allies10a to work with Lua cleanup 2021-12-29 17:13:09 +01:00
Matthias Mailänder
bb27837149 Don't make support airplanes huntable. 2021-12-28 13:08:38 -06:00
Ivaylo Draganov
87534022b3 Make size and white-space of game save/load panel more consistent 2021-12-28 17:48:21 +01:00
Ivaylo Draganov
d2c08c72bd Adjust size, position and white-space of several panels 2021-12-28 17:48:21 +01:00
Ivaylo Draganov
0261dcaa7a Make size and white-space of "Mission Browser" panel consistent 2021-12-28 17:48:21 +01:00
Ivaylo Draganov
d14ba6b04d Make size and white-space of "Replay Browser" panel consistent 2021-12-28 17:48:21 +01:00
Ivaylo Draganov
a69d86c587 Make size and white-space of "Asset Browser" panel consistent 2021-12-28 17:48:21 +01:00
Ivaylo Draganov
8fb3bbe5f9 Make size and white-space of "Music" and "Credits" panels consistent 2021-12-28 17:48:21 +01:00
Matthias Mailänder
1c00a2fbec Bump NUnit slightly. 2021-12-27 22:33:06 +01:00
Matthias Mailänder
82ce4717aa Bump Pfim 2021-12-27 22:33:06 +01:00
Matthias Mailänder
2b23bde925 Bump NVorbis 2021-12-27 22:33:06 +01:00
Matthias Mailänder
bc52e8b6a9 Bump Mono.NAT 2021-12-27 22:33:06 +01:00
Matthias Mailänder
fb296d7dcb Bump Fluent.Net 2021-12-27 22:33:06 +01:00
Matthias Mailänder
6997b973ee Fix a null reference exception. 2021-12-17 14:11:19 +01:00
dnqbob
c968a2a902 Contrail smooth&color fix 2021-12-16 23:48:03 +01:00
Matthias Mailänder
5fcc049040 Fix a null reference exception. 2021-12-08 21:07:19 +01:00
Matthias Mailänder
ff20c1c59d Avoid unnecessary variable assignment. 2021-12-06 13:19:28 +01:00
Matthias Mailänder
2c5f1f343f Code style. 2021-12-06 13:19:28 +01:00
Matthias Mailänder
b147da388a Mark fields readonly. 2021-12-06 13:19:28 +01:00
Matthias Mailänder
4a60d56753 Add a lint tag and documentation. 2021-12-06 13:19:28 +01:00
Matthias Mailänder
9d905d8291 Remove unused parameters and variables. 2021-12-06 13:19:28 +01:00
Matthias Mailänder
07815143f1 Fix CA1825 warnings on empty array initialisation. 2021-12-06 13:19:28 +01:00
RoosterDragon
727084c5fc Run spell check over solution 2021-12-05 19:23:43 +01:00
Matthias Mailänder
b3d290edd9 Rename support power field for consistency. 2021-12-05 14:56:01 +01:00
Matthias Mailänder
9852bd08e4 Clean up existing rules. 2021-12-05 14:56:01 +01:00
RoosterDragon
3bde4ebbaf Fix Move.PathSearchOrder
Each successive value of BlockedByActor is a superset of the previous value. Having a mixed up order of values in PathSearchOrder is not useful.

In the previous ordering, if a search for Immovable failed to find a path, it would then attempt Stationary. However Stationary is *more* restrictive then Immovable. If Immovable failed, there is no way Stationary could succeed. This means the search for Stationary is wasted effort.

In the fixed ordering, we try Stationary first. In the fixed ordering there are no pointless searches. Every search might succeed where the previous one failed and is therefore useful to try.
2021-12-05 13:53:56 +01:00
Matthias Mailänder
f44a2ea9a3 Fix formatting problems. 2021-12-05 13:17:32 +01:00
Matthias Mailänder
5eaba4f893 Unhardcode AI defensive priorities. 2021-12-05 13:17:32 +01:00
Matthias Mailänder
e82aa9977e Unhardcode AI air units and exclude scripted aircraft. 2021-12-05 13:17:32 +01:00
Matthias Mailänder
00ece1ba55 Add configurable tooltips to the Discord integration. 2021-12-05 13:09:59 +01:00
Matthias Mailänder
c9022bcb73 Add support for superweapon detected sounds. 2021-12-05 13:02:15 +01:00
darkademic
c51327c4cc Make yaml check utility load each map separately to reduce memory usage. 2021-12-04 20:07:19 +01:00
Ivaylo Draganov
695b7865d3 fixup! Add support for non-overlapping sound notifications 2021-11-29 23:44:59 +01:00
Ivaylo Draganov
fa6ff32f65 Add support for non-overlapping sound notifications 2021-11-29 23:44:59 +01:00
RoosterDragon
137d384304 Remove path caching.
The path cache was originally a moderate benefit, but over time a couple of things have conspired against it:

- Only paths with BlockedByActor.None are cached. Originally all paths regardless of blocking were cached but this was deemed unacceptable due to potentially returning outdated paths as actors move about. Paths with BlockedByActor.None are only invalidated if terrain conditions change, which are rarer.
- Move will try and find a path 4 times, trying with a different BlockedByActor check each time. BlockedByActor.None is the last check and only reached if the other searches fail. This is a rare scenario.

Overall, this means the hit rate for the cache is almost non-existent. Given the constraints on path validity it seems unlikely that the hit rate could be improved significantly, therefore it seems reasonable to remove the cache entirely to remove the overhead of cache management.
2021-11-29 01:03:14 +01:00
Paul Chote
31bd32e7ef Allow launcher scripts to run from other working directories. 2021-11-28 23:09:32 +01:00
GeorgeD64
e00efbf53d Added map name support to Launch.Map command line parameter
Fixed code based on feedback

Replaced try/catch block with a null check and exception throw

Fixed code based on feedback

Fixed code based on feedback

Simplified Launch.Map parameter to use map name directly
2021-11-28 23:06:26 +01:00
Ivaylo Draganov
a537346580 Move selection hotkeys out of world interaction widget
- Split SelectionUtils for selecting actors in the world to a static class
- Split selection hotkeys into their own logic classes
2021-11-27 15:06:39 +01:00
Ivaylo Draganov
e9cc89a336 Yield keyboard focus upon leaving the ingame chat tab 2021-11-27 14:57:04 +01:00
Gustas
975da89400 RAGL balance changes 2021-11-27 13:33:55 +01:00
Matthias Mailänder
5ed3f55ed2 Use global idle hunt. 2021-11-26 23:39:41 +01:00
Matthias Mailänder
b9bfbfd5ac Use global objective initialisation. 2021-11-26 23:39:41 +01:00
Matthias Mailänder
d42edfc0b9 Use global difficulty. 2021-11-26 23:39:41 +01:00
Matthias Mailänder
58b105f0d4 Reorganize global .lua files. 2021-11-26 23:39:41 +01:00
Matthias Mailänder
a502e85e68 Fail gracefully when there is just one default difficulty. 2021-11-26 23:39:41 +01:00
RoosterDragon
a59f4b2c4a Add a helper for multiplying by sqrt(2) 2021-11-25 22:50:22 +01:00
RoosterDragon
f0e24f6d21 Make knowledge of height discontinuities live in Locomotor not PathGraph. 2021-11-21 17:52:12 +01:00
RoosterDragon
8c627aa185 Clean up PathSearch
- Remove functionality for tracking cells considered during the search, as nothing relies on this.
- Rename various parameters in the expand function to closer match naming of fields used in CellInfo, intended to improve clarity.
2021-11-21 12:03:16 +01:00
Orb370
e1ade59a32 TD Balance Fall 2021 Commit
TD-Balance-Fall-2021 Commit

TD-Balance-Fall-2021-Commit
2021-11-20 17:37:43 -06:00
RoosterDragon
290ed17c9d Adjust some naming and order of parameters in CellInfo
- Make Status the first field.
- Rename EstimatedTotal to EstimatedTotalCost to make it clearer it has the same unit as the CostSoFar field.
- Rename PreviousPos to PreviousNode as node terminology is a better match for usage.
2021-11-20 12:13:42 +01:00
Matthias Mailänder
98b25ddd5e Check for dead actors when searching for exits. 2021-11-18 20:52:05 +01:00
RoosterDragon
31267aa22d Fix some incorrect logic in PathGraph.GetConnections.PathGraph
Firstly, when dealing with maps with height discontinuities, the neighbouring cells we need to search are more that the set we need to search on flat maps. We ensure that as we traverse a map with varying height, we now consider cells "behind" us that may have become accessible due to a height change.

Secondly, when considering connections available via Custom Movement Layers, make sure the target cell on the new layer is actually enterable. Previously this cell would be reported as a valid connection, even if it wasn't actually possible to enter the cell as it was blocked. We also apply the same optimization of ignoring already closed cells.
2021-11-16 00:33:19 +01:00
RoosterDragon
1d23c23d06 Adjust span comparisons for clarity and add some test cases. 2021-11-15 13:20:34 +01:00
RoosterDragon
5416910249 Remove unused method in MiniYaml 2021-11-15 13:20:34 +01:00
RoosterDragon
73547c31ec Remove MiniYamlNodes alias in MiniYaml. 2021-11-15 13:20:34 +01:00
RoosterDragon
2db312a792 In MiniYaml, presize some collections and trim lists during parsing. 2021-11-15 13:20:34 +01:00
RoosterDragon
0f01df5474 Avoid string allocations in MiniYaml parsing.
- Stream lines in as memory rather than needing to realise a string for each line, via a new method in StreamExts.
- Use span to avoid string allocations during parsing until we want to realise the node itself, in MiniYaml.FromLines.
- Change several callsites to use the streaming extension method rather than string method where possible.
2021-11-15 13:20:34 +01:00
penev92
270c566570 Fixed the Windows make script building in Release
This is a follow-up to PR 19379, which aimed to "provide an easy debug option for VSCode developers", but only did so for non-Windows users. VSCode has been building in Release mode for ever and continued to do so on Windows after that PR.
2021-11-14 10:07:18 +00:00
RoosterDragon
225bcbbd22 Normalize all support dir paths to end with a directory separator.
Previously, some paths used a separator and some did not. This broke some de-duplication logic in ExternalMods which tried to enumerate distinct paths but would end up running logic on the same directory more than one as it was provided both with and without a trailing directory separator. By normalizing the path this logic now works.
2021-11-13 22:24:43 +01:00
Vapre
d53601daa6 World, SyncHash, cache per tick. 2021-11-13 21:46:40 +01:00
RoosterDragon
dd9d600ef9 Change GetCustomMovementLayers to expose an array, not a dictionary.
As there are few custom movement layers, using an array is good for improving lookup speed. Additionally, we can simplify some code by reserving index 0 of the array for the ground layer. Code that needs to maintain a state for the ground layer and every custom movement layer can now maintain a flat array of state using index 0 for the ground layer, and the the ICustomMovementLayer.Index for the custom movement layer. This removes a lot of ternary statements checking for the ground layer special case.
2021-11-13 12:15:48 +00:00
Ivaylo Draganov
3310f14dea Use mission notifications pool with appropriate chat line template 2021-11-12 22:30:07 +01:00
penev92
a71da0a25a Unhardcoded engine credits file
Moved the file name/path to ModCredits, read from mod.yaml
2021-11-12 22:24:44 +01:00
Matthias Mailänder
9b1cec7712 Add support for gapless looping music. 2021-11-11 23:49:54 +01:00
abcdefg30
9916e4c4ac Fix a crash in the TSEditorResourceLayer neighbour validation 2021-11-11 15:09:24 +01:00
abcdefg30
31cec0c17f Fix a crash in the TSResourceLayer neighbour validation 2021-11-11 15:09:24 +01:00
reaperrr
430c7a4d7d Fix TD voice crashes
The original PR used too much copy-paste.
2021-11-08 14:46:41 +01:00
Matthias Mailänder
7d83d4d47f Add OmniSharp settings. 2021-11-08 14:41:34 +01:00
Matthias Mailänder
2fa1c05ed0 Ignore workspace settings. 2021-11-08 14:41:34 +01:00
Matthias Mailänder
f7fd3dfff8 Remove workspace settings. 2021-11-08 14:41:34 +01:00
abcdefg30
93c45255f1 Move a using into the Mono specific code path 2021-11-07 00:26:15 +01:00
abcdefg30
280dd8e2a5 Update launch-game.cmd to specify Engine.LaunchPath and support restarting 2021-11-06 11:21:13 +01:00
Paul Chote
5d83706eae Require an explicit launch path for mod registrations.
This also removes a workaround that allowed the current mod to be
registered even if it defined a bogus path. Uses of Game.ExternalMods
should therefore always use TryGetValue and correctly handle it
returning false.
2021-11-06 11:21:13 +01:00
Paul Chote
4275e87c57 Show an error prompt if restart mod switch fails. 2021-11-06 11:21:13 +01:00
Gustas
c5a6577cee Fix production scalling 2021-11-05 00:01:21 +01:00
Matthias Mailänder
ade5d211e9 IRC moved to Libera 2021-10-31 17:02:00 +01:00
Matthias Mailänder
e05b86b935 Recommend the OpenRA Lua extension. 2021-10-30 13:11:14 +02:00
Matthias Mailänder
e8ee31cd6e The launch settings have been migrated to .NET Core already. 2021-10-30 13:11:14 +02:00
Matthias Mailänder
59bed108b1 Minor csharp warning fixes. 2021-10-30 13:11:14 +02:00
abcdefg30
05420ab5ce Fix the Windows packing script not working with wget only 2021-10-29 22:25:59 +02:00
abcdefg30
32bc561878 Add a check for wine64 to the Windows packaging 2021-10-29 22:25:59 +02:00
abc013
311b6fcd83 Don't count actors owned by noncombatant players to kills statistic 2021-10-29 21:07:06 +02:00
abc013
c0da9f1eab Fix walls counting to kills/deaths statistic 2021-10-29 21:07:06 +02:00
Ivaylo Draganov
98434ef96b Add audio feedback for selection hotkeys 2021-10-29 20:55:41 +02:00
Ivaylo Draganov
9b9c116097 Add text feedback for type selection hotkey when selection is empty 2021-10-29 20:55:41 +02:00
Ivaylo Draganov
3c77df276a Add full-stop to UI feedback text notifications 2021-10-29 20:55:41 +02:00
Ivaylo Draganov
a680cae00c Move audio muted/unmuted notifications to feedback pool 2021-10-29 20:55:41 +02:00
Ivaylo Draganov
01d47566cc Add a transients panel with corresponding chrome logic and a notification template 2021-10-29 20:55:41 +02:00
Ivaylo Draganov
9e92340ea7 Rework chat line templates and logic
- Extract chat line templates and logic so they can be reused across widgets
- Make text notification styling entirely template driven (by removing chat color configuration and making color optional for `TextNotification`)
- Add a new TextNotificationsDisplay widget (based on and replacing ChatDisplayWidget)
- Add timestamp support to text notifications
2021-10-29 20:55:41 +02:00
abc013
8416dc3f2d Fix wrong subcell offsets for TS 2021-10-27 21:53:39 +02:00
abc013
c33290c19b Display error message instead of stack trace in battlefield news when failing to fetch news 2021-10-27 18:40:52 +02:00
teinarss
1a56cee9a1 Add Tick scale plumbing 2021-10-26 22:40:15 +02:00
Oliver Brakmann
7f3130c7a6 Use TimeLimitManager in mission Allies10a 2021-10-26 16:40:47 +02:00
Oliver Brakmann
be4466115d Do not update countdown label unless there is a timelimit 2021-10-26 16:40:47 +02:00
Smittytron
0c42f59656 Add Allies10a 2021-10-26 16:40:47 +02:00
Orb370
3a56532f0b Fix Europe Map Commit
Fix Europe Map Commit

Fix Europe Map Commit

Fix Europe Map Commit
2021-10-26 16:18:18 +02:00
abc013
442752b6ba Fix owner of selected actor not always being selected owner in editor 2021-10-24 22:54:10 +02:00
abc013
0898655175 Fix radar preview not updating when editing actor owner 2021-10-24 22:54:10 +02:00
RoosterDragon
3bee524c6b Avoid allocation in PackageEntry.HashFilename
This is a very frequently called method during loading, and taking some care to avoid allocations improves performance noticeably.
2021-10-24 21:40:01 +02:00
RoosterDragon
2ed4cb8aff Ensure Clear(T) also have a safety check to ensure no listener is attached.
Move related methods next to each other.
Change Clear(T) to use Array.Fill.
2021-10-23 15:43:47 +02:00
RoosterDragon
19760b04bd Allow the default value of a CellInfo to be an Unvisited location.
In CellInfoLayerPool, instead of having to store a layer with the default values, we know we can just clear the pooled layer in order to reset it. This saves on memory, and also makes resetting marginally faster.

In PathSearch, we need to amend a check to ensure a cell info is not Unvisited before we check on its cost.
2021-10-23 15:43:47 +02:00
Matthias Mailänder
6edd5d7bfa Avoid an unnecessary assignment. 2021-10-22 22:02:15 +02:00
Matthias Mailänder
a3ccc81892 Remove unused fields and parameters. 2021-10-22 22:02:15 +02:00
Matthias Mailänder
3b5bfb4bf4 Avoid initialisation of empty arrays. 2021-10-22 22:02:15 +02:00
Matthias Mailänder
5ff9d9a1f1 Add missing flags attribute required for HasFlags. 2021-10-22 22:02:15 +02:00
penev92
f056cbba13 Added a bunch of TraitLocationAttributes
Also moved a few traits to their proper subfolders.
2021-10-16 20:52:50 +02:00
penev92
67598dd151 Moved some traits from ^BaseWorld to World in TS 2021-10-16 20:52:50 +02:00
Paul Chote
166583b1ec Fix disabled chat focus issues. 2021-10-16 13:34:39 -05:00
penev92
e1e76411f7 Added a rule for unused usings to OpenRA.ruleset
Unfortunately due to bugs in the analyzers or something else, the IDE0005 doesn't work as expected. The "officially suggested" workaround is to enable XML documentation generation to trigger IDE0005 during compiling. Then we need to add three more rules to silence the warnings that come from the XML documentation generation. We also need to enable code style enforcing on build for all of this to work.
Known issue is that all of this produces a bunch (tens to hundreds) of obscure analyzer warnings on older versions of Visual Studio, but those seem to not be causing issues.
2021-10-15 13:12:33 +02:00
penev92
8ba6d13b2f Removed unused using directives 2021-10-15 13:12:33 +02:00
Matthias Mailänder
3790a4a6a4 Fix actor has multiple AttackBase traits. 2021-10-15 12:11:35 +02:00
abcdefg30
e8e0e155e5 Explain the try-finally clause in Sync.cs in a comment 2021-10-14 19:45:29 +02:00
abcdefg30
b366db7175 Revert "Directly run unsynced functions if we don't check sync hashes"
This reverts commit 1dc0a603c7.
2021-10-14 19:45:29 +02:00
penev92
3ce32fe354 Added faction-specific units for colorpicker previews in TS 2021-10-13 23:59:11 +02:00
Matthias Mailänder
2b361ad41e Add a link to the web chat
as ircs:// isn't linked on GitHub
and not everyone is familiar with IRC clients
2021-10-12 10:57:35 +02:00
Matthias Mailänder
f9f540f47e Website repository has changed. 2021-10-12 10:57:35 +02:00
Matthias Mailänder
c9b0ddf772 Add a link to Discord with badge. 2021-10-12 10:57:35 +02:00
Matthias Mailänder
139af0c2bc Fix a crash when building placements are moved out of the map. 2021-10-12 10:47:34 +02:00
Orb370
d88198e61e Allow Targeting While Defenses are in the Construction Animation Commit 2021-10-11 17:49:44 -05:00
RoosterDragon
df9398a871 Use named pathfinding constants.
- Rename CostForInvalidCell to PathCostForInvalidPath
- Add MovementCostForUnreachableCell
- Update usages of int.MaxValue and short.Maxvalue to use named constants where relevant.
- Update costs on ICustomMovementLayer to return short, for consistency with costs from Locomotor.
- Rename some methods to distinguish between path/movement cost.
2021-10-10 17:09:38 +02:00
abcdefg30
884e6cdb51 Exempt D2k Harvesters from slowness modifiers applied when damaged 2021-10-09 14:55:27 -05:00
abcdefg30
4269fc67d5 Remove hacks around checking sync while disposing the shellmap 2021-10-09 21:13:35 +02:00
abcdefg30
69248132ad Don't desync while the world is disposing 2021-10-09 21:13:35 +02:00
abcdefg30
1dc0a603c7 Directly run unsynced functions if we don't check sync hashes 2021-10-09 21:13:35 +02:00
Ivaylo Draganov
091d756c14 Change lobby options description to make sense in both disabled and enabled state 2021-10-09 08:43:28 -05:00
RoosterDragon
9bcbbdd3fc Remove unused code in Move. 2021-10-09 00:02:00 +02:00
abcdefg30
a930c123ed Initialize the Discord rich presence with an empty presence 2021-10-08 00:47:10 +03:00
abcdefg30
62c083bb01 Remove a bogus and unneeded import 2021-10-08 00:47:10 +03:00
Raf Czlonka
5cc1405f3f Update links
- _http_ `->` _https_ where appropriate
- _wiki.openra.net_ `->` _https://github.com/OpenRA/OpenRA/wiki_
- _bugs.openra.net_ `->` _https://github.com/OpenRA/OpenRA/issues_
- the IRC channel is now on Libera.Chat + make it into a URI

This is in order to be consistent (one link to GitHub wiki already
exists), reflect reality, as well as avoid unnecessary redirects.
2021-10-07 12:38:36 -05:00
VonNah
0e33d08384 Fixed incorrect voice lines for Generic-, Vehicle-, Moebius- and CommandoVoice.
Signed-off-by: VonNah <vonnahora@gmail.com>
2021-10-03 19:39:28 -05:00
abcdefg30
dc11b82fc9 Fix a NRE in DeliverUnit.ReleaseUnit.OnFirstRun 2021-10-02 22:01:24 +01:00
RoosterDragon
6e0917169d Teach FieldLoader.ParseCPos about CPos.Layer. 2021-10-02 21:54:26 +01:00
RoosterDragon
4cc33b2871 Add some ToString overrides to improve debug experience. 2021-10-02 21:54:26 +01:00
RoosterDragon
3a020e96fe Update ICustomMovementLayer to not depend on actor for costs.
No implementations require the actor.
2021-10-02 21:47:24 +01:00
RoosterDragon
6f3b4ecae9 Speed up Map.Contains checks for non-flat maps.
We observe that most cells within a map lie within a region where no matter their height, their projection would still remain in map bounds. We can utilise this to perform a fast check for such cells and skipping the expensive checks on their actual height. We only need to check the actual height of a cell if this could cause the projection to go out of bounds.
2021-10-02 21:43:11 +01:00
abcdefg30
d370cb48c5 Remove unreachable code in TransformsIntoTransforms 2021-10-02 21:34:51 +01:00
KonH
9ed809943d Just use Dispose methods without arguments (disposing always passed as true) 2021-10-02 22:14:08 +02:00
KonH
8bce6eb3ac Remove GC.SuppressFinalize calls from classes without destructors 2021-10-02 22:14:08 +02:00
teinarss
279e7eb1c9 Disable Stylecop on debug config 2021-10-02 21:13:58 +02:00
Paul Chote
defaf92752 Send order queue length in ping responses. 2021-10-02 21:03:00 +02:00
Paul Chote
2d08f2bbfd Allow the server to ack no or multiple packets in the same frame. 2021-10-02 21:03:00 +02:00
Zimmermann Gyula
a13d046304 Add twinkle effects to the gems. 2021-10-02 20:17:03 +02:00
Matthias Mailänder
a2a668077c Refactor RandomDelay:
Allow different types of random
and reflect other use cases by renaming.
2021-10-02 20:17:03 +02:00
RoosterDragon
3a7aeb5324 Ensure TargetLineRenderable width and marker size don't get lost.
By making the constructor take non-optional parameters, this highlights some calls sites which were forgetting to set these values. These are now fixed.

Set the path debug to have a marker size of 2 for better visibility.
2021-10-02 12:14:54 +01:00
Vapre
9d4d4bb924 Locomotor, PathGraph, trivial optimizations. 2021-10-01 22:21:47 +02:00
tovl
a428aaa602 Make sure OpenAlSound.Source is never accessed after the source is freed for reuse 2021-10-01 21:15:52 +02:00
tovl
2bc03b4d84 Fix OpenAlSound.Complete being incorrect when OpenAl source is reused. 2021-10-01 21:15:52 +02:00
teinarss
289c4ef2b7 Remove Microsoft.DotNet.PlatformAbstractions 2021-09-29 20:52:44 +02:00
teinarss
80b6a5a27f Update nuget packages 2021-09-29 20:52:44 +02:00
Ivaylo Draganov
7f2ac477a2 Improve Game.Mod argument check in launch script 2021-09-28 21:44:53 +02:00
Ivaylo Draganov
69b375dc48 Replace zenity select dropdown with a list 2021-09-28 21:44:53 +02:00
Vapre
e8bae2e50a Crash on capture fix. #19482. 2021-09-27 21:19:12 +01:00
Matthias Mailänder
6b3eee8481 Don't just swallow the web exception. 2021-09-27 20:16:22 +02:00
Paul Chote
3d73d5ef29 Remove incorrect comment and merge nested conditionals. 2021-09-27 19:48:05 +02:00
Paul Chote
a0d49729f5 Ignore modifiers when applying terrain damage. 2021-09-27 19:31:25 +02:00
Ikko Ashimine
a573052f2e Fix typo in rules.yaml
recieved -> received
2021-09-27 18:08:05 +02:00
Ivaylo Draganov
994ba35507 Add description to difficulty lobby option for missions 2021-09-26 21:25:08 +01:00
Ivaylo Draganov
2d178f5033 Do not center lobby options vertically in the scroll panel 2021-09-26 21:25:08 +01:00
Ivaylo Draganov
f018fdecdd Remove special handling for cases when there's only one game info tab
The addition of the lobby options tab ensures that there will always
be at least two tabs ("Objectives" and "Options").
2021-09-26 21:25:08 +01:00
Ivaylo Draganov
fa0adb5a1b Hide various lobby options from missions and minigames 2021-09-26 21:25:08 +01:00
Ivaylo Draganov
3e0834b4ef Display lobby options in-game on a dedicated tab 2021-09-26 21:25:08 +01:00
RoosterDragon
fc5f8fcd31 Fix indent 2021-09-26 15:49:48 +02:00
RoosterDragon
2f955e01f5 Fix style errors 2021-09-26 15:49:48 +02:00
Paul Chote
c958bf9680 Restore threaded renderer. 2021-09-26 15:19:20 +02:00
Matthias Mailänder
98b87004cc Bump SharpZipLib. 2021-09-26 11:18:15 +01:00
Paul Chote
8588af1001 Disable chat for the first 5s (configurable) after joining a server. 2021-09-23 12:52:20 +02:00
teinarss
9eab92e90a Add Rich Presence button with link to website 2021-09-22 15:57:54 +02:00
Paul Chote
2424ddc79a Move NetTickScale (now NetFrameInterval) control to the server. 2021-09-21 15:12:36 +02:00
Paul Chote
df798fb620 Overhaul client latency calculations.
The ping/pong orders are replaced with a dedicated
(and much smaller) Ping packet that is handled
directly in the client and server Connection wrappers.

This allows clients to respond when the orders are
processed, instead of queuing the pong order to be
sent in the next frame (which added an extra 120ms
of unwanted latency).

The ping frequency has been raised to 1Hz, and pings
are now routed through the server events queue in
preparation for the future dynamic latency system.

The raw ping numbers are no longer sent to clients,
the server instead evaluates a single ConnectionQuality
value that in the future may be based on more than
just the ping times.
2021-09-21 15:12:36 +02:00
Paul Chote
67face8cf0 Rename connection packet handling. 2021-09-21 15:12:36 +02:00
Paul Chote
4eefa637a3 Remove obsolete threaded renderer workarounds. 2021-09-20 22:33:25 +02:00
Paul Chote
be8e2cf3a4 Move OpenGL context creation to the main thread. 2021-09-20 22:33:25 +02:00
Matthias Mailänder
f08a0b113e Allow mods to customize application title. 2021-09-19 22:29:50 +02:00
Matthias Mailänder
3a8957c6f3 Remove unused function parameter. 2021-09-19 22:29:50 +02:00
Smittytron
99006a02ed Add destroy triggers for prison actors in Allies03a 2021-09-18 21:26:10 +02:00
Smittytron
77e1e77387 Use FireClusterWarhead with barrels 2021-09-18 21:26:10 +02:00
Matthias Mailänder
0604a58581 Send a ping when the server is shut down
and stop the LAN beacon.
2021-09-18 17:38:52 +01:00
Matthias Mailänder
58e482c05a Fix hashset documentation. 2021-09-18 13:16:25 +01:00
Matthias Mailänder
eba266aecf Use MarkDown tables in trait documentation export. 2021-09-18 12:52:27 +01:00
Matthias Mailänder
5bf4daddec Set the default game name to the player name. 2021-09-15 18:39:24 +02:00
Paul Chote
54c08748e0 Overhaul player disconnect notifications. 2021-09-12 22:53:50 +02:00
abcdefg30
b8e343bee9 Fix Phase Transports not uncloaking while unloading 2021-09-12 10:47:33 +02:00
Vapre
5ae4662f08 RunUnsynced, do not recalculate sync hash on reentry. Cache debug settings. 2021-09-12 10:06:44 +02:00
Paul Chote
b3159d7515 Fix d2k map editor crash if spice is placed at the edge of map. 2021-09-09 10:19:17 +02:00
abcdefg30
a1e62158e2 Add a workaround for "Check around synced code" problems 2021-09-08 20:26:53 +02:00
abcdefg30
777d966958 Remove unnecessary server creation when creating a new map 2021-09-08 20:26:53 +02:00
abcdefg30
0b75991fbc Make the editor use an EchoConnection instead of a local server 2021-09-07 20:45:50 +02:00
Paul Chote
e0e219793f Fix shellmap OrderManager use after dispose. 2021-09-02 23:23:23 +02:00
Paul Chote
8f412f869d Move order latency control to the server. 2021-09-02 23:23:23 +02:00
Paul Chote
6421c17515 Rework IConnection implementations:
* EchoConnection is now a trivial buffer that stores
  and repeats orders directly without serialization.

* NetworkConnection no longer subclasses EchoConnection,
  and now also caches local orders without serialization.

* Replay recording was moved to NetworkConnection
  (it is never used on EchoConnection).
2021-09-02 23:23:23 +02:00
Paul Chote
408f30b5cd Extract OrderIO.SerializeOrders helper.
Note: This changes immediate orders to no longer be
individually framed in their own packets.
2021-09-02 23:23:23 +02:00
teinarss
db74f155bb Update DropClient to always drop Connection 2021-09-02 08:05:37 +02:00
Matthias Mailänder
978de64903 Don't count suicides into the game score. 2021-09-02 08:00:44 +02:00
Matthias Mailänder
e616cd1bcb Save the player color in the introduction panel. 2021-09-02 07:39:54 +02:00
Andre Mohren
2c84c43607 Fixed odd sprite size "frame hopping". 2021-09-02 07:34:13 +02:00
Paul Chote
442d91537e Don't display spectator info for scripted players. 2021-08-28 23:15:02 +02:00
Paul Chote
7f92d64d84 Disable replay player visibility dropdown in singleplayer missions. 2021-08-28 23:15:02 +02:00
Paul Chote
c7700a8a2b Fix DropDownButtonWidget ignoring yaml separator overrides. 2021-08-28 23:15:02 +02:00
Paul Chote
e389c00a11 Remove packet byte wrangling from OrderManager. 2021-08-26 22:00:59 +02:00
Paul Chote
52b597d5d2 Remove order latency checks from BaseBuilderQueueManager. 2021-08-26 22:00:59 +02:00
Paul Chote
8a587ddeab Disable depth-buffer command if EnableDepthBuffer is disabled. 2021-08-25 17:42:44 +02:00
Paul Chote
512dee0ac0 Fix rendering glitches with EnableDepthBuffer disabled. 2021-08-25 17:42:44 +02:00
Matthias Mailänder
024beacafb Don't smear the paste while moving the mouse. 2021-08-22 16:06:21 +02:00
teinarss
b4256df9c1 Cleanup in Connection 2021-08-22 09:43:25 +01:00
reaperrr
c860bf19ee Add GDI 05c
Well, 05eb really, but until we implement campaign progression,
calling it 'c' is the easiest way to differentiate it without breaking
our mission title length/alignment conventions.
2021-08-21 20:34:50 +02:00
Paul Chote
06ea7bf923 Regenerate TS map previews. 2021-08-21 14:16:02 +02:00
Paul Chote
3551eb3128 Fix map preview bounds calculation. 2021-08-21 14:16:02 +02:00
Paul Chote
7f94d67d39 Replace Map.CustomTerrain radar colors with IRadarTerrainLayer.
* TSVeinsRenderer now shows border cells on the radar
* BuildableTerrainLayer now uses the radar colors defined on the individual tiles
* CliffBackImpassabilityLayer no longer overrides the underlying terrain color.
2021-08-21 14:16:02 +02:00
Paul Chote
72c0c7e38b Remove rocks and trees from TS minimap previews. 2021-08-21 14:16:02 +02:00
Paul Chote
2a2785d8c9 Fix model debug sprite rectangle rendering. 2021-08-21 13:45:41 +02:00
Paul Chote
7a3dae428a Work around clipping on ramp type 7. 2021-08-21 13:45:41 +02:00
Paul Chote
dcd3e8d444 Ignore terrain slopes when calculating model shadows.
This is less realistic, but better matches the original
game and is the only practical way to reduce visual issues
caused by long shadows being cast over multiple cells.
2021-08-21 13:45:41 +02:00
Paul Chote
68710e48a6 Add terrain orientation support for Mobile. 2021-08-21 13:45:41 +02:00
Paul Chote
c88d1bbefa Add WRot SLerp and axis-angle ctor.
These allow more complex calculations to be made
using rotations.
2021-08-21 13:45:41 +02:00
Paul Chote
d509d3f5f9 Fix carryall InitialActor creation. 2021-08-20 21:04:18 +02:00
Smittytron
8a4945d20a Polish and standardize Allies01 2021-08-20 20:48:05 +02:00
Andre Mohren
0ea6a5626f Do not enforce mods to use stylecop. 2021-08-20 20:45:31 +02:00
Paul Chote
864cc4becc Fix weather particle physics.
The trait documentation specified that the speed
and offset values are px/tick, but they have actually
always been treated as px/render.

Fix the update logic and rescale the map definitions
to account for the fixed behaviour.
2021-08-20 20:38:37 +02:00
Paul Chote
d2257f9784 Fix macOS compat build dock icon, app name, dark mode support. 2021-08-20 20:34:23 +02:00
Paul Chote
f70457a66f Remove unused symbol lookups. 2021-08-20 20:34:23 +02:00
Paul Chote
278a4acf96 Fix Engine.LaunchPath for macOS compatibility builds. 2021-08-20 20:34:23 +02:00
Paul Chote
be3412ee74 Fix depth offsets for sprite with non-zero ZRamp.
Bibs and other effects that should be drawn at ground level
can now simply define ZRamp: 1, Offset: <X>,<Y>,1, avoiding the
need to account for the Y offset or internal sprite offsets.
2021-08-20 20:17:55 +02:00
Paul Chote
27f9f35efb Fix incorrect world height to screen depth conversion. 2021-08-20 20:17:55 +02:00
Paul Chote
ef1aee5e95 Fix and document the depth buffer calculations. 2021-08-20 20:17:55 +02:00
Paul Chote
962d6496bd Overhaul depth preview rendering:
* Replace Jet color map with grayscale
* Added Shift + \ hotkey to toggle preview
* Added Shift + [, Shift + ] hotkeys to change contrast
* Added Shift + ;, Shift + ' hotkeys to change offset
2021-08-20 20:17:55 +02:00
Paul Chote
91c5f2cabe Add Cityscape map. 2021-08-20 20:17:55 +02:00
Paul Chote
a9c6430924 Fix TS map importer:
- Fix infantry subcells and facing
- Import sandbags/walls.
2021-08-20 20:17:55 +02:00
Vapre
2ea2815529 CallFunc, avoid null check. 2021-08-20 12:31:04 +02:00
Paul Chote
5eb4c6a0bb Set _KDE_NET_WM_DESKTOP_FILE x11 window property.
This explicitly tells KDE to associate the OpenRA window with
the integrated appimage desktop file, allowing it to use the
correct resolution icon for the task switcher.
2021-08-16 20:18:00 +02:00
abcdefg30
4145723ef0 Remove Sdl2HardwareCursorException 2021-08-14 17:11:34 +01:00
abcdefg30
b8ba1b36fe Don't throw an exception when creating a hardware cursor fails 2021-08-14 17:11:34 +01:00
Matthias Mailänder
f63e15f5de Fix igloos being labeled as haystacks in-game. 2021-08-11 19:09:20 -05:00
MustaphaTR
2f44b016b0 Add a condition per tileset. 2021-08-11 19:09:20 -05:00
Matthias Mailänder
b7bba5d55a Restrict sonar pulse to unshrouded water. 2021-08-11 22:46:19 +02:00
teinarss
fba4c5049c Replace Thread.CurrentThread.ManagedThreadId with Environment.CurrentManagedThreadId 2021-08-10 23:02:18 +02:00
Vapre
573a6cf645 FindAndDeliverResources, trivial optimizations. 2021-08-10 23:59:43 +03:00
reaperrr
eff7e803bf Minor MoveOrderTargeter optimization
IsTraitPaused should be cheaper than Map.Contains,
so let's perform the cheaper check first.
2021-08-10 18:26:05 +02:00
reaperrr
58f55b808a Add comment in Mobile
This isn't obvious to people not entirely familiar
with the code.
2021-08-10 18:26:05 +02:00
reaperrr
777a927c04 Cache unchanging values for MoveWithinRange
In theory, CandidateMovementCells could be called
every tick, so let's avoid creating the vars
every time.
2021-08-10 18:26:05 +02:00
Matthias Mailänder
0249116206 Rename instant to fast charge to avoid tab complete clash. 2021-08-09 13:17:47 -05:00
Ivaylo Draganov
2d0e7040db Add separate chat panel for spectators and players
Forces the chat and performance panels to be re-initialized when a
player transitions to spectators. This ensures that spectators don't
get to see faction themed widgets.
2021-08-08 12:50:31 +01:00
Paul Chote
edd3a2eb75 Fix ingame menu tab display. 2021-08-07 13:12:56 -05:00
Paul Chote
29f4f5a0cd Remove redundant ts ingame-info.yaml. 2021-08-07 13:12:56 -05:00
abcdefg30
ba7e1319ac Remove DisableWindowsRenderThread and use threaded renderering for non windowed mode 2021-08-07 14:49:09 +01:00
Ivaylo Draganov
7a1169744e Add tooltips to map name and author in map chooser 2021-08-05 18:36:50 +02:00
abcdefg30
453d59ae16 Defer rollover checks while generating selection decorations 2021-08-05 01:43:35 +03:00
Vapre
35e9fade06 PathGraph, skip closed cells early. Fix #19579. 2021-08-04 20:02:12 +02:00
Paul Chote
8fc042fed1 Fix style nits in OrderEffects. 2021-08-02 21:50:32 +02:00
Paul Chote
2c5fce5e3c Add missing TraitLocation to OrderEffects. 2021-08-02 21:50:32 +02:00
Paul Chote
7a93ff3258 Add support for TS-style tinted target flashes. 2021-08-02 21:50:32 +02:00
Paul Chote
9291263609 Fix indentation in OrderEffects. 2021-08-02 21:50:32 +02:00
Matthias Mailänder
f4a5878a53 Fix veinholes on Sunstroke. 2021-08-01 14:56:53 -05:00
Paul Chote
1ecb3bf99a Don't add empty Translations nodes to maps. 2021-07-31 13:14:46 +02:00
reaperrr
c88994a0dd Fix unarmed civilians being selected as combat units 2021-07-30 09:03:29 -05:00
fruehstueck
29b0999da3 Add GDI 8b 2021-07-30 09:03:29 -05:00
Paul Chote
b08117dc93 Don't report "Primary Building Selected" when nothing changes. 2021-07-29 16:19:53 +02:00
Paul Chote
99322cee8f Set the closest production to Primary when force-targeting rallypoints. 2021-07-29 16:19:53 +02:00
Vapre
e201e410f4 PathGraph, skip closed cells early.
In path finding GetConnections considers connections to already closed cells and calculates the cost for them. The caller afterwards ignores them. These are 15% of all connections.
2021-07-27 14:49:22 +02:00
Paul Chote
24f64ae1a8 Fix an edge case where the wrong sheet may be mapped to a depth sprite. 2021-07-25 19:20:19 +02:00
Ivaylo Draganov
1f3f489328 Show map preview tooltip only if needed 2021-07-25 00:25:08 +01:00
Ivaylo Draganov
31056d4253 Add tooltip to overflowing map title in server browser 2021-07-25 00:25:08 +01:00
Paul Chote
70892a6661 Change DrawSprite calls to provide scales instead of sizes.
This allows us to remove a hacky workaround for calculating
depth offsets when sprites have size.Z == 0.
2021-07-25 00:32:17 +02:00
Paul Chote
8e94e1d5ec Rework WidgetUtil sprite rendering helpers. 2021-07-25 00:32:17 +02:00
Vapre
2e6f444285 Map.Contains check grid type once. 2021-07-24 15:34:56 +02:00
abcdefg30
dcaa658678 Remove an outdated reference to ConditionManager 2021-07-23 11:04:07 -05:00
Hang
249bc4abc8 Fix the hide-map sequence of the d2k crate-effects
Frame 4218 belongs to explosion/nuke sequence, which should not part of crate-effects/hide-map sequence
2021-07-22 23:13:16 +02:00
Hang
757ca0561f Update misc.yaml
Typo Lenght -> Length
2021-07-21 13:36:25 +02:00
Vapre
001134ce59 Do not start DiscordService client if disabled. 2021-07-21 12:13:21 +02:00
Paul Chote
f8ed768e39 Change makefile/packaging "RUNTIME=dotnet" to "RUNTIME=net5". 2021-07-18 20:57:22 +02:00
Chris Harris
1c6ca394c1 Fix duplicate ActorIDs 2021-07-17 23:27:25 +02:00
Vapre
627fd4e68b CellLayer.Index inline CPos.ToMPos. 2021-07-16 09:51:55 +02:00
Paul Chote
7bfe83cce7 Fix WithIdleOverlay PlayerPalette editor rendering. 2021-07-15 12:05:09 +02:00
Matthias Mailänder
d169210531 Display the production overlay only where the unit will exit. 2021-07-14 19:06:22 -05:00
Matthias Mailänder
902006bf53 Fix editor losing copy after each paste. 2021-07-13 21:01:33 -05:00
Mustafa Alperen Seki
dcb70d12e3 Make Harvester conditional. 2021-07-12 15:41:04 +02:00
Matthias Mailänder
cd90c70cdf Fix a null reference exception. 2021-07-12 15:20:34 +02:00
Matthias Mailänder
f1f5df3749 Fix .shp icons being misdetected as .wsa animations. 2021-07-12 11:49:06 +02:00
Matthias Mailänder
b2f18ad0ad Add support for .ogg files. 2021-07-12 00:35:16 +02:00
Matthias Mailänder
af0b7621d2 Minor fixes. 2021-07-12 00:35:16 +02:00
Matthias Mailänder
2bd7869059 Fix haystacks being selectable buildings. 2021-07-12 00:15:38 +02:00
Matthias Mailänder
e1816a6da0 Fix a crash when opening map Chernobyl with the editor. 2021-07-11 16:38:27 +01:00
teinarss
f3777a25e6 Refactor send and receive Orders loop 2021-07-10 23:35:47 +02:00
teinarss
5e1468facb Fix NRE with ConnectionStateChanged 2021-07-08 01:12:05 +02:00
Matthias Mailänder
3e6e5a83f3 I feel like I am back. 2021-07-07 15:20:47 +02:00
Paul Chote
25c095619a Parse Mp3 length from metadata tags. 2021-07-07 15:20:47 +02:00
Matthias Mailänder
407372268d Add support for .mp3 files. 2021-07-07 15:20:47 +02:00
Paul Chote
aa798c252b Remove "X has joined the game." notification when starting a Skirmish. 2021-07-06 13:12:51 +02:00
Ivaylo Draganov
9c658ad36b Increase the opacity of panel-gray-gdi 2021-07-06 10:13:43 +02:00
Ivaylo Draganov
2923077b3d Style TD in-game chat and performance graph with faction colors 2021-07-06 10:13:43 +02:00
Ivaylo Draganov
cbdf6c3747 Add faction suffix support to text fields and scroll panels 2021-07-06 10:13:43 +02:00
Ivaylo Draganov
9687988976 Use pattern matching syntax in AddFactionSuffixLogic 2021-07-06 10:13:43 +02:00
Ivaylo Draganov
64e76e1a90 Make text fields yield keyboard focus on "Esc" in a consistent way
- search fields clear the input and yield if empty
- chat field and actor edit field yield without clearing
2021-07-04 23:37:29 +02:00
reaperrr
df8295fa2c Make aircraft turn speed scale with speed modifiers 2021-07-04 21:26:45 +01:00
reaperrr
0ac277a88d Improve Aircraft TurnSpeed getters readability 2021-07-04 21:26:45 +01:00
reaperrr
5a548d6acc Introduce IdleMovementSpeed
That actually factors in speed modifiers and trait pause/disable.
2021-07-04 21:26:45 +01:00
reaperrr
1262a9c6c9 Minor FlyIdle perf optimization
None of these prerequisites change on the fly,
so cache the result in the activity ctor.
2021-07-04 21:26:45 +01:00
reaperrr
5ecb3eec16 Fix IdleTurnSpeed ignoring trait pause/disable 2021-07-04 21:26:45 +01:00
Ivaylo Draganov
2ea6bfba7b Allow the player to toggle the display of UI Feedback chat pool 2021-07-04 14:02:58 +01:00
Ivaylo Draganov
6af354ff99 Split chat lines into pools
- Add a common class for passing around chat lines
- Add wrapper methods for adding chat lines
- Combine repeated chat lines in the display widget
2021-07-04 14:02:58 +01:00
abc013
0a02bd524a Update DestroyResourceWarhead to support Resource type and amount options. 2021-07-04 11:13:05 +01:00
Paul Chote
f9b058d36b Add click sounds to color widgets. 2021-07-03 12:18:25 -05:00
Paul Chote
bc51461427 Make sure the hue returned by Color.RgbToHsv is positive. 2021-07-03 12:18:25 -05:00
Paul Chote
fca12fd707 Validate custom color picker colors. 2021-07-03 12:18:25 -05:00
teinarss
1f1373509e Refactor server orders 2021-07-03 14:55:03 +01:00
Abdurrahmaan Iqbal
a8900d9860 Rebind chat hotkeys to prevent Tab changing chat mode
Enter/Shift+Enter now toggle team/all chat respectively and Shift+Tab switches chat mode instead of Tab
2021-07-03 14:49:03 +01:00
Abdurrahmaan Iqbal
6967c1fff3 Pass KeyInput to OnKey functions 2021-07-03 14:49:03 +01:00
abc013
2742985520 Prevent saving and starting a map when max player count is exceeded. 2021-07-03 08:25:16 -05:00
Smittytron
347a09e6cf Switch remaining RA missions to Normal default difficulty 2021-07-03 00:25:53 +02:00
Smittytron
e1a28078bb Fix difficulty dropdown for RA mission Sarin Gas 2: Down Under 2021-07-03 00:25:53 +02:00
Smittytron
27562ab88f Fix and standardize TD mission difficulty dropdowns 2021-07-03 00:25:53 +02:00
reaperrr
9371cecc00 Render disabled targetable positions in gray
Instead of not rendering them at all.
Also moved their debug overlay to HitShape.
2021-07-02 10:58:29 +01:00
reaperrr
feba9f2b9f Make orientation caching in HitShape readonly
HitShape Requires<BodyOrientation> anyway.
2021-07-02 10:58:29 +01:00
reaperrr
6d55161043 Show disabled HitShapes in gray
Instead of disabling their debug overlay entirely.
2021-07-02 10:58:29 +01:00
Andre Mohren
7356f2506b Moved flashimage to world trait. 2021-07-01 16:38:51 +02:00
Ivaylo Draganov
ad4425d11e Use cached transforms for images in widgets 2021-07-01 14:21:22 +01:00
Matthias Mailänder
c8ab409d38 Allow mod code to access the buildable terrain overlay. 2021-07-01 12:41:06 +01:00
Smittytron
9a587d2aaa Standardize player naming in Ant01 2021-06-30 23:11:10 +02:00
Smittytron
bcbd2418b4 Adjust hard difficulty in Ant01 2021-06-30 23:11:10 +02:00
Andre Mohren
8a776c7138 32bpp bullets no longer crash without palette. 2021-06-30 22:58:03 +02:00
Andre Mohren
6810469634 Updated copyright years. 2021-06-29 18:33:21 -05:00
VonNah
5a7a09a6a7 Fixed the crumble-overlay of all the buildings. 2021-06-27 15:29:57 +02:00
Leo512bit
e60e7beeef Slower damaged units 2021-06-26 09:24:30 -05:00
Ivaylo Draganov
9313638997 Fix handicap dropdown disabled state 2021-06-25 09:25:17 -05:00
Ivaylo Draganov
1af2ab566e Fix player handicap erroneously set to player team 2021-06-25 09:25:17 -05:00
Andre Mohren
de6b8b6a74 32bpp terrain does not need a palette. 2021-06-25 10:38:53 +02:00
Andre Mohren
62207313a0 32bpp assets dont need a palette to be defined. 2021-06-25 10:33:54 +02:00
teinarss
b3b10729cd Remove ServerExternalMod from OrderManager 2021-06-12 12:50:51 +02:00
teinarss
91f99eeb4c Cleanup in Connect 2021-06-12 12:50:51 +02:00
teinarss
a1b3450b47 Remove Password and Endpoint from OrderManager 2021-06-12 12:50:51 +02:00
Matthias Mailänder
da4bf7f191 Document and test the building placement preview for Dune 2000. 2021-06-12 12:43:03 +02:00
tjk-ws
a893cf9cb6 Unhardcode weapon ammo consumption
fix gh actions
2021-06-11 10:21:24 -05:00
Ivaylo Draganov
9fa5dcc055 Remove unused method in WidgetUtils 2021-06-11 10:16:34 -05:00
abcdefg30
fe146cb77a Remove superfluous Buildable traits from dummy helper actors 2021-06-10 19:22:46 -05:00
Vapre
fc49d6943a OrderIO performance improvement
OrderIO, ToOrderList, skip if frame is empty,
which occurs often each client each frame.
2021-06-09 23:31:27 +02:00
Matthias Mailänder
44b2dda585 Add an editor overlay for unbuildable terrain. 2021-06-06 21:14:44 +02:00
Matthias Mailänder
3980e4fa90 Use consistent and easy to read debug command names.
Reorganize dev cheat command handling.
2021-06-06 18:40:48 +02:00
darkademic
ca2bef3cd1 Updated TD unit speeds to account for move jumpy-ness fix. 2021-06-04 23:20:13 +02:00
darkademic
cbd82c7204 Updated RA unit speeds to account for move jumpy-ness fix. 2021-06-04 23:17:41 +02:00
teinarss
82115c6bf7 Move Text handling to its own class 2021-06-04 21:47:39 +02:00
Paul Chote
2a26ddc622 Replace server Select loop with individual client threads.
This guarantees that any unexpected blocking calls due to network
issues cannot stall the main server thread.
2021-05-30 14:37:25 +02:00
Paul Chote
6535411744 Move SendData to Connection. 2021-05-30 14:37:25 +02:00
Paul Chote
7c02b4d264 Add Connection.EndPoint property. 2021-05-30 14:37:25 +02:00
Paul Chote
7e79e69eae Remove public mutable state from Connection. 2021-05-30 14:37:25 +02:00
Paul Chote
dacacdf130 Remove global fallback to software cursors on error.
We now only fall back for the specific cursors that failed.
2021-05-30 12:01:44 +02:00
abc013
8fede9d6ba Add ValidStances checks to BlocksProjectiles and Gate. 2021-05-27 21:37:37 +02:00
teinarss
d10c592987 Fix crash in OrderManager 2021-05-23 12:44:18 +01:00
reaperrr
acccb01c76 Fix Move regression
If progress == Distance, we must not move again on the same tick,
but still 'return true' to avoid losing a tick in the case
when this is the last Move tick followed by a different activity
(or a new queued Move, for example via waypoints).
2021-05-23 10:49:40 +02:00
Matthias Mailänder
6876fe45e1 Use nameof for additional robustness in trait documentation. 2021-05-22 23:22:31 +01:00
Matthias Mailänder
52a4b5acd7 Fix documentation about GeneratesShroud to CreatesShroud rename. 2021-05-22 23:22:31 +01:00
Smittytron
92b6401360 Remove Thief from Exodus 2021-05-22 23:29:42 +02:00
Smittytron
ea24a15011 Set mission default difficulties to normal 2021-05-22 23:29:42 +02:00
Smittytron
243dc965c0 Move IdleHunt function to campaign-global.lua 2021-05-22 23:29:42 +02:00
Smittytron
f1f9098109 Move TRUK prereq overrides to campaign-rules.yaml 2021-05-22 23:29:42 +02:00
Smittytron
79b3f9bf06 Use proper title case conventions in mission names 2021-05-22 23:29:42 +02:00
Smittytron
dff245a9ce Fix tile error in Soviet09 2021-05-22 23:29:42 +02:00
Smittytron
91f9fa0f1f Add Ant 03 2021-05-22 13:42:09 +02:00
Smittytron
6f919fc232 Change anthill to correct impassable tiles 2021-05-22 13:42:09 +02:00
Smittytron
ce277481c3 Add Aftermath mission In the Nick of Time 2021-05-22 13:31:40 +02:00
Smittytron
d2039a14e1 Add Counterstrike mission Fall of Greece 2: Evacuation 2021-05-22 13:27:46 +02:00
Paul Chote
a27ef16911 Add EngineRootPath csproj property to simplify SDK inheritance. 2021-05-22 11:22:26 +02:00
Paul Chote
dc3dc7df73 Fix map-player bots not working on dedicated servers.
The host is almost never going to be client index 0
on public dedicated servers. Set to the actual host
client ID instead.
2021-05-22 11:18:45 +02:00
abcdefg30
872adbec0a Remove crate crushing from visceroids in TD and TS 2021-05-17 09:57:43 +02:00
Paul Chote
c64cfea179 Allow mods to downscale framebuffer resolution for large world viewports. 2021-05-16 14:22:52 +02:00
Paul Chote
84dff779ac Work around rendering glitches with non-unity pixel scales. 2021-05-16 14:22:52 +02:00
Paul Chote
0d4b81fe6f Set world framebuffer size based on minimum zoom.
This avoids reallocating buffers each time the player changes zoom level.
2021-05-16 14:22:52 +02:00
Paul Chote
0735345674 Set world framebuffer size based on minimum zoom.
This avoids reallocating buffers each time the player changes zoom level.
2021-05-16 14:10:32 +02:00
Paul Chote
bb15bd20c0 Add support for 16 bit floating point textures. 2021-05-16 14:10:32 +02:00
Paul Chote
95f5d162ef Increase SheetCount back to 8.
This was previously decreased to support legacy GPUs
that only supported 8 texture image units and we need
to reserve one of these for the palette texture.

OpenGL 3.X mandates a minimum of 16 (and most most GL2
cards also supported it) so we can now safely increase
this limit.
2021-05-16 14:10:32 +02:00
Smittytron
7967a462a1 Add override to prevent Orca construction in Nod07c 2021-05-16 13:27:39 +02:00
Smittytron
e225785744 Remove Medium Tank overrides from Nod08a and Nod08b 2021-05-16 13:27:39 +02:00
Smittytron
46dcdfa58e Remove Light Tank override from GDI05a 2021-05-16 13:27:39 +02:00
Smittytron
f8debe340f Add building override for Chem Trooper in GDI05b 2021-05-16 13:27:39 +02:00
Smittytron
83f99727a7 Change E3 to Infantry.Nod in GDI03 2021-05-16 13:27:39 +02:00
Smittytron
cbf84f62d4 Use correct overrides in GDI02 2021-05-16 13:27:39 +02:00
VonNah
fa68954dda Fixed incorrect Harkonnen Devastator warhead impact sound. 2021-05-15 23:07:25 +02:00
Paul Chote
98caae106f Move Palette traits to their own directory.
Also adds missing TraitLocation definitions.
2021-05-15 15:29:46 +02:00
Paul Chote
3bc42543fa Decouple color picker palette definitions to their own trait. 2021-05-15 15:29:46 +02:00
Paul Chote
57d955ec72 Change Color.ToAhsv to tuple syntax. 2021-05-15 15:29:46 +02:00
Paul Chote
96e333a30e Fix TS colorpicker turret facing. 2021-05-15 15:29:46 +02:00
Paul Chote
560f1a6466 Restrict player color choices to the hue-saturation plane. 2021-05-15 15:29:46 +02:00
Paul Chote
4042d5b179 Preserve original brightness when remapping player colors. 2021-05-15 15:29:46 +02:00
Paul Chote
9d62ce214c Move color picker actor type from metrics to ColorPickerManager. 2021-05-15 15:29:46 +02:00
Paul Chote
f65de2dd43 Merge ColorPreviewManagerWidget into ColorPickerManager. 2021-05-15 15:29:46 +02:00
Paul Chote
7b58f03f1c Move ColorValidator logic into a new ColorPickerManager trait. 2021-05-15 15:29:46 +02:00
oldnaari
52577c1de9 Fix Atreides silo damaged sequence
Fix the Atreides silo rendering as full silo instead of damaged silo when damaged
2021-05-14 13:43:13 +02:00
reaperrr
fc3f200357 Replace F in OrderManager 2021-05-09 15:00:07 +01:00
Paul Chote
d89f14dcbc Limit resource center queries to 50 maps at a time. 2021-05-09 15:37:30 +02:00
teinarss
96b8273916 Remove FrameData and update OrderManager 2021-05-09 15:08:48 +02:00
Smittytron
eda79d8626 Add Soviet10 2021-05-08 23:57:07 +02:00
teinarss
0c50057220 Update KickSpectators clientCount to be passed as int 2021-05-08 22:20:59 +02:00
teinarss
10676be377 Replace F extension with string interpolation 2021-05-08 22:20:59 +02:00
teinarss
1385aca783 Move ConnectionTarget to its own file 2021-05-08 20:41:40 +02:00
abcdefg30
121959efe4 Update the Desc of AttackBase.FacingTolerance 2021-05-04 10:44:23 -05:00
Paul Chote
8d2ec78713 Replace TerrainType.CustomCursor with Mobile.TerrainCursors. 2021-05-04 11:56:23 +02:00
Paul Chote
01371f2c65 Expose TransformsIntoAircraft move cursor to yaml. 2021-05-04 11:56:23 +02:00
Paul Chote
b344bba59a Expose Aircraft move cursor to yaml. 2021-05-04 11:56:23 +02:00
Paul Chote
a6d393f19b Fix a crash when using legacy GL. 2021-05-04 09:46:31 +02:00
Matthias Mailänder
7e19d6a205 Fix Player and Actor Properties / Commands rendering. 2021-05-04 01:13:18 +02:00
reaperrr
92a9f1e234 Split expansion mission categories in Allied and Soviet
It's not always apparent from the title which faction a mission
belongs to, and players may want to know beforehand
instead of having to guess.
2021-05-03 19:22:37 +01:00
Greg Solo
c39f7e521a Use shell param instead of duplicating the flow 2021-05-03 19:17:03 +01:00
Greg Solo
c9b9efe745 Setup debug make to support vscode debug 2021-05-03 19:17:03 +01:00
Vapre
a5a371f1ff GameSettings, EnableDiscordService. 2021-05-03 11:42:57 +01:00
Matthias Mailänder
b491e892ff Lint check the harvest cursor. 2021-05-03 10:58:35 +01:00
reaperrr
771932354b Remove unused leftovers from GDI08a
Those became redundant due to using global MoveAndHunt
in SendWaves.
2021-05-01 20:48:28 +02:00
reaperrr
89f270b67c GDI08a typo fix 2021-05-01 20:48:28 +02:00
abcdefg30
3276373745 Pause rendering when the window is minimized 2021-04-26 21:56:20 +01:00
teinarss
5667081764 Fix log should be disposed correctly 2021-04-26 21:50:26 +01:00
teinarss
b23d533006 Update Log to wait for items in worker thread 2021-04-26 21:50:26 +01:00
Matthias Mailänder
1f01d0b6b1 Add a Fluent based translation system. 2021-04-24 16:49:17 +02:00
reaperrr
f1a9a5180d Use real (milli)seconds for notifications
Allows for more fine-grained control, while
adding independence from gamespeed and getting
rid of magic * 25 multiplications.
2021-04-21 19:34:16 +02:00
Paul Chote
bb8a634ba8 Fix map installation. 2021-04-21 18:57:44 +02:00
Paul Chote
f9294f0e9e Enable dedicated server lint checks. 2021-04-21 18:57:44 +02:00
Paul Chote
9770967b04 Optimize MapPreview rule loading. 2021-04-21 18:57:44 +02:00
Paul Chote
0bbb32e8ac Rework MapPreview custom rule handling.
The previous asynchronous approach did not work particularly well,
leading to large janks when switching to custom maps or opening the
mission browser.

This commit introduces two key changes:

 * Rule loading for WorldActorInfo and PlayerActorInfo is made
   synchronous, in preparation for the next commit which will
   significantly optimize this path.
 * The full ruleset loading, which is required for map validation,
   is moved to the server-side and managed by a new ServerMapStatusCache.
   The previous syntax check is expanded to include the ability to run
   lint tests.
2021-04-21 18:57:44 +02:00
Paul Chote
61d64287e1 Move LoadMaps after InitializeFonts.
This allows text to be displayed earlier in the loading screen.
2021-04-21 18:57:44 +02:00
Paul Chote
abee274f88 Remove direct access to MapPreview.Rules. 2021-04-21 18:57:44 +02:00
reaperrr
53e6d974f0 Change Crate.Lifetime from 'seconds' to ticks
As far as I could tell, this was the last place that still
used 'seconds' instead of ticks, apart from
some sound notification intervals (which are better
converted to real [milli]seconds).

Also renamed ScaredyCat.PanicLength to PanicDuration for
consistency and easier finding.
2021-04-19 20:03:08 +02:00
reaperrr
e3fd54e147 Replace * 25 in internal tick defaults with actual total
While the idea behind it is understandable,
this was inconsistent with the bulk of other defaults.
2021-04-19 20:03:08 +02:00
Vapre
52d39db84a Fix possible endless loop in replay recorder while opening save file. 2021-04-15 18:10:45 +02:00
reaperrr
27ddae3df9 Fix Move jumpy-ness on MovePart transitions
There were 2 issues at work here, both when progress
would overshoot Distance (which is the usual case,
rather than the exception):
- the overshot progress was passed on by MoveFirstHalf, however
  OnComplete would make the next MovePart start ticking the
  same tick on which the old MovePart reached Distance,
  but move by carryoverProgress +(!!!) terrain speed instead of moving
  by just the left-over carryoverProgress.
- MoveSecondHalf would not pass any overshot progress to the
  next MoveFirstHalf queued by parent Move, leading to
  the next MoveFirstHalf performing a full-speed move the same tick
  MoveSecondHalf finished its last move.
2021-04-15 18:03:42 +02:00
reaperrr
4c7e3d8f3a Rename fraction to distance/progress in MovePart 2021-04-15 18:03:42 +02:00
reaperrr
646495fc5f Simplify MovePart code
InnerActivity and UpdateCenterLocation made this
overly complex and hard to read & debug.

This also fixes a bug that would make an outdated
facing being passed during OnComplete (because
InnerActivity was cached before UpdateCenterLocation
could set the correct final facing).
2021-04-15 18:03:42 +02:00
Mustafa Alperen Seki
a9661a233a Fix Lua error in Har8 when Merc HFac is captured after Palace is killed. 2021-04-13 21:39:24 +02:00
CrazyAlex25
2d05e10819 Modify build properties 2021-04-12 00:44:17 +02:00
Matthias Mailänder
5a0bcc01a6 Add a lint check for trait placement on hardcoded actor names. 2021-04-11 20:20:00 +02:00
Smittytron
0d3c624bbc Standardize usage of AddObjective in RA missions 2021-04-11 12:26:14 +02:00
Smittytron
26fbcf6076 Change fence owner in Soviet01 2021-04-11 12:26:14 +02:00
Smittytron
418fca3d9e Move OnAnyDamaged function to campaign-global.lua 2021-04-11 12:26:14 +02:00
Smittytron
dd366f8cf9 Rename top-o-the-world script file 2021-04-11 12:26:14 +02:00
Smittytron
6b63e88056 Move crate lifetime overrides to campaign-rules.yaml 2021-04-11 12:26:14 +02:00
Smittytron
a96e2fb588 Add Soviet11b 2021-04-10 23:00:31 +02:00
Smittytron
ca2f966c3b Add Soviet 11a 2021-04-10 22:26:40 +02:00
reaperrr
e161d9daa7 Misc TraitDictionary style fixes 2021-04-10 15:59:24 +02:00
reaperrr
aa834db1e3 Make perf.log output for ticking things opt-in
Both writing to perf.log frequently as well as GetTimestamp
aren't free and hurt performance particularly on slower systems
(which can have notably higher output to perf.log, further
amplifying the problem).
Therefore we make simulation perf logging opt-in.

Additionally, logging of the current tick and tick type
(local/net) is removed from debug.log, and some
remnant debug logging for kills and pips is removed
to keep performance-sensitive logging limited to
perf.log.
2021-04-10 15:59:24 +02:00
Paul Chote
a1df91b665 Allow BuildingInfluence to track overlapping buildings in the same cell. 2021-04-10 14:28:31 +02:00
Paul Chote
bc286b78bf Add AnyBuildingAt method to BuildingInfluence. 2021-04-10 14:28:31 +02:00
Paul Chote
19c7e14393 Simplify BuildingInfo.IsCloseEnoughToBase adjacency checks. 2021-04-10 14:28:31 +02:00
reaperrr
1a9dfc0893 Refactor GameSpeed setting
*Remove internal GameSpeed defaults
 Enforce setting values explicitly all the time
 Require definition of a DefaultSpeed

*Remove Global.Timestep default

*Remove the hacky Timestep/OrderLatency setting via LobbyInfo

*Fix shellmaps ignoring mod-defined gamespeeds

*Make DateTimeGlobal use the MapOptions gamespeed
2021-04-09 22:58:14 +01:00
Matthias Mailänder
fe129956bb Make the resource layer optional in the map editor. 2021-04-07 12:19:55 +01:00
reaperrr
eec7de4646 Fix RA infantry shadow being visible when parachuting 2021-04-04 17:06:03 -05:00
teinarss
605181efe4 Update Log to use worker thread 2021-04-03 22:53:30 +01:00
Paul Chote
fc0ed75a94 Fix Firestorm asset installation. 2021-04-03 15:12:18 -05:00
Smittytron
d95c4146e2 Rework objectives in Situation Critical 2021-04-03 17:58:00 +01:00
Vapre
38f0d50648 Minor optimization. Save a call to IsAlliedWith if not disguised. #18791. 2021-04-03 17:12:12 +01:00
reaperrr
808d8e63bc Add GiveUnitCrateActions for ships to RA 2021-04-03 16:22:27 +01:00
reaperrr
55967035d9 Update RA wcrate.shp
Now uses the original colors again.
Also added back a frame without shadow (for
parachuting).
2021-04-03 16:22:27 +01:00
reaperrr
13a101f11f Add a frame with shadow to both TD crate types
And remove the crate.shp in return
(which was just scrate.shp with shadow).

Also fixes WCRATE actor to actually use the correct .shp.
2021-04-03 16:22:27 +01:00
Paul Chote
35a0a3cf90 Remove rcedit after building windows packages. 2021-04-03 12:19:41 +02:00
Smittytron
08d8f5d8d9 Swap c1 and c7 out of CivilianEvacuees in Monster Tank Madness 2021-04-03 11:38:33 +02:00
Smittytron
f7c9eccf7a Add Selectable Class to technicians 2021-04-03 11:38:33 +02:00
teinarss
6ba9e64380 Rename modifiablePalettes to mutablePalettes in HardwarePalette 2021-04-03 11:33:31 +02:00
teinarss
8b0a3ea680 Remove our own impl of ReadOnlyList and update usages 2021-04-03 11:33:31 +02:00
teinarss
e12ff2c59d Remove our own ReadOnlyDictionary and update usages 2021-04-03 11:33:31 +02:00
Smittytron
afbdb395b2 Add SelectableSupportUnit to Thief 2021-04-03 11:24:00 +02:00
teinarss
3d381e6e32 Make SpriteFont.Measure take zero allocations 2021-04-03 11:22:45 +02:00
Matthias Mailänder
a02737107e Add a .wsa file reader. 2021-04-03 11:19:06 +02:00
Matthias Mailänder
590ab88c45 Don't prefer braces (for one liners). 2021-04-03 10:53:08 +02:00
Patrick
7a7c07e9c4 fix AreaBeam + GrantExternalConditionWarhead bug 2021-04-02 13:05:20 +02:00
reaperrr
40aafe586d Move Game.Timestep to Widget
Game.Timestep wasn't used for anything other than
UI anymore anyway, moving it makes this more clear.
2021-04-02 12:00:42 +01:00
abcdefg30
6b93f955a4 Fix a crash in LevelUpCrateAction 2021-04-02 11:57:44 +01:00
reaperrr
75a3bb4f0b CellIsEvaluating perf optimization
If an actor has Mobile, it implements IOccupySpace
so we can use OccupiesSpace to save a trait look-up.
2021-03-27 22:29:13 +00:00
reaperrr
441e18b898 Remove PathHash
This is 9(!) years old and we haven't had
pathfinding-related desyncs in quite a while.

We can still bring this back later if we ever
need it again.
2021-03-27 22:29:13 +00:00
reaperrr
b8e64df4b1 Remove SmokeTrailWhenDamaged
One of the most outdated and limited traits remaining,
which can do nothing LeavesTrails doesn't cover by now.
2021-03-27 18:42:57 +00:00
Matthias Mailänder
d15e7f76fc Port back to Mono.Nat and make discovery async. 2021-03-27 18:36:12 +00:00
Andre Mohren
3f510b6d93 Make ISpriteLoader aware of the source file name. 2021-03-27 17:36:59 +01:00
teinarss
d60c05eff3 Change to use pattern matching 2021-03-27 17:29:20 +01:00
Paul Chote
7c0e4b25ae Specify interaction bounds relative to the mod tile size. 2021-03-27 16:31:50 +01:00
Smittytron
852241d98e Add Allies05c 2021-03-27 14:46:11 +01:00
Smittytron
8deba81214 Add Allies05b 2021-03-27 13:55:27 +01:00
Smittytron
887a093f46 Add Counterstrike mission Siberian Conflict 3: Wasteland 2021-03-27 13:32:40 +01:00
Smittytron
4778dba36d Fix whitespace issues in RA mission browser 2021-03-27 13:25:22 +01:00
Smittytron
438b2240f4 Add AI infantry production to Soviet02a 2021-03-27 13:25:22 +01:00
Smittytron
58a6333834 Add AI infantry production to Soviet02b 2021-03-27 13:25:22 +01:00
Smittytron
f8feec685f Use correct sprite for Moneycrate 2021-03-27 13:25:22 +01:00
Smittytron
79404cd397 Link campaign-global.lua to remaining missions 2021-03-27 13:25:22 +01:00
Smittytron
6a6c5848c2 Remove fake building overrides from Survival01 2021-03-27 13:25:22 +01:00
Smittytron
420c8ebc4c Remove fake building overrides from Allies02 2021-03-27 13:25:22 +01:00
Matthias Mailänder
c0ea95ca46 Update itch user name. 2021-03-27 11:34:03 +00:00
Matthias Mailänder
1a8d971145 Fix a syntax error. 2021-03-27 11:07:35 +00:00
Paul Chote
7afcb9d757 Fix TS infantry player color. 2021-03-27 11:06:12 +01:00
Smittytron
8a5c0736f5 Remove CivilianKilled notification default from TD 2021-03-27 10:58:29 +01:00
Castle
12b6bb9448 Allow BlendMode of RgbaColorRenderer to be changed 2021-03-23 17:07:20 +01:00
reaperrr
e2a6b55d44 Move up Undamaged check in DamageState
A mere int comparison is obviously cheaper than
a comparison between two multiplications,
so pulling this above the checks of other damage states
allows us to bail early for undamaged actors.
2021-03-21 17:37:42 +01:00
reaperrr
c240c0a24b Only apply non-100 damage modifiers in InflictDamage
Profiling has shown that filtering them out early is cheaper
than applying those percentage modifiers anyway.

Additionally, profiling shows foreach loops to be cheaper
than LINQ here as well.
2021-03-21 17:37:42 +01:00
reaperrr
2528b79610 Fix D2k DamagesConcrete overrides 2021-03-21 11:09:41 +00:00
Matthias Mailänder
e13fd4816e Extract the directory if the registry value points to a filename. 2021-03-20 18:42:02 +01:00
Andre Mohren
1f6e0f582a Fixed aiming not propertly stopped. 2021-03-20 18:33:04 +01:00
Matthias Mailänder
bbbed49f82 Add a lint check for cursor definitions. 2021-03-20 17:37:16 +01:00
Matthias Mailänder
cefb2e7cc6 Remove unused trait lookup. 2021-03-20 17:37:16 +01:00
Paul Chote
470bc4e092 Polish deployed mobile sensor array:
- Force temperate palette to avoid glitches on snow maps
- Add missing active animation
2021-03-20 17:15:56 +01:00
Paul Chote
08c7c80bb7 Render building lights as their own tint-ignoring animations. 2021-03-20 17:15:56 +01:00
Paul Chote
5832ec76d4 Render integrated muzzle flashes as their own tint-ignoring animations. 2021-03-20 17:15:56 +01:00
Paul Chote
594e5b80d7 Add WithMakeOverlay trait. 2021-03-20 17:15:56 +01:00
Paul Chote
14a434975a Add facing support to WithAttackOverlay. 2021-03-20 17:15:56 +01:00
Paul Chote
6854b23bcc Add custom palette support to WithSpriteBody. 2021-03-20 17:15:56 +01:00
Paul Chote
0c52d275fa Add TransparentIndex to PaletteFromFile. 2021-03-20 17:15:56 +01:00
Paul Chote
e63b9b4986 Add condition support to ActorLostNotification. 2021-03-20 17:07:29 +01:00
Paul Chote
0e270bec56 Restrict Chronoshiftable trait to Mobile and Husk actors. 2021-03-20 17:04:42 +01:00
Andre Mohren
f5f06b86ad ExplosionOnDamageTransition now Conditional. 2021-03-20 17:02:47 +01:00
Paul Chote
fcc3008b00 Implement proper TS Tiberium and Vein logic. 2021-03-20 16:45:41 +01:00
Paul Chote
0bdd46451e Overhaul resource layer logic:
* ResourceType trait has been removed.
* Simulation-related data is now defined on the
  ResourceLayer (which mods can subclass/replace).
* Support non-money resources by moving the resource
  values to the PlayerResources trait.
* Allow mods to disable the neighbour density override
  and instead always use the map-defined densities.
* Allow mods to define their own resource placement
  logic (e.g. allow resources on slopes) by subclassing
  (Editor)ResourceLayer.
* Improve ability to subclass/override ResourceRenderer
  by exposing more virtual methods.
2021-03-20 16:45:41 +01:00
Paul Chote
c35e9fb016 Remove custom D2k spice variant logic and improve editor cursor sprite. 2021-03-20 16:45:41 +01:00
Paul Chote
80e92849da Replace ResourceType with strings in interfaces/public methods. 2021-03-20 16:45:41 +01:00
Paul Chote
dcd8eccee4 Replace ResourceLayer references with IResourceLayer in traits/warheads. 2021-03-20 16:45:41 +01:00
Paul Chote
5adcbe4c78 Use IResourceLayer for editor resources. 2021-03-20 16:45:41 +01:00
Paul Chote
0b93556c06 Add resource updating methods to IResourceLayer. 2021-03-20 16:45:41 +01:00
Paul Chote
7e9d291223 Add IResourceRenderer interface. 2021-03-20 16:45:41 +01:00
Paul Chote
1dc26a9b8e Pass resource type and count to IAcceptResources instead of the value. 2021-03-20 16:45:41 +01:00
Paul Chote
3dbc6400a6 Use IResourceLayer in BaseBuilderBotModule. 2021-03-20 16:45:41 +01:00
reaperrr
adf1aeb06a Add support for pool-specific ammo pips decoration 2021-03-20 15:31:41 +01:00
Vapre
be224934a5 FieldLoader, use dictionary for GetValue to make it twice as fast. 2021-03-17 18:18:53 +01:00
teinarss
6b74093c04 Add readonly to structs 2021-03-14 15:17:57 +01:00
reaperrr
65c796dec7 Use Mobile.Pathfinder to reduce trait look-ups 2021-03-14 12:28:54 +00:00
reaperrr
503e706d45 Check for IsReloading in HasAnyValidWeapons
For Attack*.CanAttack().

Allows us to drop the additional check for armaments with ammo.
2021-03-14 12:28:54 +00:00
reaperrr
a1f974bd40 Save Mobile trait look-up in AttackBase 2021-03-14 12:28:54 +00:00
Unrud
11171ff649 Makefile: Fail target version when not a git repository 2021-03-13 15:57:17 +00:00
Paul Chote
3ff1888d74 Fail Situation Critical if Volkov is killed before entering the world. 2021-03-13 15:55:24 +00:00
Paul Chote
cbea08e1fe Fix non-relative path handling in install logic. 2021-03-13 15:56:38 +01:00
Paul Chote
b622afd7fd Fix self-contained packaging. 2021-03-13 13:12:31 +01:00
Paul Chote
7694d0842b Fix two outdated comments. 2021-03-12 21:52:16 +01:00
Paul Chote
a90e6940ab Add sequence scale and alpha support to ShroudRenderer. 2021-03-12 21:52:16 +01:00
Paul Chote
96c3825b6a Add alpha support to TerrainSpriteLayer. 2021-03-12 21:52:16 +01:00
Paul Chote
a6467cb515 Fix Nullable type handling in Lua docs. 2021-03-10 17:46:44 +01:00
Paul Chote
d52ba83f96 Replace terniary null checks with coalescing. 2021-03-08 18:11:25 +01:00
reaperrr
2473b8763b Rename methods/activities with Visual in them
While they may be only 'visual' in terms of influence/cell grid,
they all do update CenterPosition, which is essentially the
actual world position of the actor.
'Visual' would imply that it only affects the position where the
actor is drawn, which is inaccurate.
Furthermore, using the term 'Visual' here would make
naming future methods/properties related to visual interpolation
unnecessarily complicated, because that's where we might
need a real 'Visual(Only)Position'.
2021-03-08 11:19:11 +01:00
teinarss
7073279ab8 Replace WebClient with HttpClient 2021-03-07 16:04:57 +00:00
teinarss
ed43071792 Compile engine and mod dlls as NET5 when not using mono 2021-03-07 16:04:57 +00:00
teinarss
4a1e4f3e16 Use expression body syntax 2021-03-07 13:00:52 +00:00
Paul Chote
555c43843b Fix lobby checkbox event rectangle overlapping with scrollbars. 2021-03-05 18:58:51 +01:00
Paul Chote
a0d75d1e63 Fix rendering artifacts with RA's dialog5 background. 2021-03-05 18:33:39 +01:00
reaperrr
1a7a47fa08 Randomize AI idle harvester scan intervals 2021-02-28 18:58:03 +01:00
teinarss
c2279d3071 Change GetField calls to use nameof 2021-02-28 18:43:51 +01:00
teinarss
ed295ae315 Change throw exceptions to use nameof in parameter 2021-02-28 18:43:51 +01:00
teinarss
53b781960c Change FieldLoader.LoadUsing to use nameof 2021-02-28 18:43:51 +01:00
penev92
6b794ba3e5 Added the "missing" song to the D2k TestFiles
Since it's music it's optional, so the players still won't know anything happened, but if they do open the content manager they will have a chance for an automatic installation.
2021-02-28 12:29:15 +00:00
penev92
2f95e56256 Fixed incorrect D2k song installation 2021-02-28 12:29:15 +00:00
pizzaoverhead
c8644adc85 Fix TLS error on Win7. 2021-02-28 10:15:29 +00:00
Paul Chote
8b81078929 Fix lighting effects in "The Pit" map. 2021-02-24 19:20:48 +01:00
Paul Chote
76a10283c4 Fix TS effect translucency/lighting effects. 2021-02-24 19:20:48 +01:00
Paul Chote
0975102e92 Add Alpha support to sequences.
Alpha can specify a single value for the sequence
or values for each frame in the sequence.

AlphaFade: True can be specified to linearly fade
to transparent over the length of the animation.
2021-02-24 19:20:48 +01:00
Paul Chote
445d943549 Remove obsolete shadow palette definitions. 2021-02-20 02:08:40 +01:00
Paul Chote
554486fb0b Replace projectile, WithShadow, WithParachute shadows with tint effects. 2021-02-20 02:08:40 +01:00
penev92
1c1af89bb9 Fixed D2k VQA videos crashing the game 2021-02-19 13:38:12 +01:00
Trevor Nichols
536c130a39 Add MSADPCM audio format decoding support 2021-02-19 01:56:57 +01:00
Matthias Mailänder
5aa67a42c1 Bump Eluant version to support Unicode strings. 2021-02-18 23:16:49 +01:00
Paul Chote
f661d1ba48 Remove OLDLST campaign overrides. 2021-02-14 18:13:21 +01:00
reaperrr
717b9ba1e6 Fix TS GDI temperate radar sprite
Unlike its snow counterpart, the transition between building
and its shadow was jarring, making it look as if there was a
1-pixel gap between them. Fixed that.

Also applied some very subtle polish by replacing some non-
remapable red pixels (nothing worth porting to snow variant, though).
2021-02-14 14:40:58 +00:00
reaperrr
c57cc96145 TS radars Z offset fixes
The lower borders of the sprites were cut off without this.
2021-02-14 14:40:58 +00:00
Smittytron
16fa1ef144 Change Z-offsets that overlap Repair Facility 2021-02-14 14:31:50 +00:00
Smittytron
788f0f3e24 Change Z-offsets that overlap Service Depot 2021-02-14 14:31:50 +00:00
Unrud
ccf9c8fb22 AppStream Metadata: Add violence-worship: moderate 2021-02-14 14:16:53 +00:00
Unrud
ee31146501 Update AppStream metadata for linux packaging
A few small improvements:
* The type `desktop` was renamed to `desktop-appliaction` (a long time ago)
* Add `launchable` to tell how the application can be launched
* Use `https` for homepage link
* Add link to the bugtracker
* Update `oars-1.0` to `oars-1.1`
* Remove all unnecessary `content_attribute` entries with value `none`
2021-02-14 14:16:53 +00:00
Vapre
f176a0ed83 Fix. Call EndGame in one place of server main loop. Ensure UPnP port forward is removed always. 18807. 2021-02-14 13:54:48 +00:00
Matthias Mailänder
e7cfd2765c Make UI cursors configurable. 2021-02-14 13:09:59 +00:00
abcdefg30
96c4554644 Remove GenericSelectTarget 2021-02-14 12:51:12 +00:00
Mustafa Alperen Seki
df94f0ec8b Add InitialActor to Carryall. 2021-02-14 12:47:08 +00:00
Adam Mitchell
9ee7294c81 Improve shroud performance 2021-02-12 20:58:56 +01:00
Smittytron
5cf622fb6e Improve AI response in Nod03b 2021-02-12 20:38:37 +01:00
Smittytron
8711d8799c Improve AI response in Nod03a 2021-02-12 20:38:37 +01:00
Smittytron
e9a6a3afc5 Add clarifying objective to Monster Tank Madness 2021-02-12 02:30:12 +01:00
abcdefg30
9c29264be7 Drop FlyAttackRun targets when we don't have valid armaments against them 2021-02-12 02:17:26 +01:00
abcdefg30
e6c9d5fc96 Don't check IsTraitDisabled twice when calculating IsTargetableBy 2021-02-12 02:17:26 +01:00
Paul Chote
d09476c603 Remove custom palettes from building placement previews. 2021-02-12 02:08:30 +01:00
Orb
5bda6852a4 APC Balance Tweak Commit 2021-02-09 23:07:44 +01:00
Ivaylo Draganov
641b05eb21 Split settings panels logic and add support for custom panels 2021-02-08 11:25:50 +01:00
reaperrr
62fd11d5c4 Fix deployed tick tank sprites
Several pixels used non-remapable red colors.
2021-02-06 22:46:52 +01:00
Smittytron
e807664437 Add TanyaRescued speech notification to Downunder 2021-02-06 01:18:48 +01:00
Smittytron
e46440b94f Correct the ownership of walls in Allies05a 2021-02-06 01:18:48 +01:00
Smittytron
30ea1e23b4 Delay SendReinforcements() to split notifications 2021-02-06 01:18:48 +01:00
Smittytron
4db2fd980a Kill pillboxes with barrels in Soviet01 2021-02-06 01:18:48 +01:00
Smittytron
9580d61fb0 Fix spaces in Monster Tank Madness 2021-02-06 01:18:48 +01:00
Smittytron
27aefcc0ce Fix tabs in Ant01 2021-02-06 01:18:48 +01:00
Smittytron
b8b1a4930c Change BarrelExplode CreateEffect to large_napalm 2021-02-05 23:41:03 +01:00
Smittytron
d7a40f3bdb Change BarrelExplode to use FireDeath 2021-02-05 23:41:03 +01:00
Paul Chote
fb0031d34a Rename remaining Stance references to PlayerRelationship. 2021-02-04 23:14:09 +01:00
Paul Chote
b3821e71dc Fix Tanya's prone firing animations. 2021-02-02 23:05:07 +01:00
Unrud
8a90413b87 Use version from VERSION file
The `VERSION` variable doesn't work with a release tarball, because it requires git to be set correctly.
Instead this reads the version from the `VERSION` file.
2021-02-01 23:13:48 +01:00
abcdefg30
115984054d Increase the reload delay of the prison colt to match the normal colt 2021-01-31 22:35:42 +00:00
abcdefg30
69addda86c Fix the gun sounds not playing in Allies05a 2021-01-31 22:35:42 +00:00
Smittytron
c2379f251d Fix crash in Allies 05a on SamBarrel kill 2021-01-31 18:52:47 +01:00
abcdefg30
3422bf1b59 Remove the 'docs' target from the windows make file 2021-01-30 21:08:25 +00:00
Paul Chote
e830d1e9c2 Fix Concrete placement on invalid terrain. 2021-01-30 14:48:04 +01:00
Smittytron
f767c24601 Remove SpawnActorOnDeath from civilian structures 2021-01-30 13:47:14 +01:00
Paul Chote
2c09b1414c Ignore invalid server entries instead of the entire list. 2021-01-30 13:44:51 +01:00
Paul Chote
df1191db5b Don't crash if a null uid is given to QueryRemoteMapDetails. 2021-01-30 13:44:51 +01:00
Paul Chote
62433ecb8b Prevent AttackPopupTurreted.TickIdle from running while disabled. 2021-01-30 13:16:58 +01:00
Paul Chote
09f680201f Restore missing versioned source code package. 2021-01-30 13:04:23 +01:00
reaperrr
5ba662cfae Fix RA inf death anims playing too fast 2021-01-30 01:23:39 +00:00
Paul Chote
d6d0fd597d Fix Tanya firing animation. 2021-01-29 15:04:42 +01:00
Paul Chote
fec1c813a5 Support burst-specific infantry attack animations. 2021-01-29 15:04:42 +01:00
Paul Chote
547b6d19dc Reduce Tanya firing rate. 2021-01-29 15:04:42 +01:00
Paul Chote
5e6dd50c3b Suppress error messages from make.ps1 clean.
Also removed obsolete ./*/bin removal.
2021-01-29 14:54:40 +01:00
Paul Chote
4251ed69bb Keep Discord join button active for spectators. 2021-01-29 14:38:13 +01:00
Paul Chote
b05ee80d5c Remove first-client check from LobbySettingsNotification.
ClientWithIndex may rarely be null, causing a crash.
In any case we do want to report these changes to the first client, as
somebody else may have changed the settings and left.
2021-01-29 14:35:32 +01:00
Paul Chote
84ced8704d Fix bot-controlled aircraft stalling above cloaked targets. 2021-01-29 14:30:27 +01:00
teinarss
58313520f0 Change renderables to class to avoid boxing 2021-01-29 00:24:27 +01:00
reaperrr
0d8ef1a1dd Make Contrail conditional and public
Also moved it to 'Render' subfolder while at it.
2021-01-28 23:30:45 +01:00
Matthias Mailänder
28ddab7cc2 Disallow building sell during construction. 2021-01-27 22:51:58 +01:00
Matthias Mailänder
8f06b0a836 Add a "structure sold" notification. 2021-01-27 22:51:58 +01:00
Paul Chote
3b768dacf5 Increase map chooser dialog size to match the lobby. 2021-01-25 20:43:53 +01:00
Smittytron
d67b3245e0 Change hospital owner in Nod07b 2021-01-24 10:47:05 +00:00
abcdefg30
a1dd1fc5d1 Fix an compiler error on bleed 2021-01-23 23:15:58 +00:00
Paul Chote
889153ce81 Add shadow rendering for resource sprites. 2021-01-23 20:59:53 +01:00
teinarss
30e5c807b0 Add Map and server name to discord details 2021-01-22 14:14:02 +01:00
Paul Chote
db2fded24d Fix AutoTarget.ChooseTarget ignoring AttackBase.TargetFrozenActors. 2021-01-21 19:25:07 +01:00
Paul Chote
82a9809192 Remove RenderSprites.Scale. 2021-01-21 18:22:11 +01:00
Paul Chote
f6b40b2bce Allow sequences to define a Scale factor. 2021-01-21 18:22:11 +01:00
abcdefg30
fd75e03d9c Use pattern matching in FieldSaver 2021-01-21 18:05:07 +01:00
Paul Chote
d6a05f2ea2 Fix Sardarkaur not attacking while attack-moving. 2021-01-17 19:42:49 +01:00
Paul Chote
f51603e440 Add ShpRemastered sprite loader. 2021-01-16 23:16:34 +01:00
Paul Chote
f341c9a8a6 Add TGA and DDS sprite loaders. 2021-01-16 23:16:34 +01:00
Paul Chote
765944cfa2 Switch macOS and Linux default toolchain to .NET 5. 2021-01-16 22:58:05 +01:00
Paul Chote
15c926b6b9 Move DefineConstants="MONO" into OpenRA.Game.csproj. 2021-01-16 22:58:05 +01:00
teinarss
b486112c98 Fix broken editorconfig 2021-01-16 22:28:17 +01:00
Paul Chote
223a0015a6 Allow terrain depth sprites to be loaded from a second file. 2021-01-16 22:09:08 +01:00
Paul Chote
632af7c7e6 Support mixed indexed/BGRA terrain tiles. 2021-01-16 22:09:08 +01:00
Paul Chote
142870d78a Support multiple sheets in TerrainSpriteLayer. 2021-01-16 22:09:08 +01:00
Paul Chote
b0aa32cd1b Support multiple palettes in TerrainSpriteLayer. 2021-01-16 22:09:08 +01:00
Matthias Mailänder
281229e8b4 Update path according to latest appstream specifications. 2021-01-16 20:02:19 +01:00
Matthias Mailänder
8f0d53ffd9 Document the target platform parameter during install
as it otherwise copies the system binaries.
2021-01-16 20:02:19 +01:00
Matthias Mailänder
ddfa5a4d35 Get rid of unnecessary Makefile variables. 2021-01-16 20:02:19 +01:00
Matthias Mailänder
cbf2e2e2ef Don't contain the build root in the wrappers. 2021-01-16 20:02:19 +01:00
Matthias Mailänder
62803aff88 Create BIN_PATH first on shortcut installation. 2021-01-16 20:02:19 +01:00
VonNah
bc9cd57fa0 Fix vehicle explosion and missile impact sounds in Tiberian Dawn 2021-01-16 01:32:20 +00:00
VonNah
93fa9bdfb8 Fixed an issue where RadarUp and DisablePower/EnablePower use the wrong sound effects.
Signed-off-by: VonNah <vonnahora@gmail.com>
2021-01-16 00:48:35 +00:00
reaperrr
808c5f0951 Remove ScriptTriggers from map deco actors
Removes it from destroyed fields, trees and rocks in RA and C&C.

This trait isn't free in terms of performance due to all the
INotify* calls, and the mentioned actors should rarely
- if ever - need it. Besides, if a mission ever really needs
this on those actors, the trait can be added back via
map rules.
2021-01-16 00:44:03 +00:00
Paul Chote
a70637f28f Fix missing System.Threading.Tasks.Parallel in packaged net5 builds. 2021-01-15 12:18:00 +01:00
Paul Chote
a12e4657ee Update apt metadata before installing dependencies. 2021-01-12 23:09:11 +01:00
Paul Chote
53db1230ab Move default tileset parsing to Mods.Common. 2021-01-11 21:57:55 +01:00
Paul Chote
207e09fea9 Add plumbing for mod-defined terrain loaders. 2021-01-11 21:57:55 +01:00
Paul Chote
b86b638700 Move editor template rendering to TerrainRenderer. 2021-01-11 21:57:55 +01:00
Paul Chote
995c33a942 Remove Ruleset.TileSet. 2021-01-11 21:57:55 +01:00
Paul Chote
6d6efd5fe8 Move tileset image validation to TerrainRendererInfo. 2021-01-11 21:57:55 +01:00
Paul Chote
2782620081 Add ITemplatedTerrainInfo interface. 2021-01-11 21:57:55 +01:00
Paul Chote
be2ca77acf Add ITerrainInfoNotifyMapCreated interface. 2021-01-11 21:57:55 +01:00
Paul Chote
87790069e9 Add ITerrainInfo interface. 2021-01-11 21:57:55 +01:00
Paul Chote
0a374e2264 Move ownership of tile sprites to the terrain renderer. 2021-01-11 21:57:55 +01:00
Paul Chote
2eee911c58 Fix infantry freezing death animations in Top o' the World mission. 2021-01-11 11:32:38 +01:00
Paul Chote
bb65646c4d Fix Grenadier death animation in Sarin Gas 3 mission. 2021-01-11 11:32:38 +01:00
Paul Chote
fea700fb72 Add death types support to the Lua Kill() API. 2021-01-11 11:32:38 +01:00
tovl
560c3230cd Let harvesters only search for refineries when needing to unload. 2021-01-10 23:43:48 +01:00
Paul Chote
e990a83b7a Cancel support power targeting if power cannot be activated. 2021-01-10 22:42:32 +01:00
Paul Chote
02a2624bcc Add a per-player handicap option to the lobby.
Handicaps reduce unit health, firepower, and build speed.
2021-01-10 22:23:52 +01:00
Paul Chote
c7c78eda80 Increase lobby and server list width. 2021-01-10 22:23:52 +01:00
Paul Chote
b1200b8078 Switch make.ps1 to downloading from our GeoIP data mirror. 2021-01-10 21:13:26 +01:00
Matthias Mailänder
0cb25f1044 Fix syntax errors. 2021-01-10 21:06:39 +01:00
Matthias Mailänder
8ee728891d Use the GitHub action variable for clickable links. 2021-01-10 21:06:39 +01:00
Matthias Mailänder
bbaa475287 Update SDL 2 nuget package to match the platform. 2021-01-10 21:04:44 +01:00
Matthias Mailänder
7bc17b59f5 Add a generic video player widget. 2021-01-10 10:21:17 +01:00
Smittytron
514652bb6a Add Aftermath mission Shock Therapy 2021-01-10 02:34:50 +01:00
Paul Chote
3e7665146a Avoid BuildUnit crash if the item is invalidated before the task runs. 2021-01-10 01:34:51 +01:00
Paul Chote
16d0f8a5a6 Add a setting to pause the shellmap. 2021-01-10 00:23:30 +01:00
Paul Chote
aeab9a8116 Group bot AttackMove orders. 2021-01-09 23:55:50 +01:00
Orb
f9a5669eb4 Playtest-Balance-Tweaks Commit 2021-01-09 23:22:49 +01:00
Paul Chote
868736fe1a Remove obsolete --check-runtime-assemblies utility command. 2021-01-09 19:53:22 +01:00
Paul Chote
0984de6bea Change install_assemblies_mono to install all bin contents. 2021-01-09 19:53:22 +01:00
Paul Chote
69ffd70d3f Switch Appimages to net 5. 2021-01-09 19:53:22 +01:00
Paul Chote
2fae69c0ba Run appimagetool directly. 2021-01-09 19:53:22 +01:00
Paul Chote
f1d66a4c70 Use ULFO format for non-compat disk images (requires macOS 10.11+). 2021-01-09 19:53:22 +01:00
Paul Chote
84ce33fe9c Trim unused assemblies to reduce packaged size further. 2021-01-09 19:53:22 +01:00
Paul Chote
e583165dff Replace duplicate runtime files with hardlinks to reduce dmg size. 2021-01-09 19:53:22 +01:00
Paul Chote
7bedba5837 Switch macOS packages to .NET 5. 2021-01-09 19:53:22 +01:00
Paul Chote
8c9b9df125 Change WithColoredOverlay to use a color instead of a palette. 2021-01-06 00:06:51 +01:00
Paul Chote
96641873ae Replace highlight palette with tint effects. 2021-01-06 00:06:51 +01:00
Paul Chote
8edd9de278 Replace ITintableRenderable with IModifyableRenderable. 2021-01-06 00:06:51 +01:00
Paul Chote
67754e8693 Add alpha support to SpriteRenderer. 2021-01-06 00:06:51 +01:00
Paul Chote
b88495c689 Remove palettes from base IRenderable. 2021-01-06 00:06:51 +01:00
Paul Chote
71b13c7b5d Disable AttackPopupTurreted state changes when paused. 2021-01-03 12:46:47 +01:00
nvrnight
b3f39bffce Issue #18936: The game no longer crashes when TextFieldWidget sends Enter key press and onSelect action is null. 2021-01-03 12:26:52 +01:00
Smittytron
b44a9ab5fc Fix palettes in Fall of Greece 1 2021-01-02 22:43:28 +01:00
Smittytron
e34255b0b2 Add campaign-palettes to missions lacking it 2021-01-02 22:43:28 +01:00
Paul Chote
2fee62af4d Fix exit priority ordering. 2021-01-02 15:43:03 +01:00
Paul Chote
fc1032cd9e Start rally point lines at the spawn position instead of the exit cell. 2021-01-02 15:29:18 +01:00
Paul Chote
60195e2842 Prevent Health: 100 from being added to actors. 2021-01-02 15:12:20 +01:00
Paul Chote
6fed31717c Fix map editor inverting preview color channels. 2021-01-02 11:43:55 +01:00
Paul Chote
aee3eb99c3 Fix --dump-sequence-sheets crash. 2021-01-02 11:43:55 +01:00
Paul Chote
3aa6fd3dc4 Add a friendly type name for Nullable<T>. 2021-01-02 11:38:24 +01:00
2874 changed files with 119883 additions and 68409 deletions

View File

@@ -8,99 +8,915 @@ end_of_line = LF
insert_final_newline = true
trim_trailing_whitespace = true
; 4-column tab indentation
[*.yaml]
indent_style = tab
indent_size = 4
; 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
#### Code Style Rules
#### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/
csharp_style_var_elsewhere = true:suggestion
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
# Severity Levels: https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/configuration-options#severity-level
# Below we enable specific rules by setting severity to warning.
# Rules are disabled by setting severity to silent (to still allow use in IDE) or none (to prevent all use).
# Rules are listed below with any options available.
# Options are commented out if they match the defaults.
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
### Language Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/language-rules
#### Naming styles ####
## this and Me preferences
# IDE0003/IDE0009 Remove 'this' or 'Me' qualification/Add 'this' or 'Me' qualification
#dotnet_style_qualification_for_field = false
#dotnet_style_qualification_for_property = false
#dotnet_style_qualification_for_method = false
#dotnet_style_qualification_for_event = false
dotnet_diagnostic.IDE0003.severity = warning
dotnet_diagnostic.IDE0009.severity = warning
## Use languages keywords for types
# IDE0049 Use language keywords instead of framework type names for type references
#dotnet_style_predefined_type_for_locals_parameters_members = true
#dotnet_style_predefined_type_for_member_access = true
dotnet_diagnostic.IDE0049.severity = warning
## Modifier preferences
# IDE0036 Order modifiers
#csharp_preferred_modifier_order = public, private, protected, internal, file, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, required, volatile, async
dotnet_diagnostic.IDE0036.severity = warning
# IDE0040 Add accessibility modifiers
dotnet_style_require_accessibility_modifiers = omit_if_default
dotnet_diagnostic.IDE0040.severity = warning
# IDE0044 Add readonly modifier
#dotnet_style_readonly_field = true
dotnet_diagnostic.IDE0044.severity = warning
# IDE0062 Make local function static
#csharp_prefer_static_local_function = true
dotnet_diagnostic.IDE0062.severity = warning
# IDE0064 Make struct fields writable
# No options
dotnet_diagnostic.IDE0064.severity = warning
## Parentheses preferences
# IDE0047/IDE0048 Remove unnecessary parentheses/Add parentheses for clarity
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary
#dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
#dotnet_style_parentheses_in_other_operators = never_if_unnecessary
dotnet_diagnostic.IDE0047.severity = warning
dotnet_diagnostic.IDE0048.severity = warning
## Expression-level preferences
# IDE0010 Add missing cases to switch statement
# No options
dotnet_diagnostic.IDE0010.severity = silent
# IDE0017 Use object initializers
#dotnet_style_object_initializer = true
dotnet_diagnostic.IDE0017.severity = warning
# IDE0018 Inline variable declaration
#csharp_style_inlined_variable_declaration = true
dotnet_diagnostic.IDE0018.severity = warning
# IDE0028 Use collection initializers
#dotnet_style_collection_initializer = true
dotnet_diagnostic.IDE0028.severity = warning
# IDE0032 Use auto-implemented property
#dotnet_style_prefer_auto_properties = true
dotnet_diagnostic.IDE0032.severity = warning
# IDE0033 Use explicitly provided tuple name
#dotnet_style_explicit_tuple_names = true
dotnet_diagnostic.IDE0033.severity = warning
# IDE0034 Simplify 'default' expression
#csharp_prefer_simple_default_expression = true
dotnet_diagnostic.IDE0034.severity = warning
# IDE0037 Use inferred member name
#dotnet_style_prefer_inferred_tuple_names = true
#dotnet_style_prefer_inferred_anonymous_type_member_names = true
dotnet_diagnostic.IDE0037.severity = silent
# IDE0039 Use local function instead of lambda
#csharp_style_prefer_local_over_anonymous_function = true
dotnet_diagnostic.IDE0039.severity = warning
# IDE0042 Deconstruct variable declaration
#csharp_style_deconstructed_variable_declaration = true
dotnet_diagnostic.IDE0042.severity = warning
# IDE0045 Use conditional expression for assignment
#dotnet_style_prefer_conditional_expression_over_assignment = true
dotnet_diagnostic.IDE0045.severity = silent
# IDE0046 Use conditional expression for return
#dotnet_style_prefer_conditional_expression_over_return = true
dotnet_diagnostic.IDE0046.severity = silent
# IDE0050 Convert anonymous type to tuple
# No options
dotnet_diagnostic.IDE0050.severity = silent
# IDE0054/IDE0074 Use compound assignment/Use coalesce compound assignment
#dotnet_style_prefer_compound_assignment = true
dotnet_diagnostic.IDE0054.severity = warning
dotnet_diagnostic.IDE0074.severity = warning
# IDE0056 Use index operator
#csharp_style_prefer_index_operator = true
dotnet_diagnostic.IDE0056.severity = warning
# IDE0057 Use range operator
#csharp_style_prefer_range_operator = true
dotnet_diagnostic.IDE0057.severity = warning
# IDE0070 Use 'System.HashCode.Combine'
# No options
dotnet_diagnostic.IDE0070.severity = warning
# IDE0071 Simplify interpolation
#dotnet_style_prefer_simplified_interpolation = true
dotnet_diagnostic.IDE0071.severity = warning
# IDE0072 Add missing cases to switch expression
# No options
dotnet_diagnostic.IDE0072.severity = silent
# IDE0075 Simplify conditional expression
#dotnet_style_prefer_simplified_boolean_expressions = true
dotnet_diagnostic.IDE0075.severity = warning
# IDE0082 Convert 'typeof' to 'nameof'
# No options
dotnet_diagnostic.IDE0082.severity = warning
# IDE0090 Simplify 'new' expression
#csharp_style_implicit_object_creation_when_type_is_apparent = true
dotnet_diagnostic.IDE0090.severity = warning
# IDE0180 Use tuple to swap values
#csharp_style_prefer_tuple_swap = true
dotnet_diagnostic.IDE0180.severity = warning
## Namespace declaration preferences
# IDE0160/IDE0161 Use block-scoped namespace/Use file-scoped namespace
#csharp_style_namespace_declarations = block_scoped
dotnet_diagnostic.IDE0160.severity = warning
dotnet_diagnostic.IDE0161.severity = warning
## Null-checking preferences
# IDE0016 Use throw expression
#csharp_style_throw_expression = true
dotnet_diagnostic.IDE0016.severity = silent
# IDE0029/IDE0030/IDE0270 Use coalesce expression (non-nullable types)/Use coalesce expression (nullable types)/Use coalesce expression (if null)
#dotnet_style_coalesce_expression = true
dotnet_diagnostic.IDE0029.severity = warning
dotnet_diagnostic.IDE0030.severity = warning
dotnet_diagnostic.IDE0270.severity = silent
# IDE0031 Use null propagation
#dotnet_style_null_propagation = true
dotnet_diagnostic.IDE0031.severity = warning
# IDE0041 Use 'is null' check
#dotnet_style_prefer_is_null_check_over_reference_equality_method = true
dotnet_diagnostic.IDE0041.severity = warning
# IDE0150 Prefer 'null' check over type check
#csharp_style_prefer_null_check_over_type_check = true
dotnet_diagnostic.IDE0150.severity = warning
# IDE1005 Use conditional delegate call
csharp_style_conditional_delegate_call = true # true is the default, but the rule is not triggered if this is not specified.
dotnet_diagnostic.IDE1005.severity = warning
## var preferences
# IDE0007/IDE0008 Use 'var' instead of explicit type/Use explicit type instead of 'var'
csharp_style_var_for_built_in_types = true
csharp_style_var_when_type_is_apparent = true
csharp_style_var_elsewhere = true
dotnet_diagnostic.IDE0007.severity = warning
dotnet_diagnostic.IDE0008.severity = warning
## Expression-bodied-members
# IDE0021 Use expression body for constructors
#csharp_style_expression_bodied_constructors = false
dotnet_diagnostic.IDE0021.severity = silent
# IDE0022 Use expression body for methods
#csharp_style_expression_bodied_methods = false
dotnet_diagnostic.IDE0022.severity = silent
# IDE0023/IDE0024 Use expression body for conversion operators/Use expression body for operators
#csharp_style_expression_bodied_operators = false
dotnet_diagnostic.IDE0023.severity = silent
dotnet_diagnostic.IDE0024.severity = silent
# IDE0025 Use expression body for properties
#csharp_style_expression_bodied_properties = true
dotnet_diagnostic.IDE0025.severity = silent
# IDE0026 Use expression body for indexers
#csharp_style_expression_bodied_indexers = true
dotnet_diagnostic.IDE0026.severity = silent
# IDE0027 Use expression body for accessors
#csharp_style_expression_bodied_accessors = true
dotnet_diagnostic.IDE0027.severity = warning
# IDE0053 Use expression body for lambdas
# This rule is buggy and not enforced for builds. ':warning' will at least enforce it in the IDE.
csharp_style_expression_bodied_lambdas = when_on_single_line:warning
dotnet_diagnostic.IDE0053.severity = warning
# IDE0061 Use expression body for local functions
csharp_style_expression_bodied_local_functions = when_on_single_line
dotnet_diagnostic.IDE0061.severity = warning
## Pattern matching preferences
# IDE0019 Use pattern matching to avoid 'as' followed by a 'null' check
#csharp_style_pattern_matching_over_as_with_null_check = true
dotnet_diagnostic.IDE0019.severity = warning
# IDE0020/IDE0038 Use pattern matching to avoid 'is' check followed by a cast (with variable)/Use pattern matching to avoid 'is' check followed by a cast (without variable)
#csharp_style_pattern_matching_over_is_with_cast_check = true
dotnet_diagnostic.IDE0020.severity = warning
dotnet_diagnostic.IDE0038.severity = warning
# IDE0066 Use switch expression
#csharp_style_prefer_switch_expression = true
dotnet_diagnostic.IDE0066.severity = silent
# IDE0078 Use pattern matching
#csharp_style_prefer_pattern_matching = true
dotnet_diagnostic.IDE0078.severity = silent
# IDE0083 Use pattern matching ('not' operator)
#csharp_style_prefer_not_pattern = true
dotnet_diagnostic.IDE0083.severity = warning
# IDE0170 Simplify property pattern
#csharp_style_prefer_extended_property_pattern = true
dotnet_diagnostic.IDE0170.severity = silent # Requires C# 10
## Code block preferences
# IDE0011 Add braces
#csharp_prefer_braces = true
# No options match the style used in OpenRA.
dotnet_diagnostic.IDE0011.severity = none
# IDE0063 Use simple 'using' statement
#csharp_prefer_simple_using_statement = true
dotnet_diagnostic.IDE0063.severity = silent
## 'using' directive preferences
# IDE0065 'using' directive placement
#csharp_using_directive_placement = outside_namespace
dotnet_diagnostic.IDE0065.severity = silent
## File header preferences
# IDE0073 Require file header
#file_header_template = unset
# This rule does not allow us to enforce our desired header, as it prefixes the header lines with // comments, meaning we can't apply a region.
dotnet_diagnostic.IDE0073.severity = none
## Namespace naming preferences
# IDE0130 Namespace does not match folder structure
#dotnet_style_namespace_match_folder = true
# This rule doesn't appear to work (never reports violations)
dotnet_diagnostic.IDE0130.severity = none
### Unnecessary Code Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/unnecessary-code-rules
# IDE0001 Simplify name
# No options
dotnet_diagnostic.IDE0001.severity = warning
# IDE0002 Simplify member access
# No options
dotnet_diagnostic.IDE0002.severity = warning
# IDE0004 Remove unnecessary cast
# No options
dotnet_diagnostic.IDE0004.severity = warning
# IDE0005 Remove unnecessary import
# No options
# IDE0005 is only enabled in the IDE by default. https://github.com/dotnet/roslyn/issues/41640
# To enable it for builds outside the IDE the 'GenerateDocumentationFile' property must be enabled on the build.
# GenerateDocumentationFile generates additional warnings about XML docs, so disable any we don't care about.
dotnet_diagnostic.CS1591.severity = none # Missing XML comment for publicly visible type or member
dotnet_diagnostic.IDE0005.severity = warning
# IDE0035 Remove unreachable code
# No options
# Duplicates compiler warning CS0162
dotnet_diagnostic.IDE0035.severity = none
# IDE0051 Remove unused private member
# No options
dotnet_diagnostic.IDE0051.severity = warning
# IDE0052 Remove unread private member
# No options
dotnet_diagnostic.IDE0052.severity = warning
# IDE0058 Remove unnecessary expression value
#csharp_style_unused_value_expression_statement_preference = discard_variable
dotnet_diagnostic.IDE0058.severity = silent
# IDE0059 Remove unnecessary value assignment
#csharp_style_unused_value_assignment_preference = discard_variable
dotnet_diagnostic.IDE0059.severity = warning
# IDE0060 Remove unused parameter
dotnet_code_quality_unused_parameters = non_public
dotnet_diagnostic.IDE0060.severity = warning
# IDE0079 Remove unnecessary suppression
#dotnet_remove_unnecessary_suppression_exclusions = none
dotnet_diagnostic.IDE0079.severity = warning
# IDE0080 Remove unnecessary suppression operator
# No options
dotnet_diagnostic.IDE0080.severity = warning
# IDE0100 Remove unnecessary equality operator
# No options
dotnet_diagnostic.IDE0100.severity = warning
# IDE0110 Remove unnecessary discard
# No options
dotnet_diagnostic.IDE0110.severity = warning
### Miscellaneous Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/miscellaneous-rules
# IDE0076 Remove invalid global 'SuppressMessageAttribute'
# No options
dotnet_diagnostic.IDE0076.severity = warning
# IDE0077 Avoid legacy format target in global 'SuppressMessageAttribute'
# No options
dotnet_diagnostic.IDE0077.severity = warning
### Formatting Rules (IDE0055)
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0055
# We may eventually wish to enforce this rule, however some existing formatting conflicts with the rule despite being reasonable.
# Additionally, the rule is buggy and likes to report spuriously after invoking Format Document in the IDE.
dotnet_diagnostic.IDE0055.severity = none
#dotnet_sort_system_directives_first = true
#dotnet_separate_import_directive_groups = false
#dotnet_style_namespace_match_folder = true
#csharp_new_line_before_open_brace = all
#csharp_new_line_before_else = true
#csharp_new_line_before_catch = true
#csharp_new_line_before_finally = true
#csharp_new_line_before_members_in_object_initializers = true
#csharp_new_line_before_members_in_anonymous_types = true
#csharp_new_line_between_query_expression_clauses = true
#csharp_indent_case_contents = true
#csharp_indent_switch_labels = true
#csharp_indent_labels = one_less_than_current
#csharp_indent_block_contents = true
#csharp_indent_braces = false
#csharp_indent_case_contents_when_block = true
#csharp_space_after_cast = false
#csharp_space_after_keywords_in_control_flow_statements = true
#csharp_space_between_parentheses =
#csharp_space_before_colon_in_inheritance_clause = true
#csharp_space_after_colon_in_inheritance_clause = true
#csharp_space_around_binary_operators = before_and_after
#csharp_space_between_method_declaration_parameter_list_parentheses = false
#csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
#csharp_space_between_method_declaration_name_and_open_parenthesis = false
#csharp_space_between_method_call_parameter_list_parentheses = false
#csharp_space_between_method_call_empty_parameter_list_parentheses = false
#csharp_space_between_method_call_name_and_opening_parenthesis = false
#csharp_space_after_comma = true
#csharp_space_before_comma = false
#csharp_space_after_dot = false
#csharp_space_before_dot = false
#csharp_space_after_semicolon_in_for_statement = true
#csharp_space_before_semicolon_in_for_statement = false
#csharp_space_around_declaration_statements = false
#csharp_space_before_open_square_brackets = false
#csharp_space_between_empty_square_brackets = false
#csharp_space_between_square_brackets = false
#csharp_preserve_single_line_statements = true
#csharp_preserve_single_line_blocks = true
### Naming Rules (IDE1006)
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/naming-rules
dotnet_diagnostic.IDE1006.severity = warning
## Naming styles
dotnet_naming_style.camel_case.capitalization = camel_case
dotnet_naming_style.pascal_case.capitalization = pascal_case
# Symbol specifications
dotnet_naming_style.i_prefix_pascal_case.capitalization = pascal_case
dotnet_naming_style.i_prefix_pascal_case.required_prefix = I
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal
## Naming Symbols
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.const_locals.applicable_kinds = local
dotnet_naming_symbols.const_locals.applicable_accessibilities = *
dotnet_naming_symbols.const_locals.required_modifiers = const
dotnet_naming_symbols.internal_field.applicable_kinds = field
dotnet_naming_symbols.internal_field.applicable_accessibilities = internal
dotnet_naming_symbols.const_fields.applicable_kinds = field
dotnet_naming_symbols.const_fields.applicable_accessibilities = *
dotnet_naming_symbols.const_fields.required_modifiers = const
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.static_readonly_fields.applicable_kinds = field
dotnet_naming_symbols.static_readonly_fields.applicable_accessibilities = *
dotnet_naming_symbols.static_readonly_fields.required_modifiers = static, readonly
dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private
dotnet_naming_symbols.non_private_readonly_fields.applicable_kinds = field
dotnet_naming_symbols.non_private_readonly_fields.applicable_accessibilities = public, internal, protected, protected_internal, private_protected
dotnet_naming_symbols.non_private_readonly_fields.required_modifiers = readonly
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.private_or_protected_fields.applicable_kinds = field
dotnet_naming_symbols.private_or_protected_fields.applicable_accessibilities = private, protected, private_protected
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
dotnet_naming_symbols.interfaces.applicable_kinds = interface
dotnet_naming_symbols.interfaces.applicable_accessibilities = *
# Naming rules
dotnet_naming_symbols.parameters_and_locals.applicable_kinds = parameter, local
dotnet_naming_symbols.parameters_and_locals.applicable_accessibilities = *
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_symbols.most_symbols.applicable_kinds = namespace, class, struct, enum, field, property, method, local_function, event, delegate, type_parameter
dotnet_naming_symbols.most_symbols.applicable_accessibilities = *
## Naming Rules
dotnet_naming_rule.const_locals_should_be_pascal_case.symbols = const_locals
dotnet_naming_rule.const_locals_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.const_locals_should_be_pascal_case.severity = warning
dotnet_naming_rule.const_fields_should_be_pascal_case.symbols = const_fields
dotnet_naming_rule.const_fields_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.const_fields_should_be_pascal_case.severity = warning
dotnet_naming_rule.static_readonly_fields_should_be_pascal_case.symbols = static_readonly_fields
dotnet_naming_rule.static_readonly_fields_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.static_readonly_fields_should_be_pascal_case.severity = warning
dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.symbols = non_private_readonly_fields
dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.severity = warning
dotnet_naming_rule.private_or_protected_fields_should_be_camel_case.symbols = private_or_protected_fields
dotnet_naming_rule.private_or_protected_fields_should_be_camel_case.style = camel_case
dotnet_naming_rule.private_or_protected_fields_should_be_camel_case.severity = warning
dotnet_naming_rule.interfaces_should_be_i_prefix_pascal_case.symbols = interfaces
dotnet_naming_rule.interfaces_should_be_i_prefix_pascal_case.style = i_prefix_pascal_case
dotnet_naming_rule.interfaces_should_be_i_prefix_pascal_case.severity = warning
dotnet_naming_rule.parameters_and_locals_should_be_camel_case.symbols = parameters_and_locals
dotnet_naming_rule.parameters_and_locals_should_be_camel_case.style = camel_case
dotnet_naming_rule.parameters_and_locals_should_be_camel_case.severity = warning
dotnet_naming_rule.most_symbols_should_be_pascal_case.symbols = most_symbols
dotnet_naming_rule.most_symbols_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.most_symbols_should_be_pascal_case.severity = warning
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
### StyleCop.Analyzers
### https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/DOCUMENTATION.md
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
# Below we enable rule categories by setting severity to warning.
# We'll only list rules to disable.
# Individual rules we wish to disable are typically set to none severity.
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
# Covers SAxxxx and SXxxxx rules
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.DocumentationRules.severity = warning
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.LayoutRules.severity = warning
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.MaintainabilityRules.severity = warning
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.NamingRules.severity = warning
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.OrderingRules.severity = warning
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.ReadabilityRules.severity = warning
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.SpacingRules.severity = warning
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.SpecialRules.severity = warning
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
# Rules that are covered by IDE0001 Simplify name
dotnet_diagnostic.SA1125.severity = none # UseShorthandForNullableTypes
# Naming rules
# Rules that are covered by IDE0047 Remove unnecessary parentheses
dotnet_diagnostic.SA1119.severity = none # StatementMustNotUseUnnecessaryParenthesis
#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
# Rules that are covered by IDE0055 Formatting Rules
dotnet_diagnostic.SA1027.severity = none # UseTabsCorrectly
#Formatting - wrapping options
# Rules that are covered by IDE1006 Naming Rules
dotnet_diagnostic.SA1300.severity = none # ElementMustBeginWithUpperCaseLetter
dotnet_diagnostic.SA1302.severity = none # InterfaceNamesMustBeginWithI
dotnet_diagnostic.SA1303.severity = none # ConstFieldNamesMustBeginWithUpperCaseLetter
dotnet_diagnostic.SA1304.severity = none # NonPrivateReadonlyFieldsMustBeginWithUpperCaseLetter
dotnet_diagnostic.SA1306.severity = none # FieldNamesMustBeginWithLowerCaseLetter
dotnet_diagnostic.SA1307.severity = none # AccessibleFieldsMustBeginWithUpperCaseLetter
dotnet_diagnostic.SA1311.severity = none # StaticReadonlyFieldsMustBeginWithUpperCaseLetter
dotnet_diagnostic.SA1312.severity = none # VariableNamesMustBeginWithLowerCaseLetter
dotnet_diagnostic.SA1313.severity = none # ParameterNamesMustBeginWithLowerCaseLetter
#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
# Rules that conflict with OpenRA project style conventions
dotnet_diagnostic.SA1101.severity = none # PrefixLocalCallsWithThis
dotnet_diagnostic.SA1107.severity = none # CodeMustNotContainMultipleStatementsOnOneLine
dotnet_diagnostic.SA1116.severity = none # SplitParametersMustStartOnLineAfterDeclaration
dotnet_diagnostic.SA1117.severity = none # ParametersMustBeOnSameLineOrSeparateLines
dotnet_diagnostic.SA1118.severity = none # ParameterMustNotSpanMultipleLines
dotnet_diagnostic.SA1122.severity = none # UseStringEmptyForEmptyStrings
dotnet_diagnostic.SA1124.severity = none # DoNotUseRegions
dotnet_diagnostic.SA1127.severity = none # GenericTypeConstraintsMustBeOnOwnLine
dotnet_diagnostic.SA1132.severity = none # DoNotCombineFields
dotnet_diagnostic.SA1135.severity = none # UsingDirectivesMustBeQualified
dotnet_diagnostic.SA1136.severity = none # EnumValuesShouldBeOnSeparateLines
dotnet_diagnostic.SA1200.severity = none # UsingDirectivesMustBePlacedCorrectly
dotnet_diagnostic.SA1201.severity = none # ElementsMustAppearInTheCorrectOrder
dotnet_diagnostic.SA1202.severity = none # ElementsMustBeOrderedByAccess
dotnet_diagnostic.SA1204.severity = none # StaticElementsMustAppearBeforeInstanceElements
dotnet_diagnostic.SA1214.severity = none # ReadonlyElementsMustAppearBeforeNonReadonlyElements
dotnet_diagnostic.SX1309.severity = none # FieldNamesMustBeginWithUnderscore
dotnet_diagnostic.SX1309S.severity = none # StaticFieldNamesMustBeginWithUnderscore
dotnet_diagnostic.SA1314.severity = none # TypeParameterNamesMustBeginWithT
dotnet_diagnostic.SA1400.severity = none # AccessModifierMustBeDeclared
dotnet_diagnostic.SA1401.severity = none # FieldsMustBePrivate
dotnet_diagnostic.SA1402.severity = none # FileMayOnlyContainASingleType
dotnet_diagnostic.SA1407.severity = none # ArithmeticExpressionsMustDeclarePrecedence
dotnet_diagnostic.SA1413.severity = none # UseTrailingCommasInMultiLineInitializers
dotnet_diagnostic.SA1501.severity = none # StatementMustNotBeOnSingleLine
dotnet_diagnostic.SA1502.severity = none # ElementMustNotBeOnSingleLine
dotnet_diagnostic.SA1503.severity = none # BracesMustNotBeOmitted
dotnet_diagnostic.SA1516.severity = none # ElementsMustBeSeparatedByBlankLine
dotnet_diagnostic.SA1519.severity = none # BracesMustNotBeOmittedFromMultiLineChildStatement
dotnet_diagnostic.SA1520.severity = none # UseBracesConsistently
dotnet_diagnostic.SA1600.severity = none # ElementsMustBeDocumented
dotnet_diagnostic.SA1601.severity = none # PartialElementsMustBeDocumented
dotnet_diagnostic.SA1602.severity = none # EnumerationItemsMustBeDocumented
dotnet_diagnostic.SA1611.severity = none # ElementParametersShouldBeDocumented
dotnet_diagnostic.SA1615.severity = none # ElementReturnValueShouldBeDocumented
dotnet_diagnostic.SA1618.severity = none # GenericTypeParametersShouldBeDocumented
dotnet_diagnostic.SA1623.severity = none # PropertySummaryDocumentationShouldMatchAccessors
dotnet_diagnostic.SA1633.severity = none # FileMustHaveHeader
dotnet_diagnostic.SA1642.severity = none # ConstructorSummaryDocumentationShouldBeginWithStandardText
dotnet_diagnostic.SA1649.severity = none # FileNameMustMatchTypeName
#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
#### Code Quality Rules
#### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/
#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
# Below we enable specific rules by setting severity to warning.
# Rules are listed below with any options available.
# Options are commented out if they match the defaults.
; 4-column tab indentation
[*.yaml]
indent_style = tab
indent_size = 4
# Rule options that apply over multiple rules are set here.
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/code-quality-rule-options
dotnet_code_quality.api_surface = all
### Design Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/design-warnings
# Collections should implement generic interface.
#dotnet_code_quality.CA1010.additional_required_generic_interfaces =
dotnet_diagnostic.CA1010.severity = warning
# Abstract types should not have public constructors.
dotnet_diagnostic.CA1012.severity = warning
# Mark attributes with 'AttributeUsageAttribute'.
dotnet_diagnostic.CA1018.severity = warning
# Override methods on comparable types.
dotnet_diagnostic.CA1036.severity = warning
# Provide ObsoleteAttribute message.
dotnet_diagnostic.CA1041.severity = warning
# Do not declare protected members in sealed types.
dotnet_diagnostic.CA1047.severity = warning
# Declare types in namespaces.
dotnet_diagnostic.CA1050.severity = warning
# Static holder types should be 'Static' or 'NotInheritable'.
dotnet_diagnostic.CA1052.severity = warning
# Do not hide base class methods.
dotnet_diagnostic.CA1061.severity = warning
# Exceptions should be public.
dotnet_diagnostic.CA1064.severity = warning
# Implement 'IEquatable' when overriding 'Equals'.
dotnet_diagnostic.CA1066.severity = warning
# Override 'Equals' when implementing 'IEquatable'.
dotnet_diagnostic.CA1067.severity = warning
# 'CancellationToken' parameters must come last.
dotnet_diagnostic.CA1068.severity = warning
# Do not declare event fields as virtual.
dotnet_diagnostic.CA1070.severity = warning
### Documentation Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/documentation-warnings
# Avoid using 'cref' tags with a prefix.
dotnet_diagnostic.CA1200.severity = warning
### Globalization Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/globalization-warnings
### Portability and Interoperability Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/interoperability-warnings
# Do not use 'OutAttribute' on string parameters for P/Invokes.
dotnet_diagnostic.CA1417.severity = warning
### Maintainability Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/maintainability-warnings
# Use 'nameof' in place of string.
dotnet_diagnostic.CA1507.severity = warning
### Naming Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/naming-warnings
# Do not prefix enum values with type name.
dotnet_code_quality.CA1712.enum_values_prefix_trigger = AnyEnumValue
dotnet_diagnostic.CA1712.severity = warning
# Flags enums should have plural names.
dotnet_diagnostic.CA1714.severity = warning
# Only 'FlagsAttribute' enums should have plural names.
dotnet_diagnostic.CA1717.severity = warning
### Performance Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/performance-warnings
# Use Literals Where Appropriate.
#dotnet_code_quality.CA1802.required_modifiers = static
dotnet_diagnostic.CA1802.severity = warning
# Remove empty finalizers.
dotnet_diagnostic.CA1821.severity = warning
# Mark members as static.
dotnet_code_quality.CA1822.api_surface = private,internal
dotnet_diagnostic.CA1822.severity = warning
# Avoid unused private fields.
dotnet_diagnostic.CA1823.severity = warning
# Avoid zero-length array allocations.
dotnet_diagnostic.CA1825.severity = warning
# Use property instead of Linq Enumerable method.
#dotnet_code_quality.CA1826.exclude_ordefault_methods = false
dotnet_diagnostic.CA1826.severity = warning
# Do not use Count/LongCount when Any can be used.
dotnet_diagnostic.CA1827.severity = warning
# Do not use CountAsync/LongCountAsync when AnyAsync can be used.
dotnet_diagnostic.CA1828.severity = warning
# Use Length/Count property instead of Enumerable.Count method.
dotnet_diagnostic.CA1829.severity = warning
# Prefer strongly-typed Append and Insert method overloads on StringBuilder.
dotnet_diagnostic.CA1830.severity = warning
# Use AsSpan instead of Range-based indexers for string when appropriate.
dotnet_diagnostic.CA1831.severity = warning
# Use AsSpan or AsMemory instead of Range-based indexers for getting ReadOnlySpan or ReadOnlyMemory portion of an array.
dotnet_diagnostic.CA1832.severity = warning
# Use AsSpan or AsMemory instead of Range-based indexers for getting Span or Memory portion of an array.
dotnet_diagnostic.CA1833.severity = warning
# Use StringBuilder.Append(char) for single character strings.
dotnet_diagnostic.CA1834.severity = warning
# Prefer the memory-based overloads of ReadAsync/WriteAsync methods in stream-based classes.
dotnet_diagnostic.CA1835.severity = warning
# Prefer IsEmpty over Count when available.
dotnet_diagnostic.CA1836.severity = warning
# Use Environment.ProcessId instead of Process.GetCurrentProcess().Id.
dotnet_diagnostic.CA1837.severity = warning
# Avoid StringBuilder parameters for P/Invokes.
dotnet_diagnostic.CA1838.severity = warning
# Use Environment.ProcessPath instead of Process.GetCurrentProcess().MainModule.FileName.
dotnet_diagnostic.CA1839.severity = warning
# Use Environment.CurrentManagedThreadId instead of Thread.CurrentThread.ManagedThreadId.
dotnet_diagnostic.CA1840.severity = warning
# Prefer Dictionary Contains methods.
dotnet_diagnostic.CA1841.severity = warning
# Do not use 'WhenAll' with a single task.
dotnet_diagnostic.CA1842.severity = warning
# Do not use 'WaitAll' with a single task.
dotnet_diagnostic.CA1843.severity = warning
# Provide memory-based overrides of async methods when subclassing 'Stream'.
dotnet_diagnostic.CA1844.severity = warning
# Use span-based 'string.Concat'. (Not available on mono)
dotnet_diagnostic.CA1845.severity = none
# Prefer AsSpan over Substring.
dotnet_diagnostic.CA1846.severity = warning
# Use string.Contains(char) instead of string.Contains(string) with single characters.
dotnet_diagnostic.CA1847.severity = warning
# Call async methods when in an async method.
dotnet_diagnostic.CA1849.severity = warning
# Prefer static HashData method over ComputeHash. (Not available on mono)
dotnet_diagnostic.CA1850.severity = none
# Seal internal types.
dotnet_diagnostic.CA1852.severity = warning
# Unnecessary call to 'Dictionary.ContainsKey(key)'.
dotnet_diagnostic.CA1853.severity = warning
# Prefer the IDictionary.TryGetValue(TKey, out TValue) method.
dotnet_diagnostic.CA1854.severity = warning
# Use Span<T>.Clear() instead of Span<T>.Fill().
dotnet_diagnostic.CA1855.severity = warning
# Use StartsWith instead of IndexOf.
dotnet_diagnostic.CA1858.severity = warning
# Avoid using 'Enumerable.Any()' extension method.
dotnet_diagnostic.CA1860.severity = warning
### Reliability Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/reliability-warnings
# Do not assign property within its setter.
dotnet_diagnostic.CA2011.severity = warning
# Use ValueTasks correctly.
dotnet_diagnostic.CA2012.severity = warning
# Do not use ReferenceEquals with value types.
dotnet_diagnostic.CA2013.severity = warning
# Do not use stackalloc in loops.
dotnet_diagnostic.CA2014.severity = warning
# Forward the CancellationToken parameter to methods that take one.
dotnet_diagnostic.CA2016.severity = warning
# The 'count' argument to Buffer.BlockCopy should specify the number of bytes to copy.
dotnet_diagnostic.CA2018.severity = warning
# ThreadStatic fields should not use inline initialization.
dotnet_diagnostic.CA2019.severity = warning
### Security Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/security-warnings
# Do Not Use Broken Cryptographic Algorithms.
dotnet_diagnostic.CA5351.severity = warning
### Usage Rules
### https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/usage-warnings
# Call GC.SuppressFinalize correctly.
dotnet_diagnostic.CA1816.severity = warning
# Rethrow to preserve stack details.
dotnet_diagnostic.CA2200.severity = warning
# Initialize value type static fields inline.
dotnet_diagnostic.CA2207.severity = warning
# Instantiate argument exceptions correctly.
dotnet_diagnostic.CA2208.severity = warning
# Dispose methods should call base class dispose.
dotnet_diagnostic.CA2215.severity = warning
# Disposable types should declare finalizer.
dotnet_diagnostic.CA2216.severity = warning
# Override GetHashCode on overriding Equals.
dotnet_diagnostic.CA2218.severity = warning
# Overload operator equals on overriding ValueType.Equals.
dotnet_diagnostic.CA2231.severity = warning
# Provide correct arguments to formatting methods.
#dotnet_code_quality.CA2241.additional_string_formatting_methods =
dotnet_code_quality.CA2241.try_determine_additional_string_formatting_methods_automatically = true
dotnet_diagnostic.CA2241.severity = warning
# Test for NaN correctly.
dotnet_diagnostic.CA2242.severity = warning
# Attribute string literals should parse correctly.
dotnet_diagnostic.CA2243.severity = warning
# Do not duplicate indexed element initializations.
dotnet_diagnostic.CA2244.severity = warning
# Do not assign a property to itself.
dotnet_diagnostic.CA2245.severity = warning
# Argument passed to TaskCompletionSource constructor should be TaskCreationOptions enum instead of TaskContinuationOptions enum.
dotnet_diagnostic.CA2247.severity = warning
# Provide correct enum argument to Enum.HasFlag.
dotnet_diagnostic.CA2248.severity = warning
# Use ThrowIfCancellationRequested.
dotnet_diagnostic.CA2250.severity = warning
# Ensure ThreadStatic is only used with static fields.
dotnet_diagnostic.CA2259.severity = warning
### Roslynator
### https://github.com/JosefPihrt/Roslynator/tree/main/docs/analyzers
# Below we enable specific rules by setting severity to warning.
# Use 'Count' property instead of 'Any' method.
dotnet_diagnostic.RCS1080.severity = warning
# Use read-only auto-implemented property.
dotnet_diagnostic.RCS1170.severity = warning
# Unnecessary interpolated string.
dotnet_diagnostic.RCS1214.severity = warning
# Unnecessary usage of verbatim string literal.
dotnet_diagnostic.RCS1192.severity = warning
# Use pattern matching instead of combination of 'as' operator and null check.
dotnet_diagnostic.RCS1221.severity = warning
# Expression is always equal to 'true'.
dotnet_diagnostic.RCS1215.severity = warning
# Use StringComparison when comparing strings.
dotnet_diagnostic.RCS1155.severity = warning
# Abstract type should not have public constructors.
dotnet_diagnostic.RCS1160.severity = warning
# Optimize 'Dictionary<TKey, TValue>.ContainsKey' call.
dotnet_diagnostic.RCS1235.severity = warning
# Call extension method as instance method.
dotnet_diagnostic.RCS1196.severity = warning

View File

@@ -10,5 +10,5 @@ contact_links:
url: https://discord.openra.net/
about: Join the community Discord server for community discussion and support.
- name: OpenRA IRC Channel
url: https://webchat.freenode.net/#openra
about: Join our development IRC channel on freenode for discussion of development topics.
url: https://web.libera.chat/#openra
about: Join our development IRC channel on Libera for discussion of development topics.

View File

@@ -13,4 +13,4 @@ You can help speed up the review process by following a few steps:
* Respond to review comments as soon as you reasonably can. Reviewers will usually prioritize Pull Requests that are still fresh in their minds. Make sure to leave a comment when you push new changes, otherwise GitHub does not automatically notify reviewers!
* Leave a polite comment asking for reviews if a week or more has passed without feedback.
If you need any help you can ask in the #openra IRC channel on freenode (most active during European evenings).
If you need any help you can ask on Discord (https://discord.openra.net) or in the #openra IRC channel on Libera (not as active as Discord).

View File

@@ -1,55 +0,0 @@
name: Continuous Integration
on:
push:
pull_request:
branches: [ bleed ]
jobs:
linux-mono:
name: Linux (mono)
runs-on: ubuntu-20.04
steps:
- name: Clone Repository
uses: actions/checkout@v2
- name: Check Code
run: |
mono --version
make check
- name: Check Mods
run: |
sudo apt-get install lua5.1
make check-scripts
make test
windows:
name: Windows (Net 5.0)
runs-on: windows-2019
steps:
- name: Clone Repository
uses: actions/checkout@v2
- name: Install .NET 5
uses: actions/setup-dotnet@v1
with:
dotnet-version: '5.0.x'
- name: Check Code
shell: powershell
run: |
# Work around runtime failures on the GH Actions runner
dotnet nuget locals all --clear
.\make.ps1 check
dotnet build OpenRA.Test\OpenRA.Test.csproj -c Debug --nologo -p:TargetPlatform=win-x64
dotnet test bin\OpenRA.Test.dll --test-adapter-path:.
- name: Check Mods
run: |
chocolatey install lua --version 5.1.5.52
$ENV:Path = $ENV:Path + ";C:\Program Files (x86)\Lua\5.1\"
.\make.ps1 check-scripts
.\make.ps1 test

81
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,81 @@
name: Continuous Integration
on:
push:
pull_request:
branches: [ bleed, 'prep-*' ]
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
linux:
name: Linux (.NET 6.0)
runs-on: ubuntu-22.04
steps:
- name: Clone Repository
uses: actions/checkout@v3
- name: Install .NET 6.0
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
- name: Check Code
run: |
make check
make tests
- name: Check Mods
run: |
sudo apt-get install lua5.1
make check-scripts
make TREAT_WARNINGS_AS_ERRORS=true test
linux-mono:
name: Linux (mono)
runs-on: ubuntu-22.04
steps:
- name: Clone Repository
uses: actions/checkout@v3
- name: Check Code
run: |
mono --version
make RUNTIME=mono check
- name: Check Mods
run: |
# check-scripts does not depend on .net/mono, so is not needed here
make RUNTIME=mono TREAT_WARNINGS_AS_ERRORS=true test
windows:
name: Windows (.NET 6.0)
runs-on: windows-2019
steps:
- name: Clone Repository
uses: actions/checkout@v3
- name: Install .NET 6.0
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
- name: Check Code
shell: powershell
run: |
# Work around runtime failures on the GH Actions runner
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
.\make.ps1 check
.\make.ps1 tests
- name: Check Mods
run: |
choco install lua --version 5.1.5.52
$ENV:Path = $ENV:Path + ";C:\Program Files (x86)\Lua\5.1\"
$ENV:TREAT_WARNINGS_AS_ERRORS = "true"
.\make.ps1 check-scripts
.\make.ps1 test

View File

@@ -8,23 +8,31 @@ on:
required: true
default: 'release-xxxxxxxx'
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
wiki:
name: Update Wiki
if: github.repository == 'openra/openra'
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Clone Repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
ref: ${{ github.event.inputs.tag }}
- name: Install .NET 6
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
- name: Prepare Environment
run: |
make all
- name: Clone Wiki
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
repository: openra/openra.wiki
token: ${{ secrets.DOCS_TOKEN }}
@@ -58,50 +66,78 @@ jobs:
docs:
name: Update docs.openra.net
if: github.repository == 'openra/openra'
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Clone Repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
ref: ${{ github.event.inputs.tag }}
- name: Install .NET 6
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
- name: Prepare Environment
run: |
make all
- name: Clone docs.openra.net
uses: actions/checkout@v2
- name: Clone docs.openra.net (Playtest)
if: startsWith(github.event.inputs.tag, 'playtest-')
uses: actions/checkout@v3
with:
repository: openra/docs
token: ${{ secrets.DOCS_TOKEN }}
path: docs
ref: playtest
- name: Clone docs.openra.net (Release)
if: startsWith(github.event.inputs.tag, 'release-')
uses: actions/checkout@v3
with:
repository: openra/docs
token: ${{ secrets.DOCS_TOKEN }}
path: docs
ref: release
- name: Update docs.openra.net (Playtest)
if: startsWith(github.event.inputs.tag, 'playtest-')
env:
GIT_TAG: ${{ github.event.inputs.tag }}
run: |
./utility.sh all --docs "${GIT_TAG}" > "docs/api/playtest/traits.md"
./utility.sh all --weapon-docs "${GIT_TAG}" > "docs/api/playtest/weapons.md"
./utility.sh all --lua-docs "${GIT_TAG}" > "docs/api/playtest/lua.md"
./utility.sh all --docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/traits.md"
./utility.sh all --weapon-docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/weapons.md"
./utility.sh all --sprite-sequence-docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/sprite-sequences.md"
./utility.sh all --lua-docs "${GIT_TAG}" > "docs/api/lua.md"
- name: Update docs.openra.net (Release)
if: startsWith(github.event.inputs.tag, 'release-')
env:
GIT_TAG: ${{ github.event.inputs.tag }}
run: |
./utility.sh all --docs "${GIT_TAG}" > "docs/api/release/traits.md"
./utility.sh all --weapon-docs "${GIT_TAG}" > "docs/api/release/weapons.md"
./utility.sh all --lua-docs "${GIT_TAG}" > "docs/api/release/lua.md"
./utility.sh all --docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/traits.md"
./utility.sh all --weapon-docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/weapons.md"
./utility.sh all --sprite-sequence-docs "${GIT_TAG}" | python3 ./packaging/format-docs.py > "docs/api/sprite-sequences.md"
./utility.sh all --lua-docs "${GIT_TAG}" > "docs/api/lua.md"
- name: Push docs.openra.net
- name: Commit docs.openra.net
env:
GIT_TAG: ${{ github.event.inputs.tag }}
run: |
cd docs
git config --local user.email "actions@github.com"
git config --local user.name "GitHub Actions"
git add --all
git add api/*.md
git commit -m "Update auto-generated documentation for ${GIT_TAG}"
git push origin master
- name: Push docs.openra.net (Release)
if: startsWith(github.event.inputs.tag, 'release-')
run: |
cd docs
git push origin release
- name: Push docs.openra.net (Playtest)
if: startsWith(github.event.inputs.tag, 'playtest-')
run: |
cd docs
git push origin playtest

View File

@@ -8,24 +8,23 @@ on:
required: true
default: 'release-xxxxxxxx'
permissions: {}
jobs:
itch:
name: Deploy to itch.io
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
if: github.repository == 'openra/openra'
steps:
- name: Download Packages
env:
GIT_TAG: ${{ github.event.inputs.tag }}
run: |
wget -q "https://github.com/${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-${GIT_TAG}-x64.exe"
wget -q "https://github.com/${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-${GIT_TAG}-x64-winportable.zip" -O "OpenRA-${GIT_TAG}-x64-win-itch.zip"
wget -q "https://github.com${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-${GIT_TAG}.dmg"
wget -q "https://github.com/${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-Dune-2000-x86_64.AppImage"
wget -q "https://github.com/${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-Red-Alert-x86_64.AppImage"
wget -q "https://github.com/${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-Tiberian-Dawn-x86_64.AppImage"
wget -q "https://raw.githubusercontent.com/${{ github.repository }}/${GIT_TAG}/packaging/.itch.toml"
zip -u "OpenRA-${GIT_TAG}-x64-win-itch.zip" .itch.toml
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-${{ github.event.inputs.tag }}-x64.exe"
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-${{ github.event.inputs.tag }}-x64-winportable.zip" -O "OpenRA-${{ github.event.inputs.tag }}-x64-win-itch.zip"
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-${{ github.event.inputs.tag }}.dmg"
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-Dune-2000-x86_64.AppImage"
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-Red-Alert-x86_64.AppImage"
wget -q "https://github.com/${{ github.repository }}/releases/download/${{ github.event.inputs.tag }}/OpenRA-Tiberian-Dawn-x86_64.AppImage"
wget -q "https://raw.githubusercontent.com/${{ github.repository }}/${{ github.event.inputs.tag }}/packaging/.itch.toml"
zip -u "OpenRA-${{ github.event.inputs.tag }}-x64-win-itch.zip" .itch.toml
- name: Publish Windows Installer
uses: josephbmanley/butler-publish-itchio-action@master
@@ -33,9 +32,9 @@ jobs:
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
CHANNEL: win
ITCH_GAME: openra
ITCH_USER: openra-developers
ITCH_USER: openra
VERSION: ${{ github.event.inputs.tag }}
PACKAGE: OpenRA-${{ github.event.inputs.tag }}}-x64.exe"
PACKAGE: OpenRA-${{ github.event.inputs.tag }}-x64.exe
- name: Publish Windows Itch Bundle
uses: josephbmanley/butler-publish-itchio-action@master
@@ -43,7 +42,7 @@ jobs:
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
CHANNEL: itch
ITCH_GAME: openra
ITCH_USER: openra-developers
ITCH_USER: openra
VERSION: ${{ github.event.inputs.tag }}
PACKAGE: OpenRA-${{ github.event.inputs.tag }}-x64-win-itch.zip
@@ -53,9 +52,9 @@ jobs:
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
CHANNEL: macos
ITCH_GAME: openra
ITCH_USER: openra-developers
ITCH_USER: openra
VERSION: ${{ github.event.inputs.tag }}
PACKAGE: OpenRA-${{ github.event.inputs.tag }}}.dmg"
PACKAGE: OpenRA-${{ github.event.inputs.tag }}.dmg
- name: Publish RA AppImage
uses: josephbmanley/butler-publish-itchio-action@master
@@ -63,7 +62,7 @@ jobs:
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
CHANNEL: linux-ra
ITCH_GAME: openra
ITCH_USER: openra-developers
ITCH_USER: openra
VERSION: ${{ github.event.inputs.tag }}
PACKAGE: OpenRA-Red-Alert-x86_64.AppImage
@@ -73,7 +72,7 @@ jobs:
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
CHANNEL: linux-cnc
ITCH_GAME: openra
ITCH_USER: openra-developers
ITCH_USER: openra
VERSION: ${{ github.event.inputs.tag }}
PACKAGE: OpenRA-Tiberian-Dawn-x86_64.AppImage
@@ -83,6 +82,6 @@ jobs:
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
CHANNEL: linux-d2k
ITCH_GAME: openra
ITCH_USER: openra-developers
ITCH_USER: openra
VERSION: ${{ github.event.inputs.tag }}
PACKAGE: OpenRA-Dune-2000-x86_64.AppImage

View File

@@ -7,13 +7,45 @@ on:
- 'playtest-*'
- 'devtest-*'
permissions:
contents: write # for release creation (svenstaro/upload-release-action)
jobs:
linux:
name: Linux AppImages
runs-on: ubuntu-20.04
source:
name: Source Tarball
runs-on: ubuntu-22.04
steps:
- name: Clone Repository
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Prepare Environment
run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
- name: Package Source
run: |
mkdir -p build/source
./packaging/source/buildpackage.sh "${GIT_TAG}" "${PWD}/build/source"
- name: Upload Packages
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.ref }}
overwrite: true
file_glob: true
file: build/source/*
linux:
name: Linux AppImages
runs-on: ubuntu-22.04
steps:
- name: Clone Repository
uses: actions/checkout@v3
- name: Install .NET 6.0
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
- name: Prepare Environment
run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
@@ -21,6 +53,7 @@ jobs:
- name: Package AppImages
run: |
mkdir -p build/linux
sudo apt install libfuse2
./packaging/linux/buildpackage.sh "${GIT_TAG}" "${PWD}/build/linux"
- name: Upload Packages
@@ -33,16 +66,21 @@ jobs:
file: build/linux/*
macos:
name: macOS Disk Images
runs-on: macos-10.15
name: macOS Disk Image
runs-on: macos-11
steps:
- name: Clone Repository
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Install .NET 6.0
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
- name: Prepare Environment
run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
- name: Package Disk Images
- name: Package Disk Image
env:
MACOS_DEVELOPER_IDENTITY: ${{ secrets.MACOS_DEVELOPER_IDENTITY }}
MACOS_DEVELOPER_CERTIFICATE_BASE64: ${{ secrets.MACOS_DEVELOPER_CERTIFICATE_BASE64 }}
@@ -53,7 +91,7 @@ jobs:
mkdir -p build/macos
./packaging/macos/buildpackage.sh "${GIT_TAG}" "${PWD}/build/macos"
- name: Upload Packages
- name: Upload Package
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
@@ -64,20 +102,21 @@ jobs:
windows:
name: Windows Installers
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Clone Repository
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Install .NET 5
uses: actions/setup-dotnet@v1
- name: Install .NET 6.0
uses: actions/setup-dotnet@v3
with:
dotnet-version: '5.0.x'
dotnet-version: '6.0.x'
- name: Prepare Environment
run: |
echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
sudo apt install nsis wine64
sudo apt-get update
sudo apt-get install nsis wine64
- name: Package Installers
run: |

19
.gitignore vendored
View File

@@ -13,19 +13,10 @@ obj
_ReSharper.*/
/.vs
# Visual Studio Code
/.vscode/settings.json
# binaries
mods/*/*.dll
mods/*/*.mdb
mods/*/*.pdb
/*.dll
/*.dll.config
/*.so
/*.dylib
/*.pdb
/*.mdb
/*.exe
/*.exe.config
thirdparty/download/*
IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP
# backup files by various editors
@@ -50,10 +41,6 @@ Settings.md
openra.6
update.log
# StyleCop
*.Cache
StyleCopViolations.xml
# SublimeText
*.sublime-project
*.sublime-workspace

View File

@@ -1,7 +1,9 @@
{
"recommendations": [
"ms-dotnettools.csharp",
"openra.oraide-vscode",
"openra.vscode-openra-lua",
"EditorConfig.EditorConfig",
"ms-vscode.mono-debug"
"macabeus.vscode-fluent",
]
}

66
.vscode/launch.json vendored
View File

@@ -3,59 +3,61 @@
"configurations": [
{
"name": "Launch (TD)",
"type": "clr",
"linux": {
"type": "mono"
},
"osx": {
"type": "mono"
},
"type": "coreclr",
"request": "launch",
"program": "${workspaceRoot}/bin/OpenRA.exe",
"program": "${workspaceRoot}/bin/OpenRA.dll",
"windows": {
"program": "${workspaceRoot}/bin/OpenRA.exe",
},
"args": ["Game.Mod=cnc", "Engine.EngineDir=.."],
"preLaunchTask": "build",
},
{
"name": "Launch (RA)",
"type": "clr",
"linux": {
"type": "mono"
},
"osx": {
"type": "mono"
},
"type": "coreclr",
"request": "launch",
"program": "${workspaceRoot}/bin/OpenRA.exe",
"program": "${workspaceRoot}/bin/OpenRA.dll",
"windows": {
"program": "${workspaceRoot}/bin/OpenRA.exe",
},
"args": ["Game.Mod=ra", "Engine.EngineDir=.."],
"preLaunchTask": "build",
},
{
"name": "Launch (D2k)",
"type": "clr",
"linux": {
"type": "mono"
},
"osx": {
"type": "mono"
},
"type": "coreclr",
"request": "launch",
"program": "${workspaceRoot}/bin/OpenRA.exe",
"program": "${workspaceRoot}/bin/OpenRA.dll",
"windows": {
"program": "${workspaceRoot}/bin/OpenRA.exe",
},
"args": ["Game.Mod=d2k", "Engine.EngineDir=.."],
"preLaunchTask": "build",
},
{
"name": "Launch (TS)",
"type": "clr",
"linux": {
"type": "mono"
},
"osx": {
"type": "mono"
},
"type": "coreclr",
"request": "launch",
"program": "${workspaceRoot}/bin/OpenRA.exe",
"program": "${workspaceRoot}/bin/OpenRA.dll",
"windows": {
"program": "${workspaceRoot}/bin/OpenRA.exe",
},
"args": ["Game.Mod=ts", "Engine.EngineDir=.."],
"preLaunchTask": "build",
},
{
"name": "Launch Utility",
"type": "coreclr",
"request": "launch",
"program": "${workspaceRoot}/bin/OpenRA.Utility.dll",
"windows": {
"program": "${workspaceRoot}/bin/OpenRA.Utility.exe",
},
"args": ["all", "--docs", "{DEV_VERSION}"],
"env": {
"ENGINE_DIR": ".."
},
"preLaunchTask": "build",
},
]
}

View File

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

25
.vscode/tasks.json vendored
View File

@@ -1,13 +1,36 @@
{
"version": "2.0.0",
"options": {
"env": {
"ENGINE_DIR": ".."
}
},
"tasks": [
{
"label": "build",
"command": "make",
"args": ["all"],
"args": ["all", "CONFIGURATION=Debug"],
"windows": {
"command": "make.cmd"
}
},
{
"label": "Run Utility",
"command": "dotnet ${workspaceRoot}/bin/OpenRA.Utility.dll ${input:modId} ${input:command}",
"type": "shell",
}
],
"inputs": [
{
"id": "modId",
"description": "ID of the mod to run",
"default": "all",
"type": "promptString"
}, {
"id": "command",
"description": "Name of the command + parameters",
"default": "",
"type": "promptString"
},
]
}

38
AUTHORS
View File

@@ -2,28 +2,30 @@ OpenRA wouldn't be where it is today without the
hard work of many contributors.
The OpenRA developers are:
* Chris Forbes (chrisf)
* Gustas Kažukauskas (PunkPun)
* Lukas Franke (abcdefg30)
* Paul Chote (pchote)
* Reaperrr
* Matthias Mailänder (Mailaender)
Previous developers included:
* Alli Witheford (alzeih)
* Caleb Anderson (RobotCaleb)
* Chris Forbes (chrisf)
* Curtis Shmyr (hamb)
* Daniel Hernandez (Mancano)
* Igor Popov (ihptru)
* Matthias Mailänder (Mailaender)
* Megan Bowra-Dean (beedee)
* Mike Bundy (kehaar)
* Oliver Brakmann (obrakmann)
* Paul Chote (pchote)
* Pavel Penev (penev92)
* Reaperrr
* Robert Pepperell (ytinasni)
* ScottNZ
* Tom Roostan (RoosterDragon)
Also thanks to:
* abmyii
* anvilvapre (anvilvapre)
* Adam Valy (Tschokky)
* Akseli Virtanen (RAGEQUIT)
* Alexander Fast (mizipzor)
@@ -37,6 +39,7 @@ Also thanks to:
* Arik Lirette (Angusm3)
* Barnaby Smith (mvi)
* Bellator
* Bernd Stellwag (burned42)
* Biofreak
* Braxton Williams (Buddytex)
* Brendan Gluth (Mechanical_Man)
@@ -46,6 +49,7 @@ Also thanks to:
* Chris Cameron (Vesuvian)
* Chris Grant (Unit158)
* Christer Ulfsparre (Holloweye)
* Christoph Lahner (chlah)
* clem
* Cody Brittain (Generalcamo)
* Constantin Helmig (CH4Code)
@@ -58,6 +62,7 @@ Also thanks to:
* DeadlySurprise
* Dmitri Suvorov (suvjunmd)
* dtluna
* Eduardo Cáceres (eduherminio)
* Erasmus Schroder (rasco)
* Eric Bajumpaa (SteelPhase)
* Evgeniy Sergeev (evgeniysergeev)
@@ -69,12 +74,14 @@ Also thanks to:
* Glenn Martin Jensen (Baxxster)
* Gordon Martin (Happy0)
* Guido Lipke (LipkeGu)
* Guillermo Cuenca (Wylli)
* Hervé Matysiak (Herve-M)
* Huw Pascoe
* Ian T. Jacobsen (Smilex)
* Imago
* Iran
* Ishan Bhargava (ishantheperson)
* Ivaylo Draganov (dragunoff)
* Jacob Dufault (jacobdufault)
* James Dunne (jsd)
* James Gilbert (DSUK)
@@ -113,6 +120,7 @@ Also thanks to:
* Mike Gagné (AngryBirdz)
* Muh
* Mustafa Alperen Seki (MustaphaTR)
* Nathan Nichols (cracksmoka420)
* Neil Shivkar (havok13888)
* Nikolay Fomin (netnazgul)
* Nooze
@@ -135,12 +143,12 @@ Also thanks to:
* Rikhardur Bjarni Einarsson (WolfGaming)
* Sascha Biedermann (bidifx)
* Sean Hunt (coppro)
* Sebastien Kerguen (xanax)
* Shawn Collins (UberWaffe)
* Simon Verbeke (Saticmotion)
* Stuart McHattie (SDJMcHattie)
* Taryn Hill (Phrohdoh)
* Teemu Nieminen (Temeez)
* Thomas Christlieb (ThomasChr)
* Tim Mylemans (gecko)
* Tirili
* Tomas Einarsson (Mesacer)
@@ -173,9 +181,17 @@ under the MIT license.
Using FuzzyLogicLibrary (fuzzynet) by Dmitry
Kaluzhny and released under the GNU GPL terms.
Using Open.Nat by Lucas Ontivero, based on the work
of Alan McGovern and Ben Motmans and distributed
under the MIT license.
Using Mono.Nat by Alan McGovern, Ben Motmans,
Nicholas Terry distributed under the MIT license.
Using MP3Sharp by Robert Bruke and Zane Wagner
licensed under the GNU LGPL Version 3.
Using TagLib# by Stephen Shaw licensed under the
GNU LGPL Version 2.1.
Using NVorbis by Andrew Ward distributed under
the MIT license.
Using ICSharpCode.SharpZipLib initially by Mike
Krueger and distributed under the GNU GPL terms.
@@ -191,6 +207,12 @@ distributed under MIT License.
Using ANGLE distributed under the BS3 3-Clause license.
Using Pfim developed by Nick Babcock
distributed under the MIT license.
Using Linguini by the Space Station 14 team
licensed under Apache and MIT terms.
This site or product includes IP2Location LITE data
available from http://www.ip2location.com.

View File

@@ -56,8 +56,8 @@ further defined and clarified by project maintainers.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by private-messaging a project team member (users with a + in front
of their name) via our IRC channel (#openra on freenode
[webchat](http://webchat.freenode.net/?channels=openra)). All
of their name) via our IRC channel (#openra on Libera
[webchat](https://web.libera.chat/#openra)). All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.

58
Directory.Build.props Normal file
View File

@@ -0,0 +1,58 @@
<Project>
<PropertyGroup>
<OutputType>Library</OutputType>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<LangVersion>9</LangVersion>
<DebugSymbols>true</DebugSymbols>
<EngineRootPath Condition="'$(EngineRootPath)' == ''">..</EngineRootPath>
<OutputPath>$(EngineRootPath)/bin</OutputPath>
<PlatformTarget>AnyCPU</PlatformTarget>
<ExternalConsole>false</ExternalConsole>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<Nullable>disable</Nullable>
<Product>OpenRA</Product>
<Copyright>Copyright (c) The OpenRA Developers and Contributors</Copyright>
</PropertyGroup>
<PropertyGroup>
<TargetFramework Condition="'$(MSBuildRuntimeType)'!='Mono'">net6.0</TargetFramework>
<TargetFramework Condition="'$(MSBuildRuntimeType)'=='Mono'">netstandard2.1</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<TargetPlatform Condition="$([MSBuild]::IsOsPlatform('Windows'))">win-x64</TargetPlatform>
<TargetPlatform Condition="$([MSBuild]::IsOsPlatform('Linux'))">linux-x64</TargetPlatform>
<TargetPlatform Condition="$([MSBuild]::IsOsPlatform('OSX'))">osx-x64</TargetPlatform>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
<Optimize>false</Optimize>
<!-- Enable only for Debug builds to improve compile-time performance for Release builds -->
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<!-- Enabling GenerateDocumentationFile is required for IDE0005 (Remove unnecessary import)
rule to run in command line builds. https://github.com/dotnet/roslyn/issues/41640
Enable only for Debug builds to improve compile-time performance for Release builds -->
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<!-- Work around an issue where Rider does not detect files in the project root using the default glob -->
<Compile Include="**/*.cs" Exclude="$(DefaultItemExcludes)" />
</ItemGroup>
<Target Name="DisableAnalyzers" BeforeTargets="CoreCompile" Condition="'$(Configuration)'=='Release'">
<!-- Disable code style analysis on Release builds to improve compile-time performance -->
<ItemGroup Condition="'$(Configuration)'=='Release'">
<Analyzer Remove="@(Analyzer)" />
</ItemGroup>
</Target>
<!-- StyleCop -->
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" />
</ItemGroup>
</Project>

View File

@@ -8,7 +8,7 @@ Windows
Compiling OpenRA requires the following dependencies:
* [Windows PowerShell >= 4.0](http://microsoft.com/powershell) (included by default in recent Windows 10 versions)
* [.NET 5 SDK](https://dotnet.microsoft.com/download/dotnet/5.0) (or via Visual Studio)
* [.NET 6 SDK](https://dotnet.microsoft.com/download/dotnet/6.0) (or via Visual Studio)
To compile OpenRA, open the `OpenRA.sln` solution in the main folder, build it from the command-line with `dotnet` or use the Makefile analogue command `make all` scripted in PowerShell syntax.
@@ -17,93 +17,67 @@ Run the game with `launch-game.cmd`. It can be handed arguments that specify the
Linux
=====
Mono, version 6.4 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.
.NET 6 or Mono (version 6.12 or later) is required to compile OpenRA. We recommend using .NET 6 when possible, as Mono is poorly packaged by most Linux distributions (e.g. missing the required `msbuild` toolchain), and has been deprecated as a standalone project.
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 [.NET 6 download page](https://dotnet.microsoft.com/download/dotnet/6.0) provides repositories for various package managers and binary releases for several architectures. If you prefer to use Mono, we suggest adding the [upstream repository](https://www.mono-project.com/download/stable/#download-lin) for your distro to obtain the latest version and the `msbuild` toolchain.
To compile OpenRA, run `make` from the command line (or `make RUNTIME=mono` if using Mono). 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`.
If you choose to use system libraries, or your system is not x86_64, you will need to install the following using your system package manager:
* [SDL 2](http://www.libsdl.org/download-2.0.php)
* [FreeType](http://gnuwin32.sourceforge.net/packages/freetype.htm)
* [OpenAL](http://kcat.strangesoft.net/openal.html)
* [liblua 5.1](http://luabinaries.sourceforge.net/download.html)
If you choose to use system libraries, or your system is not x86_64, you will need to install [SDL 2](https://www.libsdl.org/download-2.0.php), [FreeType](http://gnuwin32.sourceforge.net/packages/freetype.htm), [OpenAL](https://openal-soft.org/), and [liblua 5.1](http://luabinaries.sourceforge.net/download.html) before compiling OpenRA.
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`.
These can be installed using your package manager on various distros:
Arch Linux
----------
It is important to note there is an unofficial [`openra-git`](https://aur.archlinux.org/packages/openra-git) package in the Arch User Repository (AUR) of Arch Linux. If manually compiling is the way you wish to go the build and runtime dependencies can be installed with:
<details><summary>Arch Linux</summary>
```
sudo pacman -S mono openal libgl freetype2 sdl2 lua51 xdg-utils zenity
sudo pacman -S openal libgl freetype2 sdl2 lua51
```
Debian/Ubuntu
-------------
:warning: The `mono` packages in the Ubuntu < 19.04 and Debian < 10 repositories are too old to support OpenRA. :warning:
See the instructions under the *Linux* section above to upgrade `mono` using the upstream releases if needed.
</details>
<details><summary>Debian/Ubuntu</summary>
```
sudo apt install mono-devel libfreetype6 libopenal1 liblua5.1-0 libsdl2-2.0-0 xdg-utils zenity wget
sudo apt install libfreetype6 libopenal1 liblua5.1-0 libsdl2-2.0-0
```
Fedora
------
:warning: The `mono` packages in the Fedora repositories are too old to support OpenRA. :warning:
See the instructions under the *Linux* section above to upgrade `mono` using the upstream releases.
</details>
<details><summary>Fedora</summary>
```
sudo dnf install "pkgconfig(mono)" SDL2 freetype "lua = 5.1" openal-soft xdg-utils zenity
sudo dnf install SDL2 freetype "lua = 5.1" openal-soft
```
Gentoo
------
</details>
<details><summary>Gentoo</summary>
```
sudo emerge -av dev-lang/mono dev-dotnet/libgdiplus media-libs/freetype:2 media-libs/libsdl2 media-libs/openal virtual/jpeg virtual/opengl '=dev-lang/lua-5.1.5*' x11-misc/xdg-utils gnome-extra/zenity
sudo emerge -av media-libs/freetype:2 media-libs/libsdl2 media-libs/openal virtual/opengl '=dev-lang/lua-5.1.5*'
```
Mageia
------
</details>
<details><summary>Mageia</summary>
```
sudo dnf install "pkgconfig(mono)" SDL2 freetype "lib*lua5.1" "lib*freetype2" "lib*sdl2.0_0" openal-soft xdg-utils zenity
sudo dnf install SDL2 freetype "lib*lua5.1" "lib*freetype2" "lib*sdl2.0_0" openal-soft
```
openSUSE
--------
</details>
<details><summary>openSUSE</summary>
```
sudo zypper in mono-devel openal-soft freetype2 SDL2 lua51 xdg-utils zenity
sudo zypper in openal-soft freetype2 SDL2 lua51
```
Red Hat Enterprise Linux (and rebuilds, e.g. CentOS)
----------------------------------------------------
</details>
<details><summary>Red Hat Enterprise Linux (and rebuilds, e.g. CentOS)</summary>
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
sudo yum install SDL2 freetype "lua = 5.1" openal-soft
```
</details>
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`.
macOS
=====
Before compiling OpenRA you must install the following dependencies:
* [Mono >= 6.4](https://www.mono-project.com/download/stable/#download-mac)
To compile OpenRA, run `make` from the command line. Run with `./launch-game.sh`.
The default behaviour 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`. If you choose to use system libraries you will need to install:
* [SDL 2](http://www.libsdl.org/download-2.0.php) (`brew install sdl2`)
* [FreeType](http://gnuwin32.sourceforge.net/packages/freetype.htm) (`brew install freetype`)
* [OpenAL](http://kcat.strangesoft.net/openal.html) (`brew install openal-soft`)
* [liblua 5.1](http://luabinaries.sourceforge.net/download.html) (`brew install lua@5.1`)
[.NET 6](https://dotnet.microsoft.com/download/dotnet/6.0) or [Mono](https://www.mono-project.com/download/stable/#download-mac) (version 6.12 or later) is required to compile OpenRA. We recommend using .NET 6 unless you are running a very old version of macOS (10.9 through 10.14).
To compile OpenRA, run `make` from the command line (or `make RUNTIME=mono` if using Mono). Run with `./launch-game.sh`.

154
Makefile
View File

@@ -1,42 +1,40 @@
############################# INSTRUCTIONS #############################
#
# to compile, run:
# make [DEBUG=true]
# make
#
# to compile using Mono (version 6.12 or greater) instead of .NET 6, run:
# make RUNTIME=mono
#
# to compile using system libraries for native dependencies, run:
# make [DEBUG=true] TARGETPLATFORM=unix-generic
# make [RUNTIME=net6] TARGETPLATFORM=unix-generic
#
# to check the official mods for erroneous yaml files, run:
# make test
# make [RUNTIME=net6] test
#
# to check the engine and official mod dlls for code style violations, run:
# make check
# make [RUNTIME=net6] check
#
# to compile and install Red Alert, Tiberian Dawn, and Dune 2000, run:
# make [prefix=/foo] [bindir=/bar/bin] install
# make [RUNTIME=net6] [prefix=/foo] [bindir=/bar/bin] install
#
# to install Linux startup scripts, desktop files, icons, and MIME metadata
# to compile and install Red Alert, Tiberian Dawn, and Dune 2000
# using system libraries for native dependencies, run:
# make [prefix=/foo] [bindir=/bar/bin] TARGETPLATFORM=unix-generic install
#
# to install FreeDesktop startup scripts, desktop files, icons, and MIME metadata
# make install-linux-shortcuts
#
# to install Linux AppStream metadata
# to install FreeDesktop AppStream metadata
# make install-linux-appdata
#
# to install the Unix man page
# make install-man
#
# for help, run:
# make help
#
############################## TOOLCHAIN ###############################
#
# List of .NET assemblies that we can guarantee exist
WHITELISTED_OPENRA_ASSEMBLIES = OpenRA.dll OpenRA.Utility.dll OpenRA.Server.dll OpenRA.Platforms.Default.dll OpenRA.Game.dll OpenRA.Mods.Common.dll OpenRA.Mods.Cnc.dll OpenRA.Mods.D2k.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 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
WHITELISTED_CORE_ASSEMBLIES = mscorlib.dll System.dll System.Configuration.dll System.Core.dll System.Numerics.dll System.Security.dll System.Xml.dll Mono.Security.dll netstandard.dll Microsoft.Win32.Registry.dll System.Security.AccessControl.dll System.Security.Principal.Windows.dll System.Xml.Linq.dll System.Runtime.Serialization.dll
######################### UTILITIES/SETTINGS ###########################
#
# Install locations for local installs and downstream packaging
@@ -48,125 +46,165 @@ bindir ?= $(prefix)/bin
libdir ?= $(prefix)/lib
gameinstalldir ?= $(libdir)/openra
BIN_INSTALL_DIR = $(DESTDIR)$(bindir)
DATA_INSTALL_DIR = $(DESTDIR)$(datadir)
OPENRA_INSTALL_DIR = $(DESTDIR)$(gameinstalldir)
# Toolchain
CWD = $(shell pwd)
MSBUILD = msbuild -verbosity:m -nologo
DOTNET = dotnet
MONO = mono
RM = rm
RM_R = $(RM) -r
RM_F = $(RM) -f
RM_RF = $(RM) -rf
VERSION = $(shell git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || echo git-`git rev-parse --short HEAD`)
RUNTIME ?= net6
CONFIGURATION ?= Release
DOTNET_RID = $(shell ${DOTNET} --info | grep RID: | cut -w -f3)
ARCH_X64 = $(shell echo ${DOTNET_RID} | grep x64)
# Only for use in target version:
VERSION := $(shell git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || (c=$$(git rev-parse --short HEAD 2>/dev/null) && echo git-$$c))
# Detect target platform for dependencies if not given by the user
ifndef TARGETPLATFORM
UNAME_S := $(shell uname -s)
UNAME_M := $(shell uname -m)
ifeq ($(UNAME_S),Darwin)
ifeq ($(ARCH_X64),)
TARGETPLATFORM = osx-arm64
else
TARGETPLATFORM = osx-x64
endif
else
ifeq ($(UNAME_M),x86_64)
TARGETPLATFORM = linux-x64
else
ifeq ($(UNAME_M),aarch64)
TARGETPLATFORM = linux-arm64
else
TARGETPLATFORM = unix-generic
endif
endif
endif
OPENRA_UTILITY = ENGINE_DIR=".." $(MONO) --debug bin/OpenRA.Utility.dll
endif
##################### DEVELOPMENT BUILDS AND TESTS #####################
#
all:
@command -v $(firstword $(MSBUILD)) >/dev/null || (echo "OpenRA requires the '$(MSBUILD)' tool provided by Mono >= 5.18."; exit 1)
@$(MSBUILD) -t:Build -restore -p:Configuration=Release -p:TargetPlatform=$(TARGETPLATFORM) -p:Mono=true -p:DefineConstants="MONO"
@echo "Compiling in ${CONFIGURATION} mode..."
ifeq ($(RUNTIME), mono)
@command -v $(firstword $(MSBUILD)) >/dev/null || (echo "OpenRA requires the '$(MSBUILD)' tool provided by Mono >= 6.12."; exit 1)
@$(MSBUILD) -t:Build -restore -p:Configuration=${CONFIGURATION} -p:TargetPlatform=$(TARGETPLATFORM)
else
@$(DOTNET) build -c ${CONFIGURATION} -nologo -p:TargetPlatform=$(TARGETPLATFORM)
endif
ifeq ($(TARGETPLATFORM), unix-generic)
@./configure-system-libraries.sh
endif
@./fetch-geoip.sh
# dotnet clean and msbuild -t:Clean leave files that cause problems when switching between mono/dotnet
# Deleting the intermediate / output directories ensures the build directory is actually clean
clean:
@-$(RM_RF) ./bin ./*/bin ./*/obj
@$(MSBUILD) -t:Clean -p:Mono=true
@-$(RM_RF) ./bin ./*/obj
@-$(RM_F) IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP
check:
@echo
@echo "Compiling in debug mode..."
@$(MSBUILD) -t:build -restore -p:Configuration=Debug -p:TargetPlatform=$(TARGETPLATFORM) -p:Mono=true -p:DefineConstants="MONO"
@echo
@echo "Checking runtime assemblies..."
@$(OPENRA_UTILITY) all --check-runtime-assemblies $(WHITELISTED_OPENRA_ASSEMBLIES) $(WHITELISTED_THIRDPARTY_ASSEMBLIES) $(WHITELISTED_CORE_ASSEMBLIES)
@echo "Compiling in Debug mode..."
ifeq ($(RUNTIME), mono)
@$(MSBUILD) -t:clean\;build -restore -p:Configuration=Debug -warnaserror -p:TargetPlatform=$(TARGETPLATFORM)
else
@$(DOTNET) clean -c Debug --nologo --verbosity minimal
@$(DOTNET) build -c Debug -nologo -warnaserror -p:TargetPlatform=$(TARGETPLATFORM)
endif
ifeq ($(TARGETPLATFORM), unix-generic)
@./configure-system-libraries.sh
endif
@echo
@echo "Checking for explicit interface violations..."
@$(OPENRA_UTILITY) all --check-explicit-interfaces
@./utility.sh all --check-explicit-interfaces
@echo
@echo "Checking for incorrect conditional trait interface overrides..."
@$(OPENRA_UTILITY) all --check-conditional-trait-interface-overrides
@./utility.sh all --check-conditional-trait-interface-overrides
check-scripts:
@echo
@echo "Checking for Lua syntax errors..."
@luac -p $(shell find mods/*/maps/* -iname '*.lua')
@luac -p $(shell find lua/* -iname '*.lua')
@luac -p $(shell find mods/*/bits/scripts/* -iname '*.lua')
@find mods/*/maps/ mods/*/scripts/ -iname "*.lua" -print0 | xargs -0n1 luac -p
test: all
@echo
@echo "Testing Tiberian Sun mod MiniYAML..."
@$(OPENRA_UTILITY) ts --check-yaml
@./utility.sh ts --check-yaml
@echo
@echo "Testing Dune 2000 mod MiniYAML..."
@$(OPENRA_UTILITY) d2k --check-yaml
@./utility.sh d2k --check-yaml
@echo
@echo "Testing Tiberian Dawn mod MiniYAML..."
@$(OPENRA_UTILITY) cnc --check-yaml
@./utility.sh cnc --check-yaml
@echo
@echo "Testing Red Alert mod MiniYAML..."
@$(OPENRA_UTILITY) ra --check-yaml
@./utility.sh ra --check-yaml
tests:
@dotnet build OpenRA.Test/OpenRA.Test.csproj -c Debug --nologo -p:TargetPlatform=$(TARGETPLATFORM)
@echo
@dotnet test bin/OpenRA.Test.dll --test-adapter-path:.
############# LOCAL INSTALLATION AND DOWNSTREAM PACKAGING ##############
#
version: VERSION mods/ra/mod.yaml mods/cnc/mod.yaml mods/d2k/mod.yaml mods/ts/mod.yaml mods/modcontent/mod.yaml mods/all/mod.yaml
@sh -c '. ./packaging/functions.sh; set_engine_version $(VERSION) .'
@sh -c '. ./packaging/functions.sh; set_mod_version $(VERSION) mods/ra/mod.yaml mods/cnc/mod.yaml mods/d2k/mod.yaml mods/ts/mod.yaml mods/modcontent/mod.yaml mods/all/mod.yaml'
ifeq ($(VERSION),)
$(error Unable to determine new version (requires git or override of variable VERSION))
endif
@sh -c '. ./packaging/functions.sh; set_engine_version "$(VERSION)" .'
@sh -c '. ./packaging/functions.sh; set_mod_version "$(VERSION)" mods/ra/mod.yaml mods/cnc/mod.yaml mods/d2k/mod.yaml mods/ts/mod.yaml mods/modcontent/mod.yaml mods/all/mod.yaml'
install:
@sh -c '. ./packaging/functions.sh; install_assemblies_mono $(CWD) $(OPENRA_INSTALL_DIR) $(TARGETPLATFORM) True True True'
@sh -c '. ./packaging/functions.sh; install_data $(CWD) $(OPENRA_INSTALL_DIR) cnc d2k ra'
@sh -c '. ./packaging/functions.sh; install_assemblies $(CWD) $(DESTDIR)$(gameinstalldir) $(TARGETPLATFORM) $(RUNTIME) True True True'
@sh -c '. ./packaging/functions.sh; install_data $(CWD) $(DESTDIR)$(gameinstalldir) cnc d2k ra'
install-linux-shortcuts:
@sh -c '. ./packaging/functions.sh; install_linux_shortcuts $(CWD) $(OPENRA_INSTALL_DIR) $(BIN_INSTALL_DIR) $(DATA_INSTALL_DIR) $(VERSION) cnc d2k ra'
@sh -c '. ./packaging/functions.sh; install_linux_shortcuts $(CWD) "$(DESTDIR)" "$(gameinstalldir)" "$(bindir)" "$(datadir)" "$(shell head -n1 VERSION)" cnc d2k ra'
install-linux-appdata:
@sh -c '. ./packaging/functions.sh; install_linux_appdata $(CWD) $(DATA_INSTALL_DIR) cnc d2k ra'
@sh -c '. ./packaging/functions.sh; install_linux_appdata $(CWD) "$(DESTDIR)" "$(datadir)" cnc d2k ra'
install-man: all
@mkdir -p $(DESTDIR)$(mandir)/man6/
@./utility.sh all --man-page > $(DESTDIR)$(mandir)/man6/openra.6
help:
@echo 'to compile, run:'
@echo ' make [DEBUG=true]'
@echo ' make'
@echo
@echo 'to compile using Mono (version 6.12 or greater) instead of .NET 6, run:'
@echo ' make RUNTIME=mono'
@echo
@echo 'to compile using system libraries for native dependencies, run:'
@echo ' make [DEBUG=true] TARGETPLATFORM=unix-generic'
@echo ' make [RUNTIME=net6] TARGETPLATFORM=unix-generic'
@echo
@echo 'to check the official mods for erroneous yaml files, run:'
@echo ' make test'
@echo ' make [RUNTIME=net6] [TREAT_WARNINGS_AS_ERRORS=false] test'
@echo
@echo 'to check the engine and official mod dlls for code style violations, run:'
@echo ' make test'
@echo ' make [RUNTIME=net6] check'
@echo
@echo 'to compile and install Red Alert, Tiberian Dawn, and Dune 2000 run:'
@echo ' make [prefix=/foo] install'
@echo ' make [RUNTIME=net6] [prefix=/foo] [TARGETPLATFORM=unix-generic] install'
@echo
@echo 'to install Linux startup scripts, desktop files, icons, and MIME metadata'
@echo 'to compile and install Red Alert, Tiberian Dawn, and Dune 2000'
@echo 'using system libraries for native dependencies, run:'
@echo ' make [RUNTIME=net6] [prefix=/foo] [bindir=/bar/bin] TARGETPLATFORM=unix-generic install'
@echo
@echo 'to install FreeDesktop startup scripts, desktop files, icons, and MIME metadata'
@echo ' make install-linux-shortcuts'
@echo
@echo 'to install Linux AppStream metadata'
@echo 'to install FreeDesktop AppStream metadata'
@echo ' make install-linux-appdata'
@echo
@echo 'to install a Unix man page'
@echo ' make install-man'
########################### MAKEFILE SETTINGS ##########################
#
@@ -174,4 +212,4 @@ help:
.SUFFIXES:
.PHONY: all clean check check-scripts test version install install-linux-shortcuts install-linux-appdata help
.PHONY: all clean check check-scripts test version install install-linux-shortcuts install-linux-appdata install-man help

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -53,15 +53,15 @@ namespace OpenRA.Activities
Activity childActivity;
protected Activity ChildActivity
{
get { return SkipDoneActivities(childActivity); }
private set { childActivity = value; }
get => SkipDoneActivities(childActivity);
private set => childActivity = value;
}
Activity nextActivity;
public Activity NextActivity
{
get { return SkipDoneActivities(nextActivity); }
private set { nextActivity = value; }
get => SkipDoneActivities(nextActivity);
private set => nextActivity = value;
}
internal static Activity SkipDoneActivities(Activity first)
@@ -81,12 +81,12 @@ namespace OpenRA.Activities
public bool IsInterruptible { get; protected set; }
public bool ChildHasPriority { get; protected set; }
public bool IsCanceling { get { return State == ActivityState.Canceling; } }
public bool IsCanceling => State == ActivityState.Canceling;
bool finishing;
bool firstRunCompleted;
bool lastRun;
public Activity()
protected Activity()
{
IsInterruptible = true;
ChildHasPriority = true;
@@ -95,7 +95,7 @@ namespace OpenRA.Activities
public Activity TickOuter(Actor self)
{
if (State == ActivityState.Done)
throw new InvalidOperationException("Actor {0} attempted to tick activity {1} after it had already completed.".F(self, GetType()));
throw new InvalidOperationException($"Actor {self} attempted to tick activity {GetType()} after it had already completed.");
if (State == ActivityState.Queued)
{
@@ -105,7 +105,7 @@ namespace OpenRA.Activities
}
if (!firstRunCompleted)
throw new InvalidOperationException("Actor {0} attempted to tick activity {1} before running its OnFirstRun method.".F(self, GetType()));
throw new InvalidOperationException($"Actor {self} attempted to tick activity {GetType()} before running its OnFirstRun method.");
// Only run the parent tick when the child is done.
// We must always let the child finish on its own before continuing.

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -22,11 +22,11 @@ namespace OpenRA.Activities
IsInterruptible = interruptible;
}
Action a;
readonly Action a;
public override bool Tick(Actor self)
{
a?.Invoke();
a.Invoke();
return true;
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -11,6 +11,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using Eluant;
@@ -23,9 +24,21 @@ using OpenRA.Traits;
namespace OpenRA
{
[Flags]
public enum SystemActors
{
Player = 0,
EditorPlayer = 1,
World = 2,
EditorWorld = 4
}
public sealed class Actor : IScriptBindable, IScriptNotifyBind, ILuaTableBinding, ILuaEqualityBinding, ILuaToStringBinding, IEquatable<Actor>, IDisposable
{
internal struct SyncHash
/// <summary>Value used to represent an invalid token.</summary>
public const int InvalidConditionToken = -1;
internal readonly struct SyncHash
{
public readonly ISync Trait;
readonly Func<object, int> hashFunction;
@@ -48,57 +61,49 @@ namespace OpenRA
Activity currentActivity;
public Activity CurrentActivity
{
get { return Activity.SkipDoneActivities(currentActivity); }
private set { currentActivity = value; }
get => Activity.SkipDoneActivities(currentActivity);
private set => currentActivity = value;
}
public int Generation;
public Actor ReplacedByActor;
public IEffectiveOwner EffectiveOwner { get; private set; }
public IOccupySpace OccupiesSpace { get; private set; }
public ITargetable[] Targetables { get; private set; }
public IEffectiveOwner EffectiveOwner { get; }
public IOccupySpace OccupiesSpace { get; }
public ITargetable[] Targetables { get; }
public IEnumerable<ITargetablePositions> EnabledTargetablePositions { get; private set; }
public bool IsIdle { get { return CurrentActivity == null; } }
public bool IsDead { get { return Disposed || (health != null && health.IsDead); } }
public bool IsIdle => CurrentActivity == null;
public bool IsDead => Disposed || (health != null && health.IsDead);
public CPos Location { get { return OccupiesSpace.TopLeft; } }
public WPos CenterPosition { get { return OccupiesSpace.CenterPosition; } }
public CPos Location => OccupiesSpace.TopLeft;
public WPos CenterPosition => OccupiesSpace.CenterPosition;
public WRot Orientation
{
get
{
return facing != null ? facing.Orientation : WRot.None;
}
}
public WRot Orientation => facing?.Orientation ?? WRot.None;
/// <summary>Value used to represent an invalid token.</summary>
public static readonly int InvalidConditionToken = -1;
class ConditionState
sealed class ConditionState
{
/// <summary>Delegates that have registered to be notified when this condition changes.</summary>
public readonly List<VariableObserverNotifier> Notifiers = new List<VariableObserverNotifier>();
public readonly List<VariableObserverNotifier> Notifiers = new();
/// <summary>Unique integers identifying granted instances of the condition.</summary>
public readonly HashSet<int> Tokens = new HashSet<int>();
public readonly HashSet<int> Tokens = new();
}
readonly Dictionary<string, ConditionState> conditionStates = new Dictionary<string, ConditionState>();
readonly Dictionary<string, ConditionState> conditionStates = new();
/// <summary>Each granted condition receives a unique token that is used when revoking.</summary>
readonly Dictionary<int, string> conditionTokens = new Dictionary<int, string>();
readonly Dictionary<int, string> conditionTokens = new();
int nextConditionToken = 1;
/// <summary>Cache of condition -> enabled state for quick evaluation of token counter conditions.</summary>
readonly Dictionary<string, int> conditionCache = new Dictionary<string, int>();
readonly Dictionary<string, int> conditionCache = new();
/// <summary>Read-only version of conditionCache that is passed to IConditionConsumers.</summary>
readonly IReadOnlyDictionary<string, int> readOnlyConditionCache;
internal SyncHash[] SyncHashes { get; private set; }
internal SyncHash[] SyncHashes { get; }
readonly IFacing facing;
readonly IHealth health;
@@ -110,10 +115,8 @@ namespace OpenRA
readonly IDefaultVisibility defaultVisibility;
readonly INotifyBecomingIdle[] becomingIdles;
readonly INotifyIdle[] tickIdles;
readonly IEnumerable<ITargetablePositions> enabledTargetablePositions;
WPos[] staticTargetablePositions;
readonly IEnumerable<WPos> enabledTargetableWorldPositions;
bool created;
bool setStaticTargetablePositions;
internal Actor(World world, string name, TypeDictionary initDict)
{
@@ -121,7 +124,7 @@ namespace OpenRA
.FirstOrDefault(i => i.Count() > 1);
if (duplicateInit != null)
throw new InvalidDataException("Duplicate initializer '{0}'".F(duplicateInit.Key.Name));
throw new InvalidDataException($"Duplicate initializer '{duplicateInit.Key.Name}'");
var init = new ActorInitializer(this, initDict);
@@ -142,7 +145,6 @@ namespace OpenRA
Info = world.Map.Rules.Actors[name];
IPositionable positionable = null;
var resolveOrdersList = new List<IResolveOrder>();
var renderModifiersList = new List<IRenderModifier>();
var rendersList = new List<IRender>();
@@ -164,7 +166,6 @@ namespace OpenRA
// performance-sensitive parts of the core game engine, such as pathfinding, visibility and rendering.
// Note: The blocks are required to limit the scope of the t's, so we make an exception to our normal style
// rules for spacing in order to keep these assignments compact and readable.
{ if (trait is IPositionable t) positionable = t; }
{ if (trait is IOccupySpace t) OccupiesSpace = t; }
{ if (trait is IEffectiveOwner t) EffectiveOwner = t; }
{ if (trait is IFacing t) facing = t; }
@@ -191,10 +192,9 @@ namespace OpenRA
tickIdles = tickIdlesList.ToArray();
Targetables = targetablesList.ToArray();
var targetablePositions = targetablePositionsList.ToArray();
enabledTargetablePositions = targetablePositions.Where(Exts.IsTraitEnabled);
EnabledTargetablePositions = targetablePositions.Where(Exts.IsTraitEnabled);
enabledTargetableWorldPositions = EnabledTargetablePositions.SelectMany(tp => tp.TargetablePositions(this));
SyncHashes = syncHashesList.ToArray();
setStaticTargetablePositions = positionable == null && targetablePositions.Any() && targetablePositions.All(tp => tp.AlwaysEnabled);
}
}
@@ -229,11 +229,6 @@ namespace OpenRA
foreach (var notify in allObserverNotifiers)
notify(this, readOnlyConditionCache);
// All actors that can move or teleport should have IPositionable, if not it's pretty safe to assume the actor is completely immobile and
// all targetable positions can be cached if all ITargetablePositions have no conditional requirements.
if (setStaticTargetablePositions)
staticTargetablePositions = enabledTargetablePositions.SelectMany(tp => tp.TargetablePositions(this)).ToArray();
// TODO: Other traits may need initialization after being notified of initial condition state.
// TODO: A post condition initialization notification phase may allow queueing activities instead.
@@ -246,7 +241,7 @@ namespace OpenRA
continue;
if (creationActivity != null)
throw new InvalidOperationException("More than one enabled ICreationActivity trait: {0} and {1}".F(creationActivity.GetType().Name, ica.GetType().Name));
throw new InvalidOperationException($"More than one enabled ICreationActivity trait: {creationActivity.GetType().Name} and {ica.GetType().Name}");
var activity = ica.GetCreationActivity();
if (activity == null)
@@ -365,8 +360,7 @@ namespace OpenRA
public override bool Equals(object obj)
{
var o = obj as Actor;
return o != null && Equals(o);
return obj is Actor o && Equals(o);
}
public bool Equals(Actor other)
@@ -487,7 +481,7 @@ namespace OpenRA
health.InflictDamage(this, attacker, damage, false);
}
public void Kill(Actor attacker, BitSet<DamageType> damageTypes = default(BitSet<DamageType>))
public void Kill(Actor attacker, BitSet<DamageType> damageTypes = default)
{
if (Disposed || health == null)
return;
@@ -528,7 +522,7 @@ namespace OpenRA
{
// PERF: Avoid LINQ.
foreach (var targetable in Targetables)
if (targetable.IsTraitEnabled() && targetable.TargetableBy(this, byActor))
if (targetable.TargetableBy(this, byActor))
return true;
return false;
@@ -536,11 +530,8 @@ namespace OpenRA
public IEnumerable<WPos> GetTargetablePositions()
{
if (staticTargetablePositions != null)
return staticTargetablePositions;
if (enabledTargetablePositions.Any())
return enabledTargetablePositions.SelectMany(tp => tp.TargetablePositions(this));
if (EnabledTargetablePositions.Any())
return enabledTargetableWorldPositions;
return new[] { CenterPosition };
}
@@ -549,7 +540,7 @@ namespace OpenRA
void UpdateConditionState(string condition, int token, bool isRevoke)
{
ConditionState conditionState = conditionStates.GetOrAdd(condition);
var conditionState = conditionStates.GetOrAdd(condition);
if (isRevoke)
conditionState.Tokens.Remove(token);
@@ -589,14 +580,14 @@ namespace OpenRA
public int RevokeCondition(int token)
{
if (!conditionTokens.TryGetValue(token, out var condition))
throw new InvalidOperationException("Attempting to revoke condition with invalid token {0} for {1}.".F(token, this));
throw new InvalidOperationException($"Attempting to revoke condition with invalid token {token} for {this}.");
conditionTokens.Remove(token);
UpdateConditionState(condition, token, true);
return InvalidConditionToken;
}
/// <summary>Returns whether the specified token is valid for RevokeCondition</summary>
/// <summary>Returns whether the specified token is valid for RevokeCondition.</summary>
public bool TokenValid(int token)
{
return conditionTokens.ContainsKey(token);
@@ -609,14 +600,13 @@ namespace OpenRA
Lazy<ScriptActorInterface> luaInterface;
public void OnScriptBind(ScriptContext context)
{
if (luaInterface == null)
luaInterface = Exts.Lazy(() => new ScriptActorInterface(context, this));
luaInterface ??= Exts.Lazy(() => new ScriptActorInterface(context, this));
}
public LuaValue this[LuaRuntime runtime, LuaValue keyValue]
{
get { return luaInterface.Value[runtime, keyValue]; }
set { luaInterface.Value[runtime, keyValue] = value; }
get => luaInterface.Value[runtime, keyValue];
set => luaInterface.Value[runtime, keyValue] = value;
}
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
@@ -629,7 +619,7 @@ namespace OpenRA
public LuaValue ToString(LuaRuntime runtime)
{
return "Actor ({0})".F(this);
return $"Actor ({this})";
}
public bool HasScriptProperty(string name)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -16,7 +16,7 @@ using OpenRA.Scripting;
namespace OpenRA
{
public struct CPos : IScriptBindable, ILuaAdditionBinding, ILuaSubtractionBinding, ILuaEqualityBinding, ILuaTableBinding, IEquatable<CPos>
public readonly struct CPos : IScriptBindable, ILuaAdditionBinding, ILuaSubtractionBinding, ILuaEqualityBinding, ILuaTableBinding, IEquatable<CPos>
{
// Coordinates are packed in a 32 bit signed int
// X and Y are 12 bits (signed): -2048...2047
@@ -25,13 +25,13 @@ namespace OpenRA
public readonly int Bits;
// X is padded to MSB, so bit shift does the correct sign extension
public int X { get { return Bits >> 20; } }
public int X => Bits >> 20;
// Align Y with a short, cast, then shift the rest of the way
// The signed short bit shift does the correct sign extension
public int Y { get { return ((short)(Bits >> 4)) >> 4; } }
public int Y => ((short)(Bits >> 4)) >> 4;
public byte Layer { get { return (byte)Bits; } }
public byte Layer => (byte)Bits;
public CPos(int bits) { Bits = bits; }
public CPos(int x, int y)
@@ -41,7 +41,7 @@ namespace OpenRA
Bits = (x & 0xFFF) << 20 | (y & 0xFFF) << 8 | layer;
}
public static readonly CPos Zero = new CPos(0, 0, 0);
public static readonly CPos Zero = new(0, 0, 0);
public static explicit operator CPos(int2 a) { return new CPos(a.X, a.Y); }
@@ -56,9 +56,15 @@ namespace OpenRA
public override int GetHashCode() { return Bits.GetHashCode(); }
public bool Equals(CPos other) { return Bits == other.Bits; }
public override bool Equals(object obj) { return obj is CPos && Equals((CPos)obj); }
public override bool Equals(object obj) { return obj is CPos cell && Equals(cell); }
public override string ToString() { return X + "," + Y; }
public override string ToString()
{
if (Layer == 0)
return X + "," + Y;
return X + "," + Y + "," + Layer;
}
public MPos ToMPos(Map map)
{
@@ -90,7 +96,7 @@ namespace OpenRA
public LuaValue Add(LuaRuntime runtime, LuaValue left, LuaValue right)
{
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));
throw new LuaException($"Attempted to call CPos.Add(CPos, CVec) with invalid arguments ({left.WrappedClrType().Name}, {right.WrappedClrType().Name})");
return new LuaCustomClrObject(a + b);
}
@@ -99,7 +105,7 @@ namespace OpenRA
{
var rightType = right.WrappedClrType();
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));
throw new LuaException($"Attempted to call CPos.Subtract(CPos, (CPos|CVec)) with invalid arguments ({left.WrappedClrType().Name}, {rightType.Name})");
if (rightType == typeof(CPos))
{
@@ -112,7 +118,7 @@ namespace OpenRA
return new LuaCustomClrObject(a - b);
}
throw new LuaException("Attempted to call CPos.Subtract(CPos, (CPos|CVec)) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, rightType.Name));
throw new LuaException($"Attempted to call CPos.Subtract(CPos, (CPos|CVec)) with invalid arguments ({left.WrappedClrType().Name}, {rightType.Name})");
}
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
@@ -132,14 +138,11 @@ namespace OpenRA
case "X": return X;
case "Y": return Y;
case "Layer": return Layer;
default: throw new LuaException("CPos does not define a member '{0}'".F(key));
default: throw new LuaException($"CPos does not define a member '{key}'");
}
}
set
{
throw new LuaException("CPos is read-only. Use CPos.New to create a new value");
}
set => throw new LuaException("CPos is read-only. Use CPos.New to create a new value");
}
#endregion

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -17,12 +17,12 @@ using OpenRA.Scripting;
namespace OpenRA
{
public struct CVec : IScriptBindable, ILuaAdditionBinding, ILuaSubtractionBinding, ILuaUnaryMinusBinding, ILuaEqualityBinding, ILuaTableBinding, IEquatable<CVec>
public readonly struct CVec : IScriptBindable, ILuaAdditionBinding, ILuaSubtractionBinding, ILuaUnaryMinusBinding, ILuaEqualityBinding, ILuaTableBinding, IEquatable<CVec>
{
public readonly int X, Y;
public CVec(int x, int y) { X = x; Y = y; }
public static readonly CVec Zero = new CVec(0, 0);
public static readonly CVec Zero = new(0, 0);
public static CVec operator +(CVec a, CVec b) { return new CVec(a.X + b.X, a.Y + b.Y); }
public static CVec operator -(CVec a, CVec b) { return new CVec(a.X - b.X, a.Y - b.Y); }
@@ -42,8 +42,8 @@ namespace OpenRA
public CVec Sign() { return new CVec(Math.Sign(X), Math.Sign(Y)); }
public CVec Abs() { return new CVec(Math.Abs(X), Math.Abs(Y)); }
public int LengthSquared { get { return X * X + Y * Y; } }
public int Length { get { return Exts.ISqrt(LengthSquared); } }
public int LengthSquared => X * X + Y * Y;
public int Length => Exts.ISqrt(LengthSquared);
public CVec Clamp(Rectangle r)
{
@@ -55,7 +55,7 @@ namespace OpenRA
public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode(); }
public bool Equals(CVec other) { return other == this; }
public override bool Equals(object obj) { return obj is CVec && Equals((CVec)obj); }
public override bool Equals(object obj) { return obj is CVec vec && Equals(vec); }
public override string ToString() { return X + "," + Y; }
@@ -76,7 +76,7 @@ namespace OpenRA
public LuaValue Add(LuaRuntime runtime, LuaValue left, LuaValue right)
{
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));
throw new LuaException($"Attempted to call CVec.Add(CVec, CVec) with invalid arguments ({left.WrappedClrType().Name}, {right.WrappedClrType().Name})");
return new LuaCustomClrObject(a + b);
}
@@ -84,7 +84,7 @@ namespace OpenRA
public LuaValue Subtract(LuaRuntime runtime, LuaValue left, LuaValue right)
{
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));
throw new LuaException($"Attempted to call CVec.Subtract(CVec, CVec) with invalid arguments ({left.WrappedClrType().Name}, {right.WrappedClrType().Name})");
return new LuaCustomClrObject(a - b);
}
@@ -110,14 +110,11 @@ namespace OpenRA
{
case "X": return X;
case "Y": return Y;
default: throw new LuaException("CVec does not define a member '{0}'".F(key));
default: throw new LuaException($"CVec does not define a member '{key}'");
}
}
set
{
throw new LuaException("CVec is read-only. Use CVec.New to create a new value");
}
set => throw new LuaException("CVec is read-only. Use CVec.New to create a new value");
}
#endregion

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -187,8 +187,10 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Failed to decrypt string with exception: {0}", e);
Console.WriteLine("String decryption failed: {0}", e);
Log.Write("debug", "Failed to decrypt string with exception:");
Log.Write("debug", e);
Console.WriteLine("String decryption failed:");
Console.WriteLine(e);
return null;
}
}
@@ -211,8 +213,10 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Failed to sign string with exception: {0}", e);
Console.WriteLine("String signing failed: {0}", e);
Log.Write("debug", "Failed to sign string with exception");
Log.Write("debug", e);
Console.WriteLine("String signing failed:");
Console.WriteLine(e);
return null;
}
}
@@ -235,8 +239,10 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Failed to verify signature with exception: {0}", e);
Console.WriteLine("Signature validation failed: {0}", e);
Log.Write("debug", "Failed to verify signature with exception:");
Log.Write("debug", e);
Console.WriteLine("Signature validation failed:");
Console.WriteLine(e);
return false;
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -15,6 +15,6 @@ namespace OpenRA
{
public class DefaultPlayer : IGlobalModData
{
public readonly Color Color = Color.FromAhsl(0, 0, 238);
public readonly Color Color = Color.FromArgb(0xEE, 0xEE, 0xEE);
}
}

View File

@@ -1,96 +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.ComponentModel;
using System.Net;
namespace OpenRA
{
public class Download
{
readonly object syncObject = new object();
WebClient wc;
public static string FormatErrorMessage(Exception e)
{
var ex = e as WebException;
if (ex == null)
return e.Message;
switch (ex.Status)
{
case WebExceptionStatus.RequestCanceled:
return "Cancelled";
case WebExceptionStatus.NameResolutionFailure:
return "DNS lookup failed";
case WebExceptionStatus.Timeout:
return "Connection timeout";
case WebExceptionStatus.ConnectFailure:
return "Cannot connect to remote server";
case WebExceptionStatus.ProtocolError:
return "File not found on remote server";
default:
return ex.Message;
}
}
void EnableTLS12OnWindows()
{
// Enable TLS 1.2 on Windows: .NET 4.7 on Windows 10 only supports obsolete protocols by default
// SecurityProtocolType.Tls12 is not defined in the .NET 4.5 reference dlls used by mono,
// so we must use the enum's constant value directly
if (Platform.CurrentPlatform == PlatformType.Windows)
ServicePointManager.SecurityProtocol |= (SecurityProtocolType)3072;
}
public Download(string url, string path, Action<DownloadProgressChangedEventArgs> onProgress, Action<AsyncCompletedEventArgs> onComplete)
{
EnableTLS12OnWindows();
lock (syncObject)
{
wc = new WebClient { Proxy = null };
wc.DownloadProgressChanged += (_, a) => onProgress(a);
wc.DownloadFileCompleted += (_, a) => { DisposeWebClient(); onComplete(a); };
wc.DownloadFileAsync(new Uri(url), path);
}
}
public Download(string url, Action<DownloadProgressChangedEventArgs> onProgress, Action<DownloadDataCompletedEventArgs> onComplete)
{
EnableTLS12OnWindows();
lock (syncObject)
{
wc = new WebClient { Proxy = null };
wc.DownloadProgressChanged += (_, a) => onProgress(a);
wc.DownloadDataCompleted += (_, a) => { DisposeWebClient(); onComplete(a); };
wc.DownloadDataAsync(new Uri(url));
}
}
void DisposeWebClient()
{
lock (syncObject)
{
wc.Dispose();
wc = null;
}
}
public void CancelAsync()
{
lock (syncObject)
wc?.CancelAsync();
}
}
}

View File

@@ -1,39 +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;
using OpenRA.Graphics;
namespace OpenRA.Effects
{
public class AsyncAction : IEffect
{
Action a;
IAsyncResult ar;
public AsyncAction(IAsyncResult ar, Action a)
{
this.a = a;
this.ar = ar;
}
public void Tick(World world)
{
if (ar.IsCompleted)
{
world.AddFrameEndTask(w => { w.Remove(this); a(); });
}
}
public IEnumerable<IRenderable> Render(WorldRenderer r) { yield break; }
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -17,7 +17,7 @@ namespace OpenRA.Effects
{
public class DelayedAction : IEffect
{
Action a;
readonly Action a;
int delay;
public DelayedAction(int delay, Action a)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -41,7 +41,7 @@ namespace OpenRA
public class ExternalMods : IReadOnlyDictionary<string, ExternalMod>
{
readonly Dictionary<string, ExternalMod> mods = new Dictionary<string, ExternalMod>();
readonly Dictionary<string, ExternalMod> mods = new();
readonly SheetBuilder sheetBuilder;
Sheet CreateSheet()
@@ -66,12 +66,7 @@ namespace OpenRA
// Several types of support directory types are available, depending on
// how the player has installed and launched the game.
// Read registration metadata from all of them
var sources = Enum.GetValues(typeof(SupportDirType))
.Cast<SupportDirType>()
.Select(t => Platform.GetSupportDir(t))
.Distinct();
foreach (var source in sources)
foreach (var source in GetSupportDirs(ModRegistration.User | ModRegistration.System))
{
var metadataPath = Path.Combine(source, "ModMetadata");
if (!Directory.Exists(metadataPath))
@@ -86,8 +81,8 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Failed to parse mod metadata file '{0}'", path);
Log.Write("debug", e.ToString());
Log.Write("debug", $"Failed to parse mod metadata file '{path}'");
Log.Write("debug", e);
}
}
}
@@ -148,7 +143,7 @@ namespace OpenRA
if (stream != null)
yaml.Value.Nodes.Add(new MiniYamlNode("Icon3x", Convert.ToBase64String(stream.ReadAllBytes())));
var sources = new List<string>();
var sources = new HashSet<string>();
if (registration.HasFlag(ModRegistration.System))
sources.Add(Platform.GetSupportDir(SupportDirType.System));
@@ -167,7 +162,7 @@ namespace OpenRA
LoadMod(yaml.Value, forceRegistration: true);
var lines = new List<MiniYamlNode> { yaml }.ToLines().ToArray();
foreach (var source in sources.Distinct())
foreach (var source in sources)
{
var metadataPath = Path.Combine(source, "ModMetadata");
@@ -179,35 +174,23 @@ namespace OpenRA
catch (Exception e)
{
Log.Write("debug", "Failed to register current mod metadata");
Log.Write("debug", e.ToString());
Log.Write("debug", e);
}
}
}
/// <summary>
/// Removes invalid mod registrations:
/// * LaunchPath no longer exists
/// * LaunchPath and mod id matches the active mod, but the version is different
/// * Filename doesn't match internal key
/// * Fails to parse as a mod registration
/// <list type="bullet">
/// <item>LaunchPath no longer exists.</item>
/// <item>LaunchPath and mod id matches the active mod, but the version is different.</item>
/// <item>Filename doesn't match internal key.</item>
/// <item>Fails to parse as a mod registration.</item>
/// </list>
/// </summary>
internal void ClearInvalidRegistrations(ExternalMod activeMod, ModRegistration registration)
internal void ClearInvalidRegistrations(ModRegistration registration)
{
var sources = new List<string>();
if (registration.HasFlag(ModRegistration.System))
sources.Add(Platform.GetSupportDir(SupportDirType.System));
if (registration.HasFlag(ModRegistration.User))
{
// User support dir may be using the modern or legacy value, or overridden by the user
// Add all the possibilities and let the .Distinct() below ignore the duplicates
sources.Add(Platform.GetSupportDir(SupportDirType.User));
sources.Add(Platform.GetSupportDir(SupportDirType.ModernUser));
sources.Add(Platform.GetSupportDir(SupportDirType.LegacyUser));
}
var activeModKey = ExternalMod.MakeKey(activeMod);
foreach (var source in sources.Distinct())
foreach (var source in GetSupportDirs(registration))
{
var metadataPath = Path.Combine(source, "ModMetadata");
if (!Directory.Exists(metadataPath))
@@ -222,19 +205,16 @@ namespace OpenRA
var m = FieldLoader.Load<ExternalMod>(yaml);
modKey = ExternalMod.MakeKey(m);
// Continue to the next entry if it is the active mod (even if the LaunchPath is bogus)
if (modKey == activeModKey)
continue;
// Continue to the next entry if this one is valid
if (File.Exists(m.LaunchPath) && Path.GetFileNameWithoutExtension(path) == modKey &&
!(activeMod != null && m.LaunchPath == activeMod.LaunchPath && m.Id == activeMod.Id && m.Version != activeMod.Version))
// HACK: Explicitly invalidate paths to OpenRA.dll to clean up bogus metadata files
// that were created after the initial migration from .NET Framework to Core/5.
if (File.Exists(m.LaunchPath) && Path.GetFileNameWithoutExtension(path) == modKey && Path.GetExtension(m.LaunchPath) != ".dll")
continue;
}
catch (Exception e)
{
Log.Write("debug", "Failed to parse mod metadata file '{0}'", path);
Log.Write("debug", e.ToString());
Log.Write("debug", $"Failed to parse mod metadata file '{path}'");
Log.Write("debug", e);
}
// Remove from the ingame mod switcher
@@ -245,12 +225,12 @@ namespace OpenRA
try
{
File.Delete(path);
Log.Write("debug", "Removed invalid mod metadata file '{0}'", path);
Log.Write("debug", $"Removed invalid mod metadata file '{path}'");
}
catch (Exception e)
{
Log.Write("debug", "Failed to remove mod metadata file '{0}'", path);
Log.Write("debug", e.ToString());
Log.Write("debug", $"Failed to remove mod metadata file '{path}'");
Log.Write("debug", e);
}
}
}
@@ -258,23 +238,10 @@ namespace OpenRA
internal void Unregister(Manifest mod, ModRegistration registration)
{
var sources = new List<string>();
if (registration.HasFlag(ModRegistration.System))
sources.Add(Platform.GetSupportDir(SupportDirType.System));
if (registration.HasFlag(ModRegistration.User))
{
// User support dir may be using the modern or legacy value, or overridden by the user
// Add all the possibilities and let the .Distinct() below ignore the duplicates
sources.Add(Platform.GetSupportDir(SupportDirType.User));
sources.Add(Platform.GetSupportDir(SupportDirType.ModernUser));
sources.Add(Platform.GetSupportDir(SupportDirType.LegacyUser));
}
var key = ExternalMod.MakeKey(mod);
mods.Remove(key);
foreach (var source in sources.Distinct())
foreach (var source in GetSupportDirs(registration))
{
var path = Path.Combine(source, "ModMetadata", key + ".yaml");
try
@@ -284,16 +251,39 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Failed to remove mod metadata file '{0}'", path);
Log.Write("debug", e.ToString());
Log.Write("debug", $"Failed to remove mod metadata file '{path}'");
Log.Write("debug", e);
}
}
}
public ExternalMod this[string key] { get { return mods[key]; } }
public int Count { get { return mods.Count; } }
public ICollection<string> Keys { get { return mods.Keys; } }
public ICollection<ExternalMod> Values { get { return mods.Values; } }
static IEnumerable<string> GetSupportDirs(ModRegistration registration)
{
var sources = new HashSet<string>(4);
if (registration.HasFlag(ModRegistration.System))
sources.Add(Platform.GetSupportDir(SupportDirType.System));
if (registration.HasFlag(ModRegistration.User))
{
// User support dir may be using the modern or legacy value, or overridden by the user
// Add all the possibilities and let the HashSet ignore the duplicates
sources.Add(Platform.GetSupportDir(SupportDirType.User));
sources.Add(Platform.GetSupportDir(SupportDirType.ModernUser));
sources.Add(Platform.GetSupportDir(SupportDirType.LegacyUser));
}
return sources;
}
public ExternalMod this[string key] => mods[key];
public int Count => mods.Count;
public ICollection<string> Keys => mods.Keys;
public ICollection<ExternalMod> Values => mods.Values;
IEnumerable<string> IReadOnlyDictionary<string, ExternalMod>.Keys => ((IReadOnlyDictionary<string, ExternalMod>)mods).Keys;
IEnumerable<ExternalMod> IReadOnlyDictionary<string, ExternalMod>.Values => ((IReadOnlyDictionary<string, ExternalMod>)mods).Values;
public bool ContainsKey(string key) { return mods.ContainsKey(key); }
public IEnumerator<KeyValuePair<string, ExternalMod>> GetEnumerator() { return mods.GetEnumerator(); }
public bool TryGetValue(string key, out ExternalMod value) { return mods.TryGetValue(key, out value); }

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -27,23 +27,12 @@ namespace OpenRA
return string.Compare(str.ToUpperInvariant(), str, false) == 0;
}
public static string F(this string fmt, params object[] args)
{
return string.Format(fmt, args);
}
public static T WithDefault<T>(T def, Func<T> f)
{
try { return f(); }
catch { return def; }
}
public static void Do<T>(this IEnumerable<T> e, Action<T> fn)
{
foreach (var ee in e)
fn(ee);
}
public static Lazy<T> Lazy<T>(Func<T> p) { return new Lazy<T>(p); }
public static IEnumerable<string> GetNamespaces(this Assembly a)
@@ -51,21 +40,16 @@ namespace OpenRA
return a.GetTypes().Select(t => t.Namespace).Distinct().Where(n => n != null);
}
public static bool HasAttribute<T>(this MemberInfo mi)
public static bool HasAttribute<TAttribute>(this MemberInfo mi)
where TAttribute : Attribute
{
return mi.GetCustomAttributes(typeof(T), true).Length != 0;
return Attribute.IsDefined(mi, typeof(TAttribute));
}
public static T[] GetCustomAttributes<T>(this MemberInfo mi, bool inherit)
where T : class
public static TAttribute[] GetCustomAttributes<TAttribute>(this MemberInfo mi, bool inherit)
where TAttribute : Attribute
{
return (T[])mi.GetCustomAttributes(typeof(T), inherit);
}
public static T[] GetCustomAttributes<T>(this ParameterInfo mi)
where T : class
{
return (T[])mi.GetCustomAttributes(typeof(T), true);
return (TAttribute[])mi.GetCustomAttributes(typeof(TAttribute), inherit);
}
public static T Clamp<T>(this T val, T min, T max) where T : IComparable<T>
@@ -107,7 +91,7 @@ namespace OpenRA
// - the triangles ACD and BCD must have opposite sense (clockwise or anticlockwise)
// - the triangles CAB and DAB must have opposite sense
// Segments intersect if the orientation (clockwise or anticlockwise) of the two points in each line segment are opposite with respect to the other
// Assumes that lines are not colinear
// Assumes that lines are not collinear
return WindingDirectionTest(c, d, a) != WindingDirectionTest(c, d, b) && WindingDirectionTest(a, b, c) != WindingDirectionTest(a, b, d);
}
@@ -125,13 +109,23 @@ namespace OpenRA
public static V GetOrAdd<K, V>(this Dictionary<K, V> d, K k, V v)
{
#if NET5_0_OR_GREATER
// SAFETY: Dictionary cannot be modified whilst the ref is alive.
ref var value = ref System.Runtime.InteropServices.CollectionsMarshal.GetValueRefOrAddDefault(d, k, out var exists);
if (!exists)
value = v;
return value;
#else
if (!d.TryGetValue(k, out var ret))
d.Add(k, ret = v);
return ret;
#endif
}
public static V GetOrAdd<K, V>(this Dictionary<K, V> d, K k, Func<K, V> createFn)
{
// Cannot use CollectionsMarshal.GetValueRefOrAddDefault here,
// the creation function could mutate the dictionary which would invalidate the ref.
if (!d.TryGetValue(k, out var ret))
d.Add(k, ret = createFn(k));
return ret;
@@ -155,13 +149,13 @@ namespace OpenRA
static T Random<T>(IEnumerable<T> ts, MersenneTwister r, bool throws)
{
var xs = ts as ICollection<T>;
xs = xs ?? ts.ToList();
xs ??= ts.ToList();
if (xs.Count == 0)
{
if (throws)
throw new ArgumentException("Collection must not be empty.", "ts");
throw new ArgumentException("Collection must not be empty.", nameof(ts));
else
return default(T);
return default;
}
else
return xs.ElementAt(r.Next(xs.Count));
@@ -236,9 +230,9 @@ namespace OpenRA
{
if (!e.MoveNext())
if (throws)
throw new ArgumentException("Collection must not be empty.", "ts");
throw new ArgumentException("Collection must not be empty.", nameof(ts));
else
return default(T);
return default;
t = e.Current;
u = selector(t);
while (e.MoveNext())
@@ -278,7 +272,7 @@ namespace OpenRA
public static int ISqrt(int number, ISqrtRoundMode round = ISqrtRoundMode.Floor)
{
if (number < 0)
throw new InvalidOperationException("Attempted to calculate the square root of a negative integer: {0}".F(number));
throw new InvalidOperationException($"Attempted to calculate the square root of a negative integer: {number}");
return (int)ISqrt((uint)number, round);
}
@@ -319,7 +313,7 @@ namespace OpenRA
public static long ISqrt(long number, ISqrtRoundMode round = ISqrtRoundMode.Floor)
{
if (number < 0)
throw new InvalidOperationException("Attempted to calculate the square root of a negative integer: {0}".F(number));
throw new InvalidOperationException($"Attempted to calculate the square root of a negative integer: {number}");
return (long)ISqrt((ulong)number, round);
}
@@ -357,6 +351,11 @@ namespace OpenRA
return root;
}
public static int MultiplyBySqrtTwo(short number)
{
return number * 46341 / 32768;
}
public static int IntegerDivisionRoundingAwayFromZero(int dividend, int divisor)
{
var quotient = Math.DivRem(dividend, divisor, out var remainder);
@@ -375,6 +374,11 @@ namespace OpenRA
return ts.Concat(moreTs);
}
public static IEnumerable<T> Exclude<T>(this IEnumerable<T> ts, params T[] exclusions)
{
return ts.Except(exclusions);
}
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source)
{
return new HashSet<T>(source);
@@ -392,12 +396,13 @@ namespace OpenRA
string debugName, Func<TKey, string> logKey = null, Func<TElement, string> logValue = null)
{
// Fall back on ToString() if null functions are provided:
logKey = logKey ?? (s => s.ToString());
logValue = logValue ?? (s => s.ToString());
logKey ??= s => s.ToString();
logValue ??= s => s.ToString();
// Try to build a dictionary and log all duplicates found (if any):
var dupKeys = new Dictionary<TKey, List<string>>();
var d = new Dictionary<TKey, TElement>();
var capacity = source is ICollection<TSource> collection ? collection.Count : 0;
var d = new Dictionary<TKey, TElement>(capacity);
foreach (var item in source)
{
var key = keySelector(item);
@@ -408,29 +413,28 @@ namespace OpenRA
continue;
// Check for a key conflict:
if (d.ContainsKey(key))
if (!d.TryAdd(key, element))
{
if (!dupKeys.TryGetValue(key, out var dupKeyMessages))
{
// Log the initial conflicting value already inserted:
dupKeyMessages = new List<string>();
dupKeyMessages.Add(logValue(d[key]));
dupKeyMessages = new List<string>
{
logValue(d[key])
};
dupKeys.Add(key, dupKeyMessages);
}
// Log this conflicting value:
dupKeyMessages.Add(logValue(element));
continue;
}
d.Add(key, element);
}
// If any duplicates were found, throw a descriptive error
if (dupKeys.Count > 0)
{
var badKeysFormatted = string.Join(", ", dupKeys.Select(p => "{0}: [{1}]".F(logKey(p.Key), string.Join(",", p.Value))));
var msg = "{0}, duplicate values found for the following keys: {1}".F(debugName, badKeysFormatted);
var badKeysFormatted = string.Join(", ", dupKeys.Select(p => $"{logKey(p.Key)}: [{string.Join(",", p.Value)}]"));
var msg = $"{debugName}, duplicate values found for the following keys: {badKeysFormatted}";
throw new ArgumentException(msg);
}
@@ -511,8 +515,7 @@ namespace OpenRA
public static bool IsTraitEnabled<T>(this T trait)
{
var disabledTrait = trait as IDisabledTrait;
return disabledTrait == null || !disabledTrait.IsTraitDisabled;
return trait is not IDisabledTrait disabledTrait || !disabledTrait.IsTraitDisabled;
}
public static T FirstEnabledTraitOrDefault<T>(this IEnumerable<T> ts)
@@ -522,7 +525,7 @@ namespace OpenRA
if (t.IsTraitEnabled())
return t;
return default(T);
return default;
}
public static T FirstEnabledTraitOrDefault<T>(this T[] ts)
@@ -532,8 +535,72 @@ namespace OpenRA
if (t.IsTraitEnabled())
return t;
return default(T);
return default;
}
public static T FirstEnabledConditionalTraitOrDefault<T>(this IEnumerable<T> ts) where T : IDisabledTrait
{
// PERF: Avoid LINQ.
foreach (var t in ts)
if (!t.IsTraitDisabled)
return t;
return default;
}
public static T FirstEnabledConditionalTraitOrDefault<T>(this T[] ts) where T : IDisabledTrait
{
// PERF: Avoid LINQ.
foreach (var t in ts)
if (!t.IsTraitDisabled)
return t;
return default;
}
public static LineSplitEnumerator SplitLines(this string str, char separator)
{
return new LineSplitEnumerator(str.AsSpan(), separator);
}
}
public ref struct LineSplitEnumerator
{
ReadOnlySpan<char> str;
readonly char separator;
public LineSplitEnumerator(ReadOnlySpan<char> str, char separator)
{
this.str = str;
this.separator = separator;
Current = default;
}
public LineSplitEnumerator GetEnumerator() => this;
public bool MoveNext()
{
var span = str;
// Reach the end of the string
if (span.Length == 0)
return false;
var index = span.IndexOf(separator);
if (index == -1)
{
// The remaining string is an empty string
str = ReadOnlySpan<char>.Empty;
Current = span;
return true;
}
Current = span[..index];
str = span[(index + 1)..];
return true;
}
public ReadOnlySpan<char> Current { get; private set; }
}
public static class Enum<T>
@@ -549,7 +616,7 @@ namespace OpenRA
if (values.Any(x => !names.Contains(x)))
{
value = default(T);
value = default;
return false;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -72,32 +72,14 @@ namespace OpenRA
return "";
var t = v.GetType();
if (t == typeof(Color))
{
return ((Color)v).ToString();
}
if (t == typeof(Rectangle))
{
var r = (Rectangle)v;
return "{0},{1},{2},{3}".F(r.X, r.Y, r.Width, r.Height);
}
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(BitSet<>))
{
return ((IEnumerable<string>)v).Select(FormatValue).JoinWith(", ");
}
if (t.IsArray && t.GetArrayRank() == 1)
{
return ((Array)v).Cast<object>().Select(FormatValue).JoinWith(", ");
}
if (t.IsGenericType && (t.GetGenericTypeDefinition() == typeof(HashSet<>) || t.GetGenericTypeDefinition() == typeof(List<>)))
{
return ((System.Collections.IEnumerable)v).Cast<object>().Select(FormatValue).JoinWith(", ");
}
// This is only for documentation generation
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Dictionary<,>))
@@ -112,17 +94,14 @@ namespace OpenRA
var formattedKey = FormatValue(key);
var formattedValue = FormatValue(value);
result += "{0}: {1}{2}".F(formattedKey, formattedValue, Environment.NewLine);
result += $"{formattedKey}: {formattedValue}{Environment.NewLine}";
}
return result;
}
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Primitives.Cache<,>))
return ""; // TODO
if (t == typeof(DateTime))
return ((DateTime)v).ToString("yyyy-MM-dd HH-mm-ss", CultureInfo.InvariantCulture);
if (v is DateTime d)
return d.ToString("yyyy-MM-dd HH-mm-ss", CultureInfo.InvariantCulture);
// Try the TypeConverter
var conv = TypeDescriptor.GetConverter(t);

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -26,14 +26,14 @@ namespace OpenRA.FileFormats
{
static readonly byte[] Signature = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
public int Width { get; private set; }
public int Height { get; private set; }
public Color[] Palette { get; private set; }
public byte[] Data { get; private set; }
public SpriteFrameType Type { get; private set; }
public Dictionary<string, string> EmbeddedData = new Dictionary<string, string>();
public int Width { get; }
public int Height { get; }
public Color[] Palette { get; }
public byte[] Data { get; }
public SpriteFrameType Type { get; }
public Dictionary<string, string> EmbeddedData = new();
public int PixelStride { get { return Type == SpriteFrameType.Indexed8 ? 1 : Type == SpriteFrameType.Rgb24 ? 3 : 4; } }
public int PixelStride => Type == SpriteFrameType.Indexed8 ? 1 : Type == SpriteFrameType.Rgb24 ? 3 : 4;
public Png(Stream s)
{
@@ -67,7 +67,7 @@ namespace OpenRA.FileFormats
Height = IPAddress.NetworkToHostOrder(ms.ReadInt32());
var bitDepth = ms.ReadUInt8();
var colorType = (PngColorType)ms.ReadByte();
var colorType = (PngColorType)ms.ReadUInt8();
if (IsPaletted(bitDepth, colorType))
Type = SpriteFrameType.Indexed8;
else if (colorType == PngColorType.Color)
@@ -75,9 +75,9 @@ namespace OpenRA.FileFormats
Data = new byte[Width * Height * PixelStride];
var compression = ms.ReadByte();
/*var filter = */ms.ReadByte();
var interlace = ms.ReadByte();
var compression = ms.ReadUInt8();
/*var filter = */ms.ReadUInt8();
var interlace = ms.ReadUInt8();
if (compression != 0)
throw new InvalidDataException("Compression method not supported");
@@ -95,7 +95,7 @@ namespace OpenRA.FileFormats
Palette = new Color[256];
for (var i = 0; i < length / 3; i++)
{
var r = ms.ReadByte(); var g = ms.ReadByte(); var b = ms.ReadByte();
var r = ms.ReadUInt8(); var g = ms.ReadUInt8(); var b = ms.ReadUInt8();
Palette[i] = Color.FromArgb(r, g, b);
}
@@ -108,7 +108,7 @@ namespace OpenRA.FileFormats
throw new InvalidDataException("Non-Palette indexed PNG are not supported.");
for (var i = 0; i < length; i++)
Palette[i] = Color.FromArgb(ms.ReadByte(), Palette[i]);
Palette[i] = Color.FromArgb(ms.ReadUInt8(), Palette[i]);
break;
}
@@ -137,21 +137,56 @@ namespace OpenRA.FileFormats
var pxStride = PixelStride;
var rowStride = Width * pxStride;
var prevLine = new byte[rowStride];
Span<byte> prevLine = new byte[rowStride];
for (var y = 0; y < Height; y++)
{
var filter = (PngFilter)ds.ReadByte();
var line = ds.ReadBytes(rowStride);
var filter = (PngFilter)ds.ReadUInt8();
ds.ReadBytes(Data, y * rowStride, rowStride);
var line = Data.AsSpan(y * rowStride, rowStride);
for (var i = 0; i < rowStride; i++)
line[i] = i < pxStride
? UnapplyFilter(filter, line[i], 0, prevLine[i], 0)
: UnapplyFilter(filter, line[i], line[i - pxStride], prevLine[i], prevLine[i - pxStride]);
Array.Copy(line, 0, Data, y * rowStride, rowStride);
switch (filter)
{
case PngFilter.None:
break;
case PngFilter.Sub:
for (var i = pxStride; i < rowStride; i++)
line[i] += line[i - pxStride];
break;
case PngFilter.Up:
for (var i = 0; i < rowStride; i++)
line[i] += prevLine[i];
break;
case PngFilter.Average:
for (var i = 0; i < pxStride; i++)
line[i] += Average(0, prevLine[i]);
for (var i = pxStride; i < rowStride; i++)
line[i] += Average(line[i - pxStride], prevLine[i]);
break;
case PngFilter.Paeth:
for (var i = 0; i < pxStride; i++)
line[i] += Paeth(0, prevLine[i], 0);
for (var i = pxStride; i < rowStride; i++)
line[i] += Paeth(line[i - pxStride], prevLine[i], prevLine[i - pxStride]);
break;
default:
throw new InvalidOperationException("Unsupported Filter");
}
prevLine = line;
}
static byte Average(byte a, byte b) => (byte)((a + b) / 2);
static byte Paeth(byte a, byte b, byte c)
{
var p = a + b - c;
var pa = Math.Abs(p - a);
var pb = Math.Abs(p - b);
var pc = Math.Abs(p - c);
return (pa <= pb && pa <= pc) ? a :
(pb <= pc) ? b : c;
}
}
}
@@ -213,7 +248,7 @@ namespace OpenRA.FileFormats
}
default:
throw new InvalidDataException("Unhandled SpriteFrameType {0}".F(type));
throw new InvalidDataException($"Unhandled SpriteFrameType {type}");
}
if (embeddedData != null)
@@ -228,34 +263,9 @@ namespace OpenRA.FileFormats
return isPng;
}
static byte UnapplyFilter(PngFilter f, byte x, byte a, byte b, byte c)
{
switch (f)
{
case PngFilter.None: return x;
case PngFilter.Sub: return (byte)(x + a);
case PngFilter.Up: return (byte)(x + b);
case PngFilter.Average: return (byte)(x + (a + b) / 2);
case PngFilter.Paeth: return (byte)(x + Paeth(a, b, c));
default:
throw new InvalidOperationException("Unsupported Filter");
}
}
static byte Paeth(byte a, byte b, byte c)
{
var p = a + b - c;
var pa = Math.Abs(p - a);
var pb = Math.Abs(p - b);
var pc = Math.Abs(p - c);
return (pa <= pb && pa <= pc) ? a :
(pb <= pc) ? b : c;
}
[Flags]
enum PngColorType { Indexed = 1, Color = 2, Alpha = 4 }
enum PngFilter { None, Sub, Up, Average, Paeth }
enum PngColorType : byte { Indexed = 1, Color = 2, Alpha = 4 }
enum PngFilter : byte { None, Sub, Up, Average, Paeth }
static bool IsPaletted(byte bitDepth, PngColorType colorType)
{
@@ -271,7 +281,7 @@ namespace OpenRA.FileFormats
throw new InvalidDataException("Unknown pixel format");
}
void WritePngChunk(Stream output, string type, Stream input)
static void WritePngChunk(Stream output, string type, Stream input)
{
input.Position = 0;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -28,7 +28,7 @@ namespace OpenRA.FileFormats
public ReplayMetadata(GameInformation info)
{
if (info == null)
throw new ArgumentNullException("info");
throw new ArgumentNullException(nameof(info));
GameInfo = info;
}
@@ -44,7 +44,7 @@ namespace OpenRA.FileFormats
// Read version
var version = fs.ReadInt32();
if (version != MetaVersion)
throw new NotSupportedException("Metadata version {0} is not supported".F(version));
throw new NotSupportedException($"Metadata version {version} is not supported");
// Read game info (max 100K limit as a safeguard against corrupted files)
var data = fs.ReadString(Encoding.UTF8, 1024 * 100);

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -28,17 +28,17 @@ namespace OpenRA.FileSystem
public class FileSystem : IReadOnlyFileSystem
{
public IEnumerable<IReadOnlyPackage> MountedPackages { get { return mountedPackages.Keys; } }
readonly Dictionary<IReadOnlyPackage, int> mountedPackages = new Dictionary<IReadOnlyPackage, int>();
readonly Dictionary<string, IReadOnlyPackage> explicitMounts = new Dictionary<string, IReadOnlyPackage>();
public IEnumerable<IReadOnlyPackage> MountedPackages => mountedPackages.Keys;
readonly Dictionary<IReadOnlyPackage, int> mountedPackages = new();
readonly Dictionary<string, IReadOnlyPackage> explicitMounts = new();
readonly string modID;
// Mod packages that should not be disposed
readonly List<IReadOnlyPackage> modPackages = new List<IReadOnlyPackage>();
readonly List<IReadOnlyPackage> modPackages = new();
readonly IReadOnlyDictionary<string, Manifest> installedMods;
readonly IPackageLoader[] packageLoaders;
Cache<string, List<IReadOnlyPackage>> fileIndex = new Cache<string, List<IReadOnlyPackage>>(_ => new List<IReadOnlyPackage>());
Cache<string, List<IReadOnlyPackage>> fileIndex = new(_ => new List<IReadOnlyPackage>());
public FileSystem(string modID, IReadOnlyDictionary<string, Manifest> installedMods, IPackageLoader[] packageLoaders)
{
@@ -63,7 +63,7 @@ namespace OpenRA.FileSystem
{
// Raw directories are the easiest and one of the most common cases, so try these first
var resolvedPath = Platform.ResolvePath(filename);
if (!resolvedPath.Contains("|") && Directory.Exists(resolvedPath))
if (!resolvedPath.Contains('|') && Directory.Exists(resolvedPath))
return new Folder(resolvedPath);
// Children of another package require special handling
@@ -85,17 +85,17 @@ namespace OpenRA.FileSystem
{
var optional = name.StartsWith("~", StringComparison.Ordinal);
if (optional)
name = name.Substring(1);
name = name[1..];
try
{
IReadOnlyPackage package;
if (name.StartsWith("$", StringComparison.Ordinal))
{
name = name.Substring(1);
name = name[1..];
if (!installedMods.TryGetValue(name, out var mod))
throw new InvalidOperationException("Could not load mod '{0}'. Available mods: {1}".F(name, installedMods.Keys.JoinWith(", ")));
throw new InvalidOperationException($"Could not load mod '{name}'. Available mods: {installedMods.Keys.JoinWith(", ")}");
package = mod.Package;
modPackages.Add(package);
@@ -104,7 +104,7 @@ namespace OpenRA.FileSystem
{
package = OpenPackage(name);
if (package == null)
throw new InvalidOperationException("Could not open package '{0}', file not found or its format is not supported.".F(name));
throw new InvalidOperationException($"Could not open package '{name}', file not found or its format is not supported.");
}
Mount(package, explicitName);
@@ -203,7 +203,7 @@ namespace OpenRA.FileSystem
public Stream Open(string filename)
{
if (!TryOpen(filename, out var s))
throw new FileNotFoundException("File not found: {0}".F(filename), filename);
throw new FileNotFoundException($"File not found: {filename}", filename);
return s;
}
@@ -211,9 +211,9 @@ namespace OpenRA.FileSystem
public bool TryGetPackageContaining(string path, out IReadOnlyPackage package, out string filename)
{
var explicitSplit = path.IndexOf('|');
if (explicitSplit > 0 && explicitMounts.TryGetValue(path.Substring(0, explicitSplit), out package))
if (explicitSplit > 0 && explicitMounts.TryGetValue(path[..explicitSplit], out package))
{
filename = path.Substring(explicitSplit + 1);
filename = path[(explicitSplit + 1)..];
return true;
}
@@ -228,9 +228,9 @@ namespace OpenRA.FileSystem
var explicitSplit = filename.IndexOf('|');
if (explicitSplit > 0)
{
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out var explicitPackage))
if (explicitMounts.TryGetValue(filename[..explicitSplit], out var explicitPackage))
{
s = explicitPackage.GetStream(filename.Substring(explicitSplit + 1));
s = explicitPackage.GetStream(filename[(explicitSplit + 1)..]);
if (s != null)
return true;
}
@@ -263,15 +263,15 @@ namespace OpenRA.FileSystem
{
var explicitSplit = filename.IndexOf('|');
if (explicitSplit > 0)
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out var explicitPackage))
if (explicitPackage.Contains(filename.Substring(explicitSplit + 1)))
if (explicitMounts.TryGetValue(filename[..explicitSplit], out var explicitPackage))
if (explicitPackage.Contains(filename[(explicitSplit + 1)..]))
return true;
return fileIndex.ContainsKey(filename);
}
/// <summary>
/// Returns true if the given filename references an external mod via an explicit mount
/// Returns true if the given filename references an external mod via an explicit mount.
/// </summary>
public bool IsExternalModFile(string filename)
{
@@ -279,7 +279,7 @@ namespace OpenRA.FileSystem
if (explicitSplit < 0)
return false;
if (!explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out var explicitPackage))
if (!explicitMounts.TryGetValue(filename[..explicitSplit], out var explicitPackage))
return false;
if (installedMods[modID].Package == explicitPackage)
@@ -297,8 +297,8 @@ namespace OpenRA.FileSystem
var explicitSplit = path.IndexOf('|');
if (explicitSplit > 0 && !path.StartsWith("^"))
{
var parent = path.Substring(0, explicitSplit);
var filename = path.Substring(explicitSplit + 1);
var parent = path[..explicitSplit];
var filename = path[(explicitSplit + 1)..];
var parentPath = manifest.Packages.FirstOrDefault(kv => kv.Value == parent).Key;
if (parentPath == null)
@@ -306,10 +306,10 @@ namespace OpenRA.FileSystem
if (parentPath.StartsWith("$", StringComparison.Ordinal))
{
if (!installedMods.TryGetValue(parentPath.Substring(1), out var mod))
if (!installedMods.TryGetValue(parentPath[1..], out var mod))
return null;
if (!(mod.Package is Folder))
if (mod.Package is not Folder)
return null;
path = Path.Combine(mod.Package.Name, filename);
@@ -322,6 +322,28 @@ namespace OpenRA.FileSystem
return File.Exists(resolvedPath) ? resolvedPath : null;
}
public static string ResolveCaseInsensitivePath(string path)
{
var resolved = Path.GetPathRoot(path);
if (resolved == null)
return null;
foreach (var name in path[resolved.Length..].Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar))
{
// Filter out paths of the form /foo/bar/./baz
if (name == ".")
continue;
resolved = Directory.GetFileSystemEntries(resolved).FirstOrDefault(e => e.Equals(Path.Combine(resolved, name), StringComparison.InvariantCultureIgnoreCase));
if (resolved == null)
return null;
}
return resolved;
}
public string GetPrefix(IReadOnlyPackage package)
{
return explicitMounts.ContainsValue(package) ? explicitMounts.First(f => f.Value == package).Key : null;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -18,24 +18,22 @@ namespace OpenRA.FileSystem
{
public sealed class Folder : IReadWritePackage
{
readonly string path;
public string Name { get; }
public Folder(string path)
{
this.path = path;
Name = path;
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
}
public string Name { get { return path; } }
public IEnumerable<string> Contents
{
get
{
// Order may vary on different file systems and it matters for hashing.
return Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly)
.Concat(Directory.GetDirectories(path))
return Directory.GetFiles(Name, "*", SearchOption.TopDirectoryOnly)
.Concat(Directory.GetDirectories(Name))
.Select(Path.GetFileName)
.OrderBy(f => f);
}
@@ -43,14 +41,14 @@ namespace OpenRA.FileSystem
public Stream GetStream(string filename)
{
try { return File.OpenRead(Path.Combine(path, filename)); }
try { return File.OpenRead(Path.Combine(Name, filename)); }
catch { return null; }
}
public bool Contains(string filename)
{
var combined = Path.Combine(path, filename);
return combined.StartsWith(path, StringComparison.Ordinal) && File.Exists(combined);
var combined = Path.Combine(Name, filename);
return combined.StartsWith(Name, StringComparison.Ordinal) && File.Exists(combined);
}
public IReadOnlyPackage OpenPackage(string filename, FileSystem context)
@@ -82,7 +80,7 @@ namespace OpenRA.FileSystem
// in FileSystem.OpenPackage. Their internal name therefore contains the
// full parent path too. We need to be careful to not add a second path
// prefix to these hacked packages.
var filePath = filename.StartsWith(path) ? filename : Path.Combine(path, filename);
var filePath = filename.StartsWith(Name) ? filename : Path.Combine(Name, filename);
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
using (var s = File.Create(filePath))
@@ -96,7 +94,7 @@ namespace OpenRA.FileSystem
// in FileSystem.OpenPackage. Their internal name therefore contains the
// full parent path too. We need to be careful to not add a second path
// prefix to these hacked packages.
var filePath = filename.StartsWith(path) ? filename : Path.Combine(path, filename);
var filePath = filename.StartsWith(Name) ? filename : Path.Combine(Name, filename);
if (Directory.Exists(filePath))
Directory.Delete(filePath, true);
else if (File.Exists(filePath))

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -19,7 +19,7 @@ namespace OpenRA.FileSystem
{
public class ZipFileLoader : IPackageLoader
{
static readonly string[] Extensions = { ".zip", ".oramap" };
const uint ZipSignature = 0x04034b50;
class ReadOnlyZipFile : IReadOnlyPackage
{
@@ -55,7 +55,8 @@ namespace OpenRA.FileSystem
get
{
foreach (ZipEntry entry in pkg)
yield return entry.Name;
if (entry.IsFile)
yield return entry.Name;
}
}
@@ -94,7 +95,7 @@ namespace OpenRA.FileSystem
sealed class ReadWriteZipFile : ReadOnlyZipFile, IReadWritePackage
{
readonly MemoryStream pkgStream = new MemoryStream();
readonly MemoryStream pkgStream = new();
public ReadWriteZipFile(string filename, bool create = false)
{
@@ -141,23 +142,22 @@ namespace OpenRA.FileSystem
sealed class ZipFolder : IReadOnlyPackage
{
public string Name { get { return path; } }
public ReadOnlyZipFile Parent { get; private set; }
readonly string path;
public string Name { get; }
public ReadOnlyZipFile Parent { get; }
public ZipFolder(ReadOnlyZipFile parent, string path)
{
if (path.EndsWith("/", StringComparison.Ordinal))
path = path.Substring(0, path.Length - 1);
path = path[..^1];
Name = path;
Parent = parent;
this.path = path;
}
public Stream GetStream(string filename)
{
// Zip files use '/' as a path separator
return Parent.GetStream(path + '/' + filename);
return Parent.GetStream(Name + '/' + filename);
}
public IEnumerable<string> Contents
@@ -166,9 +166,9 @@ namespace OpenRA.FileSystem
{
foreach (var entry in Parent.Contents)
{
if (entry.StartsWith(path, StringComparison.Ordinal) && entry != path)
if (entry.StartsWith(Name, StringComparison.Ordinal) && entry != Name)
{
var filename = entry.Substring(path.Length + 1);
var filename = entry[(Name.Length + 1)..];
var dirLevels = filename.Split('/').Count(c => !string.IsNullOrEmpty(c));
if (dirLevels == 1)
yield return filename;
@@ -179,18 +179,18 @@ namespace OpenRA.FileSystem
public bool Contains(string filename)
{
return Parent.Contains(path + '/' + filename);
return Parent.Contains(Name + '/' + filename);
}
public IReadOnlyPackage OpenPackage(string filename, FileSystem context)
{
return Parent.OpenPackage(path + '/' + filename, context);
return Parent.OpenPackage(Name + '/' + filename, context);
}
public void Dispose() { /* nothing to do */ }
}
class StaticStreamDataSource : IStaticDataSource
sealed class StaticStreamDataSource : IStaticDataSource
{
readonly Stream s;
public StaticStreamDataSource(Stream s)
@@ -206,7 +206,10 @@ namespace OpenRA.FileSystem
public bool TryParsePackage(Stream s, string filename, FileSystem context, out IReadOnlyPackage package)
{
if (!Extensions.Any(e => filename.EndsWith(e, StringComparison.InvariantCultureIgnoreCase)))
var readSignature = s.ReadUInt32();
s.Position -= 4;
if (readSignature != ZipSignature)
{
package = null;
return false;
@@ -218,10 +221,13 @@ namespace OpenRA.FileSystem
public static bool TryParseReadWritePackage(string filename, out IReadWritePackage package)
{
if (!Extensions.Any(e => filename.EndsWith(e, StringComparison.InvariantCultureIgnoreCase)))
using (var s = File.OpenRead(filename))
{
package = null;
return false;
if (s.ReadUInt32() != ZipSignature)
{
package = null;
return false;
}
}
package = new ReadWriteZipFile(filename);

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -22,7 +22,7 @@ namespace OpenRA
public class Fonts : IGlobalModData
{
[FieldLoader.LoadUsing("LoadFonts")]
[FieldLoader.LoadUsing(nameof(LoadFonts))]
public readonly Dictionary<string, FontData> FontList;
static object LoadFonts(MiniYaml y)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -16,10 +16,8 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime;
using System.Threading;
using System.Threading.Tasks;
using OpenRA.Graphics;
using OpenRA.Network;
using OpenRA.Primitives;
@@ -31,8 +29,9 @@ namespace OpenRA
{
public static class Game
{
public const int NetTickScale = 3; // 120 ms net tick for 40 ms local tick
public const int Timestep = 40;
[TranslationReference("filename")]
const string SavedScreenshot = "notification-saved-screenshot";
public const int TimestepJankThreshold = 250; // Don't catch up for delays larger than 250ms
public static InstalledMods Mods { get; private set; }
@@ -42,13 +41,14 @@ namespace OpenRA
public static Settings Settings;
public static CursorManager Cursor;
public static bool HideCursor;
static WorldRenderer worldRenderer;
static string modLaunchWrapper;
internal static OrderManager OrderManager;
static Server.Server server;
public static MersenneTwister CosmeticRandom = new MersenneTwister(); // not synced
public static MersenneTwister CosmeticRandom = new(); // not synced
public static Renderer Renderer;
public static Sound Sound;
@@ -56,7 +56,6 @@ namespace OpenRA
public static string EngineVersion { get; private set; }
public static LocalPlayerProfile LocalPlayerProfile;
static Task discoverNat;
static bool takeScreenshot = false;
static Benchmark benchmark = null;
@@ -64,12 +63,18 @@ namespace OpenRA
public static OrderManager JoinServer(ConnectionTarget endpoint, string password, bool recordReplay = true)
{
var connection = new NetworkConnection(endpoint);
var newConnection = new NetworkConnection(endpoint);
if (recordReplay)
connection.StartRecording(() => { return TimestampedFilename(); });
newConnection.StartRecording(() => TimestampedFilename());
var om = new OrderManager(endpoint, password, connection);
var om = new OrderManager(newConnection);
JoinInner(om);
CurrentServerSettings.Password = password;
CurrentServerSettings.Target = endpoint;
lastConnectionState = ConnectionState.PreConnecting;
ConnectionStateChanged(OrderManager, password, newConnection);
return om;
}
@@ -81,34 +86,56 @@ namespace OpenRA
static void JoinInner(OrderManager om)
{
OrderManager?.Dispose();
// Refresh TextNotificationsManager before the game starts.
TextNotificationsManager.Clear();
// HACK: The shellmap World and OrderManager are owned by the main menu's WorldRenderer instead of Game.
// This allows us to switch Game.OrderManager from the shellmap to the new network connection when joining
// a lobby, while keeping the OrderManager that runs the shellmap intact.
// A matching check in World.Dispose (which is called by WorldRenderer.Dispose) makes sure that we dispose
// the shellmap's OM when a lobby game actually starts.
if (OrderManager?.World == null || OrderManager.World.Type != WorldType.Shellmap)
OrderManager?.Dispose();
OrderManager = om;
lastConnectionState = ConnectionState.PreConnecting;
ConnectionStateChanged(OrderManager);
}
public static void JoinReplay(string replayFile)
{
JoinInner(new OrderManager(new ConnectionTarget(), "", new ReplayConnection(replayFile)));
JoinInner(new OrderManager(new ReplayConnection(replayFile)));
}
static void JoinLocal()
{
JoinInner(new OrderManager(new ConnectionTarget(), "", new EchoConnection()));
JoinInner(new OrderManager(new EchoConnection()));
// Add a spectator client for the local player
// On the shellmap this player is controlling the map via scripted orders
OrderManager.LobbyInfo.Clients.Add(new Session.Client
{
Index = OrderManager.Connection.LocalClientId,
Name = Settings.Player.Name,
PreferredColor = Settings.Player.Color,
Color = Settings.Player.Color,
Faction = "Random",
SpawnPoint = 0,
Team = 0,
State = Session.ClientState.Ready
});
}
// More accurate replacement for Environment.TickCount
static Stopwatch stopwatch = Stopwatch.StartNew();
public static long RunTime { get { return stopwatch.ElapsedMilliseconds; } }
static readonly Stopwatch Stopwatch = Stopwatch.StartNew();
public static long RunTime => Stopwatch.ElapsedMilliseconds;
public static int RenderFrame = 0;
public static int NetFrameNumber { get { return OrderManager.NetFrameNumber; } }
public static int LocalTick { get { return OrderManager.LocalFrameNumber; } }
public static int NetFrameNumber => OrderManager.NetFrameNumber;
public static int LocalTick => OrderManager.LocalFrameNumber;
public static event Action<ConnectionTarget> OnRemoteDirectConnect = _ => { };
public static event Action<OrderManager> ConnectionStateChanged = _ => { };
public static event Action<OrderManager, string, NetworkConnection> ConnectionStateChanged = (om, pass, conn) => { };
static ConnectionState lastConnectionState = ConnectionState.PreConnecting;
public static int LocalClientId { get { return OrderManager.Connection.LocalClientId; } }
public static int LocalClientId => OrderManager.Connection.LocalClientId;
public static void RemoteDirectConnect(ConnectionTarget endpoint)
{
@@ -160,12 +187,8 @@ namespace OpenRA
Cursor.SetCursor(null);
BeforeGameStart();
Map map;
using (new PerfTimer("PrepareMap"))
map = ModData.PrepareMap(mapUID);
using (new PerfTimer("NewWorld"))
OrderManager.World = new World(ModData, map, OrderManager, type);
OrderManager.World = new World(mapUID, ModData, OrderManager, type);
OrderManager.World.GameOver += FinishBenchmark;
@@ -186,11 +209,9 @@ namespace OpenRA
Ui.MouseFocusWidget = null;
Ui.KeyboardFocusWidget = null;
OrderManager.LocalFrameNumber = 0;
OrderManager.LastTickTime = RunTime;
OrderManager.StartGame();
worldRenderer.RefreshPalette();
Cursor.SetCursor("default");
Cursor.SetCursor(ChromeMetrics.Get<string>("DefaultCursor"));
// Now loading is completed, now is the ideal time to run a GC and compact the LOH.
// - All the temporary garbage created during loading can be collected.
@@ -206,15 +227,26 @@ namespace OpenRA
public static void RestartGame()
{
var replay = OrderManager.Connection as ReplayConnection;
var replayName = replay != null ? replay.Filename : null;
var replayName = replay?.Filename;
var lobbyInfo = OrderManager.LobbyInfo;
// Reseed the RNG so this isn't an exact repeat of the last game
lobbyInfo.GlobalSettings.RandomSeed = CosmeticRandom.Next();
// Note: the map may have been changed on disk outside the game, changing its UID.
// Use the updated UID if we have tracked the update instead of failing.
lobbyInfo.GlobalSettings.Map = ModData.MapCache.GetUpdatedMap(lobbyInfo.GlobalSettings.Map);
if (lobbyInfo.GlobalSettings.Map == null)
{
Disconnect();
Ui.ResetAll();
LoadShellMap();
return;
}
var orders = new[]
{
Order.Command("sync_lobby {0}".F(lobbyInfo.Serialize())),
Order.Command($"sync_lobby {lobbyInfo.Serialize()}"),
Order.Command("startgame")
};
@@ -233,15 +265,14 @@ namespace OpenRA
{
OrderManager om = null;
Action lobbyReady = null;
lobbyReady = () =>
void LobbyReady()
{
LobbyInfoChanged -= lobbyReady;
LobbyInfoChanged -= LobbyReady;
foreach (var o in setupOrders)
om.IssueOrder(o);
};
}
LobbyInfoChanged += lobbyReady;
LobbyInfoChanged += LobbyReady;
om = JoinServer(CreateLocalServer(mapUID), "");
}
@@ -284,7 +315,7 @@ namespace OpenRA
if (!string.IsNullOrEmpty(supportDirArg))
Platform.OverrideSupportDir(supportDirArg);
Console.WriteLine("Platform is {0}", Platform.CurrentPlatform);
Console.WriteLine($"Platform is {Platform.CurrentPlatform} ({Platform.CurrentArchitecture})");
// Load the engine version as early as possible so it can be written to exception logs
try
@@ -296,13 +327,13 @@ namespace OpenRA
if (string.IsNullOrEmpty(EngineVersion))
EngineVersion = "Unknown";
Console.WriteLine("Engine version is {0}", EngineVersion);
Console.WriteLine("Runtime: {0}", Platform.RuntimeVersion);
Console.WriteLine($"Engine version is {EngineVersion}");
Console.WriteLine($"Runtime: {Platform.RuntimeVersion}");
// Special case handling of Game.Mod argument: if it matches a real filesystem path
// then we use this to override the mod search path, and replace it with the mod id
var modID = args.GetValue("Game.Mod", null);
var explicitModPaths = new string[0];
var explicitModPaths = Array.Empty<string>();
if (modID != null && (File.Exists(modID) || Directory.Exists(modID)))
{
explicitModPaths = new[] { modID };
@@ -331,12 +362,13 @@ namespace OpenRA
{
var rendererPath = Path.Combine(Platform.BinDir, "OpenRA.Platforms." + p + ".dll");
#if !MONO
#if NET5_0_OR_GREATER
var loader = new AssemblyLoader(rendererPath);
var platformType = loader.LoadDefaultAssembly().GetTypes().SingleOrDefault(t => typeof(IPlatform).IsAssignableFrom(t));
#else
var assembly = Assembly.LoadFile(rendererPath);
// NOTE: This is currently the only use of System.Reflection in this file, so would give an unused using error if we import it above
var assembly = System.Reflection.Assembly.LoadFile(rendererPath);
var platformType = assembly.GetTypes().SingleOrDefault(t => typeof(IPlatform).IsAssignableFrom(t));
#endif
@@ -351,7 +383,7 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("graphics", "{0}", e);
Log.Write("graphics", $"{e}");
Console.WriteLine("Renderer initialization failed. Check graphics.log for details.");
Renderer?.Dispose();
@@ -360,8 +392,7 @@ namespace OpenRA
}
}
if (Settings.Server.DiscoverNatDevices)
discoverNat = UPnP.DiscoverNatDevices(Settings.Server.NatDiscoveryTimeout);
Nat.Initialize();
var modSearchArg = args.GetValue("Engine.ModSearchPaths", null);
var modSearchPaths = modSearchArg != null ?
@@ -371,7 +402,7 @@ namespace OpenRA
Mods = new InstalledMods(modSearchPaths, explicitModPaths);
Console.WriteLine("Internal mods:");
foreach (var mod in Mods)
Console.WriteLine("\t{0}: {1} ({2})", mod.Key, mod.Value.Metadata.Title, mod.Value.Metadata.Version);
Console.WriteLine($"\t{mod.Key}: {mod.Value.Metadata.Title} ({mod.Value.Metadata.Version})");
modLaunchWrapper = args.GetValue("Engine.LaunchWrapper", null);
@@ -385,26 +416,18 @@ namespace OpenRA
// Sanitize input from platform-specific launchers
// Process.Start requires paths to not be quoted, even if they contain spaces
if (launchPath != null && launchPath.First() == '"' && launchPath.Last() == '"')
launchPath = launchPath.Substring(1, launchPath.Length - 2);
launchPath = launchPath[1..^1];
if (launchPath == null)
{
// When launching the assembly directly we must propagate the Engine.EngineDir argument if defined
// Platform-specific launchers are expected to manage this internally.
launchPath = Assembly.GetEntryAssembly().Location;
if (!string.IsNullOrEmpty(engineDirArg))
launchArgs.Add("Engine.EngineDir=\"" + engineDirArg + "\"");
}
// Metadata registration requires an explicit launch path
if (launchPath != null)
ExternalMods.Register(Mods[modID], launchPath, launchArgs, ModRegistration.User);
ExternalMods.Register(Mods[modID], launchPath, launchArgs, ModRegistration.User);
if (ExternalMods.TryGetValue(ExternalMod.MakeKey(Mods[modID]), out var activeMod))
ExternalMods.ClearInvalidRegistrations(activeMod, ModRegistration.User);
ExternalMods.ClearInvalidRegistrations(ModRegistration.User);
}
Console.WriteLine("External mods:");
foreach (var mod in ExternalMods)
Console.WriteLine("\t{0}: {1} ({2})", mod.Key, mod.Value.Title, mod.Value.Version);
Console.WriteLine($"\t{mod.Key}: {mod.Value.Title} ({mod.Value.Version})");
InitializeMod(modID, args);
}
@@ -413,7 +436,7 @@ namespace OpenRA
{
// Clear static state if we have switched mods
LobbyInfoChanged = () => { };
ConnectionStateChanged = om => { };
ConnectionStateChanged = (om, p, conn) => { };
BeforeGameStart = () => { };
OnRemoteDirectConnect = endpoint => { };
delayedActions = new ActionQueue();
@@ -437,9 +460,9 @@ namespace OpenRA
throw new InvalidOperationException("Game.Mod argument missing.");
if (!Mods.ContainsKey(mod))
throw new InvalidOperationException("Unknown or invalid mod '{0}'.".F(mod));
throw new InvalidOperationException($"Unknown or invalid mod '{mod}'.");
Console.WriteLine("Loading mod: {0}", mod);
Console.WriteLine($"Loading mod: {mod}");
Sound.StopVideo();
@@ -450,19 +473,22 @@ namespace OpenRA
if (!ModData.LoadScreen.BeforeLoad())
return;
using (new PerfTimer("LoadMaps"))
ModData.MapCache.LoadMaps();
ModData.InitializeLoaders(ModData.DefaultFileSystem);
Renderer.InitializeFonts(ModData);
using (new PerfTimer("LoadMaps"))
ModData.MapCache.LoadMaps();
var grid = ModData.Manifest.Contains<MapGrid>() ? ModData.Manifest.Get<MapGrid>() : null;
Renderer.InitializeDepthBuffer(grid);
Cursor?.Dispose();
Cursor = new CursorManager(ModData.CursorProvider);
var metadata = ModData.Manifest.Metadata;
if (!string.IsNullOrEmpty(metadata.WindowTitle))
Renderer.Window.SetWindowTitle(metadata.WindowTitle);
PerfHistory.Items["render"].HasNormalTick = false;
PerfHistory.Items["batches"].HasNormalTick = false;
PerfHistory.Items["render_world"].HasNormalTick = false;
@@ -472,33 +498,18 @@ namespace OpenRA
JoinLocal();
try
{
discoverNat?.Wait();
}
catch (Exception e)
{
Console.WriteLine("NAT discovery failed: {0}", e.Message);
Log.Write("nat", e.ToString());
}
ChromeMetrics.TryGet("ChatMessageColor", out chatMessageColor);
ChromeMetrics.TryGet("SystemMessageColor", out systemMessageColor);
if (!ChromeMetrics.TryGet("SystemMessageLabel", out systemMessageLabel))
systemMessageLabel = "Battlefield Control";
ModData.LoadScreen.StartGame(args);
}
public static void LoadEditor(string mapUid)
{
JoinLocal();
StartGame(mapUid, WorldType.Editor);
}
public static void LoadShellMap()
{
var shellmap = ChooseShellmap();
using (new PerfTimer("StartGame"))
{
StartGame(shellmap, WorldType.Shellmap);
@@ -552,10 +563,7 @@ namespace OpenRA
// Note: These delayed actions should only be used by widgets or disposing objects
// - things that depend on a particular world should be queuing them on the world actor.
static volatile ActionQueue delayedActions = new ActionQueue();
static Color systemMessageColor = Color.White;
static Color chatMessageColor = Color.White;
static string systemMessageLabel;
static volatile ActionQueue delayedActions = new();
public static void RunAfterTick(Action a) { delayedActions.Add(a, RunTime); }
public static void RunAfterDelay(int delayMilliseconds, Action a) { delayedActions.Add(a, RunTime + delayMilliseconds); }
@@ -573,7 +581,7 @@ namespace OpenRA
Log.Write("debug", "Taking screenshot " + path);
Renderer.SaveScreenshot(path);
Debug("Saved screenshot " + filename);
TextNotificationsManager.Debug(TranslationProvider.GetString(SavedScreenshot, Translation.Arguments("filename", filename)));
}
}
@@ -583,62 +591,38 @@ namespace OpenRA
var world = orderManager.World;
var uiTickDelta = tick - Ui.LastTickTime;
if (uiTickDelta >= Timestep)
if (Ui.LastTickTime.ShouldAdvance(tick))
{
// Explained below for the world tick calculation
var integralTickTimestep = (uiTickDelta / Timestep) * Timestep;
Ui.LastTickTime += integralTickTimestep >= TimestepJankThreshold ? integralTickTimestep : Timestep;
Sync.RunUnsynced(Settings.Debug.SyncCheckUnsyncedCode, world, Ui.Tick);
Ui.LastTickTime.AdvanceTickTime(tick);
Sync.RunUnsynced(world, Ui.Tick);
Cursor.Tick();
}
var worldTimestep = world == null ? Timestep : world.IsLoadingGameSave ? 1 : world.Timestep;
var worldTickDelta = tick - orderManager.LastTickTime;
if (worldTimestep != 0 && worldTickDelta >= worldTimestep)
if (orderManager.LastTickTime.ShouldAdvance(tick))
{
using (new PerfSample("tick_time"))
{
// Tick the world to advance the world time to match real time:
// If dt < TickJankThreshold then we should try and catch up by repeatedly ticking
// If dt >= TickJankThreshold then we should accept the jank and progress at the normal rate
// dt is rounded down to an integer tick count in order to preserve fractional tick components.
var integralTickTimestep = (worldTickDelta / worldTimestep) * worldTimestep;
orderManager.LastTickTime += integralTickTimestep >= TimestepJankThreshold ? integralTickTimestep : worldTimestep;
orderManager.LastTickTime.AdvanceTickTime(tick);
Sound.Tick();
Sync.RunUnsynced(Settings.Debug.SyncCheckUnsyncedCode, world, orderManager.TickImmediate);
Sync.RunUnsynced(world, orderManager.TickImmediate);
if (world == null)
return;
var isNetTick = LocalTick % NetTickScale == 0;
if (!isNetTick || orderManager.IsReadyForNextFrame)
if (orderManager.TryTick())
{
++orderManager.LocalFrameNumber;
Log.Write("debug", "--Tick: {0} ({1})", LocalTick, isNetTick ? "net" : "local");
if (isNetTick)
orderManager.Tick();
Sync.RunUnsynced(Settings.Debug.SyncCheckUnsyncedCode, world, () =>
{
world.OrderGenerator.Tick(world);
});
Sync.RunUnsynced(world, () => world.OrderGenerator.Tick(world));
world.Tick();
PerfHistory.Tick();
}
else if (orderManager.NetFrameNumber == 0)
orderManager.LastTickTime = RunTime;
// Wait until we have done our first world Tick before TickRendering
if (orderManager.LocalFrameNumber > 0)
Sync.RunUnsynced(Settings.Debug.SyncCheckUnsyncedCode, world, () => world.TickRender(worldRenderer));
Sync.RunUnsynced(world, () => world.TickRender(worldRenderer));
}
benchmark?.Tick(LocalTick);
@@ -649,10 +633,10 @@ namespace OpenRA
{
PerformDelayedActions();
if (OrderManager.Connection.ConnectionState != lastConnectionState)
if (OrderManager.Connection is NetworkConnection nc && nc.ConnectionState != lastConnectionState)
{
lastConnectionState = OrderManager.Connection.ConnectionState;
ConnectionStateChanged(OrderManager);
lastConnectionState = nc.ConnectionState;
ConnectionStateChanged(OrderManager, null, nc);
}
InnerLogicTick(OrderManager);
@@ -789,13 +773,20 @@ namespace OpenRA
while (state == RunStatus.Running)
{
// Ideal time between logic updates. Timestep = 0 means the game is paused
// but we still call LogicTick() because it handles pausing internally.
var logicInterval = worldRenderer != null && worldRenderer.World.Timestep != 0 ? worldRenderer.World.Timestep : Timestep;
var logicInterval = Ui.Timestep;
var logicWorld = worldRenderer?.World;
// ReplayTimestep = 0 means the replay is paused: we need to keep logicInterval as UI.Timestep to avoid breakage
if (logicWorld != null && !(logicWorld.IsReplay && logicWorld.ReplayTimestep == 0))
logicInterval = logicWorld == OrderManager.World ? OrderManager.SuggestedTimestep : logicWorld.Timestep;
// Ideal time between screen updates
var maxFramerate = Settings.Graphics.CapFramerate ? Settings.Graphics.MaxFramerate.Clamp(1, 1000) : 1000;
var renderInterval = 1000 / maxFramerate;
var renderInterval = logicInterval;
if (!Settings.Graphics.CapFramerateToGameFps)
{
var maxFramerate = Settings.Graphics.CapFramerate ? Settings.Graphics.MaxFramerate.Clamp(1, 1000) : 1000;
renderInterval = 1000 / maxFramerate;
}
// Tick as fast as possible while restoring game saves, capping rendering at 5 FPS
if (OrderManager.World != null && OrderManager.World.IsLoadingGameSave)
@@ -829,8 +820,7 @@ namespace OpenRA
var haveSomeTimeUntilNextLogic = now < nextLogic;
var isTimeToRender = now >= nextRender;
if ((isTimeToRender && haveSomeTimeUntilNextLogic) || forceRender)
if (!Renderer.WindowIsSuspended && ((isTimeToRender && haveSomeTimeUntilNextLogic) || forceRender))
{
nextRender = now + renderInterval;
@@ -845,6 +835,19 @@ namespace OpenRA
RenderTick();
renderBeforeNextTick = false;
}
// Simulate a render tick if it was time to render but we skip actually rendering
if (Renderer.WindowIsSuspended && isTimeToRender)
{
// Make sure that nextUpdate is set to a proper minimum interval
nextRender = now + renderInterval;
// Still process SDL events to allow a restore to come through
Renderer.Window.PumpInput(new NullInputHandler());
// Ensure that we still logic tick despite not rendering
renderBeforeNextTick = false;
}
}
else
Thread.Sleep((int)(nextUpdate - now));
@@ -886,26 +889,6 @@ namespace OpenRA
state = RunStatus.Success;
}
public static void AddSystemLine(string text)
{
AddSystemLine(systemMessageLabel, text);
}
public static void AddSystemLine(string name, string text)
{
OrderManager.AddChatLine(name, systemMessageColor, text, systemMessageColor);
}
public static void AddChatLine(string name, Color nameColor, string text)
{
OrderManager.AddChatLine(name, nameColor, text, chatMessageColor);
}
public static void Debug(string s, params object[] args)
{
AddSystemLine("Debug", string.Format(s, args));
}
public static void Disconnect()
{
OrderManager.World?.TraitDict.PrintReport();
@@ -977,16 +960,13 @@ namespace OpenRA
{
var orders = new List<Order>
{
Order.Command("option gamespeed {0}".F("default")),
Order.Command("state {0}".F(Session.ClientState.Ready))
Order.Command("option gamespeed default"),
Order.Command($"state {Session.ClientState.Ready}")
};
var path = Platform.ResolvePath(launchMap);
var map = ModData.MapCache.SingleOrDefault(m => m.Uid == launchMap) ??
ModData.MapCache.SingleOrDefault(m => m.Package.Name == path);
var map = ModData.MapCache.SingleOrDefault(m => m.Uid == launchMap || Path.GetFileName(m.Package.Name) == launchMap);
if (map == null)
throw new InvalidOperationException("Could not find map '{0}'.".F(launchMap));
throw new ArgumentException($"Could not find map '{launchMap}'.");
CreateAndStartLocalServer(map.Uid, orders);
}
@@ -1000,4 +980,11 @@ namespace OpenRA
}
}
}
public static class CurrentServerSettings
{
public static string Password;
public static ConnectionTarget Target;
public static ExternalMod ServerExternalMod;
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -33,12 +33,13 @@ namespace OpenRA
public DateTime EndTimeUtc;
/// <summary>Gets the game's duration, from the time the game started until the replay recording stopped.</summary>
public TimeSpan Duration { get { return EndTimeUtc > StartTimeUtc ? EndTimeUtc - StartTimeUtc : TimeSpan.Zero; } }
public IList<Player> Players { get; private set; }
public HashSet<int> DisabledSpawnPoints = new HashSet<int>();
public MapPreview MapPreview { get { return Game.ModData.MapCache[MapUid]; } }
public TimeSpan Duration => EndTimeUtc > StartTimeUtc ? EndTimeUtc - StartTimeUtc : TimeSpan.Zero;
public IList<Player> Players { get; }
public HashSet<int> DisabledSpawnPoints = new();
public MapPreview MapPreview => Game.ModData.MapCache[MapUid];
public IEnumerable<Player> HumanPlayers { get { return Players.Where(p => p.IsHuman); } }
public bool IsSinglePlayer { get { return HumanPlayers.Count() == 1; } }
public bool IsSinglePlayer => HumanPlayers.Count() == 1;
readonly Dictionary<OpenRA.Player, Player> playersByRuntime;
@@ -75,7 +76,7 @@ namespace OpenRA
}
catch (YamlException)
{
Log.Write("debug", "GameInformation deserialized invalid MiniYaml:\n{0}".F(data));
Log.Write("debug", $"GameInformation deserialized invalid MiniYaml:\n{data}");
throw;
}
}
@@ -88,7 +89,7 @@ namespace OpenRA
};
for (var i = 0; i < Players.Count; i++)
nodes.Add(new MiniYamlNode("Player@{0}".F(i), FieldSaver.Save(Players[i])));
nodes.Add(new MiniYamlNode($"Player@{i}", FieldSaver.Save(Players[i])));
return nodes.WriteToString();
}
@@ -97,10 +98,10 @@ namespace OpenRA
public void AddPlayer(OpenRA.Player runtimePlayer, Session lobbyInfo)
{
if (runtimePlayer == null)
throw new ArgumentNullException("runtimePlayer");
throw new ArgumentNullException(nameof(runtimePlayer));
if (lobbyInfo == null)
throw new ArgumentNullException("lobbyInfo");
throw new ArgumentNullException(nameof(lobbyInfo));
// We don't care about spectators and map players
if (runtimePlayer.NonCombatant || !runtimePlayer.Playable)
@@ -123,6 +124,7 @@ namespace OpenRA
DisplayFactionId = runtimePlayer.DisplayFaction.InternalName,
Color = runtimePlayer.Color,
Team = client.Team,
Handicap = client.Handicap,
SpawnPoint = runtimePlayer.SpawnPoint,
IsRandomFaction = runtimePlayer.Faction.InternalName != client.Faction,
IsRandomSpawnPoint = runtimePlayer.DisplaySpawnPoint == 0,
@@ -166,6 +168,7 @@ namespace OpenRA
/// <summary>The team ID on start-up, or 0 if the player is not part of a team.</summary>
public int Team;
public int SpawnPoint;
public int Handicap;
/// <summary>True if the faction was chosen at random; otherwise, false.</summary>
public bool IsRandomFaction;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -32,7 +32,7 @@ namespace OpenRA
/// You can remove inherited traits by adding a - in front of them as in -TraitName: to inherit everything, but this trait.
/// </summary>
public readonly string Name;
readonly TypeDictionary traits = new TypeDictionary();
readonly TypeDictionary traits = new();
List<TraitInfo> constructOrderCache = null;
public ActorInfo(ObjectCreator creator, string name, MiniYaml node)
@@ -61,7 +61,7 @@ namespace OpenRA
}
catch (YamlException e)
{
throw new YamlException("Actor type {0}: {1}".F(name, e.Message));
throw new YamlException($"Actor type {name}: {e.Message}");
}
}
@@ -76,8 +76,7 @@ namespace OpenRA
static TraitInfo LoadTraitInfo(ObjectCreator creator, string traitName, MiniYaml my)
{
if (!string.IsNullOrEmpty(my.Value))
throw new YamlException("Junk value `{0}` on trait node {1}"
.F(my.Value, traitName));
throw new YamlException($"Junk value `{my.Value}` on trait node {traitName}");
// 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
@@ -89,7 +88,7 @@ namespace OpenRA
try
{
if (traitInstance.Length > 1)
info.GetType().GetField("InstanceName").SetValue(info, traitInstance[1]);
info.GetType().GetField(nameof(info.InstanceName)).SetValue(info, traitInstance[1]);
FieldLoader.Load(info, my);
}
@@ -111,39 +110,51 @@ namespace OpenRA
{
Trait = i,
Type = i.GetType(),
Dependencies = PrerequisitesOf(i).ToList()
Dependencies = PrerequisitesOf(i).ToList(),
OptionalDependencies = OptionalPrerequisitesOf(i).ToList()
}).ToList();
var resolved = source.Where(s => !s.Dependencies.Any()).ToList();
var unresolved = source.Except(resolved);
var resolved = source.Where(s => s.Dependencies.Count == 0 && s.OptionalDependencies.Count == 0).ToList();
var unresolved = source.ToHashSet();
unresolved.ExceptWith(resolved);
var testResolve = new Func<Type, Type, bool>((a, b) => a == b || a.IsAssignableFrom(b));
static bool AreResolvable(Type a, Type b) => a.IsAssignableFrom(b);
// This query detects which unresolved traits can be immediately resolved as all their direct dependencies are met.
var more = unresolved.Where(u =>
u.Dependencies.All(d => // To be resolvable, all dependencies must be satisfied according to the following conditions:
resolved.Exists(r => testResolve(d, r.Type)) && // There must exist a resolved trait that meets the dependency.
!unresolved.Any(u1 => testResolve(d, u1.Type)))); // All matching traits that meet this dependency must be resolved first.
resolved.Exists(r => AreResolvable(d, r.Type)) && // There must exist a resolved trait that meets the dependency.
!unresolved.Any(u1 => AreResolvable(d, u1.Type))) && // All matching traits that meet this dependency must be resolved first.
u.OptionalDependencies.All(d => // To be resolvable, all optional dependencies must be satisfied according to the following condition:
!unresolved.Any(u1 => AreResolvable(d, u1.Type)))); // All matching traits that meet this optional dependencies must be resolved first.
// Continue resolving traits as long as possible.
// Each time we resolve some traits, this means dependencies for other traits may then be possible to satisfy in the next pass.
while (more.Any())
resolved.AddRange(more);
if (unresolved.Any())
var readyToResolve = more.ToList();
while (readyToResolve.Count != 0)
{
var exceptionString = "ActorInfo(\"" + Name + "\") failed to initialize because of the following:\r\n";
var missing = unresolved.SelectMany(u => u.Dependencies.Where(d => !source.Any(s => testResolve(d, s.Type)))).Distinct();
resolved.AddRange(readyToResolve);
unresolved.ExceptWith(readyToResolve);
readyToResolve.Clear();
readyToResolve.AddRange(more);
}
exceptionString += "Missing:\r\n";
if (unresolved.Count != 0)
{
var exceptionString = "ActorInfo(\"" + Name + "\") failed to initialize because of the following:\n";
var missing = unresolved.SelectMany(u => u.Dependencies.Where(d => !source.Any(s => AreResolvable(d, s.Type)))).Distinct();
exceptionString += "Missing:\n";
foreach (var m in missing)
exceptionString += m + " \r\n";
exceptionString += m + " \n";
exceptionString += "Unresolved:\r\n";
exceptionString += "Unresolved:\n";
foreach (var u in unresolved)
{
var deps = u.Dependencies.Where(d => !resolved.Exists(r => r.Type == d));
exceptionString += u.Type + ": { " + string.Join(", ", deps) + " }\r\n";
var optDeps = u.OptionalDependencies.Where(d => !resolved.Exists(r => r.Type == d));
var allDeps = string.Join(", ", deps.Select(o => o.ToString()).Concat(optDeps.Select(o => $"[{o}]")));
exceptionString += $"{u.Type}: {{ {allDeps} }}\n";
}
throw new YamlException(exceptionString);
@@ -162,6 +173,15 @@ namespace OpenRA
.Select(t => t.GetGenericArguments()[0]);
}
public static IEnumerable<Type> OptionalPrerequisitesOf(TraitInfo info)
{
return info
.GetType()
.GetInterfaces()
.Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(NotBefore<>))
.Select(t => t.GetGenericArguments()[0]);
}
public bool HasTraitInfo<T>() where T : ITraitInfoInterface { return traits.Contains<T>(); }
public T TraitInfo<T>() where T : ITraitInfoInterface { return traits.Get<T>(); }
public T TraitInfoOrDefault<T>() where T : ITraitInfoInterface { return traits.GetOrDefault<T>(); }

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -9,7 +9,6 @@
*/
#endregion
using System.IO;
using OpenRA.FileSystem;
namespace OpenRA.GameRules
@@ -29,14 +28,14 @@ namespace OpenRA.GameRules
Title = value.Value;
var nd = value.ToDictionary();
if (nd.ContainsKey("Hidden"))
bool.TryParse(nd["Hidden"].Value, out Hidden);
if (nd.TryGetValue("Hidden", out var yaml))
bool.TryParse(yaml.Value, out Hidden);
if (nd.ContainsKey("VolumeModifier"))
VolumeModifier = FieldLoader.GetValue<float>("VolumeModifier", nd["VolumeModifier"].Value);
if (nd.TryGetValue("VolumeModifier", out yaml))
VolumeModifier = FieldLoader.GetValue<float>("VolumeModifier", yaml.Value);
var ext = nd.ContainsKey("Extension") ? nd["Extension"].Value : "aud";
Filename = (nd.ContainsKey("Filename") ? nd["Filename"].Value : key) + "." + ext;
var ext = nd.TryGetValue("Extension", out yaml) ? yaml.Value : "aud";
Filename = (nd.TryGetValue("Filename", out yaml) ? yaml.Value : key) + "." + ext;
}
public void Load(IReadOnlyFileSystem fileSystem)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -15,20 +15,18 @@ using System.Linq;
using System.Threading.Tasks;
using OpenRA.FileSystem;
using OpenRA.GameRules;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA
{
public class Ruleset
{
public readonly IReadOnlyDictionary<string, ActorInfo> Actors;
public readonly ActorInfoDictionary Actors;
public readonly IReadOnlyDictionary<string, WeaponInfo> Weapons;
public readonly IReadOnlyDictionary<string, SoundInfo> Voices;
public readonly IReadOnlyDictionary<string, SoundInfo> Notifications;
public readonly IReadOnlyDictionary<string, MusicInfo> Music;
public readonly TileSet TileSet;
public readonly SequenceProvider Sequences;
public readonly ITerrainInfo TerrainInfo;
public readonly IReadOnlyDictionary<string, MiniYamlNode> ModelSequences;
public Ruleset(
@@ -37,17 +35,15 @@ namespace OpenRA
IReadOnlyDictionary<string, SoundInfo> voices,
IReadOnlyDictionary<string, SoundInfo> notifications,
IReadOnlyDictionary<string, MusicInfo> music,
TileSet tileSet,
SequenceProvider sequences,
ITerrainInfo terrainInfo,
IReadOnlyDictionary<string, MiniYamlNode> modelSequences)
{
Actors = actors;
Actors = new ActorInfoDictionary(actors);
Weapons = weapons;
Voices = voices;
Notifications = notifications;
Music = music;
TileSet = tileSet;
Sequences = sequences;
TerrainInfo = terrainInfo;
ModelSequences = modelSequences;
foreach (var a in Actors.Values)
@@ -60,15 +56,14 @@ namespace OpenRA
}
catch (YamlException e)
{
throw new YamlException("Actor type {0}: {1}".F(a.Name, e.Message));
throw new YamlException($"Actor type {a.Name}: {e.Message}");
}
}
}
foreach (var weapon in Weapons)
{
var projectileLoaded = weapon.Value.Projectile as IRulesetLoaded<WeaponInfo>;
if (projectileLoaded != null)
if (weapon.Value.Projectile is IRulesetLoaded<WeaponInfo> projectileLoaded)
{
try
{
@@ -76,14 +71,13 @@ namespace OpenRA
}
catch (YamlException e)
{
throw new YamlException("Projectile type {0}: {1}".F(weapon.Key, e.Message));
throw new YamlException($"Projectile type {weapon.Key}: {e.Message}");
}
}
foreach (var warhead in weapon.Value.Warheads)
{
var cacher = warhead as IRulesetLoaded<WeaponInfo>;
if (cacher != null)
if (warhead is IRulesetLoaded<WeaponInfo> cacher)
{
try
{
@@ -91,7 +85,7 @@ namespace OpenRA
}
catch (YamlException e)
{
throw new YamlException("Weapon type {0}: {1}".F(weapon.Key, e.Message));
throw new YamlException($"Weapon type {weapon.Key}: {e.Message}");
}
}
}
@@ -117,7 +111,7 @@ namespace OpenRA
if (filterNode != null)
yamlNodes = yamlNodes.Where(k => !filterNode(k));
return new ReadOnlyDictionary<string, T>(yamlNodes.ToDictionaryWithConflictLog(k => k.Key.ToLowerInvariant(), makeObject, "LoadFromManifest<" + name + ">"));
return yamlNodes.ToDictionaryWithConflictLog(k => k.Key.ToLowerInvariant(), makeObject, "LoadFromManifest<" + name + ">");
}
public static Ruleset LoadDefaults(ModData modData)
@@ -126,14 +120,14 @@ namespace OpenRA
var fs = modData.DefaultFileSystem;
Ruleset ruleset = null;
Action f = () =>
void LoadRuleset()
{
var actors = MergeOrDefault("Manifest,Rules", fs, m.Rules, null, null,
k => new ActorInfo(modData.ObjectCreator, k.Key.ToLowerInvariant(), k.Value),
filterNode: n => n.Key.StartsWith(ActorInfo.AbstractActorPrefix, StringComparison.Ordinal));
var weapons = MergeOrDefault("Manifest,Weapons", fs, m.Weapons, null, null,
k => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value));
k => new WeaponInfo(k.Value));
var voices = MergeOrDefault("Manifest,Voices", fs, m.Voices, null, null,
k => new SoundInfo(k.Value));
@@ -147,15 +141,15 @@ namespace OpenRA
var modelSequences = MergeOrDefault("Manifest,ModelSequences", fs, m.ModelSequences, null, null,
k => k);
// The default ruleset does not include a preferred tileset or sequence set
ruleset = new Ruleset(actors, weapons, voices, notifications, music, null, null, modelSequences);
};
// The default ruleset does not include a preferred tileset
ruleset = new Ruleset(actors, weapons, voices, notifications, music, null, modelSequences);
}
if (modData.IsOnMainThread)
{
modData.HandleLoadingProgress();
var loader = new Task(f);
var loader = new Task(LoadRuleset);
loader.Start();
// Animate the loadscreen while we wait
@@ -163,7 +157,7 @@ namespace OpenRA
modData.HandleLoadingProgress();
}
else
f();
LoadRuleset();
return ruleset;
}
@@ -171,28 +165,27 @@ namespace OpenRA
public static Ruleset LoadDefaultsForTileSet(ModData modData, string tileSet)
{
var dr = modData.DefaultRules;
var ts = modData.DefaultTileSets[tileSet];
var sequences = modData.DefaultSequences[tileSet];
var terrainInfo = modData.DefaultTerrainInfo[tileSet];
return new Ruleset(dr.Actors, dr.Weapons, dr.Voices, dr.Notifications, dr.Music, ts, sequences, dr.ModelSequences);
return new Ruleset(dr.Actors, dr.Weapons, dr.Voices, dr.Notifications, dr.Music, terrainInfo, dr.ModelSequences);
}
public static Ruleset Load(ModData modData, IReadOnlyFileSystem fileSystem, string tileSet,
MiniYaml mapRules, MiniYaml mapWeapons, MiniYaml mapVoices, MiniYaml mapNotifications,
MiniYaml mapMusic, MiniYaml mapSequences, MiniYaml mapModelSequences)
MiniYaml mapMusic, MiniYaml mapModelSequences)
{
var m = modData.Manifest;
var dr = modData.DefaultRules;
Ruleset ruleset = null;
Action f = () =>
void LoadRuleset()
{
var actors = MergeOrDefault("Rules", fileSystem, m.Rules, mapRules, dr.Actors,
k => new ActorInfo(modData.ObjectCreator, k.Key.ToLowerInvariant(), k.Value),
filterNode: n => n.Key.StartsWith(ActorInfo.AbstractActorPrefix, StringComparison.Ordinal));
var weapons = MergeOrDefault("Weapons", fileSystem, m.Weapons, mapWeapons, dr.Weapons,
k => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value));
k => new WeaponInfo(k.Value));
var voices = MergeOrDefault("Voices", fileSystem, m.Voices, mapVoices, dr.Voices,
k => new SoundInfo(k.Value));
@@ -203,26 +196,22 @@ namespace OpenRA
var music = MergeOrDefault("Music", fileSystem, m.Music, mapMusic, dr.Music,
k => new MusicInfo(k.Key, k.Value));
// TODO: Add support for merging custom tileset modifications
var ts = modData.DefaultTileSets[tileSet];
// TODO: Top-level dictionary should be moved into the Ruleset instead of in its own object
var sequences = mapSequences == null ? modData.DefaultSequences[tileSet] :
new SequenceProvider(fileSystem, modData, tileSet, mapSequences);
// TODO: Add support for merging custom terrain modifications
var terrainInfo = modData.DefaultTerrainInfo[tileSet];
var modelSequences = dr.ModelSequences;
if (mapModelSequences != null)
modelSequences = MergeOrDefault("ModelSequences", fileSystem, m.ModelSequences, mapModelSequences, dr.ModelSequences,
k => k);
ruleset = new Ruleset(actors, weapons, voices, notifications, music, ts, sequences, modelSequences);
};
ruleset = new Ruleset(actors, weapons, voices, notifications, music, terrainInfo, modelSequences);
}
if (modData.IsOnMainThread)
{
modData.HandleLoadingProgress();
var loader = new Task(f);
var loader = new Task(LoadRuleset);
loader.Start();
// Animate the loadscreen while we wait
@@ -230,14 +219,14 @@ namespace OpenRA
modData.HandleLoadingProgress();
}
else
f();
LoadRuleset();
return ruleset;
}
static bool AnyCustomYaml(MiniYaml yaml)
{
return yaml != null && (yaml.Value != null || yaml.Nodes.Any());
return yaml != null && (yaml.Value != null || yaml.Nodes.Count > 0);
}
static bool AnyFlaggedTraits(ModData modData, List<MiniYamlNode> actors)
@@ -250,12 +239,12 @@ namespace OpenRA
{
var traitName = traitNode.Key.Split('@')[0];
var traitType = modData.ObjectCreator.FindType(traitName + "Info");
if (traitType != null && traitType.GetInterface("ILobbyCustomRulesIgnore") == null)
if (traitType != null && traitType.GetInterface(nameof(ILobbyCustomRulesIgnore)) == null)
return true;
}
catch (Exception ex)
{
Log.Write("debug", "Error in AnyFlaggedTraits\r\n" + ex.ToString());
Log.Write("debug", "Error in AnyFlaggedTraits\n" + ex.ToString());
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -17,14 +17,14 @@ namespace OpenRA.GameRules
{
public class SoundInfo
{
public readonly Dictionary<string, string[]> Variants = new Dictionary<string, string[]>();
public readonly Dictionary<string, string[]> Prefixes = new Dictionary<string, string[]>();
public readonly Dictionary<string, string[]> Voices = new Dictionary<string, string[]>();
public readonly Dictionary<string, string[]> Notifications = new Dictionary<string, string[]>();
public readonly Dictionary<string, string[]> Variants = new();
public readonly Dictionary<string, string[]> Prefixes = new();
public readonly Dictionary<string, string[]> Voices = new();
public readonly Dictionary<string, string[]> Notifications = new();
public readonly string DefaultVariant = ".aud";
public readonly string DefaultPrefix = "";
public readonly HashSet<string> DisableVariants = new HashSet<string>();
public readonly HashSet<string> DisablePrefixes = new HashSet<string>();
public readonly HashSet<string> DisableVariants = new();
public readonly HashSet<string> DisablePrefixes = new();
public readonly Lazy<Dictionary<string, SoundPool>> VoicePools;
public readonly Lazy<Dictionary<string, SoundPool>> NotificationsPools;
@@ -33,23 +33,28 @@ namespace OpenRA.GameRules
{
FieldLoader.Load(this, y);
VoicePools = Exts.Lazy(() => Voices.ToDictionary(a => a.Key, a => new SoundPool(1f, a.Value)));
VoicePools = Exts.Lazy(() => Voices.ToDictionary(a => a.Key, a => new SoundPool(1f, SoundPool.DefaultInterruptType, a.Value)));
NotificationsPools = Exts.Lazy(() => ParseSoundPool(y, "Notifications"));
}
Dictionary<string, SoundPool> ParseSoundPool(MiniYaml y, string key)
static Dictionary<string, SoundPool> ParseSoundPool(MiniYaml y, string key)
{
var ret = new Dictionary<string, SoundPool>();
var classifiction = y.Nodes.First(x => x.Key == key);
foreach (var t in classifiction.Value.Nodes)
{
var volumeModifier = 1f;
var volumeModifierNode = t.Value.Nodes.FirstOrDefault(x => x.Key == "VolumeModifier");
var volumeModifierNode = t.Value.Nodes.FirstOrDefault(x => x.Key == nameof(SoundPool.VolumeModifier));
if (volumeModifierNode != null)
volumeModifier = FieldLoader.GetValue<float>(volumeModifierNode.Key, volumeModifierNode.Value.Value);
var interruptType = SoundPool.DefaultInterruptType;
var interruptTypeNode = t.Value.Nodes.FirstOrDefault(x => x.Key == nameof(SoundPool.InterruptType));
if (interruptTypeNode != null)
interruptType = FieldLoader.GetValue<SoundPool.InterruptType>(interruptTypeNode.Key, interruptTypeNode.Value.Value);
var names = FieldLoader.GetValue<string[]>(t.Key, t.Value.Value);
var sp = new SoundPool(volumeModifier, names);
var sp = new SoundPool(volumeModifier, interruptType, names);
ret.Add(t.Key, sp);
}
@@ -59,13 +64,17 @@ namespace OpenRA.GameRules
public class SoundPool
{
public enum InterruptType { DoNotPlay, Interrupt, Overlap }
public const InterruptType DefaultInterruptType = InterruptType.DoNotPlay;
public readonly float VolumeModifier;
public readonly InterruptType Type;
readonly string[] clips;
readonly List<string> liveclips = new List<string>();
readonly List<string> liveclips = new();
public SoundPool(float volumeModifier, params string[] clips)
public SoundPool(float volumeModifier, InterruptType interruptType, params string[] clips)
{
VolumeModifier = volumeModifier;
Type = interruptType;
this.clips = clips;
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -36,7 +36,7 @@ namespace OpenRA.GameRules
public class WarheadArgs
{
public WeaponInfo Weapon;
public int[] DamageModifiers = { };
public int[] DamageModifiers = Array.Empty<int>();
public WPos? Source;
public WRot ImpactOrientation;
public WPos ImpactPosition;
@@ -99,17 +99,20 @@ namespace OpenRA.GameRules
[Desc("Number of shots in a single ammo magazine.")]
public readonly int Burst = 1;
[Desc("Can this weapon target the attacker itself?")]
public readonly bool CanTargetSelf = false;
[Desc("What types of targets are affected.")]
public readonly BitSet<TargetableType> ValidTargets = new BitSet<TargetableType>("Ground", "Water");
public readonly BitSet<TargetableType> ValidTargets = new("Ground", "Water");
[Desc("What types of targets are unaffected.", "Overrules ValidTargets.")]
public readonly BitSet<TargetableType> InvalidTargets;
static readonly BitSet<TargetableType> TargetTypeAir = new BitSet<TargetableType>("Air");
static readonly BitSet<TargetableType> TargetTypeAir = new("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);
public readonly WDist AirThreshold = new(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.")]
@@ -121,13 +124,18 @@ namespace OpenRA.GameRules
[Desc("Does this weapon aim at the target's center regardless of other targetable offsets?")]
public readonly bool TargetActorCenter = false;
[FieldLoader.LoadUsing("LoadProjectile")]
[FieldLoader.LoadUsing(nameof(LoadProjectile))]
public readonly IProjectileInfo Projectile;
[FieldLoader.LoadUsing("LoadWarheads")]
public readonly List<IWarhead> Warheads = new List<IWarhead>();
[FieldLoader.LoadUsing(nameof(LoadWarheads))]
public readonly List<IWarhead> Warheads = new();
public WeaponInfo(string name, MiniYaml content)
/// <summary>
/// This constructor is used solely for documentation generation.
/// </summary>
public WeaponInfo() { }
public WeaponInfo(MiniYaml content)
{
// Resolve any weapon-level yaml inheritance or removals
// HACK: The "Defaults" sequence syntax prevents us from doing this generally during yaml parsing
@@ -139,7 +147,11 @@ namespace OpenRA.GameRules
{
if (!yaml.ToDictionary().TryGetValue("Projectile", out var proj))
return null;
var ret = Game.CreateObject<IProjectileInfo>(proj.Value + "Info");
if (ret == null)
return null;
FieldLoader.Load(ret, proj);
return ret;
}
@@ -150,6 +162,9 @@ namespace OpenRA.GameRules
foreach (var node in yaml.Nodes.Where(n => n.Key.StartsWith("Warhead")))
{
var ret = Game.CreateObject<IWarhead>(node.Value.Value + "Warhead");
if (ret == null)
continue;
FieldLoader.Load(ret, node.Value);
retList.Add(ret);
}
@@ -194,29 +209,24 @@ namespace OpenRA.GameRules
/// <summary>Checks if the weapon is valid against (can target) the actor.</summary>
public bool IsValidAgainst(Actor victim, Actor firedBy)
{
var targetTypes = victim.GetEnabledTargetTypes();
if (!IsValidTarget(targetTypes))
if (!CanTargetSelf && victim == firedBy)
return false;
// PERF: Avoid LINQ.
foreach (var warhead in Warheads)
if (warhead.IsValidAgainst(victim, firedBy))
return true;
var targetTypes = victim.GetEnabledTargetTypes();
return false;
return IsValidTarget(targetTypes);
}
/// <summary>Checks if the weapon is valid against (can target) the frozen actor.</summary>
public bool IsValidAgainst(FrozenActor victim, Actor firedBy)
{
if (!IsValidTarget(victim.TargetTypes))
if (!victim.IsValid)
return false;
if (!Warheads.Any(w => w.IsValidAgainst(victim, firedBy)))
if (!CanTargetSelf && victim.Actor == firedBy)
return false;
return true;
return IsValidTarget(victim.TargetTypes);
}
/// <summary>Applies all the weapon's warheads to the target.</summary>

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -10,26 +10,50 @@
#endregion
using System.Collections.Generic;
using System.Linq;
namespace OpenRA
{
public class GameSpeed
{
public readonly string Name = "Default";
public readonly int Timestep = 40;
public readonly int OrderLatency = 3;
[TranslationReference]
[FieldLoader.Require]
public readonly string Name;
[FieldLoader.Require]
public readonly int Timestep;
[FieldLoader.Require]
public readonly int OrderLatency;
}
public class GameSpeeds : IGlobalModData
{
[FieldLoader.LoadUsing("LoadSpeeds")]
[FieldLoader.Require]
public readonly string DefaultSpeed;
[FieldLoader.LoadUsing(nameof(LoadSpeeds))]
public readonly Dictionary<string, GameSpeed> Speeds;
static object LoadSpeeds(MiniYaml y)
{
var ret = new Dictionary<string, GameSpeed>();
foreach (var node in y.Nodes)
ret.Add(node.Key, FieldLoader.Load<GameSpeed>(node.Value));
var speedsNode = y.Nodes.FirstOrDefault(n => n.Key == "Speeds");
if (speedsNode == null)
throw new YamlException("Error parsing GameSpeeds: Missing Speeds node!");
foreach (var node in speedsNode.Value.Nodes)
{
try
{
ret.Add(node.Key, FieldLoader.Load<GameSpeed>(node.Value));
}
catch (FieldLoader.MissingFieldsException e)
{
var label = e.Missing.Length > 1 ? "Required properties missing" : "Required property missing";
throw new YamlException($"Error parsing GameSpeed {node.Key}: {label}: {e.Missing.JoinWith(", ")}");
}
}
return ret;
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -22,7 +22,7 @@ namespace OpenRA.Graphics
public string Name { get; private set; }
public bool IsDecoration { get; set; }
readonly SequenceProvider sequenceProvider;
readonly SequenceSet sequences;
readonly Func<WAngle> facingFunc;
readonly Func<bool> paused;
@@ -30,7 +30,7 @@ namespace OpenRA.Graphics
bool backwards;
bool tickAlways;
int timeUntilNextFrame;
Action tickFunc = () => { };
Action tickFunc;
public Animation(World world, string name)
: this(world, name, () => WAngle.Zero) { }
@@ -43,48 +43,57 @@ namespace OpenRA.Graphics
public Animation(World world, string name, Func<WAngle> facingFunc, Func<bool> paused)
{
sequenceProvider = world.Map.Rules.Sequences;
sequences = world.Map.Sequences;
Name = name.ToLowerInvariant();
this.facingFunc = facingFunc;
this.paused = paused;
}
public int CurrentFrame { get { return backwards ? CurrentSequence.Length - frame - 1 : frame; } }
public Sprite Image { get { return CurrentSequence.GetSprite(CurrentFrame, facingFunc()); } }
public int CurrentFrame => backwards ? CurrentSequence.Length - frame - 1 : frame;
public IRenderable[] Render(WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale)
public Sprite Image => CurrentSequence.GetSprite(CurrentFrame, facingFunc());
public IRenderable[] Render(WPos pos, in WVec offset, int zOffset, PaletteReference palette)
{
var imageRenderable = new SpriteRenderable(Image, pos, offset, CurrentSequence.ZOffset + zOffset, palette, scale, IsDecoration, CurrentSequence.IgnoreWorldTint);
var tintModifiers = CurrentSequence.IgnoreWorldTint ? TintModifiers.IgnoreWorldTint : TintModifiers.None;
var alpha = CurrentSequence.GetAlpha(CurrentFrame);
var (image, rotation) = CurrentSequence.GetSpriteWithRotation(CurrentFrame, facingFunc());
var imageRenderable = new SpriteRenderable(image, pos, offset, CurrentSequence.ZOffset + zOffset, palette, CurrentSequence.Scale, alpha, float3.Ones, tintModifiers, IsDecoration,
rotation);
if (CurrentSequence.ShadowStart >= 0)
var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc());
if (shadow != null)
{
var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc());
var shadowRenderable = new SpriteRenderable(shadow, pos, offset, CurrentSequence.ShadowZOffset + zOffset, palette, scale, true, CurrentSequence.IgnoreWorldTint);
var shadowRenderable = new SpriteRenderable(shadow, pos, offset, CurrentSequence.ShadowZOffset + zOffset, palette, CurrentSequence.Scale, 1f, float3.Ones, tintModifiers,
true, rotation);
return new IRenderable[] { shadowRenderable, imageRenderable };
}
return new IRenderable[] { imageRenderable };
}
public IRenderable[] RenderUI(WorldRenderer wr, int2 pos, WVec offset, int zOffset, PaletteReference palette, float scale)
public IRenderable[] RenderUI(WorldRenderer wr, int2 pos, in WVec offset, int zOffset, PaletteReference palette, float scale = 1f, float rotation = 0f)
{
scale *= CurrentSequence.Scale;
var screenOffset = (scale * wr.ScreenVectorComponents(offset)).XY.ToInt2();
var imagePos = pos + screenOffset - new int2((int)(scale * Image.Size.X / 2), (int)(scale * Image.Size.Y / 2));
var imageRenderable = new UISpriteRenderable(Image, WPos.Zero + offset, imagePos, CurrentSequence.ZOffset + zOffset, palette, scale);
var alpha = CurrentSequence.GetAlpha(CurrentFrame);
var imageRenderable = new UISpriteRenderable(Image, WPos.Zero + offset, imagePos, CurrentSequence.ZOffset + zOffset, palette, scale, alpha, rotation);
if (CurrentSequence.ShadowStart >= 0)
var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc());
if (shadow != null)
{
var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc());
var shadowPos = pos - new int2((int)(scale * shadow.Size.X / 2), (int)(scale * shadow.Size.Y / 2));
var shadowRenderable = new UISpriteRenderable(shadow, WPos.Zero + offset, shadowPos, CurrentSequence.ShadowZOffset + zOffset, palette, scale);
var shadowRenderable = new UISpriteRenderable(shadow, WPos.Zero + offset, shadowPos, CurrentSequence.ShadowZOffset + zOffset, palette, scale, 1f, rotation);
return new IRenderable[] { shadowRenderable, imageRenderable };
}
return new IRenderable[] { imageRenderable };
}
public Rectangle ScreenBounds(WorldRenderer wr, WPos pos, WVec offset, float scale)
public Rectangle ScreenBounds(WorldRenderer wr, WPos pos, in WVec offset)
{
var scale = CurrentSequence.Scale;
var xy = wr.ScreenPxPosition(pos) + wr.ScreenPxOffset(offset);
var cb = CurrentSequence.Bounds;
return Rectangle.FromLTRB(
@@ -96,7 +105,7 @@ namespace OpenRA.Graphics
public IRenderable[] Render(WPos pos, PaletteReference palette)
{
return Render(pos, WVec.Zero, 0, palette, 1f);
return Render(pos, WVec.Zero, 0, palette);
}
public void Play(string sequenceName)
@@ -107,7 +116,7 @@ namespace OpenRA.Graphics
int CurrentSequenceTickOrDefault()
{
const int DefaultTick = 40; // 25 fps == 40 ms
return CurrentSequence != null ? CurrentSequence.Tick : DefaultTick;
return CurrentSequence?.Tick ?? DefaultTick;
}
void PlaySequence(string sequenceName)
@@ -155,7 +164,7 @@ namespace OpenRA.Graphics
if (frame >= CurrentSequence.Length)
{
frame = CurrentSequence.Length - 1;
tickFunc = () => { };
tickFunc = null;
after?.Invoke();
}
};
@@ -203,13 +212,13 @@ namespace OpenRA.Graphics
public void Tick(int t)
{
if (tickAlways)
tickFunc();
tickFunc?.Invoke();
else
{
timeUntilNextFrame -= t;
while (timeUntilNextFrame <= 0)
{
tickFunc();
tickFunc?.Invoke();
timeUntilNextFrame += CurrentSequenceTickOrDefault();
}
}
@@ -227,11 +236,11 @@ namespace OpenRA.Graphics
}
}
public bool HasSequence(string seq) { return sequenceProvider.HasSequence(Name, seq); }
public bool HasSequence(string seq) { return sequences.HasSequence(Name, seq); }
public ISpriteSequence GetSequence(string sequenceName)
{
return sequenceProvider.GetSequence(Name, sequenceName);
return sequences.GetSequence(Name, sequenceName);
}
public string GetRandomExistingSequence(string[] sequences, MersenneTwister random)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -35,21 +35,21 @@ namespace OpenRA.Graphics
ZOffset = zOffset;
}
public IRenderable[] Render(Actor self, WorldRenderer wr, PaletteReference pal, float scale)
public IRenderable[] Render(Actor self, PaletteReference pal)
{
var center = self.CenterPosition;
var offset = OffsetFunc != null ? OffsetFunc() : WVec.Zero;
var offset = OffsetFunc?.Invoke() ?? WVec.Zero;
var z = (ZOffset != null) ? ZOffset(center + offset) : 0;
return Animation.Render(center, offset, z, pal, scale);
var z = ZOffset?.Invoke(center + offset) ?? 0;
return Animation.Render(center, offset, z, pal);
}
public Rectangle ScreenBounds(Actor self, WorldRenderer wr, float scale)
public Rectangle ScreenBounds(Actor self, WorldRenderer wr)
{
var center = self.CenterPosition;
var offset = OffsetFunc != null ? OffsetFunc() : WVec.Zero;
var offset = OffsetFunc?.Invoke() ?? WVec.Zero;
return Animation.ScreenBounds(wr, center, offset, scale);
return Animation.ScreenBounds(wr, center, offset);
}
public static implicit operator AnimationWithOffset(Animation a)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -49,10 +49,10 @@ namespace OpenRA.Graphics
public readonly int[] PanelRegion = null;
public readonly PanelSides PanelSides = PanelSides.All;
public readonly Dictionary<string, Rectangle> Regions = new Dictionary<string, Rectangle>();
public readonly Dictionary<string, Rectangle> Regions = new();
}
public static IReadOnlyDictionary<string, Collection> Collections { get; private set; }
public static IReadOnlyDictionary<string, Collection> Collections => collections;
static Dictionary<string, Collection> collections;
static Dictionary<string, (Sheet Sheet, int Density)> cachedSheets;
static Dictionary<string, Dictionary<string, Sprite>> cachedSprites;
@@ -77,8 +77,6 @@ namespace OpenRA.Graphics
cachedPanelSprites = new Dictionary<string, Sprite[]>();
cachedCollectionSheets = new Dictionary<Collection, (Sheet, int)>();
Collections = new ReadOnlyDictionary<string, Collection>(collections);
var chrome = MiniYaml.Merge(modData.Manifest.Chrome
.Select(s => MiniYaml.FromStream(fileSystem.Open(s), s)));
@@ -102,9 +100,7 @@ namespace OpenRA.Graphics
static void LoadCollection(string name, MiniYaml yaml)
{
if (Game.ModData.LoadScreen != null)
Game.ModData.LoadScreen.Display();
Game.ModData.LoadScreen?.Display();
collections.Add(name, FieldLoader.Load<Collection>(yaml));
}
@@ -146,6 +142,15 @@ namespace OpenRA.Graphics
}
public static Sprite GetImage(string collectionName, string imageName)
{
var image = TryGetImage(collectionName, imageName);
if (image == null)
throw new ArgumentException($"Sprite `{collectionName}/{imageName}` was not found.");
return image;
}
public static Sprite TryGetImage(string collectionName, string imageName)
{
if (string.IsNullOrEmpty(collectionName))
return null;
@@ -155,10 +160,7 @@ namespace OpenRA.Graphics
return sprite;
if (!collections.TryGetValue(collectionName, out var collection))
{
Log.Write("debug", "Could not find collection '{0}'", collectionName);
return null;
}
if (!collection.Regions.TryGetValue(imageName, out var mi))
return null;
@@ -178,6 +180,15 @@ namespace OpenRA.Graphics
}
public static Sprite[] GetPanelImages(string collectionName)
{
var panel = TryGetPanelImages(collectionName);
if (panel == null)
throw new ArgumentException($"Panel `{collectionName}` was not found.");
return panel;
}
public static Sprite[] TryGetPanelImages(string collectionName)
{
if (string.IsNullOrEmpty(collectionName))
return null;
@@ -187,17 +198,14 @@ namespace OpenRA.Graphics
return cachedSprites;
if (!collections.TryGetValue(collectionName, out var collection))
{
Log.Write("debug", "Could not find collection '{0}'", collectionName);
return null;
}
Sprite[] sprites;
if (collection.PanelRegion != null)
{
if (collection.PanelRegion.Length != 8)
{
Log.Write("debug", "Collection '{0}' does not define a valid PanelRegion", collectionName);
Log.Write("debug", $"Collection '{collectionName}' does not define a valid PanelRegion");
return null;
}
@@ -224,18 +232,23 @@ namespace OpenRA.Graphics
}
else
{
// PERF: We don't need to search for images if there are no definitions.
// PERF: It's more efficient to send an empty array rather than an array of 9 nulls.
if (!collection.Regions.Any())
return Array.Empty<Sprite>();
// Support manual definitions for unusual dialog layouts
sprites = new[]
{
GetImage(collectionName, "corner-tl"),
GetImage(collectionName, "border-t"),
GetImage(collectionName, "corner-tr"),
GetImage(collectionName, "border-l"),
GetImage(collectionName, "background"),
GetImage(collectionName, "border-r"),
GetImage(collectionName, "corner-bl"),
GetImage(collectionName, "border-b"),
GetImage(collectionName, "corner-br")
TryGetImage(collectionName, "corner-tl"),
TryGetImage(collectionName, "border-t"),
TryGetImage(collectionName, "corner-tr"),
TryGetImage(collectionName, "border-l"),
TryGetImage(collectionName, "background"),
TryGetImage(collectionName, "border-r"),
TryGetImage(collectionName, "corner-bl"),
TryGetImage(collectionName, "border-b"),
TryGetImage(collectionName, "corner-br")
};
}
@@ -250,13 +263,13 @@ namespace OpenRA.Graphics
if (!collections.TryGetValue(collectionName, out var collection))
{
Log.Write("debug", "Could not find collection '{0}'", collectionName);
Log.Write("debug", $"Could not find collection '{collectionName}'");
return new Size(0, 0);
}
if (collection.PanelRegion == null || collection.PanelRegion.Length != 8)
{
Log.Write("debug", "Collection '{0}' does not define a valid PanelRegion", collectionName);
Log.Write("debug", $"Collection '{collectionName}' does not define a valid PanelRegion");
return new Size(0, 0);
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -17,7 +17,7 @@ namespace OpenRA.Graphics
{
public sealed class CursorManager
{
class Cursor
sealed class Cursor
{
public string Name;
public int2 PaddedSize;
@@ -28,14 +28,14 @@ namespace OpenRA.Graphics
public IHardwareCursor[] Cursors;
}
readonly Dictionary<string, Cursor> cursors = new Dictionary<string, Cursor>();
readonly Dictionary<string, Cursor> cursors = new();
readonly SheetBuilder sheetBuilder;
readonly GraphicSettings graphicSettings;
Cursor cursor;
bool isLocked = false;
int2 lockedPosition;
bool hardwareCursorsDisabled = false;
readonly bool hardwareCursorsDisabled = false;
bool hardwareCursorsDoubled = false;
public CursorManager(CursorProvider cursorProvider)
@@ -106,34 +106,27 @@ namespace OpenRA.Graphics
// Dispose any existing cursors to avoid leaking native resources
ClearHardwareCursors();
try
foreach (var kv in cursors)
{
foreach (var kv in cursors)
var template = kv.Value;
for (var i = 0; i < template.Sprites.Length; i++)
{
var template = kv.Value;
for (var i = 0; i < template.Sprites.Length; i++)
template.Cursors[i]?.Dispose();
// Calculate the padding to position the frame within sequenceBounds
var paddingTL = -(template.Bounds.Location - template.Sprites[i].Offset.XY.ToInt2());
var paddingBR = template.PaddedSize - new int2(template.Sprites[i].Bounds.Size) - paddingTL;
var hardwareCursor = CreateHardwareCursor(kv.Key, template.Sprites[i], paddingTL, paddingBR, -template.Bounds.Location);
if (hardwareCursor != null)
template.Cursors[i] = hardwareCursor;
else
{
if (template.Cursors[i] != null)
template.Cursors[i].Dispose();
// Calculate the padding to position the frame within sequenceBounds
var paddingTL = -(template.Bounds.Location - template.Sprites[i].Offset.XY.ToInt2());
var paddingBR = template.PaddedSize - new int2(template.Sprites[i].Bounds.Size) - paddingTL;
template.Cursors[i] = CreateHardwareCursor(kv.Key, template.Sprites[i], paddingTL, paddingBR, -template.Bounds.Location);
Log.Write("debug", $"Failed to initialize hardware cursor for {template.Name}.");
Console.WriteLine($"Failed to initialize hardware cursor for {template.Name}.");
}
}
}
catch (Exception e)
{
Log.Write("debug", "Failed to initialize hardware cursors. Falling back to software cursors.");
Log.Write("debug", "Error was: " + e.Message);
Console.WriteLine("Failed to initialize hardware cursors. Falling back to software cursors.");
Console.WriteLine("Error was: " + e.Message);
ClearHardwareCursors();
}
hardwareCursorsDoubled = graphicSettings.CursorDouble;
}
@@ -177,10 +170,11 @@ namespace OpenRA.Graphics
if (cursor != null && frame >= cursor.Cursors.Length)
frame %= cursor.Cursors.Length;
if (cursor == null || isLocked)
var hardwareCursor = cursor?.Cursors[frame];
if (hardwareCursor == null || isLocked)
Game.Renderer.Window.SetHardwareCursor(null);
else
Game.Renderer.Window.SetHardwareCursor(cursor.Cursors[frame]);
Game.Renderer.Window.SetHardwareCursor(hardwareCursor);
}
public void Render(Renderer renderer)
@@ -196,17 +190,17 @@ namespace OpenRA.Graphics
// Render cursor in software
var doubleCursor = graphicSettings.CursorDouble;
var cursorSprite = cursor.Sprites[frame % cursor.Length];
var cursorSize = doubleCursor ? 2.0f * cursorSprite.Size : cursorSprite.Size;
var cursorScale = doubleCursor ? 2 : 1;
// Cursor is rendered in native window coordinates
// Apply same scaling rules as hardware cursors
if (Game.Renderer.NativeWindowScale > 1.5f)
cursorSize = 2 * cursorSize;
cursorScale *= 2;
var mousePos = isLocked ? lockedPosition : Viewport.LastMousePos;
renderer.RgbaSpriteRenderer.DrawSprite(cursorSprite,
mousePos,
cursorSize / Game.Renderer.WindowScale);
cursorScale / Game.Renderer.WindowScale);
}
public void Lock()
@@ -231,7 +225,7 @@ namespace OpenRA.Graphics
// All palettes must be explicitly referenced, even if they are embedded in the sprite.
if (palette == null)
throw new InvalidOperationException("Cursor sequence `{0}` attempted to load an indexed sprite but does not define Palette".F(name));
throw new InvalidOperationException($"Cursor sequence `{name}` attempted to load an indexed sprite but does not define Palette");
var width = frame.Size.Width;
var height = frame.Size.Height;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -31,15 +31,14 @@ namespace OpenRA.Graphics
// Overwrite previous definitions if there are duplicates
var pals = new Dictionary<string, IProvidesCursorPaletteInfo>();
foreach (var p in modData.DefaultRules.Actors["world"].TraitInfos<IProvidesCursorPaletteInfo>())
foreach (var p in modData.DefaultRules.Actors[SystemActors.World].TraitInfos<IProvidesCursorPaletteInfo>())
if (p.Palette != null)
pals[p.Palette] = p;
Palettes = nodesDict["Cursors"].Nodes.Select(n => n.Value.Value)
.Where(p => p != null)
.Distinct()
.ToDictionary(p => p, p => pals[p].ReadPalette(modData.DefaultFileSystem))
.AsReadOnly();
.ToDictionary(p => p, p => pals[p].ReadPalette(modData.DefaultFileSystem));
var frameCache = new FrameCache(fileSystem, modData.SpriteLoaders);
var cursors = new Dictionary<string, CursorSequence>();
@@ -47,7 +46,7 @@ namespace OpenRA.Graphics
foreach (var sequence in s.Value.Nodes)
cursors.Add(sequence.Key, new CursorSequence(frameCache, sequence.Key, s.Key, s.Value.Value, sequence.Value));
Cursors = cursors.AsReadOnly();
Cursors = cursors;
}
public bool HasCursorSequence(string cursor)
@@ -60,7 +59,7 @@ namespace OpenRA.Graphics
try { return Cursors[cursor]; }
catch (KeyNotFoundException)
{
throw new InvalidOperationException("Cursor does not have a sequence `{0}`".F(cursor));
throw new InvalidOperationException($"Cursor does not have a sequence `{cursor}`");
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -31,28 +31,36 @@ namespace OpenRA.Graphics
Palette = palette;
Name = name;
Frames = cache[cursorSrc].Skip(Start).ToArray();
var cursorSprites = cache[cursorSrc];
Frames = cursorSprites.Skip(Start).ToArray();
if ((d.ContainsKey("Length") && d["Length"].Value == "*") || (d.ContainsKey("End") && d["End"].Value == "*"))
if ((d.TryGetValue("Length", out var yaml) && yaml.Value == "*") ||
(d.TryGetValue("End", out yaml) && yaml.Value == "*"))
Length = Frames.Length;
else if (d.ContainsKey("Length"))
Length = Exts.ParseIntegerInvariant(d["Length"].Value);
else if (d.ContainsKey("End"))
Length = Exts.ParseIntegerInvariant(d["End"].Value) - Start;
else if (d.TryGetValue("Length", out yaml))
Length = Exts.ParseIntegerInvariant(yaml.Value);
else if (d.TryGetValue("End", out yaml))
Length = Exts.ParseIntegerInvariant(yaml.Value) - Start;
else
Length = 1;
Frames = Frames.Take(Length).ToArray();
if (d.ContainsKey("X"))
if (Start > cursorSprites.Length)
throw new YamlException($"Cursor {name}: {nameof(Start)} is greater than the length of the sprite sequence.");
if (Length > cursorSprites.Length)
throw new YamlException($"Cursor {name}: {nameof(Length)} is greater than the length of the sprite sequence.");
if (d.TryGetValue("X", out yaml))
{
Exts.TryParseIntegerInvariant(d["X"].Value, out var x);
Exts.TryParseIntegerInvariant(yaml.Value, out var x);
Hotspot = Hotspot.WithX(x);
}
if (d.ContainsKey("Y"))
if (d.TryGetValue("Y", out yaml))
{
Exts.TryParseIntegerInvariant(d["Y"].Value, out var y);
Exts.TryParseIntegerInvariant(yaml.Value, out var y);
Hotspot = Hotspot.WithY(y);
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -17,73 +17,98 @@ namespace OpenRA.Graphics
{
public sealed class HardwarePalette : IDisposable
{
public ITexture Texture { get; private set; }
public ITexture Texture { get; }
public ITexture ColorShifts { get; }
public int Height { get; private set; }
readonly Dictionary<string, ImmutablePalette> palettes = new Dictionary<string, ImmutablePalette>();
readonly Dictionary<string, MutablePalette> modifiablePalettes = new Dictionary<string, MutablePalette>();
readonly IReadOnlyDictionary<string, MutablePalette> readOnlyModifiablePalettes;
readonly Dictionary<string, int> indices = new Dictionary<string, int>();
byte[] buffer = new byte[0];
readonly Dictionary<string, ImmutablePalette> palettes = new();
readonly Dictionary<string, MutablePalette> mutablePalettes = new();
readonly Dictionary<string, int> indices = new();
byte[] buffer = Array.Empty<byte>();
float[] colorShiftBuffer = Array.Empty<float>();
public HardwarePalette()
{
Texture = Game.Renderer.Context.CreateTexture();
readOnlyModifiablePalettes = modifiablePalettes.AsReadOnly();
ColorShifts = Game.Renderer.Context.CreateTexture();
}
public bool Contains(string name)
{
return modifiablePalettes.ContainsKey(name) || palettes.ContainsKey(name);
return mutablePalettes.ContainsKey(name) || palettes.ContainsKey(name);
}
public IPalette GetPalette(string name)
{
if (modifiablePalettes.TryGetValue(name, out var mutable))
if (mutablePalettes.TryGetValue(name, out var mutable))
return mutable.AsReadOnly();
if (palettes.TryGetValue(name, out var immutable))
return immutable;
throw new InvalidOperationException("Palette `{0}` does not exist".F(name));
throw new InvalidOperationException($"Palette `{name}` does not exist");
}
public int GetPaletteIndex(string name)
{
if (!indices.TryGetValue(name, out var ret))
throw new InvalidOperationException("Palette `{0}` does not exist".F(name));
throw new InvalidOperationException($"Palette `{name}` does not exist");
return ret;
}
public void AddPalette(string name, ImmutablePalette p, bool allowModifiers)
{
if (palettes.ContainsKey(name))
throw new InvalidOperationException("Palette {0} has already been defined".F(name));
throw new InvalidOperationException($"Palette {name} has already been defined");
int index = palettes.Count;
// PERF: the first row in the palette textures is reserved as a placeholder for non-indexed sprites
// that do not have a color-shift applied. This provides a quick shortcut to avoid querying the
// color-shift texture for every pixel only to find that most are not shifted.
var index = palettes.Count + 1;
indices.Add(name, index);
palettes.Add(name, p);
if (palettes.Count > Height)
if (index >= Height)
{
Height = Exts.NextPowerOf2(palettes.Count);
Height = Exts.NextPowerOf2(index + 1);
Array.Resize(ref buffer, Height * Palette.Size * 4);
Array.Resize(ref colorShiftBuffer, Height * 8);
}
if (allowModifiers)
modifiablePalettes.Add(name, new MutablePalette(p));
mutablePalettes.Add(name, new MutablePalette(p));
else
CopyPaletteToBuffer(index, p);
}
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Performance", "CA1854:Prefer the 'IDictionary.TryGetValue(TKey, out TValue)' method",
Justification = "False positive - indexer is a set not a get.")]
public void ReplacePalette(string name, IPalette p)
{
if (modifiablePalettes.ContainsKey(name))
CopyPaletteToBuffer(indices[name], modifiablePalettes[name] = new MutablePalette(p));
if (mutablePalettes.ContainsKey(name))
CopyPaletteToBuffer(indices[name], mutablePalettes[name] = new MutablePalette(p));
else if (palettes.ContainsKey(name))
CopyPaletteToBuffer(indices[name], palettes[name] = new ImmutablePalette(p));
else
throw new InvalidOperationException("Palette `{0}` does not exist".F(name));
throw new InvalidOperationException($"Palette `{name}` does not exist");
CopyBufferToTexture();
}
public void SetColorShift(string name, float hueOffset, float satOffset, float valueMultiplier, float minHue, float maxHue)
{
var index = GetPaletteIndex(name);
colorShiftBuffer[8 * index + 0] = minHue;
colorShiftBuffer[8 * index + 1] = maxHue;
colorShiftBuffer[8 * index + 4] = hueOffset;
colorShiftBuffer[8 * index + 5] = satOffset;
colorShiftBuffer[8 * index + 6] = valueMultiplier;
}
public bool HasColorShift(string name)
{
var index = GetPaletteIndex(name);
return colorShiftBuffer[8 * index] != 0 || colorShiftBuffer[8 * index + 1] != 0;
}
public void Initialize()
{
CopyModifiablePalettesToBuffer();
@@ -97,26 +122,27 @@ namespace OpenRA.Graphics
void CopyModifiablePalettesToBuffer()
{
foreach (var kvp in modifiablePalettes)
foreach (var kvp in mutablePalettes)
CopyPaletteToBuffer(indices[kvp.Key], kvp.Value);
}
void CopyBufferToTexture()
{
Texture.SetData(buffer, Palette.Size, Height);
ColorShifts.SetFloatData(colorShiftBuffer, 2, Height);
}
public void ApplyModifiers(IEnumerable<IPaletteModifier> paletteMods)
{
foreach (var mod in paletteMods)
mod.AdjustPalette(readOnlyModifiablePalettes);
mod.AdjustPalette(mutablePalettes);
// Update our texture with the changes.
CopyModifiablePalettesToBuffer();
CopyBufferToTexture();
// Reset modified palettes back to their original colors, ready for next time.
foreach (var kvp in modifiablePalettes)
foreach (var kvp in mutablePalettes)
{
var originalPalette = palettes[kvp.Key];
var modifiedPalette = kvp.Value;
@@ -127,6 +153,7 @@ namespace OpenRA.Graphics
public void Dispose()
{
Texture.Dispose();
ColorShifts.Dispose();
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -10,6 +10,7 @@
#endregion
using System;
using System.Collections.Generic;
using OpenRA.FileSystem;
using OpenRA.Primitives;
@@ -25,11 +26,11 @@ namespace OpenRA.Graphics
float[] Bounds(uint frame);
ModelRenderData RenderData(uint section);
/// <summary>Returns the smallest rectangle that covers all rotations of all frames in a model</summary>
/// <summary>Returns the smallest rectangle that covers all rotations of all frames in a model.</summary>
Rectangle AggregateBounds { get; }
}
public struct ModelRenderData
public readonly struct ModelRenderData
{
public readonly int Start;
public readonly int Count;
@@ -61,9 +62,9 @@ namespace OpenRA.Graphics
{
public Action<string> OnMissingModelError { get; set; }
class PlaceholderModelCache : IModelCache
sealed class PlaceholderModelCache : IModelCache
{
public IVertexBuffer<Vertex> VertexBuffer { get { throw new NotImplementedException(); } }
public IVertexBuffer<Vertex> VertexBuffer => throw new NotImplementedException();
public void Dispose() { }

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -14,7 +14,7 @@ using OpenRA.Primitives;
namespace OpenRA.Graphics
{
public struct ModelAnimation
public readonly struct ModelAnimation
{
public readonly IModel Model;
public readonly Func<WVec> OffsetFunc;
@@ -46,12 +46,6 @@ namespace OpenRA.Graphics
xy.Y + (int)(r.Bottom * scale));
}
public bool IsVisible
{
get
{
return DisableFunc == null || !DisableFunc();
}
}
public bool IsVisible => DisableFunc == null || !DisableFunc();
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -37,18 +37,19 @@ namespace OpenRA.Graphics
// Static constants
static readonly float[] ShadowDiffuse = new float[] { 0, 0, 0 };
static readonly float[] ShadowAmbient = new float[] { 1, 1, 1 };
static readonly float2 SpritePadding = new float2(2, 2);
static readonly float2 SpritePadding = new(2, 2);
static readonly float[] ZeroVector = new float[] { 0, 0, 0, 1 };
static readonly float[] ZVector = new float[] { 0, 0, 1, 1 };
static readonly float[] FlipMtx = Util.ScaleMatrix(1, -1, 1);
static readonly float[] ShadowScaleFlipMtx = Util.ScaleMatrix(2, -2, 2);
static readonly float[] GroundNormal = { 0, 0, 1, 1 };
readonly Renderer renderer;
readonly IShader shader;
readonly Dictionary<Sheet, IFrameBuffer> mappedBuffers = new Dictionary<Sheet, IFrameBuffer>();
readonly Stack<KeyValuePair<Sheet, IFrameBuffer>> unmappedBuffers = new Stack<KeyValuePair<Sheet, IFrameBuffer>>();
readonly List<(Sheet Sheet, Action Func)> doRender = new List<(Sheet, Action)>();
readonly Dictionary<Sheet, IFrameBuffer> mappedBuffers = new();
readonly Stack<KeyValuePair<Sheet, IFrameBuffer>> unmappedBuffers = new();
readonly List<(Sheet Sheet, Action Func)> doRender = new();
SheetBuilder sheetBuilderForFrame;
bool isInFrame;
@@ -64,7 +65,7 @@ namespace OpenRA.Graphics
shader.SetTexture("Palette", palette);
}
public void SetViewportParams(Size screen, int2 scroll)
public void SetViewportParams()
{
var a = 2f / renderer.SheetSize;
var view = new[]
@@ -80,7 +81,7 @@ namespace OpenRA.Graphics
public ModelRenderProxy RenderAsync(
WorldRenderer wr, IEnumerable<ModelAnimation> models, in WRot camera, float scale,
float[] groundNormal, in WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
in WRot groundOrientation, in WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
PaletteReference color, PaletteReference normals, PaletteReference shadowPalette)
{
if (!isInFrame)
@@ -92,7 +93,10 @@ namespace OpenRA.Graphics
// Correct for bogus light source definition
var lightYaw = Util.MakeFloatMatrix(new WRot(WAngle.Zero, WAngle.Zero, -lightSource.Yaw).AsMatrix());
var lightPitch = Util.MakeFloatMatrix(new WRot(WAngle.Zero, -lightSource.Pitch, WAngle.Zero).AsMatrix());
var shadowTransform = Util.MatrixMultiply(lightPitch, lightYaw);
var ground = Util.MakeFloatMatrix(groundOrientation.AsMatrix());
var shadowTransform = Util.MatrixMultiply(Util.MatrixMultiply(lightPitch, lightYaw), Util.MatrixInverse(ground));
var groundNormal = Util.MatrixVectorMultiply(ground, GroundNormal);
var invShadowTransform = Util.MatrixInverse(shadowTransform);
var cameraTransform = Util.MakeFloatMatrix(camera.AsMatrix());
@@ -163,8 +167,7 @@ namespace OpenRA.Graphics
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);
sheetBuilderForFrame ??= new SheetBuilder(SheetType.BGRA, AllocateSheet);
var sprite = sheetBuilderForFrame.Allocate(spriteSize, 0, spriteOffset);
var shadowSprite = sheetBuilderForFrame.Allocate(shadowSpriteSize, 0, shadowSpriteOffset);
@@ -205,7 +208,7 @@ namespace OpenRA.Graphics
var t = m.Model.TransformationMatrix(i, frame);
var it = Util.MatrixInverse(t);
if (it == null)
throw new InvalidOperationException("Failed to invert the transformed matrix of frame {0} during RenderAsync.".F(i));
throw new InvalidOperationException($"Failed to invert the transformed matrix of frame {i} during RenderAsync.");
// Transform light vector from shadow -> world -> limb coords
var lightDirection = ExtractRotationVector(Util.MatrixMultiply(it, lightTransform));
@@ -299,7 +302,7 @@ namespace OpenRA.Graphics
return fbo;
}
void DisableFrameBuffer(IFrameBuffer fbo)
static void DisableFrameBuffer(IFrameBuffer fbo)
{
Game.Renderer.Flush();
Game.Renderer.Context.DisableDepthBuffer();

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -40,11 +40,12 @@ namespace OpenRA.Graphics
return new ReadOnlyPalette(palette);
}
class ReadOnlyPalette : IPalette
sealed class ReadOnlyPalette : IPalette
{
IPalette palette;
readonly IPalette palette;
public ReadOnlyPalette(IPalette palette) { this.palette = palette; }
public uint this[int index] { get { return palette[index]; } }
public uint this[int index] => palette[index];
public void CopyToArray(Array destination, int destinationOffset)
{
palette.CopyToArray(destination, destinationOffset);
@@ -56,28 +57,25 @@ namespace OpenRA.Graphics
{
readonly uint[] colors = new uint[Palette.Size];
public uint this[int index]
{
get { return colors[index]; }
}
public uint this[int index] => colors[index];
public void CopyToArray(Array destination, int destinationOffset)
{
Buffer.BlockCopy(colors, 0, destination, destinationOffset * 4, Palette.Size * 4);
}
public ImmutablePalette(string filename, int[] remap)
public ImmutablePalette(string filename, int[] remapTransparent, int[] remap)
{
using (var s = File.OpenRead(filename))
LoadFromStream(s, remap);
LoadFromStream(s, remapTransparent, remap);
}
public ImmutablePalette(Stream s, int[] remapShadow)
public ImmutablePalette(Stream s, int[] remapTransparent, int[] remapShadow)
{
LoadFromStream(s, remapShadow);
LoadFromStream(s, remapTransparent, remapShadow);
}
void LoadFromStream(Stream s, int[] remapShadow)
void LoadFromStream(Stream s, int[] remapTransparent, int[] remapShadow)
{
using (var reader = new BinaryReader(s))
for (var i = 0; i < Palette.Size; i++)
@@ -94,7 +92,9 @@ namespace OpenRA.Graphics
colors[i] = (uint)((255 << 24) | (r << 16) | (g << 8) | b);
}
colors[0] = 0; // Convert black background to transparency.
foreach (var i in remapTransparent)
colors[i] = 0;
foreach (var i in remapShadow)
colors[i] = 140u << 24;
}
@@ -108,7 +108,7 @@ namespace OpenRA.Graphics
public ImmutablePalette(IPalette p)
{
for (int i = 0; i < Palette.Size; i++)
for (var i = 0; i < Palette.Size; i++)
colors[i] = p[i];
}
@@ -126,8 +126,8 @@ namespace OpenRA.Graphics
public uint this[int index]
{
get { return colors[index]; }
set { colors[index] = value; }
get => colors[index];
set => colors[index] = value;
}
public void CopyToArray(Array destination, int destinationOffset)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -18,8 +18,8 @@ namespace OpenRA.Graphics
public readonly string Name;
public IPalette Palette { get; internal set; }
public float TextureIndex { get { return index / hardwarePalette.Height; } }
public float TextureMidIndex { get { return (index + 0.5f) / hardwarePalette.Height; } }
public float TextureIndex => index / hardwarePalette.Height;
public float TextureMidIndex => (index + 0.5f) / hardwarePalette.Height;
public PaletteReference(string name, int index, IPalette palette, HardwarePalette hardwarePalette)
{
@@ -28,5 +28,7 @@ namespace OpenRA.Graphics
this.index = index;
this.hardwarePalette = hardwarePalette;
}
public bool HasColorShift => hardwarePalette.HasColorShift(Name);
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -59,6 +59,7 @@ namespace OpenRA
int DisplayCount { get; }
int CurrentDisplay { get; }
bool HasInputFocus { get; }
bool IsSuspended { get; }
event Action<float, float, float, float> OnWindowScaleChanged;
@@ -71,6 +72,7 @@ namespace OpenRA
IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot, bool pixelDouble);
void SetHardwareCursor(IHardwareCursor cursor);
void SetWindowTitle(string title);
void SetRelativeMouseMode(bool mode);
void SetScaleModifier(float scale);
@@ -82,6 +84,7 @@ namespace OpenRA
public interface IGraphicsContext : IDisposable
{
IVertexBuffer<Vertex> CreateVertexBuffer(int size);
Vertex[] CreateVertices(int size);
ITexture CreateTexture();
IFrameBuffer CreateFrameBuffer(Size s);
IFrameBuffer CreateFrameBuffer(Size s, Color clearColor);
@@ -103,6 +106,11 @@ namespace OpenRA
{
void Bind();
void SetData(T[] vertices, int length);
/// <summary>
/// Upon return `vertices` may reference another array object of at least the same size - containing random values.
/// </summary>
void SetData(ref T[] vertices, int length);
void SetData(T[] vertices, int offset, int start, int length);
}
@@ -122,8 +130,8 @@ namespace OpenRA
public interface ITexture : IDisposable
{
void SetData(uint[,] colors);
void SetData(byte[] colors, int width, int height);
void SetFloatData(float[] data, int width, int height);
byte[] GetData();
Size Size { get; }
TextureScaleFilter ScaleFilter { get; set; }
@@ -145,7 +153,7 @@ namespace OpenRA
TriangleList,
}
public struct Range<T>
public readonly struct Range<T>
{
public readonly T Start, End;
public Range(T start, T end) { Start = start; End = end; }

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -10,7 +10,6 @@
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Primitives;
@@ -18,43 +17,37 @@ namespace OpenRA.Graphics
{
public class PlayerColorRemap : IPaletteRemap
{
Dictionary<int, Color> remapColors;
readonly int[] remapIndices;
readonly float hue;
readonly float saturation;
readonly float value;
public static int GetRemapIndex(int[] ramp, int i)
public PlayerColorRemap(int[] remapIndices, Color color)
{
return ramp[i];
}
this.remapIndices = remapIndices;
public PlayerColorRemap(int[] ramp, Color c, float rampFraction)
{
var h = c.GetHue() / 360.0f;
var s = c.GetSaturation();
var l = c.GetBrightness();
// Increase luminosity if required to represent the full ramp
var rampRange = (byte)((1 - rampFraction) * l);
var c1 = Color.FromAhsl(h, s, Math.Max(rampRange, l));
var c2 = Color.FromAhsl(h, s, (byte)Math.Max(0, l - rampRange));
var baseIndex = ramp[0];
var remapRamp = ramp.Select(r => r - ramp[0]);
var rampMaxIndex = ramp.Length - 1;
// reversed remapping
if (ramp[0] > ramp[rampMaxIndex])
{
baseIndex = ramp[rampMaxIndex];
for (var i = rampMaxIndex; i > 0; i--)
remapRamp = ramp.Select(r => r - ramp[rampMaxIndex]);
}
remapColors = remapRamp.Select((x, i) => (baseIndex + i, Exts.ColorLerp(x / (float)ramp.Length, c1, c2)))
.ToDictionary(u => u.Item1, u => u.Item2);
var (r, g, b) = color.ToLinear();
(hue, saturation, value) = Color.RgbToHsv(r, g, b);
}
public Color GetRemappedColor(Color original, int index)
{
return remapColors.TryGetValue(index, out var c)
? c : original;
if (!remapIndices.Contains(index))
return original;
// Color remapping is applied in a linear color space, so start
// by undoing the pre-multiplied alpha and gamma corrections
var (r, g, b) = original.ToLinear();
// Calculate the brightness (i.e HSV value) of the original colour
// This inlines the single line of Color.RgbToHsv() that we need
var value = Math.Max(Math.Max(r, g), b);
// Construct the new RGB color
(r, g, b) = Color.HsvToRgb(hue, saturation, value * this.value);
// Convert linear back to SRGB and pre-multiply by the alpha
return Color.FromLinear(original.A, r, g, b);
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -9,6 +9,7 @@
*/
#endregion
using System;
using OpenRA.Primitives;
namespace OpenRA.Graphics
@@ -16,21 +17,38 @@ namespace OpenRA.Graphics
public interface IRenderable
{
WPos Pos { get; }
PaletteReference Palette { get; }
int ZOffset { get; }
bool IsDecoration { get; }
IRenderable WithPalette(PaletteReference newPalette);
IRenderable WithZOffset(int newOffset);
IRenderable OffsetBy(WVec offset);
IRenderable OffsetBy(in WVec offset);
IRenderable AsDecoration();
IFinalizedRenderable PrepareRender(WorldRenderer wr);
}
public interface ITintableRenderable
public interface IPalettedRenderable : IRenderable
{
IRenderable WithTint(in float3 newTint);
PaletteReference Palette { get; }
IPalettedRenderable WithPalette(PaletteReference newPalette);
}
[Flags]
public enum TintModifiers
{
None = 0,
IgnoreWorldTint = 1,
ReplaceColor = 2
}
public interface IModifyableRenderable : IRenderable
{
float Alpha { get; }
float3 Tint { get; }
TintModifiers TintModifiers { get; }
IModifyableRenderable WithAlpha(float newAlpha);
IModifyableRenderable WithTint(in float3 newTint, TintModifiers newTintModifiers);
}
public interface IFinalizedRenderable

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -18,7 +18,7 @@ namespace OpenRA.Graphics
{
public class RgbaColorRenderer
{
static readonly float3 Offset = new float3(0.5f, 0.5f, 0f);
static readonly float3 Offset = new(0.5f, 0.5f, 0f);
readonly SpriteRenderer parent;
readonly Vertex[] vertices = new Vertex[6];
@@ -28,7 +28,7 @@ namespace OpenRA.Graphics
this.parent = parent;
}
public void DrawLine(in float3 start, in float3 end, float width, Color startColor, Color endColor)
public void DrawLine(in float3 start, in float3 end, float width, Color startColor, Color endColor, BlendMode blendMode = BlendMode.Alpha)
{
var delta = (end - start) / (end - start).XY.Length;
var corner = width / 2 * new float3(-delta.Y, delta.X, delta.Z);
@@ -52,10 +52,10 @@ namespace OpenRA.Graphics
vertices[4] = new Vertex(end - corner + Offset, er, eg, eb, ea, 0, 0);
vertices[5] = new Vertex(start - corner + Offset, sr, sg, sb, sa, 0, 0);
parent.DrawRGBAVertices(vertices);
parent.DrawRGBAVertices(vertices, blendMode);
}
public void DrawLine(in float3 start, in float3 end, float width, Color color)
public void DrawLine(in float3 start, in float3 end, float width, Color color, BlendMode blendMode = BlendMode.Alpha)
{
var delta = (end - start) / (end - start).XY.Length;
var corner = width / 2 * new float2(-delta.Y, delta.X);
@@ -72,15 +72,15 @@ namespace OpenRA.Graphics
vertices[3] = new Vertex(end + corner + Offset, r, g, b, a, 0, 0);
vertices[4] = new Vertex(end - corner + Offset, r, g, b, a, 0, 0);
vertices[5] = new Vertex(start - corner + Offset, r, g, b, a, 0, 0);
parent.DrawRGBAVertices(vertices);
parent.DrawRGBAVertices(vertices, blendMode);
}
/// <summary>
/// Calculate the 2D intersection of two lines.
/// Will behave badly if the lines are parallel.
/// Z position is the average of a and b (ignores actual intersection point if it exists)
/// Z position is the average of a and b (ignores actual intersection point if it exists).
/// </summary>
float3 IntersectionOf(in float3 a, in float3 da, in float3 b, in float3 db)
static float3 IntersectionOf(in float3 a, in float3 da, in float3 b, in float3 db)
{
var crossA = a.X * (a.Y + da.Y) - a.Y * (a.X + da.X);
var crossB = b.X * (b.Y + db.Y) - b.Y * (b.X + db.X);
@@ -90,7 +90,7 @@ namespace OpenRA.Graphics
return new float3(x / d, y / d, 0.5f * (a.Z + b.Z));
}
void DrawDisconnectedLine(IEnumerable<float3> points, float width, Color color)
void DrawDisconnectedLine(IEnumerable<float3> points, float width, Color color, BlendMode blendMode)
{
using (var e = points.GetEnumerator())
{
@@ -101,13 +101,13 @@ namespace OpenRA.Graphics
while (e.MoveNext())
{
var point = e.Current;
DrawLine(lastPoint, point, width, color);
DrawLine(lastPoint, point, width, color, blendMode);
lastPoint = point;
}
}
}
void DrawConnectedLine(float3[] points, float width, Color color, bool closed)
void DrawConnectedLine(float3[] points, float width, Color color, bool closed, BlendMode blendMode)
{
// Not a line
if (points.Length < 2)
@@ -116,7 +116,7 @@ namespace OpenRA.Graphics
// Single segment
if (points.Length == 2)
{
DrawLine(points[0], points[1], width, color);
DrawLine(points[0], points[1], width, color, blendMode);
return;
}
@@ -138,7 +138,7 @@ namespace OpenRA.Graphics
// Segment is part of closed loop
if (closed)
{
var prev = points[points.Length - 1];
var prev = points[^1];
var prevDir = (start - prev) / (start - prev).XY.Length;
var prevCorner = width / 2 * new float3(-prevDir.Y, prevDir.X, prevDir.Z);
ca = IntersectionOf(start - prevCorner, prevDir, start - corner, dir);
@@ -163,7 +163,7 @@ namespace OpenRA.Graphics
vertices[3] = new Vertex(cc + Offset, r, g, b, a, 0, 0);
vertices[4] = new Vertex(cd + Offset, r, g, b, a, 0, 0);
vertices[5] = new Vertex(ca + Offset, r, g, b, a, 0, 0);
parent.DrawRGBAVertices(vertices);
parent.DrawRGBAVertices(vertices, blendMode);
// Advance line segment
end = next;
@@ -175,32 +175,32 @@ namespace OpenRA.Graphics
}
}
public void DrawLine(IEnumerable<float3> points, float width, Color color, bool connectSegments = false)
public void DrawLine(IEnumerable<float3> points, float width, Color color, bool connectSegments = false, BlendMode blendMode = BlendMode.Alpha)
{
if (!connectSegments)
DrawDisconnectedLine(points, width, color);
DrawDisconnectedLine(points, width, color, blendMode);
else
DrawConnectedLine(points as float3[] ?? points.ToArray(), width, color, false);
DrawConnectedLine(points as float3[] ?? points.ToArray(), width, color, false, blendMode);
}
public void DrawPolygon(float3[] vertices, float width, Color color)
public void DrawPolygon(float3[] vertices, float width, Color color, BlendMode blendMode = BlendMode.Alpha)
{
DrawConnectedLine(vertices, width, color, true);
DrawConnectedLine(vertices, width, color, true, blendMode);
}
public void DrawPolygon(float2[] vertices, float width, Color color)
public void DrawPolygon(float2[] vertices, float width, Color color, BlendMode blendMode = BlendMode.Alpha)
{
DrawConnectedLine(vertices.Select(v => new float3(v, 0)).ToArray(), width, color, true);
DrawConnectedLine(vertices.Select(v => new float3(v, 0)).ToArray(), width, color, true, blendMode);
}
public void DrawRect(in float3 tl, in float3 br, float width, Color color)
public void DrawRect(in float3 tl, in float3 br, float width, Color color, BlendMode blendMode = BlendMode.Alpha)
{
var tr = new float3(br.X, tl.Y, tl.Z);
var bl = new float3(tl.X, br.Y, br.Z);
DrawPolygon(new[] { tl, tr, br, bl }, width, color);
DrawPolygon(new[] { tl, tr, br, bl }, width, color, blendMode);
}
public void FillTriangle(in float3 a, in float3 b, in float3 c, Color color)
public void FillTriangle(in float3 a, in float3 b, in float3 c, Color color, BlendMode blendMode = BlendMode.Alpha)
{
color = Util.PremultiplyAlpha(color);
var cr = color.R / 255.0f;
@@ -211,17 +211,17 @@ namespace OpenRA.Graphics
vertices[0] = new Vertex(a + Offset, cr, cg, cb, ca, 0, 0);
vertices[1] = new Vertex(b + Offset, cr, cg, cb, ca, 0, 0);
vertices[2] = new Vertex(c + Offset, cr, cg, cb, ca, 0, 0);
parent.DrawRGBAVertices(vertices);
parent.DrawRGBAVertices(vertices, blendMode);
}
public void FillRect(in float3 tl, in float3 br, Color color)
public void FillRect(in float3 tl, in float3 br, Color color, BlendMode blendMode = BlendMode.Alpha)
{
var tr = new float3(br.X, tl.Y, tl.Z);
var bl = new float3(tl.X, br.Y, br.Z);
FillRect(tl, tr, br, bl, color);
FillRect(tl, tr, br, bl, color, blendMode);
}
public void FillRect(in float3 a, in float3 b, in float3 c, in float3 d, Color color)
public void FillRect(in float3 a, in float3 b, in float3 c, in float3 d, Color color, BlendMode blendMode = BlendMode.Alpha)
{
color = Util.PremultiplyAlpha(color);
var cr = color.R / 255.0f;
@@ -235,10 +235,10 @@ namespace OpenRA.Graphics
vertices[3] = new Vertex(c + Offset, cr, cg, cb, ca, 0, 0);
vertices[4] = new Vertex(d + Offset, cr, cg, cb, ca, 0, 0);
vertices[5] = new Vertex(a + Offset, cr, cg, cb, ca, 0, 0);
parent.DrawRGBAVertices(vertices);
parent.DrawRGBAVertices(vertices, blendMode);
}
public void FillRect(in float3 a, in float3 b, in float3 c, in float3 d, Color topLeftColor, Color topRightColor, Color bottomRightColor, Color bottomLeftColor)
public void FillRect(in float3 a, in float3 b, in float3 c, in float3 d, Color topLeftColor, Color topRightColor, Color bottomRightColor, Color bottomLeftColor, BlendMode blendMode = BlendMode.Alpha)
{
vertices[0] = VertexWithColor(a + Offset, topLeftColor);
vertices[1] = VertexWithColor(b + Offset, topRightColor);
@@ -247,7 +247,7 @@ namespace OpenRA.Graphics
vertices[4] = VertexWithColor(d + Offset, bottomLeftColor);
vertices[5] = VertexWithColor(a + Offset, topLeftColor);
parent.DrawRGBAVertices(vertices);
parent.DrawRGBAVertices(vertices, blendMode);
}
static Vertex VertexWithColor(in float3 xyz, Color color)
@@ -261,7 +261,7 @@ namespace OpenRA.Graphics
return new Vertex(xyz, cr, cg, cb, ca, 0, 0);
}
public void FillEllipse(in float3 tl, in float3 br, Color color, int vertices = 32)
public void FillEllipse(in float3 tl, in float3 br, Color color, BlendMode blendMode = BlendMode.Alpha)
{
// TODO: Create an ellipse polygon instead
var a = (br.X - tl.X) / 2;
@@ -272,7 +272,7 @@ namespace OpenRA.Graphics
{
var z = float2.Lerp(tl.Z, br.Z, (y - tl.Y) / (br.Y - tl.Y));
var dx = a * (float)Math.Sqrt(1 - (y - yc) * (y - yc) / b / b);
DrawLine(new float3(xc - dx, y, z), new float3(xc + dx, y, z), 1, color);
DrawLine(new float3(xc - dx, y, z), new float3(xc + dx, y, z), 1, color, blendMode);
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -22,44 +22,36 @@ namespace OpenRA.Graphics
this.parent = parent;
}
public void DrawSprite(Sprite s, in float3 location, in float3 size)
public void DrawSprite(Sprite s, in float3 location, in float3 scale, float rotation = 0f)
{
if (s.Channel != TextureChannel.RGBA)
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");
parent.DrawSprite(s, location, 0, size);
parent.DrawSprite(s, 0, location, scale, rotation);
}
public void DrawSprite(Sprite s, in float3 location)
public void DrawSprite(Sprite s, in float3 location, float scale = 1f, float rotation = 0f)
{
if (s.Channel != TextureChannel.RGBA)
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");
parent.DrawSprite(s, location, 0, s.Size);
parent.DrawSprite(s, 0, location, scale, rotation);
}
public void DrawSprite(Sprite s, in float3 a, in float3 b, in float3 c, in float3 d)
public void DrawSprite(Sprite s, in float3 location, float scale, in float3 tint, float alpha, float rotation = 0f)
{
if (s.Channel != TextureChannel.RGBA)
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");
parent.DrawSprite(s, a, b, c, d);
parent.DrawSprite(s, 0, location, scale, tint, alpha, rotation);
}
public void DrawSpriteWithTint(Sprite s, in float3 location, in float3 size, in float3 tint)
public void DrawSprite(Sprite s, in float3 a, in float3 b, in float3 c, in float3 d, in float3 tint, float alpha)
{
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, in float3 a, in float3 b, in float3 c, in float3 d, in float3 tint)
{
if (s.Channel != TextureChannel.RGBA)
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");
parent.DrawSpriteWithTint(s, a, b, c, d, tint);
parent.DrawSprite(s, 0, a, b, c, d, tint, alpha);
}
}
}

View File

@@ -1,148 +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;
using OpenRA.FileSystem;
using OpenRA.Primitives;
namespace OpenRA.Graphics
{
using Sequences = IReadOnlyDictionary<string, Lazy<IReadOnlyDictionary<string, ISpriteSequence>>>;
using UnitSequences = Lazy<IReadOnlyDictionary<string, ISpriteSequence>>;
public interface ISpriteSequence
{
string Name { get; }
int Start { get; }
int Length { get; }
int Stride { get; }
int Facings { get; }
int Tick { get; }
int ZOffset { get; }
int ShadowStart { get; }
int ShadowZOffset { get; }
int[] Frames { get; }
Rectangle Bounds { get; }
bool IgnoreWorldTint { get; }
Sprite GetSprite(int frame);
Sprite GetSprite(int frame, WAngle facing);
Sprite GetShadow(int frame, WAngle facing);
}
public interface ISpriteSequenceLoader
{
IReadOnlyDictionary<string, ISpriteSequence> ParseSequences(ModData modData, string tileSet, SpriteCache cache, MiniYamlNode node);
}
public class SequenceProvider : IDisposable
{
readonly ModData modData;
readonly string tileSet;
readonly Lazy<Sequences> sequences;
readonly Lazy<SpriteCache> spriteCache;
public SpriteCache SpriteCache { get { return spriteCache.Value; } }
readonly Dictionary<string, UnitSequences> sequenceCache = new Dictionary<string, UnitSequences>();
public SequenceProvider(IReadOnlyFileSystem fileSystem, ModData modData, string tileSet, MiniYaml additionalSequences)
{
this.modData = modData;
this.tileSet = tileSet;
sequences = Exts.Lazy(() =>
{
using (new Support.PerfTimer("LoadSequences"))
return Load(fileSystem, additionalSequences);
});
spriteCache = Exts.Lazy(() => new SpriteCache(fileSystem, modData.SpriteLoaders));
}
public ISpriteSequence GetSequence(string unitName, string sequenceName)
{
if (!sequences.Value.TryGetValue(unitName, out var unitSeq))
throw new InvalidOperationException("Unit `{0}` does not have any sequences defined.".F(unitName));
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;
}
public IEnumerable<string> Images { get { return sequences.Value.Keys; } }
public bool HasSequence(string unitName)
{
return sequences.Value.ContainsKey(unitName);
}
public bool HasSequence(string unitName, string sequenceName)
{
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);
}
public IEnumerable<string> Sequences(string unitName)
{
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;
}
Sequences Load(IReadOnlyFileSystem fileSystem, MiniYaml additionalSequences)
{
var nodes = MiniYaml.Load(fileSystem, modData.Manifest.Sequences, additionalSequences);
var items = new Dictionary<string, UnitSequences>();
foreach (var node in nodes)
{
// Nodes starting with ^ are inheritable but never loaded directly
if (node.Key.StartsWith(ActorInfo.AbstractActorPrefix, StringComparison.Ordinal))
continue;
var key = node.Value.ToLines(node.Key).JoinWith("|");
if (sequenceCache.TryGetValue(key, out var t))
items.Add(node.Key, t);
else
{
t = Exts.Lazy(() => modData.SpriteSequenceLoader.ParseSequences(modData, tileSet, SpriteCache, node));
sequenceCache.Add(key, t);
items.Add(node.Key, t);
}
}
return new ReadOnlyDictionary<string, UnitSequences>(items);
}
public void Preload()
{
foreach (var sb in SpriteCache.SheetBuilders.Values)
sb.Current.CreateBuffer();
foreach (var unitSeq in sequences.Value.Values)
foreach (var seq in unitSeq.Value.Values) { }
foreach (var sb in SpriteCache.SheetBuilders.Values)
sb.Current.ReleaseBuffer();
}
public void Dispose()
{
if (spriteCache.IsValueCreated)
foreach (var sb in SpriteCache.SheetBuilders.Values)
sb.Dispose();
}
}
}

View File

@@ -0,0 +1,119 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using OpenRA.FileSystem;
using OpenRA.Primitives;
namespace OpenRA.Graphics
{
public interface ISpriteSequence
{
string Name { get; }
int Length { get; }
int Facings { get; }
int Tick { get; }
int ZOffset { get; }
int ShadowZOffset { get; }
Rectangle Bounds { get; }
bool IgnoreWorldTint { get; }
float Scale { get; }
void ResolveSprites(SpriteCache cache);
Sprite GetSprite(int frame);
Sprite GetSprite(int frame, WAngle facing);
(Sprite Sprite, WAngle Rotation) GetSpriteWithRotation(int frame, WAngle facing);
Sprite GetShadow(int frame, WAngle facing);
float GetAlpha(int frame);
}
public interface ISpriteSequenceLoader
{
int BgraSheetSize { get; }
int IndexedSheetSize { get; }
IReadOnlyDictionary<string, ISpriteSequence> ParseSequences(ModData modData, string tileSet, SpriteCache cache, MiniYamlNode node);
}
public sealed class SequenceSet : IDisposable
{
readonly ModData modData;
readonly string tileSet;
readonly IReadOnlyDictionary<string, IReadOnlyDictionary<string, ISpriteSequence>> images;
public SpriteCache SpriteCache { get; }
public SequenceSet(IReadOnlyFileSystem fileSystem, ModData modData, string tileSet, MiniYaml additionalSequences)
{
this.modData = modData;
this.tileSet = tileSet;
SpriteCache = new SpriteCache(fileSystem, modData.SpriteLoaders, modData.SpriteSequenceLoader.BgraSheetSize, modData.SpriteSequenceLoader.IndexedSheetSize);
using (new Support.PerfTimer("LoadSequences"))
images = Load(fileSystem, additionalSequences);
}
public ISpriteSequence GetSequence(string image, string sequence)
{
if (!images.TryGetValue(image, out var sequences))
throw new InvalidOperationException($"Image `{image}` does not have any sequences defined.");
if (!sequences.TryGetValue(sequence, out var seq))
throw new InvalidOperationException($"Image `{image}` does not have a sequence named `{sequence}`.");
return seq;
}
public IEnumerable<string> Images => images.Keys;
public bool HasSequence(string image, string sequence)
{
if (!images.TryGetValue(image, out var sequences))
throw new InvalidOperationException($"Image `{image}` does not have any sequences defined.");
return sequences.ContainsKey(sequence);
}
public IEnumerable<string> Sequences(string image)
{
if (!images.TryGetValue(image, out var sequences))
throw new InvalidOperationException($"Image `{image}` does not have any sequences defined.");
return sequences.Keys;
}
IReadOnlyDictionary<string, IReadOnlyDictionary<string, ISpriteSequence>> Load(IReadOnlyFileSystem fileSystem, MiniYaml additionalSequences)
{
var nodes = MiniYaml.Load(fileSystem, modData.Manifest.Sequences, additionalSequences);
var images = new Dictionary<string, IReadOnlyDictionary<string, ISpriteSequence>>();
foreach (var node in nodes)
{
// Nodes starting with ^ are inheritable but never loaded directly
if (node.Key.StartsWith(ActorInfo.AbstractActorPrefix, StringComparison.Ordinal))
continue;
images[node.Key] = modData.SpriteSequenceLoader.ParseSequences(modData, tileSet, SpriteCache, node);
}
return images;
}
public void LoadSprites()
{
SpriteCache.LoadReservations(modData);
foreach (var sequences in images.Values)
foreach (var sequence in sequences)
sequence.Value.ResolveSprites(SpriteCache);
}
public void Dispose()
{
SpriteCache.Dispose();
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -32,7 +32,7 @@ namespace OpenRA.Graphics
return data;
}
public bool Buffered { get { return data != null || texture == null; } }
public bool Buffered => data != null || texture == null;
public Sheet(SheetType type, Size size)
{
@@ -103,7 +103,7 @@ namespace OpenRA.Graphics
for (var i = 0; i < Palette.Size; i++)
palColors[i] = pal.GetColor(i);
return new Png(plane, SpriteFrameType.Bgra32, Size.Width, Size.Height, palColors);
return new Png(plane, SpriteFrameType.Indexed8, Size.Width, Size.Height, palColors);
}
public void CreateBuffer()

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -34,15 +34,16 @@ namespace OpenRA.Graphics
public sealed class SheetBuilder : IDisposable
{
public readonly SheetType Type;
readonly List<Sheet> sheets = new List<Sheet>();
readonly List<Sheet> sheets = new();
readonly Func<Sheet> allocateSheet;
readonly int margin;
Sheet current;
TextureChannel channel;
int rowHeight = 0;
int2 p;
public Sheet Current { get; private set; }
public TextureChannel CurrentChannel { get; private set; }
public IEnumerable<Sheet> AllSheets => sheets;
public static Sheet AllocateSheet(SheetType type, int sheetSize)
{
return new Sheet(type, new Size(sheetSize, sheetSize));
@@ -61,7 +62,7 @@ namespace OpenRA.Graphics
case SpriteFrameType.Rgba32:
case SpriteFrameType.Rgb24:
return SheetType.BGRA;
default: throw new NotImplementedException("Unknown SpriteFrameType {0}".F(t));
default: throw new NotImplementedException($"Unknown SpriteFrameType {t}");
}
}
@@ -73,10 +74,10 @@ namespace OpenRA.Graphics
public SheetBuilder(SheetType t, Func<Sheet> allocateSheet, int margin = 1)
{
channel = t == SheetType.Indexed ? TextureChannel.Red : TextureChannel.RGBA;
CurrentChannel = t == SheetType.Indexed ? TextureChannel.Red : TextureChannel.RGBA;
Type = t;
current = allocateSheet();
sheets.Add(current);
Current = allocateSheet();
sheets.Add(Current);
this.allocateSheet = allocateSheet;
this.margin = margin;
}
@@ -87,19 +88,19 @@ namespace OpenRA.Graphics
{
// Don't bother allocating empty sprites
if (size.Width == 0 || size.Height == 0)
return new Sprite(current, Rectangle.Empty, 0, spriteOffset, channel, BlendMode.Alpha);
return new Sprite(Current, Rectangle.Empty, 0, spriteOffset, CurrentChannel, BlendMode.Alpha);
var rect = Allocate(size, zRamp, spriteOffset);
Util.FastCopyIntoChannel(rect, src, type);
current.CommitBufferedData();
Current.CommitBufferedData();
return rect;
}
public Sprite Add(Png src, float scale = 1f)
{
var rect = Allocate(new Size(src.Width, src.Height), scale);
var rect = Allocate(new Size(src.Width, src.Height), scale);
Util.FastCopyIntoSprite(rect, src);
current.CommitBufferedData();
Current.CommitBufferedData();
return rect;
}
@@ -115,7 +116,7 @@ namespace OpenRA.Graphics
public Sprite Allocate(Size imageSize, float scale = 1f) { return Allocate(imageSize, 0, float3.Zero, scale); }
public Sprite Allocate(Size imageSize, float zRamp, in float3 spriteOffset, float scale = 1f)
{
if (imageSize.Width + p.X + margin > current.Size.Width)
if (imageSize.Width + p.X + margin > Current.Size.Width)
{
p = new int2(0, p.Y + rowHeight + margin);
rowHeight = imageSize.Height;
@@ -124,33 +125,29 @@ namespace OpenRA.Graphics
if (imageSize.Height > rowHeight)
rowHeight = imageSize.Height;
if (p.Y + imageSize.Height + margin > current.Size.Height)
if (p.Y + imageSize.Height + margin > Current.Size.Height)
{
var next = NextChannel(channel);
var next = NextChannel(CurrentChannel);
if (next == null)
{
current.ReleaseBuffer();
current = allocateSheet();
sheets.Add(current);
channel = Type == SheetType.Indexed ? TextureChannel.Red : TextureChannel.RGBA;
Current.ReleaseBuffer();
Current = allocateSheet();
sheets.Add(Current);
CurrentChannel = Type == SheetType.Indexed ? TextureChannel.Red : TextureChannel.RGBA;
}
else
channel = next.Value;
CurrentChannel = next.Value;
rowHeight = imageSize.Height;
p = int2.Zero;
}
var rect = new Sprite(current, new Rectangle(p.X + margin, p.Y + margin, imageSize.Width, imageSize.Height), zRamp, spriteOffset, channel, BlendMode.Alpha, scale);
var rect = new Sprite(Current, new Rectangle(p.X + margin, p.Y + margin, imageSize.Width, imageSize.Height), zRamp, spriteOffset, CurrentChannel, BlendMode.Alpha, scale);
p += new int2(imageSize.Width + margin, 0);
return rect;
}
public Sheet Current { get { return current; } }
public TextureChannel CurrentChannel { get { return channel; } }
public IEnumerable<Sheet> AllSheets { get { return sheets; } }
public void Dispose()
{
foreach (var sheet in sheets)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -23,7 +23,6 @@ namespace OpenRA.Graphics
public readonly float ZRamp;
public readonly float3 Size;
public readonly float3 Offset;
public readonly float3 FractionalOffset;
public readonly float Top, Left, Bottom, Right;
public Sprite(Sheet sheet, Rectangle bounds, TextureChannel channel, float scale = 1)
@@ -38,13 +37,16 @@ namespace OpenRA.Graphics
Channel = channel;
Size = scale * new float3(bounds.Size.Width, bounds.Size.Height, bounds.Size.Height * zRamp);
BlendMode = blendMode;
FractionalOffset = Size.Z != 0 ? offset / Size :
new float3(offset.X / Size.X, offset.Y / Size.Y, 0);
Left = (float)Math.Min(bounds.Left, bounds.Right) / sheet.Size.Width;
Top = (float)Math.Min(bounds.Top, bounds.Bottom) / sheet.Size.Height;
Right = (float)Math.Max(bounds.Left, bounds.Right) / sheet.Size.Width;
Bottom = (float)Math.Max(bounds.Top, bounds.Bottom) / sheet.Size.Height;
// Some GPUs suffer from precision issues when rendering into non 1:1 framebuffers that result
// in rendering a line of texels that sample outside the sprite rectangle.
// Insetting the texture coordinates by a small fraction of a pixel avoids this
// with negligible impact on the 1:1 rendering case.
var inset = 1 / 128f;
Left = (Math.Min(bounds.Left, bounds.Right) + inset) / sheet.Size.Width;
Top = (Math.Min(bounds.Top, bounds.Bottom) + inset) / sheet.Size.Height;
Right = (Math.Max(bounds.Left, bounds.Right) - inset) / sheet.Size.Width;
Bottom = (Math.Max(bounds.Top, bounds.Bottom) - inset) / sheet.Size.Height;
}
}

View File

@@ -0,0 +1,174 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OpenRA.FileSystem;
using OpenRA.Primitives;
namespace OpenRA.Graphics
{
public sealed class SpriteCache : IDisposable
{
public readonly Dictionary<SheetType, SheetBuilder> SheetBuilders;
readonly ISpriteLoader[] loaders;
readonly IReadOnlyFileSystem fileSystem;
readonly Dictionary<int, (int[] Frames, MiniYamlNode.SourceLocation Location)> spriteReservations = new();
readonly Dictionary<int, (int[] Frames, MiniYamlNode.SourceLocation Location)> frameReservations = new();
readonly Dictionary<string, List<int>> reservationsByFilename = new();
readonly Dictionary<int, ISpriteFrame[]> resolvedFrames = new();
readonly Dictionary<int, Sprite[]> resolvedSprites = new();
readonly Dictionary<int, (string Filename, MiniYamlNode.SourceLocation Location)> missingFiles = new();
int nextReservationToken = 1;
public SpriteCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders, int bgraSheetSize, int indexedSheetSize, int bgraSheetMargin = 1, int indexedSheetMargin = 1)
{
SheetBuilders = new Dictionary<SheetType, SheetBuilder>
{
{ SheetType.Indexed, new SheetBuilder(SheetType.Indexed, indexedSheetSize, indexedSheetMargin) },
{ SheetType.BGRA, new SheetBuilder(SheetType.BGRA, bgraSheetSize, bgraSheetMargin) }
};
this.fileSystem = fileSystem;
this.loaders = loaders;
}
public int ReserveSprites(string filename, IEnumerable<int> frames, MiniYamlNode.SourceLocation location)
{
var token = nextReservationToken++;
spriteReservations[token] = (frames?.ToArray(), location);
reservationsByFilename.GetOrAdd(filename, _ => new List<int>()).Add(token);
return token;
}
public int ReserveFrames(string filename, IEnumerable<int> frames, MiniYamlNode.SourceLocation location)
{
var token = nextReservationToken++;
frameReservations[token] = (frames?.ToArray(), location);
reservationsByFilename.GetOrAdd(filename, _ => new List<int>()).Add(token);
return token;
}
static ISpriteFrame[] GetFrames(IReadOnlyFileSystem fileSystem, string filename, ISpriteLoader[] loaders, out TypeDictionary metadata)
{
metadata = null;
if (!fileSystem.TryOpen(filename, out var stream))
return null;
using (stream)
{
foreach (var loader in loaders)
if (loader.TryParseSprite(stream, filename, out var frames, out metadata))
return frames;
return null;
}
}
public void LoadReservations(ModData modData)
{
foreach (var sb in SheetBuilders.Values)
sb.Current.CreateBuffer();
var spriteCache = new Dictionary<int, Sprite>();
foreach (var (filename, tokens) in reservationsByFilename)
{
modData.LoadScreen?.Display();
var loadedFrames = GetFrames(fileSystem, filename, loaders, out _);
foreach (var token in tokens)
{
if (frameReservations.TryGetValue(token, out var r))
{
if (loadedFrames != null)
{
if (r.Frames != null)
{
var resolved = new ISpriteFrame[loadedFrames.Length];
foreach (var i in r.Frames)
resolved[i] = loadedFrames[i];
resolvedFrames[token] = resolved;
}
else
resolvedFrames[token] = loadedFrames;
}
else
{
resolvedFrames[token] = null;
missingFiles[token] = (filename, r.Location);
}
}
if (spriteReservations.TryGetValue(token, out r))
{
if (loadedFrames != null)
{
var resolved = new Sprite[loadedFrames.Length];
var frames = r.Frames ?? Enumerable.Range(0, loadedFrames.Length);
foreach (var i in frames)
resolved[i] = spriteCache.GetOrAdd(i,
f => SheetBuilders[SheetBuilder.FrameTypeToSheetType(loadedFrames[f].Type)].Add(loadedFrames[f]));
resolvedSprites[token] = resolved;
}
else
{
resolvedSprites[token] = null;
missingFiles[token] = (filename, r.Location);
}
}
}
spriteCache.Clear();
}
spriteReservations.Clear();
frameReservations.Clear();
reservationsByFilename.Clear();
foreach (var sb in SheetBuilders.Values)
sb.Current.ReleaseBuffer();
}
public Sprite[] ResolveSprites(int token)
{
var resolved = resolvedSprites[token];
resolvedSprites.Remove(token);
if (missingFiles.TryGetValue(token, out var r))
throw new FileNotFoundException($"{r.Location}: {r.Filename} not found", r.Filename);
return resolved;
}
public ISpriteFrame[] ResolveFrames(int token)
{
var resolved = resolvedFrames[token];
resolvedFrames.Remove(token);
if (missingFiles.TryGetValue(token, out var r))
throw new FileNotFoundException($"{r.Location}: {r.Filename} not found", r.Filename);
return resolved;
}
public IEnumerable<(string Filename, MiniYamlNode.SourceLocation Location)> MissingFiles => missingFiles.Values.ToHashSet();
public void Dispose()
{
foreach (var sb in SheetBuilders.Values)
sb.Dispose();
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -10,7 +10,6 @@
#endregion
using System;
using System.Linq;
using OpenRA.Primitives;
using OpenRA.Support;
@@ -18,10 +17,9 @@ namespace OpenRA.Graphics
{
public sealed class SpriteFont : IDisposable
{
public int TopOffset { get; private set; }
public int TopOffset { get; }
readonly int size;
readonly SheetBuilder builder;
readonly Func<string, float> lineWidth;
readonly IFont font;
readonly Cache<char, GlyphInfo> glyphs;
readonly Cache<(char C, int Radius), Sprite> contrastGlyphs;
@@ -32,7 +30,7 @@ namespace OpenRA.Graphics
public SpriteFont(string name, byte[] data, int size, int ascender, float scale, SheetBuilder builder)
{
if (builder.Type != SheetType.BGRA)
throw new ArgumentException("The sheet builder must create BGRA sheets.", "builder");
throw new ArgumentException("The sheet builder must create BGRA sheets.", nameof(builder));
deviceScale = scale;
this.size = size;
@@ -43,13 +41,9 @@ namespace OpenRA.Graphics
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[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)
using (new PerfTimer("Precache {0} {1}px".F(name, size)))
using (new PerfTimer($"Precache {name} {size}px"))
for (var n = (char)0x20; n < (char)0x7f; n++)
if (glyphs[n] == null)
throw new InvalidOperationException();
@@ -89,10 +83,10 @@ namespace OpenRA.Graphics
if (g.Sprite != null)
{
var contrastSprite = contrastGlyphs[(s, screenContrast)];
Game.Renderer.RgbaSpriteRenderer.DrawSpriteWithTint(contrastSprite,
Game.Renderer.RgbaSpriteRenderer.DrawSprite(contrastSprite,
(screen + g.Offset - contrastVector) / deviceScale,
contrastSprite.Size / deviceScale,
tint);
1f / deviceScale,
tint, 1f);
}
screen += new int2((int)(g.Advance + 0.5f), 0);
@@ -120,16 +114,16 @@ namespace OpenRA.Graphics
// Convert screen coordinates back to UI coordinates for drawing
if (g.Sprite != null)
Game.Renderer.RgbaSpriteRenderer.DrawSpriteWithTint(g.Sprite,
Game.Renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite,
(screen + g.Offset).ToFloat2() / deviceScale,
g.Sprite.Size / deviceScale,
tint);
1f / deviceScale,
tint, 1f);
screen += new int2((int)(g.Advance + 0.5f), 0);
}
}
float2 Rotate(float2 v, float sina, float cosa, float2 offset)
static float2 Rotate(float2 v, float sina, float cosa, float2 offset)
{
return new float2(
v.X * cosa - v.Y * sina + offset.X,
@@ -172,12 +166,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.DrawSpriteWithTint(g.Sprite,
Game.Renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite,
ra + screenOffset,
rb + screenOffset,
rc + screenOffset,
rd + screenOffset,
tint);
tint, 1f);
}
p += new float2(g.Advance / deviceScale, 0);
@@ -238,16 +232,26 @@ namespace OpenRA.Graphics
if (string.IsNullOrEmpty(text))
return new int2(0, size);
var lines = text.Split('\n');
return new int2((int)Math.Ceiling(MaxLineWidth(lines, lineWidth)), lines.Length * size);
var lines = text.SplitLines('\n');
var maxWidth = 0f;
var rows = 0;
foreach (var line in lines)
{
rows++;
maxWidth = Math.Max(maxWidth, LineWidth(line));
}
return new int2((int)Math.Ceiling(maxWidth), rows * size);
}
static float MaxLineWidth(string[] lines, Func<string, float> lineWidth)
float LineWidth(ReadOnlySpan<char> line)
{
var maxWidth = 0f;
foreach (var line in lines)
maxWidth = Math.Max(maxWidth, lineWidth(line));
return maxWidth;
var result = 0f;
foreach (var c in line)
result += glyphs[c].Advance;
return result / deviceScale;
}
GlyphInfo CreateGlyph(char c)
@@ -423,7 +427,7 @@ namespace OpenRA.Graphics
}
}
class GlyphInfo
sealed class GlyphInfo
{
public float Advance;
public int2 Offset;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -9,10 +9,7 @@
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OpenRA.FileSystem;
using OpenRA.Primitives;
@@ -21,7 +18,7 @@ namespace OpenRA.Graphics
/// <summary>
/// Describes the format of the pixel data in a ISpriteFrame.
/// Note that the channel order is defined for little-endian bytes, so BGRA corresponds
/// to a 32bit ARGB value, such as that returned by Color.ToArgb()!
/// to a 32bit ARGB value, such as that returned by Color.ToArgb().
/// </summary>
public enum SpriteFrameType
{
@@ -44,7 +41,7 @@ namespace OpenRA.Graphics
public interface ISpriteLoader
{
bool TryParseSprite(Stream s, out ISpriteFrame[] frames, out TypeDictionary metadata);
bool TryParseSprite(Stream s, string filename, out ISpriteFrame[] frames, out TypeDictionary metadata);
}
public interface ISpriteFrame
@@ -67,94 +64,6 @@ namespace OpenRA.Graphics
bool DisableExportPadding { get; }
}
public class SpriteCache
{
public readonly Cache<SheetType, SheetBuilder> SheetBuilders;
readonly ISpriteLoader[] loaders;
readonly IReadOnlyFileSystem fileSystem;
readonly Dictionary<string, List<Sprite[]>> sprites = new Dictionary<string, List<Sprite[]>>();
readonly Dictionary<string, ISpriteFrame[]> unloadedFrames = new Dictionary<string, ISpriteFrame[]>();
readonly Dictionary<string, TypeDictionary> metadata = new Dictionary<string, TypeDictionary>();
public SpriteCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders)
{
SheetBuilders = new Cache<SheetType, SheetBuilder>(t => new SheetBuilder(t));
this.fileSystem = fileSystem;
this.loaders = loaders;
}
/// <summary>
/// Returns the first set of sprites with the given filename.
/// If getUsedFrames is defined then the indices returned by the function call
/// are guaranteed to be loaded. The value of other indices in the returned
/// array are undefined and should never be accessed.
/// </summary>
public Sprite[] this[string filename, Func<int, IEnumerable<int>> getUsedFrames = null]
{
get
{
var allSprites = sprites.GetOrAdd(filename);
var sprite = allSprites.FirstOrDefault();
if (!unloadedFrames.TryGetValue(filename, out var unloaded))
unloaded = null;
// This is the first time that the file has been requested
// Load all of the frames into the unused buffer and initialize
// the loaded cache (initially empty)
if (sprite == null)
{
unloaded = FrameLoader.GetFrames(fileSystem, filename, loaders, out var fileMetadata);
unloadedFrames[filename] = unloaded;
metadata[filename] = fileMetadata;
sprite = new Sprite[unloaded.Length];
allSprites.Add(sprite);
}
// HACK: The sequence code relies on side-effects from getUsedFrames
var indices = getUsedFrames != null ? getUsedFrames(sprite.Length) :
Enumerable.Range(0, sprite.Length);
// Load any unused frames into the SheetBuilder
if (unloaded != null)
{
foreach (var i in indices)
{
if (unloaded[i] != null)
{
sprite[i] = SheetBuilders[SheetBuilder.FrameTypeToSheetType(unloaded[i].Type)].Add(unloaded[i]);
unloaded[i] = null;
}
}
// All frames have been loaded
if (unloaded.All(f => f == null))
unloadedFrames.Remove(filename);
}
return sprite;
}
}
/// <summary>
/// Returns a TypeDictionary containing any metadata defined by the frame
/// or null if the frame does not define metadata.
/// </summary>
public TypeDictionary FrameMetadata(string filename)
{
if (!metadata.TryGetValue(filename, out var fileMetadata))
{
FrameLoader.GetFrames(fileSystem, filename, loaders, out fileMetadata);
metadata[filename] = fileMetadata;
}
return fileMetadata;
}
}
public class FrameCache
{
readonly Cache<string, ISpriteFrame[]> frames;
@@ -164,7 +73,7 @@ namespace OpenRA.Graphics
frames = new Cache<string, ISpriteFrame[]>(filename => FrameLoader.GetFrames(fileSystem, filename, loaders, out _));
}
public ISpriteFrame[] this[string filename] { get { return frames[filename]; } }
public ISpriteFrame[] this[string filename] => frames[filename];
}
public static class FrameLoader
@@ -173,7 +82,7 @@ namespace OpenRA.Graphics
{
using (var stream = fileSystem.Open(filename))
{
var spriteFrames = GetFrames(stream, loaders, out metadata);
var spriteFrames = GetFrames(stream, loaders, filename, out metadata);
if (spriteFrames == null)
throw new InvalidDataException(filename + " is not a valid sprite file!");
@@ -181,12 +90,12 @@ namespace OpenRA.Graphics
}
}
public static ISpriteFrame[] GetFrames(Stream stream, ISpriteLoader[] loaders, out TypeDictionary metadata)
public static ISpriteFrame[] GetFrames(Stream stream, ISpriteLoader[] loaders, string filename, out TypeDictionary metadata)
{
metadata = null;
foreach (var loader in loaders)
if (loader.TryParseSprite(stream, out var frames, out metadata))
if (loader.TryParseSprite(stream, filename, out var frames, out metadata))
return frames;
return null;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -9,79 +9,107 @@
*/
#endregion
using System;
using System.Collections.Generic;
using OpenRA.Primitives;
namespace OpenRA.Graphics
{
public struct SpriteRenderable : IRenderable, ITintableRenderable, IFinalizedRenderable
public class SpriteRenderable : IPalettedRenderable, IModifyableRenderable, IFinalizedRenderable
{
public static readonly IEnumerable<IRenderable> None = new IRenderable[0];
public static readonly IEnumerable<IRenderable> None = Array.Empty<IRenderable>();
readonly Sprite sprite;
readonly WPos pos;
readonly WVec offset;
readonly int zOffset;
readonly PaletteReference palette;
readonly float scale;
readonly float3 tint;
readonly bool isDecoration;
readonly bool ignoreWorldTint;
readonly WAngle rotation = WAngle.Zero;
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)
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, float alpha,
float3 tint, TintModifiers tintModifiers, bool isDecoration, WAngle rotation)
{
this.sprite = sprite;
this.pos = pos;
this.offset = offset;
this.zOffset = zOffset;
this.palette = palette;
Offset = offset;
ZOffset = zOffset;
Palette = palette;
this.scale = scale;
this.tint = tint;
this.isDecoration = isDecoration;
this.ignoreWorldTint = ignoreWorldTint;
this.rotation = rotation;
Tint = tint;
IsDecoration = isDecoration;
TintModifiers = tintModifiers;
Alpha = alpha;
// PERF: Remove useless palette assignments for RGBA sprites
// HACK: This is working around the fact that palettes are defined on traits rather than sequences
// and can be removed once this has been fixed
if (sprite.Channel == TextureChannel.RGBA && !(palette?.HasColorShift ?? false))
Palette = null;
}
public WPos Pos { get { return pos + offset; } }
public WVec Offset { get { return offset; } }
public PaletteReference Palette { get { return palette; } }
public int ZOffset { get { return zOffset; } }
public bool IsDecoration { get { return isDecoration; } }
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, float alpha,
float3 tint, TintModifiers tintModifiers, bool isDecoration)
: this(sprite, pos, offset, zOffset, palette, scale, alpha, tint, tintModifiers, isDecoration, WAngle.Zero) { }
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 WPos Pos => pos + Offset;
public WVec Offset { get; }
public PaletteReference Palette { get; }
public int ZOffset { get; }
public bool IsDecoration { get; }
public IRenderable WithTint(in float3 newTint) { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, newTint, isDecoration, ignoreWorldTint); }
public float Alpha { get; }
public float3 Tint { get; }
public TintModifiers TintModifiers { get; }
public IPalettedRenderable WithPalette(PaletteReference newPalette)
{
return new SpriteRenderable(sprite, pos, Offset, ZOffset, newPalette, scale, Alpha, Tint, TintModifiers, IsDecoration, rotation);
}
public IRenderable WithZOffset(int newOffset)
{
return new SpriteRenderable(sprite, pos, Offset, newOffset, Palette, scale, Alpha, Tint, TintModifiers, IsDecoration, rotation);
}
public IRenderable OffsetBy(in WVec vec)
{
return new SpriteRenderable(sprite, pos + vec, Offset, ZOffset, Palette, scale, Alpha, Tint, TintModifiers, IsDecoration, rotation);
}
public IRenderable AsDecoration()
{
return new SpriteRenderable(sprite, pos, Offset, ZOffset, Palette, scale, Alpha, Tint, TintModifiers, true, rotation);
}
public IModifyableRenderable WithAlpha(float newAlpha)
{
return new SpriteRenderable(sprite, pos, Offset, ZOffset, Palette, scale, newAlpha, Tint, TintModifiers, IsDecoration, rotation);
}
public IModifyableRenderable WithTint(in float3 newTint, TintModifiers newTintModifiers)
{
return new SpriteRenderable(sprite, pos, Offset, ZOffset, Palette, scale, Alpha, newTint, newTintModifiers, IsDecoration, rotation);
}
float3 ScreenPosition(WorldRenderer wr)
{
var xy = wr.ScreenPxPosition(pos) + wr.ScreenPxOffset(offset) - (0.5f * scale * sprite.Size.XY).ToInt2();
// HACK: The z offset needs to be applied somewhere, but this probably is the wrong place.
return new float3(xy, sprite.Offset.Z + wr.ScreenZPosition(pos, 0) - 0.5f * scale * sprite.Size.Z);
var s = 0.5f * scale * sprite.Size;
return wr.Screen3DPxPosition(pos) + wr.ScreenPxOffset(Offset) - new float3((int)s.X, (int)s.Y, s.Z);
}
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
public void Render(WorldRenderer wr)
{
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);
var t = Alpha * Tint;
if (wr.TerrainLighting != null && (TintModifiers & TintModifiers.IgnoreWorldTint) == 0)
t *= wr.TerrainLighting.TintAt(pos);
wsr.DrawSpriteWithTint(sprite, ScreenPosition(wr), palette, scale * sprite.Size, t);
}
// Shader interprets negative alpha as a flag to use the tint colour directly instead of multiplying the sprite colour
var a = Alpha;
if ((TintModifiers & TintModifiers.ReplaceColor) != 0)
a *= -1;
wsr.DrawSprite(sprite, Palette, ScreenPosition(wr), scale, t, a, rotation.RendererRadians());
}
public void RenderDebugGeometry(WorldRenderer wr)
@@ -89,13 +117,16 @@ namespace OpenRA.Graphics
var pos = ScreenPosition(wr) + sprite.Offset;
var tl = wr.Viewport.WorldToViewPx(pos);
var br = wr.Viewport.WorldToViewPx(pos + sprite.Size);
Game.Renderer.RgbaColorRenderer.DrawRect(tl, br, 1, Color.Red);
if (rotation == WAngle.Zero)
Game.Renderer.RgbaColorRenderer.DrawRect(tl, br, 1, Color.Red);
else
Game.Renderer.RgbaColorRenderer.DrawPolygon(Util.RotateQuad(tl, br - tl, rotation.RendererRadians()), 1, Color.Red);
}
public Rectangle ScreenBounds(WorldRenderer wr)
{
var screenOffset = ScreenPosition(wr) + sprite.Offset;
return new Rectangle((int)screenOffset.X, (int)screenOffset.Y, (int)sprite.Size.X, (int)sprite.Size.Y);
return Util.BoundingRectangle(screenOffset, sprite.Size, rotation.RendererRadians());
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -10,20 +10,20 @@
#endregion
using System;
using System.Linq;
using System.Collections.Generic;
using OpenRA.Primitives;
namespace OpenRA.Graphics
{
public class SpriteRenderer : Renderer.IBatchRenderer
{
const int SheetCount = 7;
static readonly string[] SheetIndexToTextureName = Exts.MakeArray(SheetCount, i => "Texture{0}".F(i));
public const int SheetCount = 8;
static readonly string[] SheetIndexToTextureName = Exts.MakeArray(SheetCount, i => $"Texture{i}");
readonly Renderer renderer;
readonly IShader shader;
readonly Vertex[] vertices;
Vertex[] vertices;
readonly Sheet[] sheets = new Sheet[SheetCount];
BlendMode currentBlend = BlendMode.Alpha;
@@ -34,7 +34,7 @@ namespace OpenRA.Graphics
{
this.renderer = renderer;
this.shader = shader;
vertices = new Vertex[renderer.TempBufferSize];
vertices = renderer.Context.CreateVertices(renderer.TempBufferSize);
}
public void Flush()
@@ -49,7 +49,9 @@ namespace OpenRA.Graphics
renderer.Context.SetBlendMode(currentBlend);
shader.PrepareRender();
renderer.DrawBatch(vertices, nv, PrimitiveType.TriangleList);
// PERF: The renderer may choose to replace vertices with a different temporary buffer.
renderer.DrawBatch(ref vertices, nv, PrimitiveType.TriangleList);
renderer.Context.SetBlendMode(BlendMode.None);
nv = 0;
@@ -81,119 +83,170 @@ namespace OpenRA.Graphics
for (; secondarySheetIndex < ns; secondarySheetIndex++)
if (sheets[secondarySheetIndex] == secondarySheet)
break;
// If neither sheet has been mapped both index values will be set to ns.
// This is fine if they both reference the same texture, but if they don't
// we must increment the secondary sheet index to the next free sampler.
if (secondarySheetIndex == sheetIndex && secondarySheet != sheet)
secondarySheetIndex++;
}
// Make sure that we have enough free samplers to map both if needed, otherwise flush
var needSamplers = (sheetIndex == ns ? 1 : 0) + (secondarySheetIndex == ns ? 1 : 0);
if (ns + needSamplers >= sheets.Length)
if (Math.Max(sheetIndex, secondarySheetIndex) >= sheets.Length)
{
Flush();
sheetIndex = 0;
if (ss != null)
secondarySheetIndex = 1;
secondarySheetIndex = ss != null && ss.SecondarySheet != sheet ? 1 : 0;
}
if (sheetIndex >= ns)
{
sheets[sheetIndex] = sheet;
ns += 1;
ns++;
}
if (secondarySheetIndex >= ns && ss != null)
{
sheets[secondarySheetIndex] = ss.SecondarySheet;
ns += 1;
ns++;
}
return new int2(sheetIndex, secondarySheetIndex);
}
internal void DrawSprite(Sprite s, in float3 location, float paletteTextureIndex, in float3 size)
static float ResolveTextureIndex(Sprite s, PaletteReference pal)
{
if (pal == null)
return 0;
// PERF: Remove useless palette assignments for RGBA sprites
// HACK: This is working around the limitation that palettes are defined on traits rather than on sequences,
// and can be removed once this has been fixed
if (s.Channel == TextureChannel.RGBA && !pal.HasColorShift)
return 0;
return pal.TextureIndex;
}
internal void DrawSprite(Sprite s, float paletteTextureIndex, in float3 location, in float3 scale, float rotation = 0f)
{
var samplers = SetRenderStateForSprite(s);
Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, samplers, paletteTextureIndex, nv, size, float3.Ones);
Util.FastCreateQuad(vertices, location + scale * s.Offset, s, samplers, paletteTextureIndex, nv, scale * s.Size, float3.Ones,
1f, rotation);
nv += 6;
}
public void DrawSprite(Sprite s, in float3 location, PaletteReference pal)
{
DrawSprite(s, location, pal.TextureIndex, s.Size);
}
public void DrawSprite(Sprite s, in float3 location, PaletteReference pal, float3 size)
{
DrawSprite(s, location, pal.TextureIndex, size);
}
public void DrawSprite(Sprite s, in float3 a, in float3 b, in float3 c, in float3 d)
internal void DrawSprite(Sprite s, float paletteTextureIndex, in float3 location, float scale, float rotation = 0f)
{
var samplers = SetRenderStateForSprite(s);
Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, 0, float3.Ones, nv);
Util.FastCreateQuad(vertices, location + scale * s.Offset, s, samplers, paletteTextureIndex, nv, scale * s.Size, float3.Ones,
1f, rotation);
nv += 6;
}
internal void DrawSpriteWithTint(Sprite s, in float3 location, float paletteTextureIndex, in float3 size, in float3 tint)
public void DrawSprite(Sprite s, PaletteReference pal, in float3 location, float scale = 1f, float rotation = 0f)
{
DrawSprite(s, ResolveTextureIndex(s, pal), location, scale, rotation);
}
internal void DrawSprite(Sprite s, float paletteTextureIndex, in float3 location, float scale, in float3 tint, float alpha,
float rotation = 0f)
{
var samplers = SetRenderStateForSprite(s);
Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, samplers, paletteTextureIndex, nv, size, tint);
Util.FastCreateQuad(vertices, location + scale * s.Offset, s, samplers, paletteTextureIndex, nv, scale * s.Size, tint, alpha,
rotation);
nv += 6;
}
public void DrawSpriteWithTint(Sprite s, in float3 location, PaletteReference pal, in float3 size, in float3 tint)
public void DrawSprite(Sprite s, PaletteReference pal, in float3 location, float scale, in float3 tint, float alpha,
float rotation = 0f)
{
DrawSpriteWithTint(s, location, pal.TextureIndex, size, tint);
DrawSprite(s, ResolveTextureIndex(s, pal), location, scale, tint, alpha, rotation);
}
public void DrawSpriteWithTint(Sprite s, in float3 a, in float3 b, in float3 c, in float3 d, in float3 tint)
internal void DrawSprite(Sprite s, float paletteTextureIndex, in float3 a, in float3 b, in float3 c, in float3 d, in float3 tint, float alpha)
{
var samplers = SetRenderStateForSprite(s);
Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, 0, tint, nv);
Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, paletteTextureIndex, tint, alpha, nv);
nv += 6;
}
public void DrawVertexBuffer(IVertexBuffer<Vertex> buffer, int start, int length, PrimitiveType type, Sheet sheet, BlendMode blendMode)
public void DrawVertexBuffer(IVertexBuffer<Vertex> buffer, int start, int length, PrimitiveType type, IEnumerable<Sheet> sheets, BlendMode blendMode)
{
shader.SetTexture("Texture0", sheet.GetTexture());
var i = 0;
foreach (var s in sheets)
{
if (i >= SheetCount)
ThrowSheetOverflow(nameof(sheets));
if (s != null)
shader.SetTexture(SheetIndexToTextureName[i++], s.GetTexture());
}
renderer.Context.SetBlendMode(blendMode);
shader.PrepareRender();
renderer.DrawBatch(buffer, start, length, type);
renderer.Context.SetBlendMode(BlendMode.None);
}
// PERF: methods that throw won't be inlined by the JIT, so extract a static helper for use on hot paths
static void ThrowSheetOverflow(string paramName)
{
throw new ArgumentException($"SpriteRenderer only supports {SheetCount} simultaneous textures", paramName);
}
// For RGBAColorRenderer
internal void DrawRGBAVertices(Vertex[] v)
internal void DrawRGBAVertices(Vertex[] v, BlendMode blendMode)
{
renderer.CurrentBatchRenderer = this;
if (currentBlend != BlendMode.Alpha || nv + v.Length > renderer.TempBufferSize)
if (currentBlend != blendMode || nv + v.Length > renderer.TempBufferSize)
Flush();
currentBlend = BlendMode.Alpha;
currentBlend = blendMode;
Array.Copy(v, 0, vertices, nv, v.Length);
nv += v.Length;
}
public void SetPalette(ITexture palette)
public void SetPalette(ITexture palette, ITexture colorShifts)
{
shader.SetTexture("Palette", palette);
shader.SetTexture("ColorShifts", colorShifts);
}
public void SetViewportParams(Size screen, float depthScale, float depthOffset, int2 scroll)
public void SetViewportParams(Size sheetSize, int downscale, float depthMargin, int2 scroll)
{
shader.SetVec("Scroll", scroll.X, scroll.Y, scroll.Y);
shader.SetVec("r1",
2f / screen.Width,
2f / screen.Height,
-depthScale / screen.Height);
shader.SetVec("r2", -1, -1, 1 - depthOffset);
// Calculate the scale (r1) and offset (r2) that convert from OpenRA viewport pixels
// to OpenGL normalized device coordinates (NDC). OpenGL expects coordinates to vary from [-1, 1],
// so we rescale viewport pixels to the range [0, 2] using r1 then subtract 1 using r2.
var width = 2f / (downscale * sheetSize.Width);
var height = 2f / (downscale * sheetSize.Height);
// Texture index is sampled as a float, so convert to pixels then scale
shader.SetVec("DepthTextureScale", 128 * depthScale / screen.Height);
// Depth is more complicated:
// * The OpenGL z axis is inverted (negative is closer) relative to OpenRA (positive is closer).
// * We want to avoid clipping pixels that are behind the nominal z == y plane at the
// top of the map, or above the nominal z == y plane at the bottom of the map.
// We therefore expand the depth range by an extra margin that is calculated based on
// the maximum expected world height (see Renderer.InitializeDepthBuffer).
// * Sprites can specify an additional per-pixel depth offset map, which is applied in the
// fragment shader. The fragment shader operates in OpenGL window coordinates, not NDC,
// with a depth range [0, 1] corresponding to the NDC [-1, 1]. We must therefore multiply the
// sprite channel value [0, 1] by 255 to find the pixel depth offset, then by our depth scale
// to find the equivalent NDC offset, then divide by 2 to find the window coordinate offset.
// * If depthMargin == 0 (which indicates per-pixel depth testing is disabled) sprites that
// extend beyond the top of bottom edges of the screen may be pushed outside [-1, 1] and
// culled by the GPU. We avoid this by forcing everything into the z = 0 plane.
var depth = depthMargin != 0f ? 2f / (downscale * (sheetSize.Height + depthMargin)) : 0;
shader.SetVec("DepthTextureScale", 128 * depth);
shader.SetVec("Scroll", scroll.X, scroll.Y, depthMargin != 0f ? scroll.Y : 0);
shader.SetVec("r1", width, height, -depth);
shader.SetVec("r2", -1, -1, depthMargin != 0f ? 1 : 0);
}
public void SetDepthPreviewEnabled(bool enabled)
public void SetDepthPreview(bool enabled, float contrast, float offset)
{
shader.SetBool("EnableDepthPreview", enabled);
shader.SetVec("DepthPreviewParams", contrast, offset);
}
public void SetAntialiasingPixelsPerTexel(float pxPerTx)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -15,14 +15,14 @@ using OpenRA.Primitives;
namespace OpenRA.Graphics
{
public struct TargetLineRenderable : IRenderable, IFinalizedRenderable
public class TargetLineRenderable : IRenderable, IFinalizedRenderable
{
readonly IEnumerable<WPos> waypoints;
readonly Color color;
readonly int width;
readonly int markerSize;
public TargetLineRenderable(IEnumerable<WPos> waypoints, Color color, int width = 1, int markerSize = 1)
public TargetLineRenderable(IEnumerable<WPos> waypoints, Color color, int width, int markerSize)
{
this.waypoints = waypoints;
this.color = color;
@@ -30,14 +30,19 @@ namespace OpenRA.Graphics
this.markerSize = markerSize;
}
public WPos Pos { get { return waypoints.First(); } }
public PaletteReference Palette { get { return null; } }
public int ZOffset { get { return 0; } }
public bool IsDecoration { get { return true; } }
public WPos Pos => waypoints.First();
public int ZOffset => 0;
public bool IsDecoration => true;
public IRenderable WithZOffset(int newOffset) { return this; }
public IRenderable OffsetBy(in WVec vec)
{
// Lambdas can't use 'in' variables, so capture a copy for later
var offset = vec;
return new TargetLineRenderable(waypoints.Select(w => w + offset), color, width, markerSize);
}
public IRenderable WithPalette(PaletteReference newPalette) { return new TargetLineRenderable(waypoints, color); }
public IRenderable WithZOffset(int newOffset) { return new TargetLineRenderable(waypoints, color); }
public IRenderable OffsetBy(WVec vec) { return new TargetLineRenderable(waypoints.Select(w => w + vec), color); }
public IRenderable AsDecoration() { return this; }
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
@@ -51,14 +56,14 @@ namespace OpenRA.Graphics
foreach (var b in waypoints.Skip(1).Select(pos => wr.Viewport.WorldToViewPx(wr.Screen3DPosition(pos))))
{
Game.Renderer.RgbaColorRenderer.DrawLine(a, b, width, color);
DrawTargetMarker(wr, color, b, markerSize);
DrawTargetMarker(color, b, markerSize);
a = b;
}
DrawTargetMarker(wr, color, first);
DrawTargetMarker(color, first);
}
public static void DrawTargetMarker(WorldRenderer wr, Color color, int2 screenPos, int size = 1)
public static void DrawTargetMarker(Color color, int2 screenPos, int size = 1)
{
var offset = new int2(size, size);
var tl = screenPos - offset;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -12,7 +12,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using OpenRA.Primitives;
namespace OpenRA.Graphics
{
@@ -20,37 +19,37 @@ namespace OpenRA.Graphics
{
static readonly int[] CornerVertexMap = { 0, 1, 2, 2, 3, 0 };
public readonly Sheet Sheet;
public readonly BlendMode BlendMode;
readonly Sheet[] sheets;
readonly Sprite emptySprite;
readonly IVertexBuffer<Vertex> vertexBuffer;
readonly Vertex[] vertices;
readonly bool[] ignoreTint;
readonly HashSet<int> dirtyRows = new HashSet<int>();
readonly HashSet<int> dirtyRows = new();
readonly int rowStride;
readonly bool restrictToBounds;
readonly WorldRenderer worldRenderer;
readonly Map map;
readonly PaletteReference palette;
readonly PaletteReference[] palettes;
public TerrainSpriteLayer(World world, WorldRenderer wr, Sheet sheet, BlendMode blendMode, PaletteReference palette, bool restrictToBounds)
public TerrainSpriteLayer(World world, WorldRenderer wr, Sprite emptySprite, BlendMode blendMode, bool restrictToBounds)
{
worldRenderer = wr;
this.restrictToBounds = restrictToBounds;
Sheet = sheet;
this.emptySprite = emptySprite;
sheets = new Sheet[SpriteRenderer.SheetCount];
BlendMode = blendMode;
this.palette = palette;
map = world.Map;
rowStride = 6 * map.MapSize.X;
vertices = new Vertex[rowStride * map.MapSize.Y];
palettes = new PaletteReference[map.MapSize.X * map.MapSize.Y];
vertexBuffer = Game.Renderer.Context.CreateVertexBuffer(vertices.Length);
emptySprite = new Sprite(sheet, Rectangle.Empty, TextureChannel.Alpha);
wr.PaletteInvalidated += UpdatePaletteIndices;
@@ -63,12 +62,11 @@ namespace OpenRA.Graphics
void UpdatePaletteIndices()
{
// Everything in the layer uses the same palette,
// so we can fix the indices in one pass
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, v.R, v.G, v.B);
var p = palettes[i / 6]?.TextureIndex ?? 0;
vertices[i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, p, v.C, v.R, v.G, v.B, v.A);
}
for (var row = 0; row < map.MapSize.Y; row++)
@@ -77,24 +75,24 @@ namespace OpenRA.Graphics
public void Clear(CPos cell)
{
Update(cell, null, true);
Update(cell, null, null, 1f, 1f, true);
}
public void Update(CPos cell, ISpriteSequence sequence, int frame)
public void Update(CPos cell, ISpriteSequence sequence, PaletteReference palette, int frame)
{
Update(cell, sequence.GetSprite(frame), sequence.IgnoreWorldTint);
Update(cell, sequence.GetSprite(frame), palette, sequence.Scale, sequence.GetAlpha(frame), sequence.IgnoreWorldTint);
}
public void Update(CPos cell, Sprite sprite, bool ignoreTint)
public void Update(CPos cell, Sprite sprite, PaletteReference palette, float scale = 1f, float alpha = 1f, bool ignoreTint = false)
{
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;
xyz = worldRenderer.Screen3DPosition(cellOrigin) + scale * (sprite.Offset - 0.5f * sprite.Size);
}
Update(cell.ToMPos(map.Grid.Type), sprite, xyz, ignoreTint);
Update(cell.ToMPos(map.Grid.Type), sprite, palette, xyz, scale, alpha, ignoreTint);
}
void UpdateTint(MPos uv)
@@ -102,11 +100,10 @@ namespace OpenRA.Graphics
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);
vertices[offset + i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, v.P, v.C, v.A * float3.Ones, v.A);
}
return;
@@ -117,7 +114,7 @@ namespace OpenRA.Graphics
// 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 step = map.Grid.TileScale / 2;
var weights = new[]
{
tl.TintAt(pos + new WVec(-step, -step, 0)),
@@ -131,31 +128,61 @@ namespace OpenRA.Graphics
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]]);
vertices[offset + i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, v.P, v.C, v.A * weights[CornerVertexMap[i]], v.A);
}
dirtyRows.Add(uv.V);
}
public void Update(MPos uv, Sprite sprite, in float3 pos, bool ignoreTint)
int GetOrAddSheetIndex(Sheet sheet)
{
if (sheet == null)
return 0;
for (var i = 0; i < sheets.Length; i++)
{
if (sheets[i] == sheet)
return i;
if (sheets[i] == null)
{
sheets[i] = sheet;
return i;
}
}
throw new InvalidDataException("Sheet overflow");
}
public void Update(MPos uv, Sprite sprite, PaletteReference palette, in float3 pos, float scale, float alpha, bool ignoreTint)
{
int2 samplers;
if (sprite != null)
{
if (sprite.Sheet != Sheet)
throw new InvalidDataException("Attempted to add sprite from a different sheet");
if (sprite.BlendMode != BlendMode)
throw new InvalidDataException("Attempted to add sprite with a different blend mode");
samplers = new int2(GetOrAddSheetIndex(sprite.Sheet), GetOrAddSheetIndex((sprite as SpriteWithSecondaryData)?.SecondarySheet));
// PERF: Remove useless palette assignments for RGBA sprites
// HACK: This is working around the limitation that palettes are defined on traits rather than on sequences,
// and can be removed once this has been fixed
if (sprite.Channel == TextureChannel.RGBA && !(palette?.HasColorShift ?? false))
palette = null;
}
else
{
sprite = emptySprite;
samplers = int2.Zero;
}
// The vertex buffer does not have geometry for cells outside the map
if (!map.Tiles.Contains(uv))
return;
var offset = rowStride * uv.V + 6 * uv.U;
Util.FastCreateQuad(vertices, pos, sprite, int2.Zero, palette.TextureIndex, offset, sprite.Size, float3.Ones);
Util.FastCreateQuad(vertices, pos, sprite, samplers, palette?.TextureIndex ?? 0, offset, scale * sprite.Size, alpha * float3.Ones, alpha);
palettes[uv.V * map.MapSize.X + uv.U] = palette;
if (worldRenderer.TerrainLighting != null)
{
@@ -188,7 +215,7 @@ namespace OpenRA.Graphics
Game.Renderer.WorldSpriteRenderer.DrawVertexBuffer(
vertexBuffer, rowStride * firstRow, rowStride * (lastRow - firstRow),
PrimitiveType.TriangleList, Sheet, BlendMode);
PrimitiveType.TriangleList, sheets, BlendMode);
Game.Renderer.Flush();
}

View File

@@ -1,198 +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;
using System.IO;
using System.Linq;
using OpenRA.Primitives;
using OpenRA.Support;
namespace OpenRA.Graphics
{
class TheaterTemplate
{
public readonly Sprite[] Sprites;
public readonly int Stride;
public readonly int Variants;
public TheaterTemplate(Sprite[] sprites, int stride, int variants)
{
Sprites = sprites;
Stride = stride;
Variants = variants;
}
}
public sealed class Theater : IDisposable
{
readonly Dictionary<ushort, TheaterTemplate> templates = new Dictionary<ushort, TheaterTemplate>();
SheetBuilder sheetBuilder;
readonly Sprite missingTile;
readonly MersenneTwister random;
TileSet tileset;
public Theater(TileSet tileset, Action<uint, string> onMissingImage = null)
{
this.tileset = tileset;
var allocated = false;
Func<Sheet> allocate = () =>
{
if (allocated)
throw new SheetOverflowException("Terrain sheet overflow. Try increasing the tileset SheetSize parameter.");
allocated = true;
return new Sheet(SheetType.Indexed, new Size(tileset.SheetSize, tileset.SheetSize));
};
random = new MersenneTwister();
var frameCache = new FrameCache(Game.ModData.DefaultFileSystem, Game.ModData.SpriteLoaders);
foreach (var t in tileset.Templates)
{
var variants = new List<Sprite[]>();
foreach (var i in t.Value.Images)
{
ISpriteFrame[] allFrames;
if (onMissingImage != null)
{
try
{
allFrames = frameCache[i];
}
catch (FileNotFoundException)
{
onMissingImage(t.Key, i);
continue;
}
}
else
allFrames = frameCache[i];
var frameCount = tileset.EnableDepth ? allFrames.Length / 2 : allFrames.Length;
var indices = t.Value.Frames != null ? t.Value.Frames : Exts.MakeArray(t.Value.TilesCount, j => j);
var start = indices.Min();
var end = indices.Max();
if (start < 0 || end >= frameCount)
throw new YamlException("Template `{0}` uses frames [{1}..{2}] of {3}, but only [0..{4}] actually exist"
.F(t.Key, start, end, i, frameCount - 1));
variants.Add(indices.Select(j =>
{
var f = allFrames[j];
var tile = t.Value.Contains(j) ? t.Value[j] : null;
// The internal z axis is inverted from expectation (negative is closer)
var zOffset = tile != null ? -tile.ZOffset : 0;
var zRamp = tile != null ? tile.ZRamp : 1f;
var offset = new float3(f.Offset, zOffset);
var type = SheetBuilder.FrameTypeToSheetType(f.Type);
// Defer SheetBuilder creation until we know what type of frames we are loading!
// TODO: Support mixed indexed and BGRA frames
if (sheetBuilder == null)
sheetBuilder = new SheetBuilder(SheetBuilder.FrameTypeToSheetType(f.Type), allocate);
else if (type != sheetBuilder.Type)
throw new YamlException("Sprite type mismatch. Terrain sprites must all be either Indexed or RGBA.");
var s = sheetBuilder.Allocate(f.Size, zRamp, offset);
Util.FastCopyIntoChannel(s, f.Data, f.Type);
if (tileset.EnableDepth)
{
var ss = sheetBuilder.Allocate(f.Size, zRamp, offset);
var depthFrame = allFrames[j + frameCount];
Util.FastCopyIntoChannel(ss, depthFrame.Data, depthFrame.Type);
// s and ss are guaranteed to use the same sheet
// because of the custom terrain sheet allocation
s = new SpriteWithSecondaryData(s, s.Sheet, ss.Bounds, ss.Channel);
}
return s;
}).ToArray());
}
var allSprites = variants.SelectMany(s => s);
// Ignore the offsets baked into R8 sprites
if (tileset.IgnoreTileSpriteOffsets)
allSprites = allSprites.Select(s => new Sprite(s.Sheet, s.Bounds, s.ZRamp, new float3(float2.Zero, s.Offset.Z), s.Channel, s.BlendMode));
if (onMissingImage != null && !variants.Any())
continue;
templates.Add(t.Value.Id, new TheaterTemplate(allSprites.ToArray(), variants.First().Count(), t.Value.Images.Length));
}
// 1x1px transparent tile
if (sheetBuilder.Type == SheetType.BGRA)
missingTile = sheetBuilder.Add(new byte[4], SpriteFrameType.Bgra32, new Size(1, 1));
else
missingTile = sheetBuilder.Add(new byte[1], SpriteFrameType.Indexed8, new Size(1, 1));
Sheet.ReleaseBuffer();
}
public bool HasTileSprite(TerrainTile r, int? variant = null)
{
return TileSprite(r, variant) != missingTile;
}
public Sprite TileSprite(TerrainTile r, int? variant = null)
{
if (!templates.TryGetValue(r.Type, out var template))
return missingTile;
if (r.Index >= template.Stride)
return missingTile;
var start = template.Variants > 1 ? variant.HasValue ? variant.Value : random.Next(template.Variants) : 0;
return template.Sprites[start * template.Stride + r.Index];
}
public Rectangle TemplateBounds(TerrainTemplateInfo template, Size tileSize, MapGridType mapGrid)
{
Rectangle? templateRect = null;
var i = 0;
for (var y = 0; y < template.Size.Y; y++)
{
for (var x = 0; x < template.Size.X; x++)
{
var tile = new TerrainTile(template.Id, (byte)(i++));
if (!tileset.TryGetTileInfo(tile, out var tileInfo))
continue;
var sprite = TileSprite(tile);
var u = mapGrid == MapGridType.Rectangular ? x : (x - y) / 2f;
var v = mapGrid == MapGridType.Rectangular ? y : (x + y) / 2f;
var tl = new float2(u * tileSize.Width, (v - 0.5f * tileInfo.Height) * tileSize.Height) - 0.5f * sprite.Size;
var rect = new Rectangle((int)(tl.X + sprite.Offset.X), (int)(tl.Y + sprite.Offset.Y), (int)sprite.Size.X, (int)sprite.Size.Y);
templateRect = templateRect.HasValue ? Rectangle.Union(templateRect.Value, rect) : rect;
}
}
return templateRect.HasValue ? templateRect.Value : Rectangle.Empty;
}
public Sheet Sheet { get { return sheetBuilder.Current; } }
public void Dispose()
{
sheetBuilder.Dispose();
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -13,54 +13,64 @@ using OpenRA.Primitives;
namespace OpenRA.Graphics
{
public struct UISpriteRenderable : IRenderable, IFinalizedRenderable
public class UISpriteRenderable : IRenderable, IPalettedRenderable, IFinalizedRenderable
{
readonly Sprite sprite;
readonly WPos effectiveWorldPos;
readonly int2 screenPos;
readonly int zOffset;
readonly PaletteReference palette;
readonly float scale;
readonly float alpha;
readonly float rotation = 0f;
public UISpriteRenderable(Sprite sprite, WPos effectiveWorldPos, int2 screenPos, int zOffset, PaletteReference palette, float scale)
public UISpriteRenderable(Sprite sprite, WPos effectiveWorldPos, int2 screenPos, int zOffset, PaletteReference palette, float scale = 1f, float alpha = 1f, float rotation = 0f)
{
this.sprite = sprite;
this.effectiveWorldPos = effectiveWorldPos;
Pos = effectiveWorldPos;
this.screenPos = screenPos;
this.zOffset = zOffset;
this.palette = palette;
ZOffset = zOffset;
Palette = palette;
this.scale = scale;
this.alpha = alpha;
this.rotation = rotation;
// PERF: Remove useless palette assignments for RGBA sprites
// HACK: This is working around the fact that palettes are defined on traits rather than sequences
// and can be removed once this has been fixed
if (sprite.Channel == TextureChannel.RGBA && !(palette?.HasColorShift ?? false))
Palette = null;
}
// Does not exist in the world, so a world positions don't make sense
public WPos Pos { get { return effectiveWorldPos; } }
public WVec Offset { get { return WVec.Zero; } }
public bool IsDecoration { get { return true; } }
public WPos Pos { get; }
public WVec Offset => WVec.Zero;
public bool IsDecoration => true;
public PaletteReference Palette { get { return palette; } }
public int ZOffset { get { return zOffset; } }
public PaletteReference Palette { get; }
public int ZOffset { get; }
public IRenderable WithPalette(PaletteReference newPalette) { return new UISpriteRenderable(sprite, effectiveWorldPos, screenPos, zOffset, newPalette, scale); }
public IPalettedRenderable WithPalette(PaletteReference newPalette) { return new UISpriteRenderable(sprite, Pos, screenPos, ZOffset, newPalette, scale, alpha, rotation); }
public IRenderable WithZOffset(int newOffset) { return this; }
public IRenderable OffsetBy(WVec vec) { return this; }
public IRenderable OffsetBy(in WVec vec) { return this; }
public IRenderable AsDecoration() { return this; }
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
public void Render(WorldRenderer wr)
{
Game.Renderer.SpriteRenderer.DrawSprite(sprite, screenPos, palette, scale * sprite.Size);
Game.Renderer.SpriteRenderer.DrawSprite(sprite, Palette, screenPos, scale, float3.Ones, alpha, rotation);
}
public void RenderDebugGeometry(WorldRenderer wr)
{
var offset = screenPos + sprite.Offset.XY;
Game.Renderer.RgbaColorRenderer.DrawRect(offset, offset + sprite.Size.XY, 1, Color.Red);
if (rotation == 0f)
Game.Renderer.RgbaColorRenderer.DrawRect(offset, offset + sprite.Size.XY, 1, Color.Red);
else
Game.Renderer.RgbaColorRenderer.DrawPolygon(Util.RotateQuad(offset, sprite.Size, rotation), 1, Color.Red);
}
public Rectangle ScreenBounds(WorldRenderer wr)
{
var offset = screenPos + sprite.Offset;
return new Rectangle((int)offset.X, (int)offset.Y, (int)sprite.Size.X, (int)sprite.Size.Y);
return Util.BoundingRectangle(offset, sprite.Size, rotation);
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -20,29 +20,60 @@ namespace OpenRA.Graphics
// yes, our channel order is nuts.
static readonly int[] ChannelMasks = { 2, 1, 0, 3 };
public static void FastCreateQuad(Vertex[] vertices, in float3 o, Sprite r, int2 samplers, float paletteTextureIndex, int nv, in float3 size, in float3 tint)
public static void FastCreateQuad(Vertex[] vertices, in float3 o, Sprite r, int2 samplers, float paletteTextureIndex, int nv,
in float3 size, in float3 tint, float alpha, float rotation = 0f)
{
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, tint, nv);
float3 a, b, c, d;
// Rotate sprite if rotation angle is not equal to 0
if (rotation != 0f)
{
var center = o + 0.5f * size;
var angleSin = (float)Math.Sin(-rotation);
var angleCos = (float)Math.Cos(-rotation);
// Rotated offset for +/- x with +/- y
var ra = 0.5f * new float3(
size.X * angleCos - size.Y * angleSin,
size.X * angleSin + size.Y * angleCos,
(size.X * angleSin + size.Y * angleCos) * size.Z / size.Y);
// Rotated offset for +/- x with -/+ y
var rb = 0.5f * new float3(
size.X * angleCos + size.Y * angleSin,
size.X * angleSin - size.Y * angleCos,
(size.X * angleSin - size.Y * angleCos) * size.Z / size.Y);
a = center - ra;
b = center + rb;
c = center + ra;
d = center - rb;
}
else
{
a = o;
b = new float3(o.X + size.X, o.Y, o.Z);
c = new float3(o.X + size.X, o.Y + size.Y, o.Z + size.Z);
d = new float3(o.X, o.Y + size.Y, o.Z + size.Z);
}
FastCreateQuad(vertices, a, b, c, d, r, samplers, paletteTextureIndex, tint, alpha, nv);
}
public static void FastCreateQuad(Vertex[] vertices,
in float3 a, in float3 b, in float3 c, in float3 d,
Sprite r, int2 samplers, float paletteTextureIndex,
in float3 tint, int nv)
in float3 tint, float alpha, int nv)
{
float sl = 0;
float st = 0;
float sr = 0;
float sb = 0;
// See shp.vert for documentation on the channel attribute format
// See combined.vert for documentation on the channel attribute format
var attribC = r.Channel == TextureChannel.RGBA ? 0x02 : ((byte)r.Channel) << 1 | 0x01;
attribC |= samplers.X << 6;
var ss = r as SpriteWithSecondaryData;
if (ss != null)
if (r is SpriteWithSecondaryData ss)
{
sl = ss.SecondaryLeft;
st = ss.SecondaryTop;
@@ -54,12 +85,12 @@ namespace OpenRA.Graphics
}
var fAttribC = (float)attribC;
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);
vertices[nv] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC, tint, alpha);
vertices[nv + 1] = new Vertex(b, r.Right, r.Top, sr, st, paletteTextureIndex, fAttribC, tint, alpha);
vertices[nv + 2] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC, tint, alpha);
vertices[nv + 3] = new Vertex(c, r.Right, r.Bottom, sr, sb, paletteTextureIndex, fAttribC, tint, alpha);
vertices[nv + 4] = new Vertex(d, r.Left, r.Bottom, sl, sb, paletteTextureIndex, fAttribC, tint, alpha);
vertices[nv + 5] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC, tint, alpha);
}
public static void FastCopyIntoChannel(Sprite dest, byte[] src, SpriteFrameType srcType)
@@ -109,7 +140,7 @@ namespace OpenRA.Graphics
}
default:
throw new InvalidOperationException("Unknown SpriteFrameType {0}".F(srcType));
throw new InvalidOperationException($"Unknown SpriteFrameType {srcType}");
}
var cc = Color.FromArgb(a, r, g, b);
@@ -182,7 +213,7 @@ namespace OpenRA.Graphics
// Pngs don't support BGR[A], so no need to include them here
default:
throw new InvalidOperationException("Unknown SpriteFrameType {0}".F(src.Type));
throw new InvalidOperationException($"Unknown SpriteFrameType {src.Type}");
}
data[(y + j) * destStride + x + i] = PremultiplyAlpha(cc).ToArgb();
@@ -192,6 +223,69 @@ namespace OpenRA.Graphics
}
}
/// <summary>Rotates a quad about its center in the x-y plane.</summary>
/// <param name="tl">The top left vertex of the quad.</param>
/// <param name="size">A float3 containing the X, Y, and Z lengths of the quad.</param>
/// <param name="rotation">The number of radians to rotate by.</param>
/// <returns>An array of four vertices representing the rotated quad (top-left, top-right, bottom-right, bottom-left).</returns>
public static float3[] RotateQuad(float3 tl, float3 size, float rotation)
{
var center = tl + 0.5f * size;
var angleSin = (float)Math.Sin(-rotation);
var angleCos = (float)Math.Cos(-rotation);
// Rotated offset for +/- x with +/- y
var ra = 0.5f * new float3(
size.X * angleCos - size.Y * angleSin,
size.X * angleSin + size.Y * angleCos,
(size.X * angleSin + size.Y * angleCos) * size.Z / size.Y);
// Rotated offset for +/- x with -/+ y
var rb = 0.5f * new float3(
size.X * angleCos + size.Y * angleSin,
size.X * angleSin - size.Y * angleCos,
(size.X * angleSin - size.Y * angleCos) * size.Z / size.Y);
return new float3[]
{
center - ra,
center + rb,
center + ra,
center - rb
};
}
/// <summary>
/// Returns the bounds of an object. Used for determining which objects need to be rendered on screen, and which do not.
/// </summary>
/// <param name="offset">The top left vertex of the object.</param>
/// <param name="size">A float 3 containing the X, Y, and Z lengths of the object.</param>
/// <param name="rotation">The angle to rotate the object by (use 0f if there is no rotation).</param>
public static Rectangle BoundingRectangle(float3 offset, float3 size, float rotation)
{
if (rotation == 0f)
return new Rectangle((int)offset.X, (int)offset.Y, (int)size.X, (int)size.Y);
var rotatedQuad = RotateQuad(offset, size, rotation);
var minX = rotatedQuad[0].X;
var maxX = rotatedQuad[0].X;
var minY = rotatedQuad[0].Y;
var maxY = rotatedQuad[0].Y;
for (var i = 1; i < rotatedQuad.Length; i++)
{
minX = Math.Min(rotatedQuad[i].X, minX);
maxX = Math.Max(rotatedQuad[i].X, maxX);
minY = Math.Min(rotatedQuad[i].Y, minY);
maxY = Math.Max(rotatedQuad[i].Y, maxY);
}
return new Rectangle(
(int)minX,
(int)minY,
(int)Math.Ceiling(maxX) - (int)minX,
(int)Math.Ceiling(maxY) - (int)minY);
}
public static Color PremultiplyAlpha(Color c)
{
if (c.A == byte.MaxValue)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -14,7 +14,7 @@ using System.Runtime.InteropServices;
namespace OpenRA.Graphics
{
[StructLayout(LayoutKind.Sequential)]
public struct Vertex
public readonly struct Vertex
{
// 3d position
public readonly float X, Y, Z;
@@ -26,24 +26,24 @@ namespace OpenRA.Graphics
public readonly float P, C;
// Color tint
public readonly float R, G, B;
public readonly float R, G, B, A;
public Vertex(in 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, float3.Ones) { }
: this(xyz.X, xyz.Y, xyz.Z, s, t, u, v, p, c, float3.Ones, 1f) { }
public Vertex(in float3 xyz, float s, float t, float u, float v, float p, float c, in float3 tint)
: this(xyz.X, xyz.Y, xyz.Z, s, t, u, v, p, c, tint.X, tint.Y, tint.Z) { }
public Vertex(in float3 xyz, float s, float t, float u, float v, float p, float c, in float3 tint, float a)
: this(xyz.X, xyz.Y, xyz.Z, s, t, u, v, p, c, tint.X, tint.Y, tint.Z, a) { }
public Vertex(float x, float y, float z, float s, float t, float u, float v, float p, float c, in 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, in float3 tint, float a)
: this(x, y, z, s, t, u, v, p, c, tint.X, tint.Y, tint.Z, a) { }
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)
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, float a)
{
X = x; Y = y; Z = z;
S = s; T = t;
U = u; V = v;
P = p; C = c;
R = r; G = g; B = b;
R = r; G = g; B = b; A = a;
}
}
}

View File

@@ -0,0 +1,36 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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
namespace OpenRA.Video
{
public interface IVideo
{
ushort FrameCount { get; }
byte Framerate { get; }
ushort Width { get; }
ushort Height { get; }
/// <summary>
/// Current frame color data in 32-bit BGRA.
/// </summary>
byte[] CurrentFrameData { get; }
int CurrentFrameIndex { get; }
void AdvanceFrame();
bool HasAudio { get; }
byte[] AudioData { get; }
int AudioChannels { get; }
int SampleBits { get; }
int SampleRate { get; }
void Reset();
}
}

View File

@@ -0,0 +1,32 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.IO;
namespace OpenRA.Video
{
public interface IVideoLoader
{
bool TryParseVideo(Stream s, bool useFramePadding, out IVideo video);
}
public static class VideoLoader
{
public static IVideo GetVideo(Stream stream, bool useFramePadding, IVideoLoader[] loaders)
{
foreach (var loader in loaders)
if (loader.TryParseVideo(stream, useFramePadding, out var video))
return video;
return null;
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -51,11 +51,11 @@ namespace OpenRA.Graphics
// Viewport geometry (world-px)
public int2 CenterLocation { get; private set; }
public WPos CenterPosition { get { return worldRenderer.ProjectedPosition(CenterLocation); } }
public WPos CenterPosition => worldRenderer.ProjectedPosition(CenterLocation);
public Rectangle Rectangle { get { return new Rectangle(TopLeft, new Size(viewportSize.X, viewportSize.Y)); } }
public int2 TopLeft { get { return CenterLocation - viewportSize / 2; } }
public int2 BottomRight { get { return CenterLocation + viewportSize / 2; } }
public Rectangle Rectangle => new(TopLeft, new Size(viewportSize.X, viewportSize.Y));
public int2 TopLeft => CenterLocation - viewportSize / 2;
public int2 BottomRight => CenterLocation + viewportSize / 2;
int2 viewportSize;
ProjectedCellRegion cells;
bool cellsDirty = true;
@@ -66,19 +66,13 @@ namespace OpenRA.Graphics
WorldViewport lastViewportDistance;
float zoom = 1f;
float minZoom = 1f;
float maxZoom = 2f;
bool unlockMinZoom;
float unlockedMinZoomScale;
float unlockedMinZoom = 1f;
public float Zoom
{
get
{
return zoom;
}
get => zoom;
private set
{
@@ -89,12 +83,13 @@ namespace OpenRA.Graphics
}
}
public float MinZoom { get { return minZoom; } }
public float MinZoom { get; private set; } = 1f;
public float MaxZoom { get; private set; } = 2f;
public void AdjustZoom(float dz)
{
// Exponential ensures that equal positive and negative steps have the same effect
Zoom = (zoom * (float)Math.Exp(dz)).Clamp(unlockMinZoom ? unlockedMinZoom : minZoom, maxZoom);
Zoom = (zoom * (float)Math.Exp(dz)).Clamp(unlockMinZoom ? unlockedMinZoom : MinZoom, MaxZoom);
}
public void AdjustZoom(float dz, int2 center)
@@ -108,10 +103,10 @@ namespace OpenRA.Graphics
public void ToggleZoom()
{
// Unlocked zooms always reset to the default zoom
if (zoom < minZoom)
Zoom = minZoom;
if (zoom < MinZoom)
Zoom = MinZoom;
else
Zoom = zoom > minZoom ? minZoom : maxZoom;
Zoom = zoom > MinZoom ? MinZoom : MaxZoom;
}
public void UnlockMinimumZoom(float scale)
@@ -124,23 +119,6 @@ namespace OpenRA.Graphics
public static long LastMoveRunTime = 0;
public static int2 LastMousePos;
float ClosestTo(float[] collection, float target)
{
var closestValue = collection.First();
var subtractResult = Math.Abs(closestValue - target);
foreach (var element in collection)
{
if (Math.Abs(element - target) < subtractResult)
{
subtractResult = Math.Abs(element - target);
closestValue = element;
}
}
return closestValue;
}
public ScrollDirection GetBlockedDirections()
{
var ret = ScrollDirection.None;
@@ -194,7 +172,7 @@ namespace OpenRA.Graphics
UpdateViewportZooms();
}
float CalculateMinimumZoom(float minHeight, float maxHeight)
static float CalculateMinimumZoom(float minHeight, float maxHeight)
{
var h = Game.Renderer.NativeResolution.Height;
@@ -230,31 +208,34 @@ namespace OpenRA.Graphics
var vd = graphicSettings.ViewportDistance;
if (viewportSizes.AllowNativeZoom && vd == WorldViewport.Native)
minZoom = 1;
MinZoom = viewportSizes.DefaultScale;
else
{
var range = viewportSizes.GetSizeRange(vd);
minZoom = CalculateMinimumZoom(range.X, range.Y);
MinZoom = CalculateMinimumZoom(range.X, range.Y) * viewportSizes.DefaultScale;
}
maxZoom = Math.Min(minZoom * viewportSizes.MaxZoomScale, Game.Renderer.NativeResolution.Height * 1f / viewportSizes.MaxZoomWindowHeight);
MaxZoom = Math.Min(MinZoom * viewportSizes.MaxZoomScale, Game.Renderer.NativeResolution.Height * viewportSizes.DefaultScale / viewportSizes.MaxZoomWindowHeight);
if (unlockMinZoom)
{
// Specators and the map editor support zooming out by an extra factor of two.
// Spectators and the map editor support zooming out by an extra factor of two.
// TODO: Allow zooming out until the full map is visible
// We need to improve our viewport scroll handling to center the map as we zoom out
// before this will work well enough to enable
unlockedMinZoom = minZoom * unlockedMinZoomScale;
unlockedMinZoom = MinZoom * unlockedMinZoomScale;
}
if (resetCurrentZoom)
Zoom = minZoom;
Zoom = MinZoom;
else
Zoom = Zoom.Clamp(minZoom, maxZoom);
Zoom = Zoom.Clamp(MinZoom, MaxZoom);
var maxSize = 1f / (unlockMinZoom ? unlockedMinZoom : MinZoom) * new float2(Game.Renderer.NativeResolution);
Game.Renderer.SetMaximumViewportSize(new Size((int)maxSize.X, (int)maxSize.Y));
foreach (var t in worldRenderer.World.WorldActor.TraitsImplementing<INotifyViewportZoomExtentsChanged>())
t.ViewportZoomExtentsChanged(minZoom, maxZoom);
t.ViewportZoomExtentsChanged(MinZoom, MaxZoom);
}
public CPos ViewToWorld(int2 view)
@@ -297,7 +278,7 @@ namespace OpenRA.Graphics
return worldRenderer.World.Map.CellContaining(worldRenderer.ProjectedPosition(ViewToWorldPx(view)));
}
/// <summary> Returns an unfiltered list of all cells that could potentially contain the mouse cursor</summary>
/// <summary>Returns an unfiltered list of all cells that could potentially contain the mouse cursor.</summary>
IEnumerable<MPos> CandidateMouseoverCells(int2 world)
{
var map = worldRenderer.World.Map;
@@ -313,8 +294,8 @@ namespace OpenRA.Graphics
}
public int2 ViewToWorldPx(int2 view) { return (graphicSettings.UIScale / Zoom * view.ToFloat2()).ToInt2() + TopLeft; }
public int2 WorldToViewPx(int2 world) { return ((Zoom / graphicSettings.UIScale) * (world - TopLeft).ToFloat2()).ToInt2(); }
public int2 WorldToViewPx(in float3 world) { return ((Zoom / graphicSettings.UIScale) * (world - TopLeft).XY).ToInt2(); }
public int2 WorldToViewPx(int2 world) { return (Zoom / graphicSettings.UIScale * (world - TopLeft).ToFloat2()).ToInt2(); }
public int2 WorldToViewPx(in float3 world) { return (Zoom / graphicSettings.UIScale * (world - TopLeft).XY).ToInt2(); }
public void Center(IEnumerable<Actor> actors)
{

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -21,38 +21,35 @@ namespace OpenRA.Graphics
public sealed class WorldRenderer : IDisposable
{
public static readonly Func<IRenderable, int> RenderableZPositionComparisonKey =
r => ZPosition(r.Pos, r.ZOffset);
r => r.Pos.Y + r.Pos.Z + r.ZOffset;
public readonly Size TileSize;
public readonly int TileScale;
public readonly World World;
public readonly Theater Theater;
public Viewport Viewport { get; private set; }
public Viewport Viewport { get; }
public readonly ITerrainLighting TerrainLighting;
public event Action PaletteInvalidated = null;
readonly HashSet<Actor> onScreenActors = new HashSet<Actor>();
readonly HardwarePalette palette = new HardwarePalette();
readonly Dictionary<string, PaletteReference> palettes = new Dictionary<string, PaletteReference>();
readonly HashSet<Actor> onScreenActors = new();
readonly HardwarePalette palette = new();
readonly Dictionary<string, PaletteReference> palettes = new();
readonly IRenderTerrain terrainRenderer;
readonly Lazy<DebugVisualizations> debugVis;
readonly Func<string, PaletteReference> createPaletteReference;
readonly bool enableDepthBuffer;
readonly List<IFinalizedRenderable> preparedRenderables = new List<IFinalizedRenderable>();
readonly List<IFinalizedRenderable> preparedOverlayRenderables = new List<IFinalizedRenderable>();
readonly List<IFinalizedRenderable> preparedAnnotationRenderables = new List<IFinalizedRenderable>();
readonly List<IFinalizedRenderable> preparedRenderables = new();
readonly List<IFinalizedRenderable> preparedOverlayRenderables = new();
readonly List<IFinalizedRenderable> preparedAnnotationRenderables = new();
readonly List<IRenderable> renderablesBuffer = new List<IRenderable>();
bool lastDepthPreviewEnabled;
readonly List<IRenderable> renderablesBuffer = new();
internal WorldRenderer(ModData modData, World world)
{
World = world;
TileSize = World.Map.Grid.TileSize;
TileScale = World.Map.Grid.Type == MapGridType.RectangularIsometric ? 1448 : 1024;
TileScale = World.Map.Grid.TileScale;
Viewport = new Viewport(this, world.Map);
createPaletteReference = CreatePaletteReference;
@@ -68,7 +65,6 @@ namespace OpenRA.Graphics
palette.Initialize();
Theater = new Theater(world.Map.Rules.TileSet);
TerrainLighting = world.WorldActor.TraitOrDefault<ITerrainLighting>();
terrainRenderer = world.WorldActor.TraitOrDefault<IRenderTerrain>();
@@ -87,7 +83,13 @@ namespace OpenRA.Graphics
return new PaletteReference(name, palette.GetPaletteIndex(name), pal, palette);
}
public PaletteReference Palette(string name) { return palettes.GetOrAdd(name, createPaletteReference); }
public PaletteReference Palette(string name)
{
// HACK: This is working around the fact that palettes are defined on traits rather than sequences
// and can be removed once this has been fixed.
return string.IsNullOrEmpty(name) ? null : palettes.GetOrAdd(name, createPaletteReference);
}
public void AddPalette(string name, ImmutablePalette pal, bool allowModifiers = false, bool allowOverwrite = false)
{
if (allowOverwrite && palette.Contains(name))
@@ -107,8 +109,13 @@ namespace OpenRA.Graphics
palette.ReplacePalette(name, pal);
// Update cached PlayerReference if one exists
if (palettes.ContainsKey(name))
palettes[name].Palette = pal;
if (palettes.TryGetValue(name, out var paletteReference))
paletteReference.Palette = pal;
}
public void SetPaletteColorShift(string name, float hueOffset, float satOffset, float valueModifier, float minHue, float maxHue)
{
palette.SetColorShift(name, hueOffset, satOffset, valueModifier, minHue, maxHue);
}
// PERF: Avoid LINQ.
@@ -144,14 +151,14 @@ namespace OpenRA.Graphics
// PERF: Avoid LINQ.
void GenerateOverlayRenderables()
{
foreach (var a in World.ActorsWithTrait<IRenderAboveShroud>())
World.ApplyToActorsWithTrait<IRenderAboveShroud>((actor, trait) =>
{
if (!a.Actor.IsInWorld || a.Actor.Disposed || (a.Trait.SpatiallyPartitionable && !onScreenActors.Contains(a.Actor)))
continue;
if (!actor.IsInWorld || actor.Disposed || (trait.SpatiallyPartitionable && !onScreenActors.Contains(actor)))
return;
foreach (var renderable in a.Trait.RenderAboveShroud(a.Actor, this))
foreach (var renderable in trait.RenderAboveShroud(actor, this))
preparedOverlayRenderables.Add(renderable.PrepareRender(this));
}
});
foreach (var a in World.Selection.Actors)
{
@@ -170,8 +177,7 @@ namespace OpenRA.Graphics
foreach (var e in World.Effects)
{
var ea = e as IEffectAboveShroud;
if (ea == null)
if (e is not IEffectAboveShroud ea)
continue;
foreach (var renderable in ea.RenderAboveShroud(this))
@@ -186,14 +192,14 @@ namespace OpenRA.Graphics
// PERF: Avoid LINQ.
void GenerateAnnotationRenderables()
{
foreach (var a in World.ActorsWithTrait<IRenderAnnotations>())
World.ApplyToActorsWithTrait<IRenderAnnotations>((actor, trait) =>
{
if (!a.Actor.IsInWorld || a.Actor.Disposed || (a.Trait.SpatiallyPartitionable && !onScreenActors.Contains(a.Actor)))
continue;
if (!actor.IsInWorld || actor.Disposed || (trait.SpatiallyPartitionable && !onScreenActors.Contains(actor)))
return;
foreach (var renderAnnotation in a.Trait.RenderAnnotations(a.Actor, this))
foreach (var renderAnnotation in trait.RenderAnnotations(actor, this))
preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this));
}
});
foreach (var a in World.Selection.Actors)
{
@@ -212,8 +218,7 @@ namespace OpenRA.Graphics
foreach (var e in World.Effects)
{
var ea = e as IEffectAnnotation;
if (ea == null)
if (e is not IEffectAnnotation ea)
continue;
foreach (var renderAnnotation in ea.RenderAnnotation(this))
@@ -247,11 +252,7 @@ namespace OpenRA.Graphics
if (World.WorldActor.Disposed)
return;
if (debugVis.Value != null && lastDepthPreviewEnabled != debugVis.Value.DepthBuffer)
{
lastDepthPreviewEnabled = debugVis.Value.DepthBuffer;
Game.Renderer.WorldSpriteRenderer.SetDepthPreviewEnabled(lastDepthPreviewEnabled);
}
debugVis.Value?.UpdateDepthBuffer();
var bounds = Viewport.GetScissorBounds(World.Type != WorldType.Editor);
Game.Renderer.EnableScissor(bounds);
@@ -269,15 +270,16 @@ namespace OpenRA.Graphics
if (enableDepthBuffer)
Game.Renderer.ClearDepthBuffer();
foreach (var a in World.ActorsWithTrait<IRenderAboveWorld>())
if (a.Actor.IsInWorld && !a.Actor.Disposed)
a.Trait.RenderAboveWorld(a.Actor, this);
World.ApplyToActorsWithTrait<IRenderAboveWorld>((actor, trait) =>
{
if (actor.IsInWorld && !actor.Disposed)
trait.RenderAboveWorld(actor, this);
});
if (enableDepthBuffer)
Game.Renderer.ClearDepthBuffer();
foreach (var a in World.ActorsWithTrait<IRenderShroud>())
a.Trait.RenderShroud(this);
World.ApplyToActorsWithTrait<IRenderShroud>((actor, trait) => trait.RenderShroud(this));
if (enableDepthBuffer)
Game.Renderer.Context.DisableDepthBuffer();
@@ -356,7 +358,13 @@ namespace OpenRA.Graphics
public float3 Screen3DPosition(WPos pos)
{
var z = ZPosition(pos, 0) * (float)TileSize.Height / TileScale;
// The projection from world coordinates to screen coordinates has
// a non-obvious relationship between the y and z coordinates:
// * A flat surface with constant y (e.g. a vertical wall) in world coordinates
// transforms into a flat surface with constant z (depth) in screen coordinates.
// * Increasing the world y coordinate increases screen y and z coordinates equally.
// * Increases the world z coordinate decreases screen y but doesn't change screen z.
var z = pos.Y * (float)TileSize.Height / TileScale;
return new float3((float)TileSize.Width * pos.X / TileScale, (float)TileSize.Height * (pos.Y - pos.Z) / TileScale, z);
}
@@ -375,7 +383,7 @@ namespace OpenRA.Graphics
}
// For scaling vectors to pixel sizes in the model renderer
public float3 ScreenVectorComponents(WVec vec)
public float3 ScreenVectorComponents(in WVec vec)
{
return new float3(
(float)TileSize.Width * vec.X / TileScale,
@@ -384,29 +392,19 @@ namespace OpenRA.Graphics
}
// For scaling vectors to pixel sizes in the model renderer
public float[] ScreenVector(WVec vec)
public float[] ScreenVector(in WVec vec)
{
var xyz = ScreenVectorComponents(vec);
return new[] { xyz.X, xyz.Y, xyz.Z, 1f };
}
public int2 ScreenPxOffset(WVec vec)
public int2 ScreenPxOffset(in WVec vec)
{
// Round to nearest pixel
var xyz = ScreenVectorComponents(vec);
return new int2((int)Math.Round(xyz.X), (int)Math.Round(xyz.Y));
}
public float ScreenZPosition(WPos pos, int offset)
{
return ZPosition(pos, offset) * (float)TileSize.Height / TileScale;
}
static int ZPosition(WPos pos, int offset)
{
return pos.Y + pos.Z + offset;
}
/// <summary>
/// Returns a position in the world that is projected to the given screen position.
/// There are many possible world positions, and the returned value chooses the value with no elevation.
@@ -425,7 +423,6 @@ namespace OpenRA.Graphics
World.Dispose();
palette.Dispose();
Theater.Dispose();
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -19,7 +19,9 @@ namespace OpenRA
public readonly string Name;
public readonly Hotkey Default = Hotkey.Invalid;
public readonly string Description = "";
public readonly HashSet<string> Types = new HashSet<string>();
public readonly HashSet<string> Types = new();
public readonly HashSet<string> Contexts = new();
public readonly bool Readonly = false;
public bool HasDuplicates { get; internal set; }
public HotkeyDefinition(string name, MiniYaml node)
@@ -36,6 +38,22 @@ namespace OpenRA
var typesNode = node.Nodes.FirstOrDefault(n => n.Key == "Types");
if (typesNode != null)
Types = FieldLoader.GetValue<HashSet<string>>("Types", typesNode.Value.Value);
var contextsNode = node.Nodes.FirstOrDefault(n => n.Key == "Contexts");
if (contextsNode != null)
Contexts = FieldLoader.GetValue<HashSet<string>>("Contexts", contextsNode.Value.Value);
var platformNode = node.Nodes.FirstOrDefault(n => n.Key == "Platform");
if (platformNode != null)
{
var platformOverride = platformNode.Value.Nodes.FirstOrDefault(n => n.Key == Platform.CurrentPlatform.ToString());
if (platformOverride != null)
Default = FieldLoader.GetValue<Hotkey>("value", platformOverride.Value.Value);
}
var readonlyNode = node.Nodes.FirstOrDefault(n => n.Key == "Readonly");
if (readonlyNode != null)
Readonly = FieldLoader.GetValue<bool>("Readonly", readonlyNode.Value.Value);
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -18,8 +18,8 @@ namespace OpenRA
public sealed class HotkeyManager
{
readonly Dictionary<string, Hotkey> settings;
readonly Dictionary<string, HotkeyDefinition> definitions = new Dictionary<string, HotkeyDefinition>();
readonly Dictionary<string, Hotkey> keys = new Dictionary<string, Hotkey>();
readonly Dictionary<string, HotkeyDefinition> definitions = new();
readonly Dictionary<string, Hotkey> keys = new();
public HotkeyManager(IReadOnlyFileSystem fileSystem, Dictionary<string, Hotkey> settings, Manifest manifest)
{
@@ -35,14 +35,17 @@ namespace OpenRA
foreach (var kv in settings)
{
if (definitions.ContainsKey(kv.Key))
if (definitions.TryGetValue(kv.Key, out var definition) && !definition.Readonly)
keys[kv.Key] = kv.Value;
}
foreach (var hd in definitions)
hd.Value.HasDuplicates = GetFirstDuplicate(hd.Value.Name, this[hd.Value.Name].GetValue(), hd.Value) != null;
hd.Value.HasDuplicates = GetFirstDuplicate(hd.Value, this[hd.Value.Name].GetValue()) != null;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Performance", "CA1854:Prefer the 'IDictionary.TryGetValue(TKey, out TValue)' method",
Justification = "Func must perform a live lookup in the collection, as the lookup value can change.")]
internal Func<Hotkey> GetHotkeyReference(string name)
{
// Is this a mod-defined hotkey?
@@ -61,6 +64,9 @@ namespace OpenRA
if (!definitions.TryGetValue(name, out var definition))
return;
if (definition.Readonly)
return;
keys[name] = value;
if (value != definition.Default)
settings[name] = value;
@@ -68,7 +74,7 @@ namespace OpenRA
settings.Remove(name);
var hadDuplicates = definition.HasDuplicates;
definition.HasDuplicates = GetFirstDuplicate(definition.Name, this[definition.Name].GetValue(), definition) != null;
definition.HasDuplicates = GetFirstDuplicate(definition, this[definition.Name].GetValue()) != null;
if (hadDuplicates || definition.HasDuplicates)
{
@@ -77,33 +83,30 @@ namespace OpenRA
if (hd.Value == definition)
continue;
hd.Value.HasDuplicates = GetFirstDuplicate(hd.Value.Name, this[hd.Value.Name].GetValue(), hd.Value) != null;
hd.Value.HasDuplicates = GetFirstDuplicate(hd.Value, this[hd.Value.Name].GetValue()) != null;
}
}
}
public HotkeyDefinition GetFirstDuplicate(string name, Hotkey value, HotkeyDefinition definition)
public HotkeyDefinition GetFirstDuplicate(HotkeyDefinition definition, Hotkey value)
{
if (definition == null)
return null;
foreach (var kv in keys)
{
if (kv.Key == name)
if (kv.Key == definition.Name)
continue;
if (kv.Value == value && definitions[kv.Key].Types.Overlaps(definition.Types))
if (kv.Value == value && definitions[kv.Key].Contexts.Overlaps(definition.Contexts))
return definitions[kv.Key];
}
return null;
}
public HotkeyReference this[string name]
{
get
{
return new HotkeyReference(GetHotkeyReference(name));
}
}
public HotkeyReference this[string name] => new(GetHotkeyReference(name));
public IEnumerable<HotkeyDefinition> Definitions { get { return definitions.Values; } }
public IEnumerable<HotkeyDefinition> Definitions => definitions.Values;
}
}

View File

@@ -0,0 +1,63 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace OpenRA
{
public delegate void OnProgress(long total, long totalRead, int progressPercentage);
public static class HttpExtension
{
public static async Task ReadAsStreamWithProgress(this HttpResponseMessage response, Stream outputStream, OnProgress onProgress, CancellationToken token)
{
var total = response.Content.Headers.ContentLength ?? -1;
var canReportProgress = total > 0;
#if NET5_0_OR_GREATER
using (var contentStream = await response.Content.ReadAsStreamAsync(token))
#else
using (var contentStream = await response.Content.ReadAsStreamAsync())
#endif
{
var totalRead = 0L;
var buffer = new byte[8192];
var hasMoreToRead = true;
do
{
var read = await contentStream.ReadAsync(buffer.AsMemory(0, buffer.Length), token);
if (read == 0)
hasMoreToRead = false;
else
{
await outputStream.WriteAsync(buffer.AsMemory(0, read), token);
totalRead += read;
if (canReportProgress)
{
var progressPercentage = (int)((double)totalRead / total * 100);
onProgress?.Invoke(total, totalRead, progressPercentage);
}
}
}
while (hasMoreToRead && !token.IsCancellationRequested);
onProgress?.Invoke(total, totalRead, 100);
}
}
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -9,12 +9,41 @@
*/
#endregion
using System;
using System.Reflection;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA
{
public class Utility
{
static readonly ConcurrentCache<Type, FieldInfo[]> TypeFields =
new(type => type.GetFields());
static readonly ConcurrentCache<(MemberInfo Member, Type AttributeType), bool> MemberHasAttribute =
new(x => Attribute.IsDefined(x.Member, x.AttributeType));
static readonly ConcurrentCache<(MemberInfo Member, Type AttributeType, bool Inherit), object[]> MemberCustomAttributes =
new(x => x.Member.GetCustomAttributes(x.AttributeType, x.Inherit));
public static FieldInfo[] GetFields(Type type)
{
return TypeFields[type];
}
public static bool HasAttribute<TAttribute>(MemberInfo member)
where TAttribute : Attribute
{
return MemberHasAttribute[(member, typeof(TAttribute))];
}
public static TAttribute[] GetCustomAttributes<TAttribute>(MemberInfo member, bool inherit)
where TAttribute : Attribute
{
return (TAttribute[])MemberCustomAttributes[(member, typeof(TAttribute), inherit)];
}
public readonly ModData ModData;
public readonly InstalledMods Mods;

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -13,9 +13,9 @@ using System;
namespace OpenRA
{
public struct Hotkey : IEquatable<Hotkey>
public readonly struct Hotkey : IEquatable<Hotkey>
{
public static Hotkey Invalid = new Hotkey(Keycode.UNKNOWN, Modifiers.None);
public static Hotkey Invalid = new(Keycode.UNKNOWN, Modifiers.None);
public bool IsValid()
{
return Key != Keycode.UNKNOWN;
@@ -42,7 +42,7 @@ namespace OpenRA
var mods = Modifiers.None;
if (parts.Length >= 2)
{
var modString = s.Substring(s.IndexOf(' '));
var modString = s[s.IndexOf(' ')..];
if (!Enum<Modifiers>.TryParse(modString, true, out mods))
return false;
}
@@ -81,11 +81,10 @@ namespace OpenRA
public override bool Equals(object obj)
{
var o = obj as Hotkey?;
return o != null && o == this;
return obj is Hotkey o && (Hotkey?)o == this;
}
public override string ToString() { return "{0} {1}".F(Key, Modifiers.ToString("F")); }
public override string ToString() { return $"{Key} {Modifiers:F}"; }
public string DisplayString()
{

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -14,7 +14,7 @@ using System;
namespace OpenRA
{
/// <summary>
/// A reference to either a named hotkey (defined in the game settings) or a statically assigned hotkey
/// A reference to either a named hotkey (defined in the game settings) or a statically assigned hotkey.
/// </summary>
public class HotkeyReference
{

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -37,36 +37,24 @@ namespace OpenRA
public void OnKeyInput(KeyInput input)
{
Sync.RunUnsynced(Game.Settings.Debug.SyncCheckUnsyncedCode, world, () => Ui.HandleKeyPress(input));
Sync.RunUnsynced(world, () => Ui.HandleKeyPress(input));
}
public void OnTextInput(string text)
{
Sync.RunUnsynced(Game.Settings.Debug.SyncCheckUnsyncedCode, world, () => Ui.HandleTextInput(text));
Sync.RunUnsynced(world, () => Ui.HandleTextInput(text));
}
public void OnMouseInput(MouseInput input)
{
Sync.RunUnsynced(Game.Settings.Debug.SyncCheckUnsyncedCode, world, () => Ui.HandleInput(input));
Sync.RunUnsynced(world, () => Ui.HandleInput(input));
}
}
public class MouseButtonPreference
{
public MouseButton Action
{
get
{
return Game.Settings.Game.UseClassicMouseStyle ? MouseButton.Left : MouseButton.Right;
}
}
public MouseButton Action => Game.Settings.Game.UseClassicMouseStyle ? MouseButton.Left : MouseButton.Right;
public MouseButton Cancel
{
get
{
return Game.Settings.Game.UseClassicMouseStyle ? MouseButton.Right : MouseButton.Left;
}
}
public MouseButton Cancel => Game.Settings.Game.UseClassicMouseStyle ? MouseButton.Right : MouseButton.Left;
}
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -13,7 +13,8 @@ using System.Collections.Generic;
namespace OpenRA
{
// List of keycodes, duplicated from SDL 2.0.1
// List of keycodes. Duplicated from SDL 2.0.1, with the addition
// of MOUSE4 and MOUSE5.
public enum Keycode
{
UNKNOWN = 0,
@@ -252,11 +253,13 @@ namespace OpenRA
KBDILLUMUP = 280 | (1 << 30),
EJECT = 281 | (1 << 30),
SLEEP = 282 | (1 << 30),
MOUSE4 = 283 | (1 << 30),
MOUSE5 = 284 | (1 << 30)
}
public static class KeycodeExts
{
static readonly Dictionary<Keycode, string> KeyNames = new Dictionary<Keycode, string>
static readonly Dictionary<Keycode, string> KeyNames = new()
{
{ Keycode.UNKNOWN, "Undefined" },
{ Keycode.RETURN, "Return" },
@@ -494,6 +497,8 @@ namespace OpenRA
{ Keycode.KBDILLUMUP, "KBDIllumUp" },
{ Keycode.EJECT, "Eject" },
{ Keycode.SLEEP, "Sleep" },
{ Keycode.MOUSE4, "Mouse 4" },
{ Keycode.MOUSE5, "Mouse 5" },
};
public static string DisplayString(Keycode k)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -15,22 +15,18 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using OpenRA.FileSystem;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA
{
public class InstalledMods : IReadOnlyDictionary<string, Manifest>
{
readonly Dictionary<string, Manifest> mods;
readonly SheetBuilder sheetBuilder;
/// <summary>Initializes the collection of locally installed mods.</summary>
/// <param name="searchPaths">Filesystem paths to search for mod packages.</param>
/// <param name="explicitPaths">Filesystem paths to additional mod packages.</param>
public InstalledMods(IEnumerable<string> searchPaths, IEnumerable<string> explicitPaths)
{
sheetBuilder = new SheetBuilder(SheetType.BGRA, 256);
mods = GetInstalledMods(searchPaths, explicitPaths);
}
@@ -58,7 +54,7 @@ namespace OpenRA
return mods;
}
Manifest LoadMod(string id, string path)
static Manifest LoadMod(string id, string path)
{
IReadOnlyPackage package = null;
try
@@ -75,7 +71,7 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Load mod '{0}': {1}".F(path, e));
Log.Write("debug", $"Load mod '{path}': {e}");
}
package?.Dispose();
@@ -83,7 +79,7 @@ namespace OpenRA
return null;
}
Dictionary<string, Manifest> GetInstalledMods(IEnumerable<string> searchPaths, IEnumerable<string> explicitPaths)
static Dictionary<string, Manifest> GetInstalledMods(IEnumerable<string> searchPaths, IEnumerable<string> explicitPaths)
{
var ret = new Dictionary<string, Manifest>();
var candidates = GetCandidateMods(searchPaths)
@@ -99,10 +95,10 @@ namespace OpenRA
return ret;
}
public Manifest this[string key] { get { return mods[key]; } }
public int Count { get { return mods.Count; } }
public ICollection<string> Keys { get { return mods.Keys; } }
public ICollection<Manifest> Values { get { return mods.Values; } }
public Manifest this[string key] => mods[key];
public IEnumerable<string> Keys => mods.Keys;
public IEnumerable<Manifest> Values => mods.Values;
public int Count => mods.Count;
public bool ContainsKey(string key) { return mods.ContainsKey(key); }
public IEnumerator<KeyValuePair<string, Manifest>> GetEnumerator() { return mods.GetEnumerator(); }
public bool TryGetValue(string key, out Manifest value) { return mods.TryGetValue(key, out value); }

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -12,10 +12,10 @@
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using OpenRA.Support;
namespace OpenRA
{
@@ -24,11 +24,11 @@ namespace OpenRA
const int AuthKeySize = 2048;
public enum LinkState { Uninitialized, GeneratingKeys, Unlinked, CheckingLink, ConnectionFailed, Linked }
public LinkState State { get { return innerState; } }
public string Fingerprint { get { return innerFingerprint; } }
public string PublicKey { get { return innerPublicKey; } }
public LinkState State => innerState;
public string Fingerprint => innerFingerprint;
public string PublicKey => innerPublicKey;
public PlayerProfile ProfileData { get { return innerData; } }
public PlayerProfile ProfileData => innerData;
volatile LinkState innerState;
volatile PlayerProfile innerData;
@@ -66,8 +66,10 @@ namespace OpenRA
}
catch (Exception e)
{
Console.WriteLine("Failed to load keys: {0}", e);
Log.Write("debug", "Failed to load player keypair from `{0}` with exception: {1}", filePath, e);
Console.WriteLine("Failed to load keys:");
Console.WriteLine(e);
Log.Write("debug", $"Failed to load player keypair from `{filePath}` with exception:");
Log.Write("debug", e);
}
}
@@ -76,23 +78,22 @@ namespace OpenRA
if (State != LinkState.Unlinked && State != LinkState.Linked && State != LinkState.ConnectionFailed)
return;
Action<DownloadDataCompletedEventArgs> onQueryComplete = i =>
Task.Run(async () =>
{
try
{
if (i.Error != null)
{
innerState = LinkState.ConnectionFailed;
return;
}
var client = HttpClientFactory.Create();
var yaml = MiniYaml.FromString(Encoding.UTF8.GetString(i.Result)).First();
var httpResponseMessage = await client.GetAsync(playerDatabase.Profile + Fingerprint);
var result = await httpResponseMessage.Content.ReadAsStreamAsync();
var yaml = MiniYaml.FromStream(result).First();
if (yaml.Key == "Player")
{
innerData = FieldLoader.Load<PlayerProfile>(yaml.Value);
if (innerData.KeyRevoked)
{
Log.Write("debug", "Revoking key with fingerprint {0}", Fingerprint);
Log.Write("debug", $"Revoking key with fingerprint {Fingerprint}");
DeleteKeypair();
}
else
@@ -103,17 +104,17 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Failed to parse player data result with exception: {0}", e);
Log.Write("debug", "Failed to parse player data result with exception:");
Log.Write("debug", e);
innerState = LinkState.ConnectionFailed;
}
finally
{
onComplete?.Invoke();
}
};
});
innerState = LinkState.CheckingLink;
new Download(playerDatabase.Profile + Fingerprint, _ => { }, onQueryComplete);
}
public void GenerateKeypair()
@@ -138,8 +139,10 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Failed to generate keypair with exception: {1}", e);
Console.WriteLine("Key generation failed: {0}", e);
Log.Write("debug", "Failed to generate keypair with exception:");
Log.Write("debug", e);
Console.WriteLine("Key generation failed:");
Console.WriteLine(e);
innerState = LinkState.Uninitialized;
}
@@ -154,12 +157,14 @@ namespace OpenRA
}
catch (Exception e)
{
Log.Write("debug", "Failed to delete keypair with exception: {1}", e);
Console.WriteLine("Key deletion failed: {0}", e);
Log.Write("debug", "Failed to delete keypair with exception:");
Log.Write("debug", e);
Console.WriteLine("Key deletion failed:");
Console.WriteLine(e);
}
innerState = LinkState.Uninitialized;
parameters = default(RSAParameters);
parameters = default;
innerFingerprint = null;
innerData = null;
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -14,12 +14,12 @@ using OpenRA.Primitives;
namespace OpenRA
{
public struct MPos : IEquatable<MPos>
public readonly struct MPos : IEquatable<MPos>
{
public readonly int U, V;
public MPos(int u, int v) { U = u; V = v; }
public static readonly MPos Zero = new MPos(0, 0);
public static readonly MPos Zero = new(0, 0);
public static bool operator ==(MPos me, MPos other) { return me.U == other.U && me.V == other.V; }
public static bool operator !=(MPos me, MPos other) { return !(me == other); }
@@ -27,7 +27,7 @@ namespace OpenRA
public override int GetHashCode() { return U.GetHashCode() ^ V.GetHashCode(); }
public bool Equals(MPos other) { return other == this; }
public override bool Equals(object obj) { return obj is MPos && Equals((MPos)obj); }
public override bool Equals(object obj) { return obj is MPos uv && Equals(uv); }
public MPos Clamp(Rectangle r)
{
@@ -64,14 +64,14 @@ namespace OpenRA
}
/// <summary>
/// Projected map position
/// Projected map position.
/// </summary>
public struct PPos : IEquatable<PPos>
public readonly struct PPos : IEquatable<PPos>
{
public readonly int U, V;
public PPos(int u, int v) { U = u; V = v; }
public static readonly PPos Zero = new PPos(0, 0);
public static readonly PPos Zero = new(0, 0);
public static bool operator ==(PPos me, PPos other) { return me.U == other.U && me.V == other.V; }
public static bool operator !=(PPos me, PPos other) { return !(me == other); }
@@ -88,7 +88,7 @@ namespace OpenRA
public override int GetHashCode() { return U.GetHashCode() ^ V.GetHashCode(); }
public bool Equals(PPos other) { return other == this; }
public override bool Equals(object obj) { return obj is PPos && Equals((PPos)obj); }
public override bool Equals(object obj) { return obj is PPos puv && Equals(puv); }
public override string ToString() { return U + "," + V; }
}

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -11,6 +11,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using OpenRA.FileSystem;
@@ -20,6 +21,17 @@ namespace OpenRA
{
public interface IGlobalModData { }
public sealed class TerrainFormat : IGlobalModData
{
public readonly string Type;
public readonly IReadOnlyDictionary<string, MiniYaml> Metadata;
public TerrainFormat(MiniYaml yaml)
{
Type = yaml.Value;
Metadata = new ReadOnlyDictionary<string, MiniYaml>(yaml.ToDictionary());
}
}
public sealed class SpriteSequenceFormat : IGlobalModData
{
public readonly string Type;
@@ -48,11 +60,12 @@ namespace OpenRA
public string Version;
public string Website;
public string WebIcon32;
public string WindowTitle;
public bool Hidden;
}
/// <summary> Describes what is to be loaded in order to run a mod. </summary>
public class Manifest : IDisposable
/// <summary>Describes what is to be loaded in order to run a mod.</summary>
public sealed class Manifest : IDisposable
{
public readonly string Id;
public readonly IReadOnlyPackage Package;
@@ -60,27 +73,29 @@ namespace OpenRA
public readonly string[]
Rules, ServerTraits,
Sequences, ModelSequences, Cursors, Chrome, Assemblies, ChromeLayout,
Weapons, Voices, Notifications, Music, TileSets,
Weapons, Voices, Notifications, Music, Translations, TileSets,
ChromeMetrics, MapCompatibility, Missions, Hotkeys;
public readonly IReadOnlyDictionary<string, string> Packages;
public readonly IReadOnlyDictionary<string, string> MapFolders;
public readonly MiniYaml LoadScreen;
public readonly string DefaultOrderGenerator;
public readonly string[] SoundFormats = { };
public readonly string[] SpriteFormats = { };
public readonly string[] PackageFormats = { };
public readonly string[] SoundFormats = Array.Empty<string>();
public readonly string[] SpriteFormats = Array.Empty<string>();
public readonly string[] PackageFormats = Array.Empty<string>();
public readonly string[] VideoFormats = Array.Empty<string>();
readonly string[] reservedModuleNames =
{
"Include", "Metadata", "Folders", "MapFolders", "Packages", "Rules",
"Sequences", "ModelSequences", "Cursors", "Chrome", "Assemblies", "ChromeLayout", "Weapons",
"Voices", "Notifications", "Music", "Translations", "TileSets", "ChromeMetrics", "Missions", "Hotkeys",
"ServerTraits", "LoadScreen", "SupportsMapsFrom", "SoundFormats", "SpriteFormats",
"ServerTraits", "LoadScreen", "DefaultOrderGenerator", "SupportsMapsFrom", "SoundFormats", "SpriteFormats", "VideoFormats",
"RequiresMods", "PackageFormats"
};
readonly TypeDictionary modules = new TypeDictionary();
readonly TypeDictionary modules = new();
readonly Dictionary<string, MiniYaml> yaml;
bool customDataLoaded;
@@ -100,7 +115,7 @@ namespace OpenRA
var filename = nodes[i].Value.Value;
var contents = package.GetStream(filename);
if (contents == null)
throw new YamlException("{0}: File `{1}` not found.".F(nodes[i].Location, filename));
throw new YamlException($"{nodes[i].Location}: File `{filename}` not found.");
nodes.RemoveAt(i);
nodes.InsertRange(i, MiniYaml.FromStream(contents, filename));
@@ -115,7 +130,7 @@ namespace OpenRA
MapFolders = YamlDictionary(yaml, "MapFolders");
if (yaml.TryGetValue("Packages", out var packages))
Packages = packages.ToDictionary(x => x.Value).AsReadOnly();
Packages = packages.ToDictionary(x => x.Value);
Rules = YamlList(yaml, "Rules");
Sequences = YamlList(yaml, "Sequences");
@@ -128,6 +143,7 @@ namespace OpenRA
Voices = YamlList(yaml, "Voices");
Notifications = YamlList(yaml, "Notifications");
Music = YamlList(yaml, "Music");
Translations = YamlList(yaml, "Translations");
TileSets = YamlList(yaml, "TileSets");
ChromeMetrics = YamlList(yaml, "ChromeMetrics");
Missions = YamlList(yaml, "Missions");
@@ -141,19 +157,25 @@ namespace OpenRA
// Allow inherited mods to import parent maps.
var compat = new List<string> { Id };
if (yaml.ContainsKey("SupportsMapsFrom"))
compat.AddRange(yaml["SupportsMapsFrom"].Value.Split(',').Select(c => c.Trim()));
if (yaml.TryGetValue("SupportsMapsFrom", out var entry))
compat.AddRange(entry.Value.Split(',').Select(c => c.Trim()));
MapCompatibility = compat.ToArray();
if (yaml.ContainsKey("PackageFormats"))
PackageFormats = FieldLoader.GetValue<string[]>("PackageFormats", yaml["PackageFormats"].Value);
if (yaml.TryGetValue("DefaultOrderGenerator", out entry))
DefaultOrderGenerator = entry.Value;
if (yaml.ContainsKey("SoundFormats"))
SoundFormats = FieldLoader.GetValue<string[]>("SoundFormats", yaml["SoundFormats"].Value);
if (yaml.TryGetValue("PackageFormats", out entry))
PackageFormats = FieldLoader.GetValue<string[]>("PackageFormats", entry.Value);
if (yaml.ContainsKey("SpriteFormats"))
SpriteFormats = FieldLoader.GetValue<string[]>("SpriteFormats", yaml["SpriteFormats"].Value);
if (yaml.TryGetValue("SoundFormats", out entry))
SoundFormats = FieldLoader.GetValue<string[]>("SoundFormats", entry.Value);
if (yaml.TryGetValue("SpriteFormats", out entry))
SpriteFormats = FieldLoader.GetValue<string[]>("SpriteFormats", entry.Value);
if (yaml.TryGetValue("VideoFormats", out entry))
VideoFormats = FieldLoader.GetValue<string[]>("VideoFormats", entry.Value);
}
public void LoadCustomData(ObjectCreator oc)
@@ -165,7 +187,7 @@ namespace OpenRA
var t = oc.FindType(kv.Key);
if (t == null || !typeof(IGlobalModData).IsAssignableFrom(t))
throw new InvalidDataException("`{0}` is not a valid mod manifest entry.".F(kv.Key));
throw new InvalidDataException($"`{kv.Key}` is not a valid mod manifest entry.");
IGlobalModData module;
var ctor = t.GetConstructor(new[] { typeof(MiniYaml) });
@@ -187,10 +209,10 @@ namespace OpenRA
customDataLoaded = true;
}
static string[] YamlList(Dictionary<string, MiniYaml> yaml, string key, bool parsePaths = false)
static string[] YamlList(Dictionary<string, MiniYaml> yaml, string key)
{
if (!yaml.ContainsKey(key))
return new string[] { };
return Array.Empty<string>();
return yaml[key].ToDictionary().Keys.ToArray();
}
@@ -198,10 +220,9 @@ namespace OpenRA
static IReadOnlyDictionary<string, string> YamlDictionary(Dictionary<string, MiniYaml> yaml, string key)
{
if (!yaml.ContainsKey(key))
return new ReadOnlyDictionary<string, string>();
return new Dictionary<string, string>();
var inner = yaml[key].ToDictionary(my => my.Value);
return new ReadOnlyDictionary<string, string>(inner);
return yaml[key].ToDictionary(my => my.Value);
}
public bool Contains<T>() where T : IGlobalModData

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* Copyright (c) The OpenRA Developers and Contributors
* 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
@@ -37,7 +37,7 @@ namespace OpenRA
public class ActorInitializer : IActorInitializer
{
public readonly Actor Self;
public World World { get { return Self.World; } }
public World World => Self.World;
internal TypeDictionary Dict;
@@ -54,7 +54,7 @@ namespace OpenRA
// 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))
if (!string.IsNullOrEmpty(info?.InstanceName))
return inits.LastOrDefault(i => i.InstanceName == info.InstanceName) ??
inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName));
@@ -66,7 +66,7 @@ namespace OpenRA
{
var init = GetOrDefault<T>(info);
if (init == null)
throw new InvalidOperationException("TypeDictionary does not contain instance of type `{0}`".F(typeof(T)));
throw new InvalidOperationException($"TypeDictionary does not contain instance of type `{typeof(T)}`");
return init;
}
@@ -140,7 +140,7 @@ namespace OpenRA
public abstract class ValueActorInit<T> : ActorInit
{
protected readonly T value;
readonly T value;
protected ValueActorInit(TraitInfo info, T value)
: base(info.InstanceName) { this.value = value; }
@@ -150,18 +150,18 @@ namespace OpenRA
protected ValueActorInit(T value) { this.value = value; }
public virtual T Value { get { return value; } }
public virtual T Value => value;
public virtual void Initialize(MiniYaml yaml)
{
Initialize((T)FieldLoader.GetValue("value", typeof(T), yaml.Value));
Initialize((T)FieldLoader.GetValue(nameof(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);
typeof(ValueActorInit<T>)
.GetField(nameof(value), BindingFlags.NonPublic | BindingFlags.Instance)
?.SetValue(this, value);
}
public override MiniYaml Save()
@@ -226,7 +226,7 @@ namespace OpenRA
public class OwnerInit : ActorInit, ISingleInstanceInit
{
public readonly string InternalName;
protected readonly Player value;
readonly Player value;
public OwnerInit(Player value)
{
@@ -246,16 +246,16 @@ namespace OpenRA
public void Initialize(MiniYaml yaml)
{
var field = GetType().GetField("InternalName", BindingFlags.Public | BindingFlags.Instance);
if (field != null)
field.SetValue(this, yaml.Value);
typeof(OwnerInit)
.GetField(nameof(InternalName), BindingFlags.Public | BindingFlags.Instance)
?.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);
typeof(OwnerInit)
.GetField(nameof(value), BindingFlags.NonPublic | BindingFlags.Instance)
?.SetValue(this, player);
}
public override MiniYaml Save()

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