diff --git a/.editorconfig b/.editorconfig index c4bb72adbe..a26479d084 100644 --- a/.editorconfig +++ b/.editorconfig @@ -834,6 +834,12 @@ dotnet_diagnostic.CA1849.severity = warning # Prefer static HashData method over ComputeHash. (Not available on mono) 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_diagnostic.CA1851.severity = warning + # Seal internal types. dotnet_diagnostic.CA1852.severity = warning diff --git a/OpenRA.Game/GameRules/ActorInfo.cs b/OpenRA.Game/GameRules/ActorInfo.cs index 035549ad5d..834f70fff9 100644 --- a/OpenRA.Game/GameRules/ActorInfo.cs +++ b/OpenRA.Game/GameRules/ActorInfo.cs @@ -185,7 +185,7 @@ namespace OpenRA public bool HasTraitInfo() where T : ITraitInfoInterface { return traits.Contains(); } public T TraitInfo() where T : ITraitInfoInterface { return traits.Get(); } public T TraitInfoOrDefault() where T : ITraitInfoInterface { return traits.GetOrDefault(); } - public IEnumerable TraitInfos() where T : ITraitInfoInterface { return traits.WithInterface(); } + public IReadOnlyCollection TraitInfos() where T : ITraitInfoInterface { return traits.WithInterface(); } public BitSet GetAllTargetTypes() { diff --git a/OpenRA.Game/Graphics/ChromeProvider.cs b/OpenRA.Game/Graphics/ChromeProvider.cs index 163fd70846..713588950f 100644 --- a/OpenRA.Game/Graphics/ChromeProvider.cs +++ b/OpenRA.Game/Graphics/ChromeProvider.cs @@ -234,7 +234,7 @@ namespace OpenRA.Graphics { // 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()) + if (collection.Regions.Count == 0) return Array.Empty(); // Support manual definitions for unusual dialog layouts diff --git a/OpenRA.Game/Map/ActorReference.cs b/OpenRA.Game/Map/ActorReference.cs index 7cdf657848..c82c55bdf3 100644 --- a/OpenRA.Game/Map/ActorReference.cs +++ b/OpenRA.Game/Map/ActorReference.cs @@ -139,7 +139,7 @@ namespace OpenRA return removed; } - public IEnumerable GetAll() where T : ActorInit + public IReadOnlyCollection GetAll() where T : ActorInit { return initDict.Value.WithInterface(); } diff --git a/OpenRA.Game/Map/CellRegion.cs b/OpenRA.Game/Map/CellRegion.cs index 0f9fcb52de..b6edac0906 100644 --- a/OpenRA.Game/Map/CellRegion.cs +++ b/OpenRA.Game/Map/CellRegion.cs @@ -12,7 +12,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; namespace OpenRA { @@ -64,9 +63,9 @@ namespace OpenRA } /// Returns the minimal region that covers at least the specified cells. - public static CellRegion BoundingRegion(MapGridType shape, IEnumerable cells) + public static CellRegion BoundingRegion(MapGridType shape, IReadOnlyCollection cells) { - if (cells == null || !cells.Any()) + if (cells == null || cells.Count == 0) throw new ArgumentException("cells must not be null or empty.", nameof(cells)); var minU = int.MaxValue; diff --git a/OpenRA.Game/Network/SyncReport.cs b/OpenRA.Game/Network/SyncReport.cs index 44aff3fa0f..19b18cfcf5 100644 --- a/OpenRA.Game/Network/SyncReport.cs +++ b/OpenRA.Game/Network/SyncReport.cs @@ -201,8 +201,12 @@ namespace OpenRA.Network public TypeInfo(Type type) { const BindingFlags Flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; - var fields = type.GetFields(Flags).Where(fi => !fi.IsLiteral && !fi.IsStatic && fi.HasAttribute()); - var properties = type.GetProperties(Flags).Where(pi => pi.HasAttribute()); + var fields = type.GetFields(Flags) + .Where(fi => !fi.IsLiteral && !fi.IsStatic && fi.HasAttribute()) + .ToList(); + var properties = type.GetProperties(Flags) + .Where(pi => pi.HasAttribute()) + .ToList(); foreach (var prop in properties) if (!prop.CanRead || prop.GetIndexParameters().Length > 0) diff --git a/OpenRA.Game/ObjectCreator.cs b/OpenRA.Game/ObjectCreator.cs index e8b6900620..15db6a0c4a 100644 --- a/OpenRA.Game/ObjectCreator.cs +++ b/OpenRA.Game/ObjectCreator.cs @@ -134,8 +134,8 @@ namespace OpenRA public ConstructorInfo GetCtor(Type type) { var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; - var ctors = type.GetConstructors(flags).Where(x => x.HasAttribute()); - if (ctors.Count() > 1) + var ctors = type.GetConstructors(flags).Where(x => x.HasAttribute()).ToList(); + if (ctors.Count > 1) throw new InvalidOperationException("ObjectCreator: UseCtor on multiple constructors; invalid."); return ctors.FirstOrDefault(); } diff --git a/OpenRA.Game/Player.cs b/OpenRA.Game/Player.cs index 35f910524b..20ba3c35fe 100644 --- a/OpenRA.Game/Player.cs +++ b/OpenRA.Game/Player.cs @@ -133,7 +133,7 @@ namespace OpenRA static FactionInfo ResolveDisplayFaction(World world, string factionName) { - var factions = world.Map.Rules.Actors[SystemActors.World].TraitInfos().ToArray(); + var factions = world.Map.Rules.Actors[SystemActors.World].TraitInfos(); return factions.FirstOrDefault(f => f.InternalName == factionName) ?? factions.First(); } diff --git a/OpenRA.Game/Primitives/TypeDictionary.cs b/OpenRA.Game/Primitives/TypeDictionary.cs index 52560f8f61..6aa99590bc 100644 --- a/OpenRA.Game/Primitives/TypeDictionary.cs +++ b/OpenRA.Game/Primitives/TypeDictionary.cs @@ -12,14 +12,20 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; namespace OpenRA.Primitives { public class TypeDictionary : IEnumerable { - static readonly Func> CreateList = type => new List(); - readonly Dictionary> data = new(); + static readonly Func CreateTypeContainer = t => + (ITypeContainer)typeof(TypeContainer<>).MakeGenericType(t).GetConstructor(Type.EmptyTypes).Invoke(null); + + readonly Dictionary data = new(); + + ITypeContainer InnerGet(Type t) + { + return data.GetOrAdd(t, CreateTypeContainer); + } public void Add(object val) { @@ -33,7 +39,7 @@ namespace OpenRA.Primitives void InnerAdd(Type t, object val) { - data.GetOrAdd(t, CreateList).Add(val); + InnerGet(t).Add(val); } public bool Contains() @@ -48,35 +54,33 @@ namespace OpenRA.Primitives public T Get() { - return (T)Get(typeof(T), true); + return Get(true); } public T GetOrDefault() { - var result = Get(typeof(T), false); - if (result == null) - return default; - return (T)result; + return Get(false); } - object Get(Type t, bool throwsIfMissing) + T Get(bool throwsIfMissing) { - if (!data.TryGetValue(t, out var ret)) + if (!data.TryGetValue(typeof(T), out var container)) { if (throwsIfMissing) - throw new InvalidOperationException($"TypeDictionary does not contain instance of type `{t}`"); - return null; + throw new InvalidOperationException($"TypeDictionary does not contain instance of type `{typeof(T)}`"); + return default; } - if (ret.Count > 1) - throw new InvalidOperationException($"TypeDictionary contains multiple instances of type `{t}`"); - return ret[0]; + var list = ((TypeContainer)container).Objects; + if (list.Count > 1) + throw new InvalidOperationException($"TypeDictionary contains multiple instances of type `{typeof(T)}`"); + return list[0]; } - public IEnumerable WithInterface() + public IReadOnlyCollection WithInterface() { - if (data.TryGetValue(typeof(T), out var objs)) - return objs.Cast(); + if (data.TryGetValue(typeof(T), out var container)) + return ((TypeContainer)container).Objects; return Array.Empty(); } @@ -92,18 +96,19 @@ namespace OpenRA.Primitives void InnerRemove(Type t, object val) { - if (!data.TryGetValue(t, out var objs)) + if (!data.TryGetValue(t, out var container)) return; - objs.Remove(val); - if (objs.Count == 0) + + container.Remove(val); + if (container.Count == 0) data.Remove(t); } public void TrimExcess() { data.TrimExcess(); - foreach (var objs in data.Values) - objs.TrimExcess(); + foreach (var t in data.Keys) + InnerGet(t).TrimExcess(); } public IEnumerator GetEnumerator() @@ -115,6 +120,36 @@ namespace OpenRA.Primitives { return GetEnumerator(); } + + interface ITypeContainer + { + int Count { get; } + void Add(object value); + void Remove(object value); + void TrimExcess(); + } + + sealed class TypeContainer : ITypeContainer + { + public List Objects { get; } = new List(); + + public int Count => Objects.Count; + + public void Add(object value) + { + Objects.Add((T)value); + } + + public void Remove(object value) + { + Objects.Remove((T)value); + } + + public void TrimExcess() + { + Objects.TrimExcess(); + } + } } public static class TypeExts diff --git a/OpenRA.Game/WPos.cs b/OpenRA.Game/WPos.cs index d96b4d182b..18de50de8a 100644 --- a/OpenRA.Game/WPos.cs +++ b/OpenRA.Game/WPos.cs @@ -11,7 +11,6 @@ using System; using System.Collections.Generic; -using System.Linq; using Eluant; using Eluant.ObjectBinding; using OpenRA.Scripting; @@ -139,20 +138,21 @@ namespace OpenRA { public static WPos Average(this IEnumerable source) { - var length = source.Count(); - if (length == 0) - return WPos.Zero; - + var length = 0; var x = 0L; var y = 0L; var z = 0L; foreach (var pos in source) { + length++; x += pos.X; y += pos.Y; z += pos.Z; } + if (length == 0) + return WPos.Zero; + x /= length; y /= length; z /= length; diff --git a/OpenRA.Mods.Cnc/Activities/Teleport.cs b/OpenRA.Mods.Cnc/Activities/Teleport.cs index 899b3a37d0..9884860110 100644 --- a/OpenRA.Mods.Cnc/Activities/Teleport.cs +++ b/OpenRA.Mods.Cnc/Activities/Teleport.cs @@ -10,7 +10,6 @@ #endregion using System; -using System.Linq; using OpenRA.Activities; using OpenRA.Mods.Cnc.Traits; using OpenRA.Mods.Common.Traits; @@ -120,7 +119,7 @@ namespace OpenRA.Mods.Cnc.Activities if (teleporter == null) return null; - var restrictTo = maximumDistance == null ? null : self.World.Map.FindTilesInCircle(self.Location, maximumDistance.Value); + var restrictTo = maximumDistance == null ? null : self.World.Map.FindTilesInCircle(self.Location, maximumDistance.Value).ToHashSet(); if (maximumDistance != null) destination = restrictTo.MinBy(x => (x - destination).LengthSquared); diff --git a/OpenRA.Mods.Cnc/Traits/GpsWatcher.cs b/OpenRA.Mods.Cnc/Traits/GpsWatcher.cs index b7a094d14b..1e352e6ec7 100644 --- a/OpenRA.Mods.Cnc/Traits/GpsWatcher.cs +++ b/OpenRA.Mods.Cnc/Traits/GpsWatcher.cs @@ -80,7 +80,7 @@ namespace OpenRA.Mods.Cnc.Traits { var wasGranted = Granted; var wasGrantedAllies = GrantedAllies; - var allyWatchers = owner.World.ActorsWithTrait().Where(kv => kv.Actor.Owner.IsAlliedWith(owner)); + var allyWatchers = owner.World.ActorsWithTrait().Where(kv => kv.Actor.Owner.IsAlliedWith(owner)).ToList(); Granted = actors.Count > 0 && Launched; GrantedAllies = allyWatchers.Any(w => w.Trait.Granted); diff --git a/OpenRA.Mods.Cnc/Traits/SupportPowers/ChronoshiftPower.cs b/OpenRA.Mods.Cnc/Traits/SupportPowers/ChronoshiftPower.cs index 06eda13de1..926d6c6d79 100644 --- a/OpenRA.Mods.Cnc/Traits/SupportPowers/ChronoshiftPower.cs +++ b/OpenRA.Mods.Cnc/Traits/SupportPowers/ChronoshiftPower.cs @@ -358,14 +358,11 @@ namespace OpenRA.Mods.Cnc.Traits bool IsValidTarget(CPos xy) { - // Don't teleport if there are no units in range (either all moved out of range, or none yet moved into range) - var unitsInRange = power.UnitsInRange(sourceLocation); - if (!unitsInRange.Any()) - return false; - var canTeleport = false; - foreach (var unit in unitsInRange) + var anyUnitsInRange = false; + foreach (var unit in power.UnitsInRange(sourceLocation)) { + anyUnitsInRange = true; var targetCell = unit.Location + (xy - sourceLocation); if (manager.Self.Owner.Shroud.IsExplored(targetCell) && unit.Trait().CanChronoshiftTo(unit, targetCell)) { @@ -374,6 +371,10 @@ namespace OpenRA.Mods.Cnc.Traits } } + // Don't teleport if there are no units in range (either all moved out of range, or none yet moved into range) + if (!anyUnitsInRange) + return false; + if (!canTeleport) { // Check the terrain types. This will allow chronoshifts to occur on empty terrain to terrain of diff --git a/OpenRA.Mods.Common/Activities/Attack.cs b/OpenRA.Mods.Common/Activities/Attack.cs index a493125ca0..5090a2cdc0 100644 --- a/OpenRA.Mods.Common/Activities/Attack.cs +++ b/OpenRA.Mods.Common/Activities/Attack.cs @@ -196,8 +196,8 @@ namespace OpenRA.Mods.Common.Activities // Update ranges. Exclude paused armaments except when ALL weapons are paused // (e.g. out of ammo), in which case use the paused, valid weapon with highest range. - var activeArmaments = armaments.Where(x => !x.IsTraitPaused); - if (activeArmaments.Any()) + var activeArmaments = armaments.Where(x => !x.IsTraitPaused).ToList(); + if (activeArmaments.Count != 0) { minRange = activeArmaments.Max(a => a.Weapon.MinRange); maxRange = activeArmaments.Min(a => a.MaxRange()); diff --git a/OpenRA.Mods.Common/Lint/CheckConflictingMouseBounds.cs b/OpenRA.Mods.Common/Lint/CheckConflictingMouseBounds.cs index 1c441b78d3..e88c3d1e9a 100644 --- a/OpenRA.Mods.Common/Lint/CheckConflictingMouseBounds.cs +++ b/OpenRA.Mods.Common/Lint/CheckConflictingMouseBounds.cs @@ -10,7 +10,6 @@ #endregion using System; -using System.Linq; using OpenRA.Mods.Common.Traits; namespace OpenRA.Mods.Common.Lint @@ -21,8 +20,8 @@ namespace OpenRA.Mods.Common.Lint { foreach (var actorInfo in rules.Actors) { - var selectable = actorInfo.Value.TraitInfos().Count(); - var interactable = actorInfo.Value.TraitInfos().Count(); + var selectable = actorInfo.Value.TraitInfos().Count; + var interactable = actorInfo.Value.TraitInfos().Count; if (selectable > 0 && selectable != interactable) emitWarning($"Actor `{actorInfo.Value.Name}` defines both Interactable and Selectable traits. This may cause unexpected results."); } diff --git a/OpenRA.Mods.Common/Lint/CheckDefaultVisibility.cs b/OpenRA.Mods.Common/Lint/CheckDefaultVisibility.cs index 551b951ea2..d45da21cf2 100644 --- a/OpenRA.Mods.Common/Lint/CheckDefaultVisibility.cs +++ b/OpenRA.Mods.Common/Lint/CheckDefaultVisibility.cs @@ -37,7 +37,7 @@ namespace OpenRA.Mods.Common.Lint try { var visibilityTypes = actorInfo.Value.TraitInfos(); - var count = visibilityTypes.Count(); + var count = visibilityTypes.Count; if (count == 0) emitError($"Actor type `{actorInfo.Key}` does not define a default visibility type."); diff --git a/OpenRA.Mods.Common/Lint/CheckHitShapes.cs b/OpenRA.Mods.Common/Lint/CheckHitShapes.cs index 0c356e0859..00e9348ace 100644 --- a/OpenRA.Mods.Common/Lint/CheckHitShapes.cs +++ b/OpenRA.Mods.Common/Lint/CheckHitShapes.cs @@ -10,7 +10,6 @@ #endregion using System; -using System.Linq; using OpenRA.Mods.Common.Traits; using OpenRA.Server; using OpenRA.Traits; @@ -41,7 +40,7 @@ namespace OpenRA.Mods.Common.Lint continue; var hitShapes = actorInfo.Value.TraitInfos(); - if (!hitShapes.Any()) + if (hitShapes.Count == 0) emitError($"Actor type `{actorInfo.Key}` has a Health trait but no HitShape trait."); } catch (InvalidOperationException e) diff --git a/OpenRA.Mods.Common/Lint/CheckLocomotorReferences.cs b/OpenRA.Mods.Common/Lint/CheckLocomotorReferences.cs index 794b893b88..8828963bf7 100644 --- a/OpenRA.Mods.Common/Lint/CheckLocomotorReferences.cs +++ b/OpenRA.Mods.Common/Lint/CheckLocomotorReferences.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Traits; using OpenRA.Server; @@ -32,12 +33,15 @@ namespace OpenRA.Mods.Common.Lint static void Run(Action emitError, Ruleset rules) { var worldActor = rules.Actors[SystemActors.World]; - var locomotorInfos = worldActor.TraitInfos().ToArray(); - foreach (var li in locomotorInfos) - foreach (var otherLocomotor in locomotorInfos) - if (li != otherLocomotor && li.Name == otherLocomotor.Name) - emitError($"More than one Locomotor exists with the name `{li.Name}`."); + var locomotorNames = worldActor.TraitInfos().Select(li => li.Name).ToList(); + var duplicateNames = locomotorNames + .GroupBy(name => name) + .Where(g => g.Count() > 1) + .Select(g => g.Key); + foreach (var duplicateName in duplicateNames) + emitError($"More than one Locomotor exists with the name `{duplicateName}`."); + var locomotorNamesSet = locomotorNames.ToHashSet(); foreach (var actorInfo in rules.Actors) { foreach (var traitInfo in actorInfo.Value.TraitInfos()) @@ -51,16 +55,16 @@ namespace OpenRA.Mods.Common.Lint if (string.IsNullOrEmpty(locomotor)) continue; - CheckLocomotors(actorInfo.Value, emitError, locomotorInfos, locomotor); + CheckLocomotors(actorInfo.Value, emitError, locomotorNamesSet, locomotor); } } } } } - static void CheckLocomotors(ActorInfo actorInfo, Action emitError, LocomotorInfo[] locomotorInfos, string locomotor) + static void CheckLocomotors(ActorInfo actorInfo, Action emitError, HashSet locomotorNames, string locomotor) { - if (!locomotorInfos.Any(l => l.Name == locomotor)) + if (!locomotorNames.Contains(locomotor)) emitError($"Actor `{actorInfo.Name}` defines Locomotor `{locomotor}` not found on World actor."); } } diff --git a/OpenRA.Mods.Common/Lint/CheckSpriteBodies.cs b/OpenRA.Mods.Common/Lint/CheckSpriteBodies.cs index 670133d483..aed9b58a68 100644 --- a/OpenRA.Mods.Common/Lint/CheckSpriteBodies.cs +++ b/OpenRA.Mods.Common/Lint/CheckSpriteBodies.cs @@ -32,10 +32,12 @@ namespace OpenRA.Mods.Common.Lint { foreach (var actorInfo in rules.Actors) { - var wsbs = actorInfo.Value.TraitInfos(); - foreach (var wsb in wsbs) - if (wsbs.Any(w => w != wsb && w.Name == wsb.Name)) - emitError($"Actor type `{actorInfo.Key}` has more than one *SpriteBody with Name: {wsb.Name}."); + var duplicateNames = actorInfo.Value.TraitInfos() + .GroupBy(wsb => wsb.Name) + .Where(g => g.Count() > 1) + .Select(g => g.Key); + foreach (var duplicateName in duplicateNames) + emitError($"Actor type `{actorInfo.Key}` has more than one *SpriteBody with Name: {duplicateName}."); } } } diff --git a/OpenRA.Mods.Common/Orders/UnitOrderGenerator.cs b/OpenRA.Mods.Common/Orders/UnitOrderGenerator.cs index ec44d30f38..4c462d1c84 100644 --- a/OpenRA.Mods.Common/Orders/UnitOrderGenerator.cs +++ b/OpenRA.Mods.Common/Orders/UnitOrderGenerator.cs @@ -50,13 +50,13 @@ namespace OpenRA.Mods.Common.Orders .Where(o => o != null) .ToList(); - var actorsInvolved = orders.Select(o => o.Actor).Distinct(); - if (!actorsInvolved.Any()) + var actorsInvolved = orders.Select(o => o.Actor).Distinct().ToArray(); + if (actorsInvolved.Length == 0) yield break; // HACK: This is required by the hacky player actions-per-minute calculation // TODO: Reimplement APM properly and then remove this - yield return new Order("CreateGroup", actorsInvolved.First().Owner.PlayerActor, false, actorsInvolved.ToArray()); + yield return new Order("CreateGroup", actorsInvolved.First().Owner.PlayerActor, false, actorsInvolved); foreach (var o in orders) yield return CheckSameOrder(o.Order, o.Trait.IssueOrder(o.Actor, o.Order, o.Target, mi.Modifiers.HasModifier(Modifiers.Shift))); @@ -165,7 +165,8 @@ namespace OpenRA.Mods.Common.Orders var orders = self.TraitsImplementing() .SelectMany(trait => trait.Orders.Select(x => new { Trait = trait, Order = x })) .Select(x => x) - .OrderByDescending(x => x.Order.OrderPriority); + .OrderByDescending(x => x.Order.OrderPriority) + .ToList(); for (var i = 0; i < 2; i++) { diff --git a/OpenRA.Mods.Common/Scripting/Properties/ProductionProperties.cs b/OpenRA.Mods.Common/Scripting/Properties/ProductionProperties.cs index 403d12ffe0..df75f81d33 100644 --- a/OpenRA.Mods.Common/Scripting/Properties/ProductionProperties.cs +++ b/OpenRA.Mods.Common/Scripting/Properties/ProductionProperties.cs @@ -245,7 +245,7 @@ namespace OpenRA.Mods.Common.Scripting foreach (var actorType in actorTypes.Distinct()) typeToQueueMap.Add(actorType, GetBuildableInfo(actorType).Queue.First()); - var queueTypes = typeToQueueMap.Values.Distinct(); + var queueTypes = typeToQueueMap.Values.Distinct().ToList(); if (queueTypes.Any(t => !queues.ContainsKey(t) || productionHandlers.ContainsKey(t))) return false; diff --git a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs index c9d47cbe9f..57c65d8f57 100644 --- a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs +++ b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs @@ -748,10 +748,11 @@ namespace OpenRA.Mods.Common.Server teamCount = teamCount.Clamp(0, maxTeams); var clients = server.LobbyInfo.Slots .Select(slot => server.LobbyInfo.ClientInSlot(slot.Key)) - .Where(c => c != null && !server.LobbyInfo.Slots[c.Slot].LockTeam); + .Where(c => c != null && !server.LobbyInfo.Slots[c.Slot].LockTeam) + .ToList(); var assigned = 0; - var clientCount = clients.Count(); + var clientCount = clients.Count; foreach (var player in clients) { // Free for all diff --git a/OpenRA.Mods.Common/Traits/AppearsOnMapPreview.cs b/OpenRA.Mods.Common/Traits/AppearsOnMapPreview.cs index 19252a7d24..79e0149200 100644 --- a/OpenRA.Mods.Common/Traits/AppearsOnMapPreview.cs +++ b/OpenRA.Mods.Common/Traits/AppearsOnMapPreview.cs @@ -41,8 +41,8 @@ namespace OpenRA.Mods.Common.Traits else { var owner = map.PlayerDefinitions.Single(p => s.Get().InternalName == p.Value.Nodes.Last(k => k.Key == "Name").Value.Value); - var colorValue = owner.Value.Nodes.Where(n => n.Key == "Color"); - var ownerColor = colorValue.Any() ? colorValue.First().Value.Value : "FFFFFF"; + var colorValue = owner.Value.Nodes.FirstOrDefault(n => n.Key == "Color"); + var ownerColor = colorValue?.Value.Value ?? "FFFFFF"; Color.TryParse(ownerColor, out color); } diff --git a/OpenRA.Mods.Common/Traits/Attack/AttackBase.cs b/OpenRA.Mods.Common/Traits/Attack/AttackBase.cs index f0db64c6f8..8e8bdee8d0 100644 --- a/OpenRA.Mods.Common/Traits/Attack/AttackBase.cs +++ b/OpenRA.Mods.Common/Traits/Attack/AttackBase.cs @@ -445,15 +445,15 @@ namespace OpenRA.Mods.Common.Traits modifiers |= TargetModifiers.ForceAttack; var forceAttack = modifiers.HasModifier(TargetModifiers.ForceAttack); - var armaments = ab.ChooseArmamentsForTarget(target, forceAttack); - if (!armaments.Any()) - return false; // Use valid armament with highest range out of those that have ammo // If all are out of ammo, just use valid armament with highest range - armaments = armaments.OrderByDescending(x => x.MaxRange()); - var a = armaments.FirstOrDefault(x => !x.IsTraitPaused); - a ??= armaments.First(); + var a = ab.ChooseArmamentsForTarget(target, forceAttack) + .OrderBy(x => x.IsTraitPaused) + .ThenByDescending(x => x.MaxRange()) + .FirstOrDefault(); + if (a == null) + return false; var outOfRange = !target.IsInRange(self.CenterPosition, a.MaxRange()) || (!forceAttack && target.Type == TargetType.FrozenActor && !ab.Info.TargetFrozenActors); @@ -482,15 +482,15 @@ namespace OpenRA.Mods.Common.Traits return false; var target = Target.FromCell(self.World, location); - var armaments = ab.ChooseArmamentsForTarget(target, true); - if (!armaments.Any()) - return false; // Use valid armament with highest range out of those that have ammo // If all are out of ammo, just use valid armament with highest range - armaments = armaments.OrderByDescending(x => x.MaxRange()); - var a = armaments.FirstOrDefault(x => !x.IsTraitPaused); - a ??= armaments.First(); + var a = ab.ChooseArmamentsForTarget(target, true) + .OrderBy(x => x.IsTraitPaused) + .ThenByDescending(x => x.MaxRange()) + .FirstOrDefault(); + if (a == null) + return false; cursor = !target.IsInRange(self.CenterPosition, a.MaxRange()) ? ab.Info.OutsideRangeCursor ?? a.Info.OutsideRangeCursor diff --git a/OpenRA.Mods.Common/Traits/BotModules/Squads/AttackOrFleeFuzzy.cs b/OpenRA.Mods.Common/Traits/BotModules/Squads/AttackOrFleeFuzzy.cs index 51fd55de6b..565601e349 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/Squads/AttackOrFleeFuzzy.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/Squads/AttackOrFleeFuzzy.cs @@ -163,7 +163,7 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads fuzzyEngine.Rules.Add(fuzzyEngine.ParseRule(rule)); } - public bool CanAttack(IEnumerable ownUnits, IEnumerable enemyUnits) + public bool CanAttack(IReadOnlyCollection ownUnits, IReadOnlyCollection enemyUnits) { double attackChance; var inputValues = new Dictionary(); @@ -201,7 +201,7 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads return (int)((long)sumOfHp * normalizeByValue / sumOfMaxHp); } - static float RelativePower(IEnumerable own, IEnumerable enemy) + static float RelativePower(IReadOnlyCollection own, IReadOnlyCollection enemy) { return RelativeValue(own, enemy, 100, SumOfValues, a => { @@ -225,18 +225,18 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads }); } - static float RelativeSpeed(IEnumerable own, IEnumerable enemy) + static float RelativeSpeed(IReadOnlyCollection own, IReadOnlyCollection enemy) { return RelativeValue(own, enemy, 100, Average, (Actor a) => a.Info.TraitInfo().Speed); } - static float RelativeValue(IEnumerable own, IEnumerable enemy, float normalizeByValue, - Func, Func, float> relativeFunc, Func getValue) + static float RelativeValue(IReadOnlyCollection own, IReadOnlyCollection enemy, float normalizeByValue, + Func, Func, float> relativeFunc, Func getValue) { - if (!enemy.Any()) + if (enemy.Count == 0) return 999.0f; - if (!own.Any()) + if (own.Count == 0) return 0.0f; var relative = relativeFunc(own, getValue) / relativeFunc(enemy, getValue) * normalizeByValue; diff --git a/OpenRA.Mods.Common/Traits/BotModules/Squads/States/AirStates.cs b/OpenRA.Mods.Common/Traits/BotModules/Squads/States/AirStates.cs index 43b8137d7e..b23423c219 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/Squads/States/AirStates.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/Squads/States/AirStates.cs @@ -22,9 +22,9 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads protected const int MissileUnitMultiplier = 3; - protected static int CountAntiAirUnits(IEnumerable units) + protected static int CountAntiAirUnits(IReadOnlyCollection units) { - if (!units.Any()) + if (units.Count == 0) return 0; var missileUnitsCount = 0; diff --git a/OpenRA.Mods.Common/Traits/BotModules/Squads/States/StateBase.cs b/OpenRA.Mods.Common/Traits/BotModules/Squads/States/StateBase.cs index 169fc21b33..983f65a052 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/Squads/States/StateBase.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/Squads/States/StateBase.cs @@ -80,7 +80,7 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads return false; } - protected virtual bool ShouldFlee(Squad squad, Func, bool> flee) + protected virtual bool ShouldFlee(Squad squad, Func, bool> flee) { if (!squad.IsValid) return false; @@ -95,8 +95,10 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads if (u.Owner == squad.Bot.Player && u.Info.HasTraitInfo()) return false; - var enemyAroundUnit = units.Where(unit => squad.SquadManager.IsPreferredEnemyUnit(unit) && unit.Info.HasTraitInfo()); - if (!enemyAroundUnit.Any()) + var enemyAroundUnit = units + .Where(unit => squad.SquadManager.IsPreferredEnemyUnit(unit) && unit.Info.HasTraitInfo()) + .ToList(); + if (enemyAroundUnit.Count == 0) return false; return flee(enemyAroundUnit); diff --git a/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs index a1a2d62ad4..ce044fe92a 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs @@ -172,17 +172,18 @@ namespace OpenRA.Mods.Common.Traits ActorInfo ChooseUnitToBuild(ProductionQueue queue) { - var buildableThings = queue.BuildableItems(); - if (!buildableThings.Any()) + var buildableThings = queue.BuildableItems().Select(b => b.Name).ToHashSet(); + if (buildableThings.Count == 0) return null; var myUnits = player.World .ActorsHavingTrait() .Where(a => a.Owner == player) - .Select(a => a.Info.Name).ToList(); + .Select(a => a.Info.Name) + .ToList(); foreach (var unit in Info.UnitsToBuild.Shuffle(world.LocalRandom)) - if (buildableThings.Any(b => b.Name == unit.Key)) + if (buildableThings.Contains(unit.Key)) if (myUnits.Count(a => a == unit.Key) * 100 < unit.Value * myUnits.Count) if (HasAdequateAirUnitReloadBuildings(world.Map.Rules.Actors[unit.Key])) return world.Map.Rules.Actors[unit.Key]; diff --git a/OpenRA.Mods.Common/Traits/Buildings/Exit.cs b/OpenRA.Mods.Common/Traits/Buildings/Exit.cs index 0352dab7f4..72ccf77fd0 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/Exit.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/Exit.cs @@ -80,9 +80,6 @@ namespace OpenRA.Mods.Common.Traits return null; var allOfType = Exits(actor, productionType); - if (!allOfType.Any()) - return null; - foreach (var g in allOfType.GroupBy(e => e.Info.Priority)) { var shuffled = g.Shuffle(world.SharedRandom); diff --git a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoMobile.cs b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoMobile.cs index 753be42533..8599e0950f 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoMobile.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/TransformsIntoMobile.cs @@ -54,13 +54,15 @@ namespace OpenRA.Mods.Common.Traits public override void RulesetLoaded(Ruleset rules, ActorInfo ai) { - var locomotorInfos = rules.Actors[SystemActors.World].TraitInfos(); - LocomotorInfo = locomotorInfos.FirstOrDefault(li => li.Name == Locomotor); - if (LocomotorInfo == null) + var locomotorInfos = rules.Actors[SystemActors.World].TraitInfos() + .Where(li => li.Name == Locomotor).ToList(); + if (locomotorInfos.Count == 0) throw new YamlException($"A locomotor named '{Locomotor}' doesn't exist."); - else if (locomotorInfos.Count(li => li.Name == Locomotor) > 1) + else if (locomotorInfos.Count > 1) throw new YamlException($"There is more than one locomotor named '{Locomotor}'."); + LocomotorInfo = locomotorInfos[0]; + base.RulesetLoaded(rules, ai); } } diff --git a/OpenRA.Mods.Common/Traits/Crates/Crate.cs b/OpenRA.Mods.Common/Traits/Crates/Crate.cs index 481194f577..f2acb8c303 100644 --- a/OpenRA.Mods.Common/Traits/Crates/Crate.cs +++ b/OpenRA.Mods.Common/Traits/Crates/Crate.cs @@ -113,23 +113,32 @@ namespace OpenRA.Mods.Common.Traits void INotifyParachute.OnLanded(Actor self) { // Check whether the crate landed on anything - var landedOn = self.World.ActorMap.GetActorsAt(self.Location) - .Where(a => a != self); - - if (!landedOn.Any()) - return; - - var collector = landedOn.FirstOrDefault(a => + var anyOtherActors = false; + Actor collector = null; + foreach (var otherActor in self.World.ActorMap.GetActorsAt(self.Location)) { + if (self == otherActor) + continue; + + anyOtherActors = true; + // Mobile is (currently) the only trait that supports crushing - var mi = a.Info.TraitInfoOrDefault(); + var mi = otherActor.Info.TraitInfoOrDefault(); if (mi == null) - return false; + continue; // Make sure that the actor can collect this crate type // Crate can only be crushed if it is not in the air. - return self.IsAtGroundLevel() && mi.LocomotorInfo.Crushes.Contains(info.CrushClass); - }); + if (self.IsAtGroundLevel() && mi.LocomotorInfo.Crushes.Contains(info.CrushClass)) + { + collector = otherActor; + break; + } + } + + // The crate can land unhindered. + if (!anyOtherActors) + return; // Destroy the crate if none of the units in the cell are valid collectors if (collector != null) @@ -148,10 +157,11 @@ namespace OpenRA.Mods.Common.Traits self.Dispose(); collected = true; - if (crateActions.Any()) + var shares = crateActions + .Select(a => (Action: a, Shares: a.GetSelectionSharesOuter(crusher))) + .ToList(); + if (shares.Count != 0) { - var shares = crateActions.Select(a => (Action: a, Shares: a.GetSelectionSharesOuter(crusher))); - var totalShares = shares.Sum(a => a.Shares); var n = self.World.SharedRandom.Next(totalShares); diff --git a/OpenRA.Mods.Common/Traits/Crates/GrantExternalConditionCrateAction.cs b/OpenRA.Mods.Common/Traits/Crates/GrantExternalConditionCrateAction.cs index d7ec1d198f..7997e642e2 100644 --- a/OpenRA.Mods.Common/Traits/Crates/GrantExternalConditionCrateAction.cs +++ b/OpenRA.Mods.Common/Traits/Crates/GrantExternalConditionCrateAction.cs @@ -76,7 +76,8 @@ namespace OpenRA.Mods.Common.Traits void GrantCondition(Actor actor) { var externals = actor.TraitsImplementing() - .Where(t => t.Info.Condition == info.Condition); + .Where(t => t.Info.Condition == info.Condition) + .ToList(); ExternalCondition external = null; for (var n = 0; n < info.Levels; n++) diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index 2a6f71ad95..94cf8e5af8 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -104,13 +104,15 @@ namespace OpenRA.Mods.Common.Traits public override void RulesetLoaded(Ruleset rules, ActorInfo ai) { - var locomotorInfos = rules.Actors[SystemActors.World].TraitInfos(); - LocomotorInfo = locomotorInfos.FirstOrDefault(li => li.Name == Locomotor); - if (LocomotorInfo == null) + var locomotorInfos = rules.Actors[SystemActors.World].TraitInfos() + .Where(li => li.Name == Locomotor).ToList(); + if (locomotorInfos.Count == 0) throw new YamlException($"A locomotor named '{Locomotor}' doesn't exist."); - else if (locomotorInfos.Count(li => li.Name == Locomotor) > 1) + else if (locomotorInfos.Count > 1) throw new YamlException($"There is more than one locomotor named '{Locomotor}'."); + LocomotorInfo = locomotorInfos[0]; + // We need to reset the reference to the locomotor between each worlds, otherwise we are reference the previous state. locomotor = null; diff --git a/OpenRA.Mods.Common/Traits/Player/ClassicParallelProductionQueue.cs b/OpenRA.Mods.Common/Traits/Player/ClassicParallelProductionQueue.cs index af206115b9..b4013132f8 100644 --- a/OpenRA.Mods.Common/Traits/Player/ClassicParallelProductionQueue.cs +++ b/OpenRA.Mods.Common/Traits/Player/ClassicParallelProductionQueue.cs @@ -133,15 +133,15 @@ namespace OpenRA.Mods.Common.Traits public override TraitPair MostLikelyProducer() { - var productionActors = self.World.ActorsWithTrait() + var productionActor = self.World.ActorsWithTrait() .Where(x => x.Actor.Owner == self.Owner && !x.Trait.IsTraitDisabled && x.Trait.Info.Produces.Contains(Info.Type)) - .OrderByDescending(x => x.Actor.IsPrimaryBuilding()) + .OrderBy(x => x.Trait.IsTraitPaused) + .ThenByDescending(x => x.Actor.IsPrimaryBuilding()) .ThenByDescending(x => x.Actor.ActorID) - .ToList(); + .FirstOrDefault(); - var unpaused = productionActors.FirstOrDefault(a => !a.Trait.IsTraitPaused); - return unpaused.Trait != null ? unpaused : productionActors.FirstOrDefault(); + return productionActor; } protected override bool BuildUnit(ActorInfo unit) @@ -159,14 +159,10 @@ namespace OpenRA.Mods.Common.Traits .OrderByDescending(x => x.Actor.IsPrimaryBuilding()) .ThenByDescending(x => x.Actor.ActorID); - if (!producers.Any()) - { - CancelProduction(unit.Name, 1); - return false; - } - + var anyProducers = false; foreach (var p in producers) { + anyProducers = true; if (p.Trait.IsTraitPaused) continue; @@ -184,6 +180,12 @@ namespace OpenRA.Mods.Common.Traits } } + if (!anyProducers) + { + CancelProduction(unit.Name, 1); + return false; + } + return false; } diff --git a/OpenRA.Mods.Common/Traits/Player/ClassicProductionQueue.cs b/OpenRA.Mods.Common/Traits/Player/ClassicProductionQueue.cs index 3540f51d37..13f1a7be3d 100644 --- a/OpenRA.Mods.Common/Traits/Player/ClassicProductionQueue.cs +++ b/OpenRA.Mods.Common/Traits/Player/ClassicProductionQueue.cs @@ -83,15 +83,15 @@ namespace OpenRA.Mods.Common.Traits public override TraitPair MostLikelyProducer() { - var productionActors = self.World.ActorsWithTrait() + var productionActor = self.World.ActorsWithTrait() .Where(x => x.Actor.Owner == self.Owner && !x.Trait.IsTraitDisabled && x.Trait.Info.Produces.Contains(Info.Type)) - .OrderByDescending(x => x.Actor.IsPrimaryBuilding()) + .OrderBy(x => x.Trait.IsTraitPaused) + .ThenByDescending(x => x.Actor.IsPrimaryBuilding()) .ThenByDescending(x => x.Actor.ActorID) - .ToList(); + .FirstOrDefault(); - var unpaused = productionActors.FirstOrDefault(a => !a.Trait.IsTraitPaused); - return unpaused.Trait != null ? unpaused : productionActors.FirstOrDefault(); + return productionActor; } protected override bool BuildUnit(ActorInfo unit) @@ -109,14 +109,10 @@ namespace OpenRA.Mods.Common.Traits .OrderByDescending(x => x.Actor.IsPrimaryBuilding()) .ThenByDescending(x => x.Actor.ActorID); - if (!producers.Any()) - { - CancelProduction(unit.Name, 1); - return false; - } - + var anyProducers = false; foreach (var p in producers) { + anyProducers = true; if (p.Trait.IsTraitPaused) continue; @@ -134,6 +130,12 @@ namespace OpenRA.Mods.Common.Traits } } + if (!anyProducers) + { + CancelProduction(unit.Name, 1); + return false; + } + return false; } diff --git a/OpenRA.Mods.Common/Traits/Player/ConquestVictoryConditions.cs b/OpenRA.Mods.Common/Traits/Player/ConquestVictoryConditions.cs index 51811a7f7e..0d8eae8401 100644 --- a/OpenRA.Mods.Common/Traits/Player/ConquestVictoryConditions.cs +++ b/OpenRA.Mods.Common/Traits/Player/ConquestVictoryConditions.cs @@ -81,13 +81,14 @@ namespace OpenRA.Mods.Common.Traits return; var myTeam = self.World.LobbyInfo.ClientWithIndex(self.Owner.ClientIndex).Team; - var teams = self.World.Players.Where(p => !p.NonCombatant && p.Playable) + var victoriousTeam = self.World.Players.Where(p => !p.NonCombatant && p.Playable) .Select(p => (Player: p, PlayerStatistics: p.PlayerActor.TraitOrDefault())) .OrderByDescending(p => p.PlayerStatistics?.Experience ?? 0) .GroupBy(p => (self.World.LobbyInfo.ClientWithIndex(p.Player.ClientIndex) ?? new Session.Client()).Team) - .OrderByDescending(g => g.Sum(gg => gg.PlayerStatistics?.Experience ?? 0)); + .OrderByDescending(g => g.Sum(gg => gg.PlayerStatistics?.Experience ?? 0)) + .First(); - if (teams.First().Key == myTeam && (myTeam != 0 || teams.First().First().Player == self.Owner)) + if (victoriousTeam.Key == myTeam && (myTeam != 0 || victoriousTeam.First().Player == self.Owner)) { mo.MarkCompleted(self.Owner, objectiveID); return; diff --git a/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs b/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs index cbb24260b6..fa328a0316 100644 --- a/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs +++ b/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs @@ -594,9 +594,11 @@ namespace OpenRA.Mods.Common.Traits // Returns the actor/trait that is most likely (but not necessarily guaranteed) to produce something in this queue public virtual TraitPair MostLikelyProducer() { - var traits = productionTraits.Where(p => !p.IsTraitDisabled && p.Info.Produces.Contains(Info.Type)); - var unpaused = traits.FirstOrDefault(a => !a.IsTraitPaused); - return new TraitPair(Actor, unpaused ?? traits.FirstOrDefault()); + var trait = productionTraits + .Where(p => !p.IsTraitDisabled && p.Info.Produces.Contains(Info.Type)) + .OrderBy(p => p.IsTraitPaused) + .FirstOrDefault(); + return new TraitPair(Actor, trait); } // Builds a unit from the actor that holds this queue (1 queue per building) diff --git a/OpenRA.Mods.Common/Traits/Player/StrategicVictoryConditions.cs b/OpenRA.Mods.Common/Traits/Player/StrategicVictoryConditions.cs index ab89be038d..9e6dc647c9 100644 --- a/OpenRA.Mods.Common/Traits/Player/StrategicVictoryConditions.cs +++ b/OpenRA.Mods.Common/Traits/Player/StrategicVictoryConditions.cs @@ -89,13 +89,21 @@ namespace OpenRA.Mods.Common.Traits if (!self.Owner.NonCombatant && self.Owner.HasNoRequiredUnits(shortGame)) mo.MarkFailed(self.Owner, objectiveID); - var others = self.World.Players.Where(p => !p.NonCombatant - && !p.IsAlliedWith(self.Owner)); + var allOthersLost = true; + var anyOtherWon = false; + foreach (var other in self.World.Players) + { + if (other.NonCombatant || other.IsAlliedWith(self.Owner)) + continue; - if (others.All(p => p.WinState == WinState.Lost)) + allOthersLost = allOthersLost && other.WinState == WinState.Lost; + anyOtherWon = anyOtherWon || other.WinState == WinState.Won; + } + + if (allOthersLost) mo.MarkCompleted(player, objectiveID); - if (others.Any(p => p.WinState == WinState.Won)) + if (anyOtherWon) mo.MarkFailed(player, objectiveID); // See if any of the conditions are met to increase the count @@ -119,13 +127,14 @@ namespace OpenRA.Mods.Common.Traits return; var myTeam = self.World.LobbyInfo.ClientWithIndex(self.Owner.ClientIndex).Team; - var teams = self.World.Players.Where(p => !p.NonCombatant && p.Playable) + var victoriousTeam = self.World.Players.Where(p => !p.NonCombatant && p.Playable) .Select(p => (Player: p, PlayerStatistics: p.PlayerActor.TraitOrDefault())) .OrderByDescending(p => p.PlayerStatistics?.Experience ?? 0) .GroupBy(p => (self.World.LobbyInfo.ClientWithIndex(p.Player.ClientIndex) ?? new Session.Client()).Team) - .OrderByDescending(g => g.Sum(gg => gg.PlayerStatistics?.Experience ?? 0)); + .OrderByDescending(g => g.Sum(gg => gg.PlayerStatistics?.Experience ?? 0)) + .First(); - if (teams.First().Key == myTeam && (myTeam != 0 || teams.First().First().Player == self.Owner)) + if (victoriousTeam.Key == myTeam && (myTeam != 0 || victoriousTeam.First().Player == self.Owner)) { mo.MarkCompleted(self.Owner, objectiveID); return; diff --git a/OpenRA.Mods.Common/Traits/RejectsOrders.cs b/OpenRA.Mods.Common/Traits/RejectsOrders.cs index bba8dd1bcf..149d0c583d 100644 --- a/OpenRA.Mods.Common/Traits/RejectsOrders.cs +++ b/OpenRA.Mods.Common/Traits/RejectsOrders.cs @@ -44,10 +44,19 @@ namespace OpenRA.Mods.Common.Traits if (rejectsOrdersTraits.Length == 0) return true; - var reject = rejectsOrdersTraits.SelectMany(t => t.Reject); - var except = rejectsOrdersTraits.SelectMany(t => t.Except); + foreach (var rejectsOrdersTrait in rejectsOrdersTraits) + if (rejectsOrdersTrait.Except.Contains(orderString)) + return true; - return except.Contains(orderString) || (reject.Any() && !reject.Contains(orderString)); + var anyRejects = false; + foreach (var rejectsOrdersTrait in rejectsOrdersTraits) + { + anyRejects = anyRejects || rejectsOrdersTrait.Reject.Count > 0; + if (rejectsOrdersTrait.Reject.Contains(orderString)) + return false; + } + + return anyRejects; } } } diff --git a/OpenRA.Mods.Common/Traits/Render/WithShadow.cs b/OpenRA.Mods.Common/Traits/Render/WithShadow.cs index 02d0b67bc2..a9833f0fe5 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithShadow.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithShadow.cs @@ -51,29 +51,27 @@ namespace OpenRA.Mods.Common.Traits.Render if (IsTraitDisabled) return r; + var renderables = r.ToList(); var height = self.World.Map.DistanceAboveTerrain(self.CenterPosition).Length; - var shadowSprites = r.Where(s => !s.IsDecoration && s is IModifyableRenderable) + var shadowSprites = renderables.Where(s => !s.IsDecoration && s is IModifyableRenderable) .Select(ma => ((IModifyableRenderable)ma).WithTint(shadowColor, ((IModifyableRenderable)ma).TintModifiers | TintModifiers.ReplaceColor) .WithAlpha(shadowAlpha) .OffsetBy(info.Offset - new WVec(0, 0, height)) .WithZOffset(ma.ZOffset + height + info.ZOffset) .AsDecoration()); - return shadowSprites.Concat(r); + return shadowSprites.Concat(renderables); } IEnumerable IRenderModifier.ModifyScreenBounds(Actor self, WorldRenderer wr, IEnumerable bounds) { - foreach (var r in bounds) - yield return r; - if (IsTraitDisabled) - yield break; + return bounds; + var boundsList = bounds.ToList(); var height = self.World.Map.DistanceAboveTerrain(self.CenterPosition).Length; var offset = wr.ScreenPxOffset(info.Offset - new WVec(0, 0, height)); - foreach (var r in bounds) - yield return new Rectangle(r.X + offset.X, r.Y + offset.Y, r.Width, r.Height); + return boundsList.Concat(boundsList.Select(r => new Rectangle(r.X + offset.X, r.Y + offset.Y, r.Width, r.Height))); } } } diff --git a/OpenRA.Mods.Common/Traits/World/ActorMap.cs b/OpenRA.Mods.Common/Traits/World/ActorMap.cs index 7da45446d7..010b3dc775 100644 --- a/OpenRA.Mods.Common/Traits/World/ActorMap.cs +++ b/OpenRA.Mods.Common/Traits/World/ActorMap.cs @@ -205,8 +205,8 @@ namespace OpenRA.Mods.Common.Traits actorShouldBeRemoved = removeActorPosition.Contains; LargestActorRadius = map.Rules.Actors.SelectMany(a => a.Value.TraitInfos()).Max(h => h.Type.OuterRadius); - var blockers = map.Rules.Actors.Where(a => a.Value.HasTraitInfo()); - LargestBlockingActorRadius = blockers.Any() ? blockers.SelectMany(a => a.Value.TraitInfos()).Max(h => h.Type.OuterRadius) : WDist.Zero; + var blockers = map.Rules.Actors.Where(a => a.Value.HasTraitInfo()).ToList(); + LargestBlockingActorRadius = blockers.Count != 0 ? blockers.SelectMany(a => a.Value.TraitInfos()).Max(h => h.Type.OuterRadius) : WDist.Zero; } void INotifyCreated.Created(Actor self) diff --git a/OpenRA.Mods.Common/Traits/World/CreateMapPlayers.cs b/OpenRA.Mods.Common/Traits/World/CreateMapPlayers.cs index 4c0fc0940a..95f390aded 100644 --- a/OpenRA.Mods.Common/Traits/World/CreateMapPlayers.cs +++ b/OpenRA.Mods.Common/Traits/World/CreateMapPlayers.cs @@ -28,7 +28,7 @@ namespace OpenRA.Mods.Common.Traits /// void ICreatePlayersInfo.CreateServerPlayers(MapPreview map, Session lobbyInfo, List players, MersenneTwister playerRandom) { - var factions = map.WorldActorInfo.TraitInfos().ToArray(); + var factions = map.WorldActorInfo.TraitInfos(); var assignSpawnLocations = map.WorldActorInfo.TraitInfoOrDefault(); var spawnState = assignSpawnLocations?.InitializeState(map, lobbyInfo); @@ -41,7 +41,7 @@ namespace OpenRA.Mods.Common.Traits } // Create the regular playable players. - var bots = map.PlayerActorInfo.TraitInfos().ToArray(); + var bots = map.PlayerActorInfo.TraitInfos(); foreach (var kv in lobbyInfo.Slots) { diff --git a/OpenRA.Mods.Common/Traits/World/EditorActorPreview.cs b/OpenRA.Mods.Common/Traits/World/EditorActorPreview.cs index 2b1857821d..736e3ee333 100644 --- a/OpenRA.Mods.Common/Traits/World/EditorActorPreview.cs +++ b/OpenRA.Mods.Common/Traits/World/EditorActorPreview.cs @@ -178,7 +178,7 @@ namespace OpenRA.Mods.Common.Traits return reference.GetOrDefault(info); } - public IEnumerable GetInits() where T : ActorInit + public IReadOnlyCollection GetInits() where T : ActorInit { return reference.GetAll(); } diff --git a/OpenRA.Mods.Common/Traits/World/Locomotor.cs b/OpenRA.Mods.Common/Traits/World/Locomotor.cs index fa52f55693..2d14b273a5 100644 --- a/OpenRA.Mods.Common/Traits/World/Locomotor.cs +++ b/OpenRA.Mods.Common/Traits/World/Locomotor.cs @@ -136,7 +136,7 @@ namespace OpenRA.Mods.Common.Traits public readonly LongBitSet Crushable; public readonly CellFlag CellFlag; - public CellCache(LongBitSet immovable, CellFlag cellFlag, LongBitSet crushable = default) + public CellCache(LongBitSet immovable, CellFlag cellFlag, LongBitSet crushable) { Immovable = immovable; Crushable = crushable; @@ -459,26 +459,17 @@ namespace OpenRA.Mods.Common.Traits using (new PerfSample("locomotor_cache")) { var cache = blockingCache[cell.Layer]; - - var actors = actorMap.GetActorsAt(cell); var cellFlag = CellFlag.HasFreeSpace; - - if (!actors.Any()) - { - cache[cell] = new CellCache(default, cellFlag); - return; - } - - if (sharesCell && actorMap.HasFreeSubCell(cell)) - { - cache[cell] = new CellCache(default, cellFlag); - return; - } - var cellImmovablePlayers = default(LongBitSet); var cellCrushablePlayers = world.AllPlayersMask; - foreach (var actor in actors) + if (sharesCell && actorMap.HasFreeSubCell(cell)) + { + cache[cell] = new CellCache(cellImmovablePlayers, cellFlag, cellCrushablePlayers); + return; + } + + foreach (var actor in actorMap.GetActorsAt(cell)) { var actorImmovablePlayers = world.AllPlayersMask; var actorCrushablePlayers = world.NoPlayersMask; @@ -493,11 +484,10 @@ namespace OpenRA.Mods.Common.Traits if (isTransitOnly) cellFlag |= CellFlag.HasTransitOnlyActor; - if (crushables.Any()) + foreach (var crushable in crushables) { cellFlag |= CellFlag.HasCrushableActor; - foreach (var crushable in crushables) - actorCrushablePlayers = actorCrushablePlayers.Union(crushable.CrushableBy(actor, Info.Crushes)); + actorCrushablePlayers = actorCrushablePlayers.Union(crushable.CrushableBy(actor, Info.Crushes)); } if (isMoving) diff --git a/OpenRA.Mods.Common/UpdateRules/Rules/20210321/UnhardcodeVeteranProductionIconOverlay.cs b/OpenRA.Mods.Common/UpdateRules/Rules/20210321/UnhardcodeVeteranProductionIconOverlay.cs index 8483f27c36..5bdffb65ee 100644 --- a/OpenRA.Mods.Common/UpdateRules/Rules/20210321/UnhardcodeVeteranProductionIconOverlay.cs +++ b/OpenRA.Mods.Common/UpdateRules/Rules/20210321/UnhardcodeVeteranProductionIconOverlay.cs @@ -10,7 +10,6 @@ #endregion using System.Collections.Generic; -using System.Linq; namespace OpenRA.Mods.Common.UpdateRules.Rules { @@ -24,7 +23,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules public override IEnumerable AfterUpdate(ModData modData) { - if (locations.Any()) + if (locations.Count != 0) yield return "Icon overlay logic has been split from ProducibleWithLevel to WithProductionIconOverlay trait.\n" + "If you have been using VeteranProductionIconOverlay trait, add WithProductionIconOverlay to following actors:\n" + UpdateUtils.FormatMessageList(locations); diff --git a/OpenRA.Mods.Common/UpdateRules/Rules/20230225/ExplicitSequenceFilenames.cs b/OpenRA.Mods.Common/UpdateRules/Rules/20230225/ExplicitSequenceFilenames.cs index 62732081bf..88ca1d2c62 100644 --- a/OpenRA.Mods.Common/UpdateRules/Rules/20230225/ExplicitSequenceFilenames.cs +++ b/OpenRA.Mods.Common/UpdateRules/Rules/20230225/ExplicitSequenceFilenames.cs @@ -45,16 +45,16 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules foreach (var sequenceNode in imageNode.Value.Nodes) { var useTilesetExtensionNode = sequenceNode.LastChildMatching("UseTilesetExtension"); - if (useTilesetExtensionNode != null && !tilesetExtensions.Any()) + if (useTilesetExtensionNode != null && tilesetExtensions.Count == 0) requiredMetadata.Add("TilesetExtensions"); var useTilesetCodeNode = sequenceNode.LastChildMatching("UseTilesetCode"); - if (useTilesetCodeNode != null && !tilesetCodes.Any()) + if (useTilesetCodeNode != null && tilesetCodes.Count == 0) requiredMetadata.Add("TilesetCodes"); } } - if (requiredMetadata.Any()) + if (requiredMetadata.Count != 0) { yield return $"The ExplicitSequenceFilenames rule requires {requiredMetadata.JoinWith(", ")}\n" + "to be defined under the SpriteSequenceFormat definition in mod.yaml.\n" + @@ -317,7 +317,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules if (allSequencesHaveTilesetFilenames) sequenceNode.RemoveNodes("TilesetFilenames"); - if (!sequenceNode.Value.Nodes.Any()) + if (sequenceNode.Value.Nodes.Count == 0) imageNode.RemoveNode(sequenceNode); } @@ -328,13 +328,13 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules sequenceNode.RemoveNodes("TilesetFilenames"); var tilesetFilenamesNode = sequenceNode.LastChildMatching("TilesetFilenames"); - if (allSequencesHaveTilesetFilenames && tilesetFilenamesNode != null && !tilesetFilenamesNode.Value.Nodes.Any()) + if (allSequencesHaveTilesetFilenames && tilesetFilenamesNode != null && tilesetFilenamesNode.Value.Nodes.Count == 0) sequenceNode.RemoveNode(tilesetFilenamesNode); } } foreach (var sequenceNode in imageNode.Value.Nodes.ToList()) - if (implicitInheritedSequences.Contains(sequenceNode.Key) && !sequenceNode.Value.Nodes.Any()) + if (implicitInheritedSequences.Contains(sequenceNode.Key) && sequenceNode.Value.Nodes.Count == 0) imageNode.RemoveNode(sequenceNode); } @@ -424,7 +424,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules if (overrideNode.Value.Value == filenameNode.Value.Value) tilesetFilenamesNode.Value.Nodes.Remove(overrideNode); - if (!tilesetFilenamesNode.Value.Nodes.Any()) + if (tilesetFilenamesNode.Value.Nodes.Count == 0) sequenceNode.RemoveNode(tilesetFilenamesNode); sequenceNode.Value.Nodes.Insert(0, filenameNode); diff --git a/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs b/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs index 627bd19746..129ec04ba0 100644 --- a/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs +++ b/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs @@ -114,7 +114,7 @@ namespace OpenRA.Mods.Common.UpdateRules }), }; - public static IEnumerable FromSource(ObjectCreator objectCreator, string source, bool chain = true) + public static IReadOnlyCollection FromSource(ObjectCreator objectCreator, string source, bool chain = true) { // Use reflection to identify types var namedType = objectCreator.FindType(source); @@ -143,12 +143,12 @@ namespace OpenRA.Mods.Common.UpdateRules this.chainToSource = chainToSource; } - IEnumerable Rules(bool chain = true) + IReadOnlyCollection Rules(bool chain = true) { if (chainToSource != null && chain) { var child = Paths.First(p => p.source == chainToSource); - return rules.Concat(child.Rules(chain)); + return rules.Concat(child.Rules(chain)).ToList(); } return rules; diff --git a/OpenRA.Mods.Common/UtilityCommands/ExtractEmmyLuaAPI.cs b/OpenRA.Mods.Common/UtilityCommands/ExtractEmmyLuaAPI.cs index d88415fa38..7f4a703c51 100644 --- a/OpenRA.Mods.Common/UtilityCommands/ExtractEmmyLuaAPI.cs +++ b/OpenRA.Mods.Common/UtilityCommands/ExtractEmmyLuaAPI.cs @@ -271,7 +271,7 @@ namespace OpenRA.Mods.Common.UtilityCommands if (isActivity) Console.WriteLine(" --- *Queued Activity*"); - if (requiredTraits.Any()) + if (requiredTraits.Length != 0) Console.WriteLine($" --- **Requires {(requiredTraits.Length == 1 ? "Trait" : "Traits")}:** {requiredTraits.Select(GetDocumentationUrl).JoinWith(", ")}"); if (memberInfo is MethodInfo methodInfo) diff --git a/OpenRA.Mods.Common/UtilityCommands/UpdateMapCommand.cs b/OpenRA.Mods.Common/UtilityCommands/UpdateMapCommand.cs index c6e1b70ef7..9aeb9135ad 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpdateMapCommand.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpdateMapCommand.cs @@ -38,7 +38,7 @@ namespace OpenRA.Mods.Common.UtilityCommands if (folder.OpenPackage(args[1], modData.ModFiles) is not IReadWritePackage package) throw new FileNotFoundException(args[1]); - IEnumerable rules = null; + IReadOnlyCollection rules = null; if (args.Length > 2) rules = UpdatePath.FromSource(modData.ObjectCreator, args[2]); @@ -77,9 +77,10 @@ namespace OpenRA.Mods.Common.UtilityCommands } var other = UpdatePath.KnownRules(modData.ObjectCreator) - .Where(r => !ruleGroups.Values.Any(g => g.Contains(r))); + .Where(r => !ruleGroups.Values.Any(g => g.Contains(r))) + .ToList(); - if (other.Any()) + if (other.Count != 0) { Console.WriteLine(" Other:"); foreach (var r in other) diff --git a/OpenRA.Mods.Common/UtilityCommands/UpdateModCommand.cs b/OpenRA.Mods.Common/UtilityCommands/UpdateModCommand.cs index 4883545e53..64f14b0146 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpdateModCommand.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpdateModCommand.cs @@ -32,7 +32,7 @@ namespace OpenRA.Mods.Common.UtilityCommands // HACK: The engine code assumes that Game.modData is set. var modData = Game.ModData = utility.ModData; - IEnumerable rules = null; + IReadOnlyCollection rules = null; if (args.Length > 1) rules = UpdatePath.FromSource(modData.ObjectCreator, args[1]); @@ -71,9 +71,10 @@ namespace OpenRA.Mods.Common.UtilityCommands } var other = UpdatePath.KnownRules(modData.ObjectCreator) - .Where(r => !ruleGroups.Values.Any(g => g.Contains(r))); + .Where(r => !ruleGroups.Values.Any(g => g.Contains(r))) + .ToList(); - if (other.Any()) + if (other.Count != 0) { Console.WriteLine(" Other:"); foreach (var r in other) @@ -111,9 +112,9 @@ namespace OpenRA.Mods.Common.UtilityCommands PrintSummary(rules, args.Contains("--detailed")); } - public static void PrintSummary(IEnumerable rules, bool detailed) + public static void PrintSummary(IReadOnlyCollection rules, bool detailed) { - var count = rules.Count(); + var count = rules.Count; if (count == 1) Console.WriteLine("Found 1 API change:"); else diff --git a/OpenRA.Mods.Common/Warheads/FireClusterWarhead.cs b/OpenRA.Mods.Common/Warheads/FireClusterWarhead.cs index ad4b591781..b80ae5e0db 100644 --- a/OpenRA.Mods.Common/Warheads/FireClusterWarhead.cs +++ b/OpenRA.Mods.Common/Warheads/FireClusterWarhead.cs @@ -60,9 +60,9 @@ namespace OpenRA.Mods.Common.Warheads if (RandomClusterCount != 0) { - var randomTargetCells = CellsMatching(targetCell, true); - var clusterCount = RandomClusterCount < 0 ? randomTargetCells.Count() : RandomClusterCount; - if (randomTargetCells.Any()) + var randomTargetCells = CellsMatching(targetCell, true).ToList(); + var clusterCount = RandomClusterCount < 0 ? randomTargetCells.Count : RandomClusterCount; + if (randomTargetCells.Count != 0) for (var i = 0; i < clusterCount; i++) FireProjectileAtCell(map, firedBy, target, randomTargetCells.Random(firedBy.World.SharedRandom), args); } diff --git a/OpenRA.Mods.Common/Widgets/Logic/EncyclopediaLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/EncyclopediaLogic.cs index 24261e5aaf..a387297c93 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/EncyclopediaLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/EncyclopediaLogic.cs @@ -84,7 +84,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic var actors = new List>(); foreach (var actor in modData.DefaultRules.Actors.Values) { - if (!actor.TraitInfos().Any()) + if (actor.TraitInfos().Count == 0) continue; var statistics = actor.TraitInfoOrDefault(); diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/GameInfoStatsLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/GameInfoStatsLogic.cs index 16409c6e9a..ac30271ffb 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/GameInfoStatsLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/GameInfoStatsLogic.cs @@ -113,7 +113,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic .Select(p => (Player: p, PlayerStatistics: p.PlayerActor.TraitOrDefault())) .OrderByDescending(p => p.PlayerStatistics?.Experience ?? 0) .GroupBy(p => (world.LobbyInfo.ClientWithIndex(p.Player.ClientIndex) ?? new Session.Client()).Team) - .OrderByDescending(g => g.Sum(gg => gg.PlayerStatistics?.Experience ?? 0)); + .OrderByDescending(g => g.Sum(gg => gg.PlayerStatistics?.Experience ?? 0)) + .ToList(); void KickAction(Session.Client client) { @@ -133,7 +134,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic foreach (var t in teams) { - if (teams.Count() > 1) + if (teams.Count > 1) { var teamHeader = ScrollItemWidget.Setup(teamTemplate, () => false, () => { }); var team = t.Key > 0 diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ObserverShroudSelectorLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ObserverShroudSelectorLogic.cs index 688b3fc58c..b990115c83 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ObserverShroudSelectorLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ObserverShroudSelectorLogic.cs @@ -114,9 +114,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic .GroupBy(p => (world.LobbyInfo.ClientWithIndex(p.Player.ClientIndex) ?? new Session.Client()).Team) .OrderBy(g => g.Key); - var noTeams = teams.Count() == 1; + var teamsList = teams.ToList(); + var noTeams = teamsList.Count == 1; var totalPlayers = 0; - foreach (var t in teams) + foreach (var t in teamsList) { totalPlayers += t.Count(); var label = noTeams ? TranslationProvider.GetString(Players) : t.Key > 0 @@ -209,12 +210,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (e.Key >= Keycode.NUMBER_0 && e.Key <= Keycode.NUMBER_9) { var key = (int)e.Key - (int)Keycode.NUMBER_0; - var team = teams.Where(t => t.Key == key).SelectMany(s => s); - if (!team.Any()) + var team = teams.Where(t => t.Key == key).SelectMany(s => s).ToList(); + if (team.Count == 0) return false; if (e.Modifiers == Modifiers.Shift) - team = team.Reverse(); + team.Reverse(); selected = team.SkipWhile(t => t.Player != selected.Player).Skip(1).FirstOrDefault() ?? team.FirstOrDefault(); selected.OnClick(); diff --git a/OpenRA.Mods.Common/Widgets/Logic/Installation/InstallFromSourceLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Installation/InstallFromSourceLogic.cs index 80f100e2c3..aabf39b7a3 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Installation/InstallFromSourceLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Installation/InstallFromSourceLogic.cs @@ -183,7 +183,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic selectedPackages = availablePackages.ToDictionary(x => x.Identifier, y => y.Required); // Ignore source if content is already installed - if (availablePackages.Any()) + if (availablePackages.Length != 0) { Game.RunAfterTick(() => { @@ -218,10 +218,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic var options = new Dictionary>(); - if (gameSources.Any()) + if (gameSources.Count != 0) options.Add(TranslationProvider.GetString(GameSources), gameSources); - if (digitalInstalls.Any()) + if (digitalInstalls.Count != 0) options.Add(TranslationProvider.GetString(DigitalInstalls), digitalInstalls); Game.RunAfterTick(() => diff --git a/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs index 73c14c5555..7864cc6fdd 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs @@ -322,18 +322,19 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (!int.TryParse(mapFilter, out var playerCountFilter)) playerCountFilter = -1; - var validMaps = tabMaps[tab] + var maps = tabMaps[tab] .Where(m => category == null || m.Categories.Contains(category)) .Where(m => mapFilter == null || (m.Title != null && m.Title.Contains(mapFilter, StringComparison.CurrentCultureIgnoreCase)) || (m.Author != null && m.Author.Contains(mapFilter, StringComparison.CurrentCultureIgnoreCase)) || m.PlayerCount == playerCountFilter); - IOrderedEnumerable maps; if (orderByFunc == null) - maps = validMaps.OrderBy(m => m.Title); + maps = maps.OrderBy(m => m.Title); else - maps = validMaps.OrderBy(orderByFunc).ThenBy(m => m.Title); + maps = maps.OrderBy(orderByFunc).ThenBy(m => m.Title); + + maps = maps.ToList(); scrollpanels[tab].RemoveChildren(); foreach (var loop in maps) diff --git a/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs index e0ebb20101..7284d49a4e 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs @@ -715,10 +715,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic var players = replay.GameInfo.Players .GroupBy(p => p.Team) - .OrderBy(g => g.Key); + .OrderBy(g => g.Key) + .ToList(); var teams = new Dictionary>(); - var noTeams = players.Count() == 1; + var noTeams = players.Count == 1; foreach (var p in players) { var label = noTeams ? TranslationProvider.GetString(Players) : p.Key > 0 diff --git a/OpenRA.Mods.Common/Widgets/Logic/ServerListLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ServerListLogic.cs index c0b60a6754..445791c800 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/ServerListLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/ServerListLogic.cs @@ -572,10 +572,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic var players = server.Clients .Where(c => !c.IsSpectator) .GroupBy(p => p.Team) - .OrderBy(g => g.Key); + .OrderBy(g => g.Key) + .ToList(); var teams = new Dictionary>(); - var noTeams = players.Count() == 1; + var noTeams = players.Count == 1; foreach (var p in players) { var label = noTeams ? TranslationProvider.GetString(Players) : p.Key > 0 diff --git a/OpenRA.Mods.Common/Widgets/Logic/Settings/HotkeysSettingsLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Settings/HotkeysSettingsLogic.cs index 5848cdd99d..561f8f991e 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Settings/HotkeysSettingsLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Settings/HotkeysSettingsLogic.cs @@ -183,9 +183,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic { var typesInGroup = hg.Value; var keysInGroup = modData.Hotkeys.Definitions - .Where(hd => IsHotkeyVisibleInFilter(hd) && hd.Types.Overlaps(typesInGroup)); + .Where(hd => IsHotkeyVisibleInFilter(hd) && hd.Types.Overlaps(typesInGroup)) + .ToList(); - if (!keysInGroup.Any()) + if (keysInGroup.Count == 0) continue; var header = headerTemplate.Clone(); diff --git a/OpenRA.Mods.Common/Widgets/ObserverProductionIconsWidget.cs b/OpenRA.Mods.Common/Widgets/ObserverProductionIconsWidget.cs index 20c81702f5..e62ff665d7 100644 --- a/OpenRA.Mods.Common/Widgets/ObserverProductionIconsWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ObserverProductionIconsWidget.cs @@ -102,14 +102,15 @@ namespace OpenRA.Mods.Common.Widgets var queues = world.ActorsWithTrait() .Where(a => a.Actor.Owner == player) - .Select((a, i) => new { a.Trait, i }); + .Select(a => a.Trait) + .ToList(); foreach (var queue in queues) - if (!clocks.ContainsKey(queue.Trait)) - clocks.Add(queue.Trait, new Animation(world, ClockAnimation)); + if (!clocks.ContainsKey(queue)) + clocks.Add(queue, new Animation(world, ClockAnimation)); var currentItemsByItem = queues - .Select(a => a.Trait.CurrentItem()) + .Select(q => q.CurrentItem()) .Where(pi => pi != null) .GroupBy(pr => pr.Item) .OrderBy(g => g.First().Queue.Info.DisplayOrder) diff --git a/OpenRA.Mods.Common/Widgets/ProductionTabsWidget.cs b/OpenRA.Mods.Common/Widgets/ProductionTabsWidget.cs index 1bb8769367..e1f3e83e3f 100644 --- a/OpenRA.Mods.Common/Widgets/ProductionTabsWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ProductionTabsWidget.cs @@ -183,9 +183,9 @@ namespace OpenRA.Mods.Common.Widgets public override void Draw() { - var tabs = Groups[queueGroup].Tabs.Where(t => t.Queue.BuildableItems().Any()); + var tabs = Groups[queueGroup].Tabs.Where(t => t.Queue.BuildableItems().Any()).ToList(); - if (!tabs.Any()) + if (tabs.Count == 0) return; var rb = RenderBounds; diff --git a/OpenRA.Mods.D2k/Activities/SwallowActor.cs b/OpenRA.Mods.D2k/Activities/SwallowActor.cs index 9ab4ef94df..87923ab355 100644 --- a/OpenRA.Mods.D2k/Activities/SwallowActor.cs +++ b/OpenRA.Mods.D2k/Activities/SwallowActor.cs @@ -51,7 +51,7 @@ namespace OpenRA.Mods.D2k.Activities swallow = self.Trait(); } - bool AttackTargets(Actor self, IEnumerable targets) + bool AttackTargets(Actor self, IReadOnlyCollection targets) { var targetLocation = target.Actor.Location; foreach (var t in targets) @@ -129,9 +129,10 @@ namespace OpenRA.Mods.D2k.Activities } var targets = self.World.ActorMap.GetActorsAt(targetLocation) - .Where(t => !t.Equals(self) && weapon.IsValidAgainst(t, self)); + .Where(t => !t.Equals(self) && weapon.IsValidAgainst(t, self)) + .ToList(); - if (!targets.Any()) + if (targets.Count == 0) { RevokeCondition(self); return true; diff --git a/OpenRA.Mods.D2k/UtilityCommands/D2kMapImporter.cs b/OpenRA.Mods.D2k/UtilityCommands/D2kMapImporter.cs index 67e2f7158d..462b733880 100644 --- a/OpenRA.Mods.D2k/UtilityCommands/D2kMapImporter.cs +++ b/OpenRA.Mods.D2k/UtilityCommands/D2kMapImporter.cs @@ -509,17 +509,12 @@ namespace OpenRA.Mods.D2k.UtilityCommands // HACK: The arrakis.yaml tileset file seems to be missing some tiles, so just get a replacement for them // Also used for duplicate tiles that are taken from only tileset - if (template == null) + // Just get a template that contains a tile with the same ID as requested + template ??= terrainInfo.Templates.FirstOrDefault(t => { - // Just get a template that contains a tile with the same ID as requested - var templates = terrainInfo.Templates.Where(t => - { - var templateInfo = (DefaultTerrainTemplateInfo)t.Value; - return templateInfo.Frames != null && templateInfo.Frames.Contains(tileIndex); - }); - if (templates.Any()) - template = templates.First().Value; - } + var templateInfo = (DefaultTerrainTemplateInfo)t.Value; + return templateInfo.Frames != null && templateInfo.Frames.Contains(tileIndex); + }).Value; if (template == null) {