diff --git a/.editorconfig b/.editorconfig index a26479d084..e63f1f56b8 100644 --- a/.editorconfig +++ b/.editorconfig @@ -836,8 +836,8 @@ dotnet_diagnostic.CA1850.severity = none # Possible multiple enumerations of IEnumerable collection. #dotnet_code_quality.CA1851.enumeration_methods = -#dotnet_code_quality.CA1851.linq_chain_methods = -#dotnet_code_quality.CA1851.assume_method_enumerates_parameters = false +dotnet_code_quality.CA1851.linq_chain_methods = M:OpenRA.Traits.IRenderModifier.Modify* +dotnet_code_quality.CA1851.assume_method_enumerates_parameters = true dotnet_diagnostic.CA1851.severity = warning # Seal internal types. diff --git a/OpenRA.Game/Exts.cs b/OpenRA.Game/Exts.cs index 97a79ed0eb..26c38fa702 100644 --- a/OpenRA.Game/Exts.cs +++ b/OpenRA.Game/Exts.cs @@ -147,7 +147,7 @@ namespace OpenRA static T Random(IEnumerable ts, MersenneTwister r, bool throws) { - var xs = ts as ICollection; + var xs = ts as IReadOnlyCollection; xs ??= ts.ToList(); if (xs.Count == 0) { diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index 892579ca9e..6520b0476a 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -523,10 +523,11 @@ namespace OpenRA .Where(m => m.Status == MapStatus.Available && m.Visibility.HasFlag(MapVisibility.Shellmap)) .Select(m => m.Uid); - if (!shellmaps.Any()) + var shellmap = shellmaps.RandomOrDefault(CosmeticRandom); + if (shellmap == null) throw new InvalidDataException("No valid shellmaps available"); - return shellmaps.Random(CosmeticRandom); + return shellmap; } public static void SwitchToExternalMod(ExternalMod mod, string[] launchArguments = null, Action onFailed = null) diff --git a/OpenRA.Game/GameRules/ActorInfo.cs b/OpenRA.Game/GameRules/ActorInfo.cs index 834f70fff9..710b08f865 100644 --- a/OpenRA.Game/GameRules/ActorInfo.cs +++ b/OpenRA.Game/GameRules/ActorInfo.cs @@ -130,6 +130,7 @@ namespace OpenRA // 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. +#pragma warning disable CA1851 // Possible multiple enumerations of 'IEnumerable' collection var readyToResolve = more.ToList(); while (readyToResolve.Count != 0) { @@ -138,6 +139,7 @@ namespace OpenRA readyToResolve.Clear(); readyToResolve.AddRange(more); } +#pragma warning restore CA1851 if (unresolved.Count != 0) { diff --git a/OpenRA.Game/Graphics/Viewport.cs b/OpenRA.Game/Graphics/Viewport.cs index 1100422eda..a28bfbd66f 100644 --- a/OpenRA.Game/Graphics/Viewport.cs +++ b/OpenRA.Game/Graphics/Viewport.cs @@ -299,10 +299,13 @@ namespace OpenRA.Graphics public void Center(IEnumerable actors) { - if (!actors.Any()) + var actorsCollection = actors as IReadOnlyCollection; + actorsCollection ??= actors.ToList(); + + if (actorsCollection.Count == 0) return; - Center(actors.Select(a => a.CenterPosition).Average()); + Center(actorsCollection.Select(a => a.CenterPosition).Average()); } public void Center(WPos pos) diff --git a/OpenRA.Game/SelectableExts.cs b/OpenRA.Game/SelectableExts.cs index 6b73198926..10b6507b0b 100644 --- a/OpenRA.Game/SelectableExts.cs +++ b/OpenRA.Game/SelectableExts.cs @@ -62,10 +62,7 @@ namespace OpenRA.Traits public static Actor WithHighestSelectionPriority(this IEnumerable actors, int2 selectionPixel, Modifiers modifiers) { - if (!actors.Any()) - return null; - - return actors.MaxBy(a => CalculateActorSelectionPriority(a.Actor.Info, a.Bounds, selectionPixel, modifiers)).Actor; + return actors.MaxByOrDefault(a => CalculateActorSelectionPriority(a.Actor.Info, a.Bounds, selectionPixel, modifiers)).Actor; } public static FrozenActor WithHighestSelectionPriority(this IEnumerable actors, int2 selectionPixel, Modifiers modifiers) diff --git a/OpenRA.Game/Support/AssemblyLoader.cs b/OpenRA.Game/Support/AssemblyLoader.cs index 00a093e566..de6fa2ebd7 100644 --- a/OpenRA.Game/Support/AssemblyLoader.cs +++ b/OpenRA.Game/Support/AssemblyLoader.cs @@ -349,7 +349,7 @@ namespace OpenRA.Support return Enumerable.Concat(new[] { runtimeGraph.Runtime }, runtimeGraph?.Fallbacks ?? Enumerable.Empty()); } - static IEnumerable SelectAssets(IEnumerable rids, IEnumerable groups) + static IEnumerable SelectAssets(IEnumerable rids, IReadOnlyCollection groups) { foreach (var rid in rids) { diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index e103625250..89ce8ba4f6 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -455,7 +455,7 @@ namespace OpenRA.Traits public interface ISelection { int Hash { get; } - IEnumerable Actors { get; } + IReadOnlyCollection Actors { get; } void Add(Actor a); void Remove(Actor a); diff --git a/OpenRA.Mods.Common/Activities/Air/Land.cs b/OpenRA.Mods.Common/Activities/Air/Land.cs index ebcbecc798..a4fc45a79c 100644 --- a/OpenRA.Mods.Common/Activities/Air/Land.cs +++ b/OpenRA.Mods.Common/Activities/Air/Land.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Linq; using OpenRA.Activities; using OpenRA.Mods.Common.Traits; using OpenRA.Primitives; @@ -211,7 +212,7 @@ namespace OpenRA.Mods.Common.Activities if (!landingInitiated) { - var blockingCells = clearCells.Append(landingCell); + var blockingCells = clearCells.Append(landingCell).ToList(); if (!aircraft.CanLand(blockingCells, target.Actor)) { diff --git a/OpenRA.Mods.Common/Lint/CheckConditions.cs b/OpenRA.Mods.Common/Lint/CheckConditions.cs index 4dc4060f25..93e671a49e 100644 --- a/OpenRA.Mods.Common/Lint/CheckConditions.cs +++ b/OpenRA.Mods.Common/Lint/CheckConditions.cs @@ -66,12 +66,12 @@ namespace OpenRA.Mods.Common.Lint granted.Add(g); } - var unconsumed = granted.Except(consumed); - if (unconsumed.Any()) + var unconsumed = granted.Except(consumed).ToList(); + if (unconsumed.Count != 0) emitWarning($"Actor type `{actorInfo.Key}` grants conditions that are not consumed: {unconsumed.JoinWith(", ")}."); - var ungranted = consumed.Except(granted); - if (ungranted.Any()) + var ungranted = consumed.Except(granted).ToList(); + if (ungranted.Count != 0) emitError($"Actor type `{actorInfo.Key}` consumes conditions that are not granted: {ungranted.JoinWith(", ")}."); } } diff --git a/OpenRA.Mods.Common/Lint/CheckSyncAnnotations.cs b/OpenRA.Mods.Common/Lint/CheckSyncAnnotations.cs index 43791f7410..db6c2afb23 100644 --- a/OpenRA.Mods.Common/Lint/CheckSyncAnnotations.cs +++ b/OpenRA.Mods.Common/Lint/CheckSyncAnnotations.cs @@ -21,8 +21,7 @@ namespace OpenRA.Mods.Common.Lint public void Run(Action emitError, Action emitWarning, ModData modData) { var modTypes = modData.ObjectCreator.GetTypes(); - CheckTypesWithSyncableMembersImplementSyncInterface(modTypes, emitWarning); - CheckTypesImplementingSyncInterfaceHaveSyncableMembers(modTypes, emitWarning); + CheckTypes(modTypes, emitWarning); } static readonly Type SyncInterface = typeof(ISync); @@ -45,18 +44,18 @@ namespace OpenRA.Mods.Common.Lint return false; } - static void CheckTypesWithSyncableMembersImplementSyncInterface(IEnumerable types, Action emitWarning) + static void CheckTypes(IEnumerable types, Action emitWarning) { foreach (var type in types) - if (!TypeImplementsSync(type) && AnyTypeMemberIsSynced(type)) - emitWarning($"{type.FullName} has members with the Sync attribute but does not implement ISync."); - } + { + var typeImplementsSync = TypeImplementsSync(type); + var anyTypeMemberIsSynced = AnyTypeMemberIsSynced(type); - static void CheckTypesImplementingSyncInterfaceHaveSyncableMembers(IEnumerable types, Action emitWarning) - { - foreach (var type in types) - if (TypeImplementsSync(type) && !AnyTypeMemberIsSynced(type)) + if (!typeImplementsSync && anyTypeMemberIsSynced) + emitWarning($"{type.FullName} has members with the Sync attribute but does not implement ISync."); + else if (typeImplementsSync && !anyTypeMemberIsSynced) emitWarning($"{type.FullName} implements ISync but does not use the Sync attribute on any members."); + } } } } diff --git a/OpenRA.Mods.Common/Orders/UnitOrderGenerator.cs b/OpenRA.Mods.Common/Orders/UnitOrderGenerator.cs index 4c462d1c84..5a33219181 100644 --- a/OpenRA.Mods.Common/Orders/UnitOrderGenerator.cs +++ b/OpenRA.Mods.Common/Orders/UnitOrderGenerator.cs @@ -85,7 +85,7 @@ namespace OpenRA.Mods.Common.Orders return cursorOrder.Cursor; useSelect = target.Type == TargetType.Actor && target.Actor.Info.HasTraitInfo() && - (mi.Modifiers.HasModifier(Modifiers.Shift) || !world.Selection.Actors.Any()); + (mi.Modifiers.HasModifier(Modifiers.Shift) || world.Selection.Actors.Count == 0); } return useSelect ? worldSelectCursor : worldDefaultCursor; diff --git a/OpenRA.Mods.Common/Scripting/Global/ActorGlobal.cs b/OpenRA.Mods.Common/Scripting/Global/ActorGlobal.cs index ea8d22b990..9dc5d0c130 100644 --- a/OpenRA.Mods.Common/Scripting/Global/ActorGlobal.cs +++ b/OpenRA.Mods.Common/Scripting/Global/ActorGlobal.cs @@ -72,7 +72,8 @@ namespace OpenRA.Mods.Common.Scripting } var initializers = initType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) - .Where(m => m.Name == "Initialize" && m.GetParameters().Length == 1); + .Where(m => m.Name == "Initialize" && m.GetParameters().Length == 1) + .ToList(); foreach (var initializer in initializers) { diff --git a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs index 57c65d8f57..1599e3e53f 100644 --- a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs +++ b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs @@ -530,9 +530,9 @@ namespace OpenRA.Mods.Common.Server // Pick a random color for the bot var colorManager = server.ModData.DefaultRules.Actors[SystemActors.World].TraitInfo(); - var terrainColors = server.ModData.DefaultTerrainInfo[server.Map.TileSet].RestrictedPlayerColors; + var terrainColors = server.ModData.DefaultTerrainInfo[server.Map.TileSet].RestrictedPlayerColors.ToList(); var playerColors = server.LobbyInfo.Clients.Select(c => c.Color) - .Concat(server.Map.Players.Players.Values.Select(p => p.Color)); + .Concat(server.Map.Players.Players.Values.Select(p => p.Color)).ToList(); bot.Color = bot.PreferredColor = colorManager.RandomPresetColor(server.Random, terrainColors, playerColors); @@ -935,7 +935,8 @@ namespace OpenRA.Mods.Common.Server return true; var factions = server.Map.WorldActorInfo.TraitInfos() - .Where(f => f.Selectable).Select(f => f.InternalName); + .Where(f => f.Selectable).Select(f => f.InternalName) + .ToList(); var faction = parts[1]; if (!factions.Contains(faction)) @@ -1250,7 +1251,7 @@ namespace OpenRA.Mods.Common.Server server.SendLocalizedMessageTo(connectionToEcho, message); } - var terrainColors = server.ModData.DefaultTerrainInfo[server.Map.TileSet].RestrictedPlayerColors; + var terrainColors = server.ModData.DefaultTerrainInfo[server.Map.TileSet].RestrictedPlayerColors.ToList(); var playerColors = server.LobbyInfo.Clients.Where(c => c.Index != playerIndex).Select(c => c.Color) .Concat(server.Map.Players.Players.Values.Select(p => p.Color)).ToList(); diff --git a/OpenRA.Mods.Common/Traits/BotModules/BaseBuilderBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/BaseBuilderBotModule.cs index 09776a5e22..fa137dc4dc 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/BaseBuilderBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/BaseBuilderBotModule.cs @@ -239,9 +239,10 @@ namespace OpenRA.Mods.Common.Traits CPos ChooseRallyLocationNear(Actor producer) { var possibleRallyPoints = world.Map.FindTilesInCircle(producer.Location, Info.RallyPointScanRadius) - .Where(c => IsRallyPointValid(c, producer.Info.TraitInfoOrDefault())); + .Where(c => IsRallyPointValid(c, producer.Info.TraitInfoOrDefault())) + .ToList(); - if (!possibleRallyPoints.Any()) + if (possibleRallyPoints.Count == 0) { AIUtils.BotDebug("{0} has no possible rallypoint near {1}", producer.Owner, producer.Location); return producer.Location; diff --git a/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/BaseBuilderQueueManager.cs b/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/BaseBuilderQueueManager.cs index f9a212aca2..83db0b4c41 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/BaseBuilderQueueManager.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/BaseBuilderQueueManager.cs @@ -229,7 +229,7 @@ namespace OpenRA.Mods.Common.Traits ActorInfo ChooseBuildingToBuild(ProductionQueue queue) { - var buildableThings = queue.BuildableItems(); + var buildableThings = queue.BuildableItems().ToList(); // This gets used quite a bit, so let's cache it here var power = GetProducibleBuilding(baseBuilder.Info.PowerTypes, buildableThings, diff --git a/OpenRA.Mods.Common/Traits/BotModules/CaptureManagerBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/CaptureManagerBotModule.cs index b43fdd35d4..14a208429a 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/CaptureManagerBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/CaptureManagerBotModule.cs @@ -154,12 +154,13 @@ namespace OpenRA.Mods.Common.Traits if (Info.CapturableActorTypes.Count > 0) capturableTargetOptions = capturableTargetOptions.Where(target => Info.CapturableActorTypes.Contains(target.Info.Name.ToLowerInvariant())); - if (!capturableTargetOptions.Any()) + var capturableTargetOptionsList = capturableTargetOptions.ToList(); + if (capturableTargetOptionsList.Count == 0) return; foreach (var capturer in capturers) { - var targetActor = capturableTargetOptions.MinByOrDefault(target => (target.CenterPosition - capturer.Actor.CenterPosition).LengthSquared); + var targetActor = capturableTargetOptionsList.MinByOrDefault(target => (target.CenterPosition - capturer.Actor.CenterPosition).LengthSquared); if (targetActor == null) continue; diff --git a/OpenRA.Mods.Common/Traits/BotModules/SquadManagerBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/SquadManagerBotModule.cs index 03cf48e29a..fee5de3f6a 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/SquadManagerBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/SquadManagerBotModule.cs @@ -197,7 +197,7 @@ namespace OpenRA.Mods.Common.Traits internal Actor FindClosestEnemy(WPos pos) { - var units = World.Actors.Where(IsPreferredEnemyUnit); + var units = World.Actors.Where(IsPreferredEnemyUnit).ToList(); return units.Where(IsNotHiddenUnit).ClosestTo(pos) ?? units.ClosestTo(pos); } diff --git a/OpenRA.Mods.Common/Traits/BotModules/Squads/States/NavyStates.cs b/OpenRA.Mods.Common/Traits/BotModules/Squads/States/NavyStates.cs index 4371aff747..8c706545e1 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/Squads/States/NavyStates.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/Squads/States/NavyStates.cs @@ -35,10 +35,9 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads && mobile.PathFinder.PathExistsForLocomotor(mobile.Locomotor, first.Location, a.Location) && a.AppearsHostileTo(first)); - if (navalProductions.Any()) + var nearest = navalProductions.ClosestTo(first); + if (nearest != null) { - var nearest = navalProductions.ClosestTo(first); - // Return nearest when it is FAR enough. // If the naval production is within MaxBaseRadius, it implies that // this squad is close to enemy territory and they should expect a naval combat; diff --git a/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs index ce044fe92a..b388387d63 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs @@ -163,11 +163,8 @@ namespace OpenRA.Mods.Common.Traits ActorInfo ChooseRandomUnitToBuild(ProductionQueue queue) { var buildableThings = queue.BuildableItems(); - if (!buildableThings.Any()) - return null; - - var unit = buildableThings.Random(world.LocalRandom); - return HasAdequateAirUnitReloadBuildings(unit) ? unit : null; + var unit = buildableThings.RandomOrDefault(world.LocalRandom); + return unit != null && HasAdequateAirUnitReloadBuildings(unit) ? unit : null; } ActorInfo ChooseUnitToBuild(ProductionQueue queue) diff --git a/OpenRA.Mods.Common/Traits/Buildings/RepairableBuilding.cs b/OpenRA.Mods.Common/Traits/Buildings/RepairableBuilding.cs index fac895924a..f78d44b8d3 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/RepairableBuilding.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/RepairableBuilding.cs @@ -115,7 +115,7 @@ namespace OpenRA.Mods.Common.Traits if (Repairers.Remove(player)) { UpdateCondition(self); - if (!Repairers.Any()) + if (Repairers.Count == 0) { Game.Sound.PlayNotification(self.World.Map.Rules, player, "Speech", Info.RepairingStoppedNotification, player.Faction.InternalName); TextNotificationsManager.AddTransientLine(self.Owner, Info.RepairingStoppedTextNotification); diff --git a/OpenRA.Mods.Common/Traits/Crates/GiveUnitCrateAction.cs b/OpenRA.Mods.Common/Traits/Crates/GiveUnitCrateAction.cs index 05eabd013d..68ff26d5c2 100644 --- a/OpenRA.Mods.Common/Traits/Crates/GiveUnitCrateAction.cs +++ b/OpenRA.Mods.Common/Traits/Crates/GiveUnitCrateAction.cs @@ -111,11 +111,9 @@ namespace OpenRA.Mods.Common.Traits CPos? ChooseEmptyCellNear(Actor a, string unit) { - var possibleCells = GetSuitableCells(a.Location, unit); - if (!possibleCells.Any()) - return null; - - return possibleCells.Random(self.World.SharedRandom); + return GetSuitableCells(a.Location, unit) + .Cast() + .RandomOrDefault(self.World.SharedRandom); } } } diff --git a/OpenRA.Mods.Common/Traits/World/ColorPickerManager.cs b/OpenRA.Mods.Common/Traits/World/ColorPickerManager.cs index 2a250fc67c..feddec58ac 100644 --- a/OpenRA.Mods.Common/Traits/World/ColorPickerManager.cs +++ b/OpenRA.Mods.Common/Traits/World/ColorPickerManager.cs @@ -82,7 +82,7 @@ namespace OpenRA.Mods.Common.Traits return false; } - Color MakeValid(float hue, float sat, float val, MersenneTwister random, IEnumerable terrainColors, IEnumerable playerColors, Action onError) + Color MakeValid(float hue, float sat, float val, MersenneTwister random, IReadOnlyCollection terrainColors, IReadOnlyCollection playerColors, Action onError) { // Clamp saturation without triggering a warning // This can only happen due to rounding errors (common) or modified clients (rare) @@ -132,7 +132,7 @@ namespace OpenRA.Mods.Common.Traits Color[] IColorPickerManagerInfo.PresetColors => PresetColors; - Color IColorPickerManagerInfo.RandomPresetColor(MersenneTwister random, IEnumerable terrainColors, IEnumerable playerColors) + Color IColorPickerManagerInfo.RandomPresetColor(MersenneTwister random, IReadOnlyCollection terrainColors, IReadOnlyCollection playerColors) { foreach (var color in PresetColors.Shuffle(random)) { @@ -148,13 +148,13 @@ namespace OpenRA.Mods.Common.Traits return MakeValid(randomHue, randomSat, randomVal, random, terrainColors, playerColors, null); } - Color IColorPickerManagerInfo.MakeValid(Color color, MersenneTwister random, IEnumerable terrainColors, IEnumerable playerColors, Action onError) + Color IColorPickerManagerInfo.MakeValid(Color color, MersenneTwister random, IReadOnlyCollection terrainColors, IReadOnlyCollection playerColors, Action onError) { var (_, h, s, v) = color.ToAhsv(); return MakeValid(h, s, v, random, terrainColors, playerColors, onError); } - Color IColorPickerManagerInfo.RandomValidColor(MersenneTwister random, IEnumerable terrainColors, IEnumerable playerColors) + Color IColorPickerManagerInfo.RandomValidColor(MersenneTwister random, IReadOnlyCollection terrainColors, IReadOnlyCollection playerColors) { var h = random.NextFloat(); var s = float2.Lerp(HsvSaturationRange[0], HsvSaturationRange[1], random.NextFloat()); diff --git a/OpenRA.Mods.Common/Traits/World/ControlGroups.cs b/OpenRA.Mods.Common/Traits/World/ControlGroups.cs index 605562e4e5..3579b7d1a3 100644 --- a/OpenRA.Mods.Common/Traits/World/ControlGroups.cs +++ b/OpenRA.Mods.Common/Traits/World/ControlGroups.cs @@ -47,7 +47,7 @@ namespace OpenRA.Mods.Common.Traits public void CreateControlGroup(int group) { - if (!world.Selection.Actors.Any()) + if (world.Selection.Actors.Count == 0) return; controlGroups[group].Clear(); @@ -59,7 +59,7 @@ namespace OpenRA.Mods.Common.Traits public void AddSelectionToControlGroup(int group) { - if (!world.Selection.Actors.Any()) + if (world.Selection.Actors.Count == 0) return; RemoveActorsFromAllControlGroups(world.Selection.Actors); diff --git a/OpenRA.Mods.Common/Traits/World/PathFinder.cs b/OpenRA.Mods.Common/Traits/World/PathFinder.cs index 0acb83e9bc..ffa4a51866 100644 --- a/OpenRA.Mods.Common/Traits/World/PathFinder.cs +++ b/OpenRA.Mods.Common/Traits/World/PathFinder.cs @@ -94,7 +94,7 @@ namespace OpenRA.Mods.Common.Traits Actor ignoreActor = null, bool laneBias = true) { - return FindPathToTarget(self, sources, target, check, customCost, ignoreActor, laneBias); + return FindPathToTarget(self, sources.ToList(), target, check, customCost, ignoreActor, laneBias); } /// @@ -125,11 +125,14 @@ namespace OpenRA.Mods.Common.Traits // and calling the existing methods that allow multiple sources and one target. // However there is a case of asymmetry we must handle, an actor may move out of a inaccessible source, // but may not move onto a inaccessible target. We must account for this when performing the swap. + var targetsList = targets.ToList(); + if (targetsList.Count == 0) + return NoPath; // As targets must be accessible, determine accessible targets in advance so when they becomes the sources // we don't accidentally allow an inaccessible position to become viable. var locomotor = GetActorLocomotor(self); - var accessibleTargets = targets + var accessibleTargets = targetsList .Where(target => PathSearch.CellAllowsMovement(self.World, locomotor, target, customCost) && locomotor.MovementCostToEnterCell(self, target, check, ignoreActor, true) != PathGraph.MovementCostForUnreachableCell) @@ -146,7 +149,7 @@ namespace OpenRA.Mods.Common.Traits if (sourceIsAccessible) { // As both ends are accessible, we can freely swap them. - path = FindPathToTarget(self, targets, source, check, customCost, ignoreActor, laneBias); + path = FindPathToTarget(self, targetsList, source, check, customCost, ignoreActor, laneBias); } else { @@ -167,11 +170,10 @@ namespace OpenRA.Mods.Common.Traits } List FindPathToTarget( - Actor self, IEnumerable sources, CPos target, BlockedByActor check, + Actor self, List sources, CPos target, BlockedByActor check, Func customCost, Actor ignoreActor, bool laneBias) { - var sourcesList = sources.ToList(); - if (sourcesList.Count == 0) + if (sources.Count == 0) return NoPath; var locomotor = GetActorLocomotor(self); @@ -183,9 +185,9 @@ namespace OpenRA.Mods.Common.Traits return NoPath; // When searching from only one source cell, some optimizations are possible. - if (sourcesList.Count == 1) + if (sources.Count == 1) { - var source = sourcesList[0]; + var source = sources[0]; // For adjacent cells on the same layer, we can return the path without invoking a full search. if (source.Layer == target.Layer && (source - target).LengthSquared < 3) @@ -204,7 +206,7 @@ namespace OpenRA.Mods.Common.Traits // Use a hierarchical path search, which performs a guided unidirectional search. return GetHierarchicalPathFinder(locomotor, check, ignoreActor).FindPath( - self, sourcesList, target, check, DefaultHeuristicWeightPercentage, customCost, ignoreActor, laneBias, pathFinderOverlay); + self, sources, target, check, DefaultHeuristicWeightPercentage, customCost, ignoreActor, laneBias, pathFinderOverlay); } HierarchicalPathFinder GetHierarchicalPathFinder(Locomotor locomotor, BlockedByActor check, Actor ignoreActor) diff --git a/OpenRA.Mods.Common/Traits/World/Selection.cs b/OpenRA.Mods.Common/Traits/World/Selection.cs index 17ea008e4c..64e081cf1e 100644 --- a/OpenRA.Mods.Common/Traits/World/Selection.cs +++ b/OpenRA.Mods.Common/Traits/World/Selection.cs @@ -25,7 +25,7 @@ namespace OpenRA.Mods.Common.Traits public class Selection : ISelection, INotifyCreated, INotifyOwnerChanged, ITick, IGameSaveTraitData { public int Hash { get; private set; } - public IEnumerable Actors => actors; + public IReadOnlyCollection Actors => actors; readonly HashSet actors = new(); readonly List rolloverActors = new(); @@ -91,10 +91,13 @@ namespace OpenRA.Mods.Common.Traits public virtual void Combine(World world, IEnumerable newSelection, bool isCombine, bool isClick) { + var newSelectionCollection = newSelection as IReadOnlyCollection; + newSelectionCollection ??= newSelection.ToList(); + if (isClick) { // TODO: select BEST, not FIRST - var adjNewSelection = newSelection.Take(1); + var adjNewSelection = newSelectionCollection.Take(1); if (isCombine) actors.SymmetricExceptWith(adjNewSelection); else @@ -106,17 +109,17 @@ namespace OpenRA.Mods.Common.Traits else { if (isCombine) - actors.UnionWith(newSelection); + actors.UnionWith(newSelectionCollection); else { actors.Clear(); - actors.UnionWith(newSelection); + actors.UnionWith(newSelectionCollection); } } UpdateHash(); - foreach (var a in newSelection) + foreach (var a in newSelectionCollection) foreach (var sel in a.TraitsImplementing()) sel.Selected(a); diff --git a/OpenRA.Mods.Common/TraitsInterfaces.cs b/OpenRA.Mods.Common/TraitsInterfaces.cs index 5f57f28b76..df9fd2ea34 100644 --- a/OpenRA.Mods.Common/TraitsInterfaces.cs +++ b/OpenRA.Mods.Common/TraitsInterfaces.cs @@ -380,9 +380,9 @@ namespace OpenRA.Mods.Common.Traits (float VMin, float VMax) ValueRange { get; } event Action OnColorPickerColorUpdate; Color[] PresetColors { get; } - Color RandomPresetColor(MersenneTwister random, IEnumerable terrainColors, IEnumerable playerColors); - Color RandomValidColor(MersenneTwister random, IEnumerable terrainColors, IEnumerable playerColors); - Color MakeValid(Color color, MersenneTwister random, IEnumerable terrainColors, IEnumerable playerColors, Action onError = null); + Color RandomPresetColor(MersenneTwister random, IReadOnlyCollection terrainColors, IReadOnlyCollection playerColors); + Color RandomValidColor(MersenneTwister random, IReadOnlyCollection terrainColors, IReadOnlyCollection playerColors); + Color MakeValid(Color color, MersenneTwister random, IReadOnlyCollection terrainColors, IReadOnlyCollection playerColors, Action onError = null); void ShowColorDropDown(DropDownButtonWidget dropdownButton, Color initialColor, string initialFaction, WorldRenderer worldRenderer, Action onExit); } diff --git a/OpenRA.Mods.Common/Widgets/Logic/ColorPickerLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ColorPickerLogic.cs index 88174a785a..396ac8e6f3 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/ColorPickerLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/ColorPickerLogic.cs @@ -57,7 +57,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic .SelectMany(t => t.Value.RestrictedPlayerColors) .Distinct() .ToList(); - var playerColors = Enumerable.Empty(); + var playerColors = Array.Empty(); randomButton.OnClick = () => { var randomColor = colorManager.RandomValidColor(world.LocalRandom, terrainColors, playerColors); @@ -140,7 +140,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic var colorIndex = j * paletteCols + i; var newSwatch = (ColorBlockWidget)customColorTemplate.Clone(); - var getColor = new CachedTransform(c => colorManager.MakeValid(c, world.LocalRandom, Enumerable.Empty(), Enumerable.Empty())); + var getColor = new CachedTransform(c => colorManager.MakeValid(c, world.LocalRandom, Array.Empty(), Array.Empty())); newSwatch.GetColor = () => getColor.Update(Game.Settings.Player.CustomColors[colorIndex]); newSwatch.IsVisible = () => Game.Settings.Player.CustomColors.Length > colorIndex; diff --git a/OpenRA.Mods.Common/Widgets/Logic/EncyclopediaLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/EncyclopediaLogic.cs index a387297c93..eec50c2e22 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/EncyclopediaLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/EncyclopediaLogic.cs @@ -79,7 +79,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic }; } - IEnumerable> GetFilteredActorEncyclopediaPairs() + IReadOnlyCollection> GetFilteredActorEncyclopediaPairs() { var actors = new List>(); foreach (var actor in modData.DefaultRules.Actors.Values) @@ -149,9 +149,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic var buildable = actor.TraitInfoOrDefault(); if (buildable != null) { - var prerequisites = buildable.Prerequisites.Select(a => ActorName(modData.DefaultRules, a)) - .Where(s => !s.StartsWith('~') && !s.StartsWith('!')); - if (prerequisites.Any()) + var prerequisites = buildable.Prerequisites + .Select(a => ActorName(modData.DefaultRules, a)) + .Where(s => !s.StartsWith('~') && !s.StartsWith('!')) + .ToList(); + if (prerequisites.Count != 0) text += $"Requires {prerequisites.JoinWith(", ")}\n\n"; } diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/SelectAllUnitsHotkeyLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/SelectAllUnitsHotkeyLogic.cs index 040768c313..1c6821470a 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/SelectAllUnitsHotkeyLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/SelectAllUnitsHotkeyLogic.cs @@ -53,7 +53,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic.Ingame var newSelection = SelectionUtils.SelectActorsOnScreen(world, worldRenderer, null, eligiblePlayers).SubsetWithHighestSelectionPriority(e.Modifiers).ToList(); // Check if selecting actors on the screen has selected new units - if (newSelection.Count > selection.Actors.Count()) + if (newSelection.Count > selection.Actors.Count) TextNotificationsManager.AddFeedbackLine(SelectedUnitsAcrossScreen, Translation.Arguments("units", newSelection.Count)); else { diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/SelectUnitsByTypeHotkeyLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/SelectUnitsByTypeHotkeyLogic.cs index 3352ece933..6e95cdebbf 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/SelectUnitsByTypeHotkeyLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/Hotkeys/SelectUnitsByTypeHotkeyLogic.cs @@ -52,7 +52,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic.Ingame if (world.IsGameOver) return false; - if (!selection.Actors.Any()) + if (selection.Actors.Count == 0) { TextNotificationsManager.AddFeedbackLine(NothingSelected); Game.Sound.PlayNotification(world.Map.Rules, world.LocalPlayer, "Sounds", ClickDisabledSound, null); @@ -78,7 +78,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic.Ingame var newSelection = SelectionUtils.SelectActorsOnScreen(world, worldRenderer, selectedClasses, eligiblePlayers).ToList(); // Check if selecting actors on the screen has selected new units - if (newSelection.Count > selection.Actors.Count()) + if (newSelection.Count > selection.Actors.Count) TextNotificationsManager.AddFeedbackLine(SelectedUnitsAcrossScreen, Translation.Arguments("units", newSelection.Count)); else { diff --git a/OpenRA.Mods.Common/Widgets/Logic/Installation/ModContentLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Installation/ModContentLogic.cs index 83fe274687..9fe0f02bef 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Installation/ModContentLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Installation/ModContentLogic.cs @@ -112,9 +112,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic requiredWidget.IsVisible = () => p.Value.Required; var sourceWidget = container.Get("SOURCE"); - var sourceTitles = p.Value.Sources.Select(s => sources[s].Title).Distinct(); + var sourceTitles = p.Value.Sources.Select(s => sources[s].Title).Distinct().ToList(); var sourceList = sourceTitles.JoinWith("\n"); - var isSourceAvailable = sourceTitles.Any(); + var isSourceAvailable = sourceTitles.Count != 0; sourceWidget.GetTooltipText = () => sourceList; sourceWidget.IsVisible = () => isSourceAvailable; diff --git a/OpenRA.Mods.Common/Widgets/Logic/MissionBrowserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/MissionBrowserLogic.cs index 54082e8d42..0224602384 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/MissionBrowserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/MissionBrowserLogic.cs @@ -136,9 +136,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic }) .Where(x => x.Index != -1) .OrderBy(x => x.Index) - .Select(x => x.Preview); + .Select(x => x.Preview) + .ToList(); - if (previews.Any()) + if (previews.Count != 0) { CreateMissionGroup(kv.Key, previews, onExit); allPreviews.AddRange(previews); @@ -148,9 +149,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic // Add an additional group for loose missions var loosePreviews = modData.MapCache - .Where(p => p.Status == MapStatus.Available && p.Visibility.HasFlag(MapVisibility.MissionSelector) && !allPreviews.Any(a => a.Uid == p.Uid)); + .Where(p => p.Status == MapStatus.Available && + p.Visibility.HasFlag(MapVisibility.MissionSelector) && + !allPreviews.Any(a => a.Uid == p.Uid)) + .ToList(); - if (loosePreviews.Any()) + if (loosePreviews.Count != 0) { CreateMissionGroup("Missions", loosePreviews, onExit); allPreviews.AddRange(loosePreviews); diff --git a/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs b/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs index de6e84d3c6..66edfbd2b2 100644 --- a/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs @@ -443,7 +443,7 @@ namespace OpenRA.Mods.Common.Widgets if (facility == null || facility.OccupiesSpace == null) return true; - if (selection.Actors.Count() == 1 && selection.Contains(facility)) + if (selection.Actors.Count == 1 && selection.Contains(facility)) viewport.Center(selection.Actors); else selection.Combine(World, new[] { facility }, false, true); diff --git a/OpenRA.Mods.Common/Widgets/WorldInteractionControllerWidget.cs b/OpenRA.Mods.Common/Widgets/WorldInteractionControllerWidget.cs index b2a7280d15..b1fb3ca1c8 100644 --- a/OpenRA.Mods.Common/Widgets/WorldInteractionControllerWidget.cs +++ b/OpenRA.Mods.Common/Widgets/WorldInteractionControllerWidget.cs @@ -109,7 +109,7 @@ namespace OpenRA.Mods.Common.Widgets { if (useClassicMouseStyle && HasMouseFocus) { - if (!IsValidDragbox && World.Selection.Actors.Any() && !multiClick && uog.InputOverridesSelection(World, mousePos, mi)) + if (!IsValidDragbox && World.Selection.Actors.Count != 0 && !multiClick && uog.InputOverridesSelection(World, mousePos, mi)) { // Order units instead of selecting ApplyOrders(World, mi); diff --git a/OpenRA.Mods.D2k/Traits/SpiceBloom.cs b/OpenRA.Mods.D2k/Traits/SpiceBloom.cs index df7e93305d..ddc4cfd47b 100644 --- a/OpenRA.Mods.D2k/Traits/SpiceBloom.cs +++ b/OpenRA.Mods.D2k/Traits/SpiceBloom.cs @@ -120,7 +120,7 @@ namespace OpenRA.Mods.D2k.Traits if (pieces < info.Pieces[0]) pieces = info.Pieces[0]; - var cells = self.World.Map.FindTilesInAnnulus(self.Location, 1, info.Range); + var cells = self.World.Map.FindTilesInAnnulus(self.Location, 1, info.Range).ToList(); for (var i = 0; i < pieces; i++) { diff --git a/OpenRA.Test/OpenRA.Game/PriorityQueueTest.cs b/OpenRA.Test/OpenRA.Game/PriorityQueueTest.cs index e28fc536f1..690d86cb13 100644 --- a/OpenRA.Test/OpenRA.Game/PriorityQueueTest.cs +++ b/OpenRA.Test/OpenRA.Game/PriorityQueueTest.cs @@ -53,7 +53,7 @@ namespace OpenRA.Test public void PriorityQueueAddThenRemoveTest(int count, int seed) { var mt = new MersenneTwister(seed); - var values = Enumerable.Range(0, count); + var values = Enumerable.Range(0, count).ToList(); var shuffledValues = values.Shuffle(mt).ToArray(); var queue = new Primitives.PriorityQueue(default);