Fix CA1851

This commit is contained in:
RoosterDragon
2023-07-13 20:08:36 +01:00
committed by abcdefg30
parent 88f830a9e5
commit 3275875ae5
63 changed files with 349 additions and 267 deletions

View File

@@ -834,6 +834,12 @@ dotnet_diagnostic.CA1849.severity = warning
# Prefer static HashData method over ComputeHash. (Not available on mono) # Prefer static HashData method over ComputeHash. (Not available on mono)
dotnet_diagnostic.CA1850.severity = none 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. # Seal internal types.
dotnet_diagnostic.CA1852.severity = warning dotnet_diagnostic.CA1852.severity = warning

View File

@@ -185,7 +185,7 @@ namespace OpenRA
public bool HasTraitInfo<T>() where T : ITraitInfoInterface { return traits.Contains<T>(); } public bool HasTraitInfo<T>() where T : ITraitInfoInterface { return traits.Contains<T>(); }
public T TraitInfo<T>() where T : ITraitInfoInterface { return traits.Get<T>(); } public T TraitInfo<T>() where T : ITraitInfoInterface { return traits.Get<T>(); }
public T TraitInfoOrDefault<T>() where T : ITraitInfoInterface { return traits.GetOrDefault<T>(); } public T TraitInfoOrDefault<T>() where T : ITraitInfoInterface { return traits.GetOrDefault<T>(); }
public IEnumerable<T> TraitInfos<T>() where T : ITraitInfoInterface { return traits.WithInterface<T>(); } public IReadOnlyCollection<T> TraitInfos<T>() where T : ITraitInfoInterface { return traits.WithInterface<T>(); }
public BitSet<TargetableType> GetAllTargetTypes() public BitSet<TargetableType> GetAllTargetTypes()
{ {

View File

@@ -234,7 +234,7 @@ namespace OpenRA.Graphics
{ {
// PERF: We don't need to search for images if there are no definitions. // 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. // 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<Sprite>(); return Array.Empty<Sprite>();
// Support manual definitions for unusual dialog layouts // Support manual definitions for unusual dialog layouts

View File

@@ -139,7 +139,7 @@ namespace OpenRA
return removed; return removed;
} }
public IEnumerable<T> GetAll<T>() where T : ActorInit public IReadOnlyCollection<T> GetAll<T>() where T : ActorInit
{ {
return initDict.Value.WithInterface<T>(); return initDict.Value.WithInterface<T>();
} }

View File

@@ -12,7 +12,6 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace OpenRA namespace OpenRA
{ {
@@ -64,9 +63,9 @@ namespace OpenRA
} }
/// <summary>Returns the minimal region that covers at least the specified cells.</summary> /// <summary>Returns the minimal region that covers at least the specified cells.</summary>
public static CellRegion BoundingRegion(MapGridType shape, IEnumerable<CPos> cells) public static CellRegion BoundingRegion(MapGridType shape, IReadOnlyCollection<CPos> cells)
{ {
if (cells == null || !cells.Any()) if (cells == null || cells.Count == 0)
throw new ArgumentException("cells must not be null or empty.", nameof(cells)); throw new ArgumentException("cells must not be null or empty.", nameof(cells));
var minU = int.MaxValue; var minU = int.MaxValue;

View File

@@ -201,8 +201,12 @@ namespace OpenRA.Network
public TypeInfo(Type type) public TypeInfo(Type type)
{ {
const BindingFlags Flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; const BindingFlags Flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
var fields = type.GetFields(Flags).Where(fi => !fi.IsLiteral && !fi.IsStatic && fi.HasAttribute<SyncAttribute>()); var fields = type.GetFields(Flags)
var properties = type.GetProperties(Flags).Where(pi => pi.HasAttribute<SyncAttribute>()); .Where(fi => !fi.IsLiteral && !fi.IsStatic && fi.HasAttribute<SyncAttribute>())
.ToList();
var properties = type.GetProperties(Flags)
.Where(pi => pi.HasAttribute<SyncAttribute>())
.ToList();
foreach (var prop in properties) foreach (var prop in properties)
if (!prop.CanRead || prop.GetIndexParameters().Length > 0) if (!prop.CanRead || prop.GetIndexParameters().Length > 0)

View File

@@ -134,8 +134,8 @@ namespace OpenRA
public ConstructorInfo GetCtor(Type type) public ConstructorInfo GetCtor(Type type)
{ {
var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
var ctors = type.GetConstructors(flags).Where(x => x.HasAttribute<UseCtorAttribute>()); var ctors = type.GetConstructors(flags).Where(x => x.HasAttribute<UseCtorAttribute>()).ToList();
if (ctors.Count() > 1) if (ctors.Count > 1)
throw new InvalidOperationException("ObjectCreator: UseCtor on multiple constructors; invalid."); throw new InvalidOperationException("ObjectCreator: UseCtor on multiple constructors; invalid.");
return ctors.FirstOrDefault(); return ctors.FirstOrDefault();
} }

View File

@@ -133,7 +133,7 @@ namespace OpenRA
static FactionInfo ResolveDisplayFaction(World world, string factionName) static FactionInfo ResolveDisplayFaction(World world, string factionName)
{ {
var factions = world.Map.Rules.Actors[SystemActors.World].TraitInfos<FactionInfo>().ToArray(); var factions = world.Map.Rules.Actors[SystemActors.World].TraitInfos<FactionInfo>();
return factions.FirstOrDefault(f => f.InternalName == factionName) ?? factions.First(); return factions.FirstOrDefault(f => f.InternalName == factionName) ?? factions.First();
} }

View File

@@ -12,14 +12,20 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace OpenRA.Primitives namespace OpenRA.Primitives
{ {
public class TypeDictionary : IEnumerable<object> public class TypeDictionary : IEnumerable<object>
{ {
static readonly Func<Type, List<object>> CreateList = type => new List<object>(); static readonly Func<Type, ITypeContainer> CreateTypeContainer = t =>
readonly Dictionary<Type, List<object>> data = new(); (ITypeContainer)typeof(TypeContainer<>).MakeGenericType(t).GetConstructor(Type.EmptyTypes).Invoke(null);
readonly Dictionary<Type, ITypeContainer> data = new();
ITypeContainer InnerGet(Type t)
{
return data.GetOrAdd(t, CreateTypeContainer);
}
public void Add(object val) public void Add(object val)
{ {
@@ -33,7 +39,7 @@ namespace OpenRA.Primitives
void InnerAdd(Type t, object val) void InnerAdd(Type t, object val)
{ {
data.GetOrAdd(t, CreateList).Add(val); InnerGet(t).Add(val);
} }
public bool Contains<T>() public bool Contains<T>()
@@ -48,35 +54,33 @@ namespace OpenRA.Primitives
public T Get<T>() public T Get<T>()
{ {
return (T)Get(typeof(T), true); return Get<T>(true);
} }
public T GetOrDefault<T>() public T GetOrDefault<T>()
{ {
var result = Get(typeof(T), false); return Get<T>(false);
if (result == null)
return default;
return (T)result;
} }
object Get(Type t, bool throwsIfMissing) T Get<T>(bool throwsIfMissing)
{ {
if (!data.TryGetValue(t, out var ret)) if (!data.TryGetValue(typeof(T), out var container))
{ {
if (throwsIfMissing) if (throwsIfMissing)
throw new InvalidOperationException($"TypeDictionary does not contain instance of type `{t}`"); throw new InvalidOperationException($"TypeDictionary does not contain instance of type `{typeof(T)}`");
return null; return default;
} }
if (ret.Count > 1) var list = ((TypeContainer<T>)container).Objects;
throw new InvalidOperationException($"TypeDictionary contains multiple instances of type `{t}`"); if (list.Count > 1)
return ret[0]; throw new InvalidOperationException($"TypeDictionary contains multiple instances of type `{typeof(T)}`");
return list[0];
} }
public IEnumerable<T> WithInterface<T>() public IReadOnlyCollection<T> WithInterface<T>()
{ {
if (data.TryGetValue(typeof(T), out var objs)) if (data.TryGetValue(typeof(T), out var container))
return objs.Cast<T>(); return ((TypeContainer<T>)container).Objects;
return Array.Empty<T>(); return Array.Empty<T>();
} }
@@ -92,18 +96,19 @@ namespace OpenRA.Primitives
void InnerRemove(Type t, object val) void InnerRemove(Type t, object val)
{ {
if (!data.TryGetValue(t, out var objs)) if (!data.TryGetValue(t, out var container))
return; return;
objs.Remove(val);
if (objs.Count == 0) container.Remove(val);
if (container.Count == 0)
data.Remove(t); data.Remove(t);
} }
public void TrimExcess() public void TrimExcess()
{ {
data.TrimExcess(); data.TrimExcess();
foreach (var objs in data.Values) foreach (var t in data.Keys)
objs.TrimExcess(); InnerGet(t).TrimExcess();
} }
public IEnumerator<object> GetEnumerator() public IEnumerator<object> GetEnumerator()
@@ -115,6 +120,36 @@ namespace OpenRA.Primitives
{ {
return GetEnumerator(); return GetEnumerator();
} }
interface ITypeContainer
{
int Count { get; }
void Add(object value);
void Remove(object value);
void TrimExcess();
}
sealed class TypeContainer<T> : ITypeContainer
{
public List<T> Objects { get; } = new List<T>();
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 public static class TypeExts

View File

@@ -11,7 +11,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Eluant; using Eluant;
using Eluant.ObjectBinding; using Eluant.ObjectBinding;
using OpenRA.Scripting; using OpenRA.Scripting;
@@ -139,20 +138,21 @@ namespace OpenRA
{ {
public static WPos Average(this IEnumerable<WPos> source) public static WPos Average(this IEnumerable<WPos> source)
{ {
var length = source.Count(); var length = 0;
if (length == 0)
return WPos.Zero;
var x = 0L; var x = 0L;
var y = 0L; var y = 0L;
var z = 0L; var z = 0L;
foreach (var pos in source) foreach (var pos in source)
{ {
length++;
x += pos.X; x += pos.X;
y += pos.Y; y += pos.Y;
z += pos.Z; z += pos.Z;
} }
if (length == 0)
return WPos.Zero;
x /= length; x /= length;
y /= length; y /= length;
z /= length; z /= length;

View File

@@ -10,7 +10,6 @@
#endregion #endregion
using System; using System;
using System.Linq;
using OpenRA.Activities; using OpenRA.Activities;
using OpenRA.Mods.Cnc.Traits; using OpenRA.Mods.Cnc.Traits;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
@@ -120,7 +119,7 @@ namespace OpenRA.Mods.Cnc.Activities
if (teleporter == null) if (teleporter == null)
return 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) if (maximumDistance != null)
destination = restrictTo.MinBy(x => (x - destination).LengthSquared); destination = restrictTo.MinBy(x => (x - destination).LengthSquared);

View File

@@ -80,7 +80,7 @@ namespace OpenRA.Mods.Cnc.Traits
{ {
var wasGranted = Granted; var wasGranted = Granted;
var wasGrantedAllies = GrantedAllies; var wasGrantedAllies = GrantedAllies;
var allyWatchers = owner.World.ActorsWithTrait<GpsWatcher>().Where(kv => kv.Actor.Owner.IsAlliedWith(owner)); var allyWatchers = owner.World.ActorsWithTrait<GpsWatcher>().Where(kv => kv.Actor.Owner.IsAlliedWith(owner)).ToList();
Granted = actors.Count > 0 && Launched; Granted = actors.Count > 0 && Launched;
GrantedAllies = allyWatchers.Any(w => w.Trait.Granted); GrantedAllies = allyWatchers.Any(w => w.Trait.Granted);

View File

@@ -358,14 +358,11 @@ namespace OpenRA.Mods.Cnc.Traits
bool IsValidTarget(CPos xy) 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; 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); var targetCell = unit.Location + (xy - sourceLocation);
if (manager.Self.Owner.Shroud.IsExplored(targetCell) && unit.Trait<Chronoshiftable>().CanChronoshiftTo(unit, targetCell)) if (manager.Self.Owner.Shroud.IsExplored(targetCell) && unit.Trait<Chronoshiftable>().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) if (!canTeleport)
{ {
// Check the terrain types. This will allow chronoshifts to occur on empty terrain to terrain of // Check the terrain types. This will allow chronoshifts to occur on empty terrain to terrain of

View File

@@ -196,8 +196,8 @@ namespace OpenRA.Mods.Common.Activities
// Update ranges. Exclude paused armaments except when ALL weapons are paused // 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. // (e.g. out of ammo), in which case use the paused, valid weapon with highest range.
var activeArmaments = armaments.Where(x => !x.IsTraitPaused); var activeArmaments = armaments.Where(x => !x.IsTraitPaused).ToList();
if (activeArmaments.Any()) if (activeArmaments.Count != 0)
{ {
minRange = activeArmaments.Max(a => a.Weapon.MinRange); minRange = activeArmaments.Max(a => a.Weapon.MinRange);
maxRange = activeArmaments.Min(a => a.MaxRange()); maxRange = activeArmaments.Min(a => a.MaxRange());

View File

@@ -10,7 +10,6 @@
#endregion #endregion
using System; using System;
using System.Linq;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
namespace OpenRA.Mods.Common.Lint namespace OpenRA.Mods.Common.Lint
@@ -21,8 +20,8 @@ namespace OpenRA.Mods.Common.Lint
{ {
foreach (var actorInfo in rules.Actors) foreach (var actorInfo in rules.Actors)
{ {
var selectable = actorInfo.Value.TraitInfos<SelectableInfo>().Count(); var selectable = actorInfo.Value.TraitInfos<SelectableInfo>().Count;
var interactable = actorInfo.Value.TraitInfos<InteractableInfo>().Count(); var interactable = actorInfo.Value.TraitInfos<InteractableInfo>().Count;
if (selectable > 0 && selectable != interactable) if (selectable > 0 && selectable != interactable)
emitWarning($"Actor `{actorInfo.Value.Name}` defines both Interactable and Selectable traits. This may cause unexpected results."); emitWarning($"Actor `{actorInfo.Value.Name}` defines both Interactable and Selectable traits. This may cause unexpected results.");
} }

View File

@@ -37,7 +37,7 @@ namespace OpenRA.Mods.Common.Lint
try try
{ {
var visibilityTypes = actorInfo.Value.TraitInfos<IDefaultVisibilityInfo>(); var visibilityTypes = actorInfo.Value.TraitInfos<IDefaultVisibilityInfo>();
var count = visibilityTypes.Count(); var count = visibilityTypes.Count;
if (count == 0) if (count == 0)
emitError($"Actor type `{actorInfo.Key}` does not define a default visibility type."); emitError($"Actor type `{actorInfo.Key}` does not define a default visibility type.");

View File

@@ -10,7 +10,6 @@
#endregion #endregion
using System; using System;
using System.Linq;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Server; using OpenRA.Server;
using OpenRA.Traits; using OpenRA.Traits;
@@ -41,7 +40,7 @@ namespace OpenRA.Mods.Common.Lint
continue; continue;
var hitShapes = actorInfo.Value.TraitInfos<HitShapeInfo>(); var hitShapes = actorInfo.Value.TraitInfos<HitShapeInfo>();
if (!hitShapes.Any()) if (hitShapes.Count == 0)
emitError($"Actor type `{actorInfo.Key}` has a Health trait but no HitShape trait."); emitError($"Actor type `{actorInfo.Key}` has a Health trait but no HitShape trait.");
} }
catch (InvalidOperationException e) catch (InvalidOperationException e)

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits;
using OpenRA.Server; using OpenRA.Server;
@@ -32,12 +33,15 @@ namespace OpenRA.Mods.Common.Lint
static void Run(Action<string> emitError, Ruleset rules) static void Run(Action<string> emitError, Ruleset rules)
{ {
var worldActor = rules.Actors[SystemActors.World]; var worldActor = rules.Actors[SystemActors.World];
var locomotorInfos = worldActor.TraitInfos<LocomotorInfo>().ToArray(); var locomotorNames = worldActor.TraitInfos<LocomotorInfo>().Select(li => li.Name).ToList();
foreach (var li in locomotorInfos) var duplicateNames = locomotorNames
foreach (var otherLocomotor in locomotorInfos) .GroupBy(name => name)
if (li != otherLocomotor && li.Name == otherLocomotor.Name) .Where(g => g.Count() > 1)
emitError($"More than one Locomotor exists with the name `{li.Name}`."); .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 actorInfo in rules.Actors)
{ {
foreach (var traitInfo in actorInfo.Value.TraitInfos<TraitInfo>()) foreach (var traitInfo in actorInfo.Value.TraitInfos<TraitInfo>())
@@ -51,16 +55,16 @@ namespace OpenRA.Mods.Common.Lint
if (string.IsNullOrEmpty(locomotor)) if (string.IsNullOrEmpty(locomotor))
continue; continue;
CheckLocomotors(actorInfo.Value, emitError, locomotorInfos, locomotor); CheckLocomotors(actorInfo.Value, emitError, locomotorNamesSet, locomotor);
} }
} }
} }
} }
} }
static void CheckLocomotors(ActorInfo actorInfo, Action<string> emitError, LocomotorInfo[] locomotorInfos, string locomotor) static void CheckLocomotors(ActorInfo actorInfo, Action<string> emitError, HashSet<string> 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."); emitError($"Actor `{actorInfo.Name}` defines Locomotor `{locomotor}` not found on World actor.");
} }
} }

View File

@@ -32,10 +32,12 @@ namespace OpenRA.Mods.Common.Lint
{ {
foreach (var actorInfo in rules.Actors) foreach (var actorInfo in rules.Actors)
{ {
var wsbs = actorInfo.Value.TraitInfos<WithSpriteBodyInfo>(); var duplicateNames = actorInfo.Value.TraitInfos<WithSpriteBodyInfo>()
foreach (var wsb in wsbs) .GroupBy(wsb => wsb.Name)
if (wsbs.Any(w => w != wsb && w.Name == wsb.Name)) .Where(g => g.Count() > 1)
emitError($"Actor type `{actorInfo.Key}` has more than one *SpriteBody with Name: {wsb.Name}."); .Select(g => g.Key);
foreach (var duplicateName in duplicateNames)
emitError($"Actor type `{actorInfo.Key}` has more than one *SpriteBody with Name: {duplicateName}.");
} }
} }
} }

View File

@@ -50,13 +50,13 @@ namespace OpenRA.Mods.Common.Orders
.Where(o => o != null) .Where(o => o != null)
.ToList(); .ToList();
var actorsInvolved = orders.Select(o => o.Actor).Distinct(); var actorsInvolved = orders.Select(o => o.Actor).Distinct().ToArray();
if (!actorsInvolved.Any()) if (actorsInvolved.Length == 0)
yield break; yield break;
// HACK: This is required by the hacky player actions-per-minute calculation // HACK: This is required by the hacky player actions-per-minute calculation
// TODO: Reimplement APM properly and then remove this // 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) foreach (var o in orders)
yield return CheckSameOrder(o.Order, o.Trait.IssueOrder(o.Actor, o.Order, o.Target, mi.Modifiers.HasModifier(Modifiers.Shift))); 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<IIssueOrder>() var orders = self.TraitsImplementing<IIssueOrder>()
.SelectMany(trait => trait.Orders.Select(x => new { Trait = trait, Order = x })) .SelectMany(trait => trait.Orders.Select(x => new { Trait = trait, Order = x }))
.Select(x => x) .Select(x => x)
.OrderByDescending(x => x.Order.OrderPriority); .OrderByDescending(x => x.Order.OrderPriority)
.ToList();
for (var i = 0; i < 2; i++) for (var i = 0; i < 2; i++)
{ {

View File

@@ -245,7 +245,7 @@ namespace OpenRA.Mods.Common.Scripting
foreach (var actorType in actorTypes.Distinct()) foreach (var actorType in actorTypes.Distinct())
typeToQueueMap.Add(actorType, GetBuildableInfo(actorType).Queue.First()); 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))) if (queueTypes.Any(t => !queues.ContainsKey(t) || productionHandlers.ContainsKey(t)))
return false; return false;

View File

@@ -748,10 +748,11 @@ namespace OpenRA.Mods.Common.Server
teamCount = teamCount.Clamp(0, maxTeams); teamCount = teamCount.Clamp(0, maxTeams);
var clients = server.LobbyInfo.Slots var clients = server.LobbyInfo.Slots
.Select(slot => server.LobbyInfo.ClientInSlot(slot.Key)) .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 assigned = 0;
var clientCount = clients.Count(); var clientCount = clients.Count;
foreach (var player in clients) foreach (var player in clients)
{ {
// Free for all // Free for all

View File

@@ -41,8 +41,8 @@ namespace OpenRA.Mods.Common.Traits
else else
{ {
var owner = map.PlayerDefinitions.Single(p => s.Get<OwnerInit>().InternalName == p.Value.Nodes.Last(k => k.Key == "Name").Value.Value); var owner = map.PlayerDefinitions.Single(p => s.Get<OwnerInit>().InternalName == p.Value.Nodes.Last(k => k.Key == "Name").Value.Value);
var colorValue = owner.Value.Nodes.Where(n => n.Key == "Color"); var colorValue = owner.Value.Nodes.FirstOrDefault(n => n.Key == "Color");
var ownerColor = colorValue.Any() ? colorValue.First().Value.Value : "FFFFFF"; var ownerColor = colorValue?.Value.Value ?? "FFFFFF";
Color.TryParse(ownerColor, out color); Color.TryParse(ownerColor, out color);
} }

View File

@@ -445,15 +445,15 @@ namespace OpenRA.Mods.Common.Traits
modifiers |= TargetModifiers.ForceAttack; modifiers |= TargetModifiers.ForceAttack;
var forceAttack = modifiers.HasModifier(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 // 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 // If all are out of ammo, just use valid armament with highest range
armaments = armaments.OrderByDescending(x => x.MaxRange()); var a = ab.ChooseArmamentsForTarget(target, forceAttack)
var a = armaments.FirstOrDefault(x => !x.IsTraitPaused); .OrderBy(x => x.IsTraitPaused)
a ??= armaments.First(); .ThenByDescending(x => x.MaxRange())
.FirstOrDefault();
if (a == null)
return false;
var outOfRange = !target.IsInRange(self.CenterPosition, a.MaxRange()) || var outOfRange = !target.IsInRange(self.CenterPosition, a.MaxRange()) ||
(!forceAttack && target.Type == TargetType.FrozenActor && !ab.Info.TargetFrozenActors); (!forceAttack && target.Type == TargetType.FrozenActor && !ab.Info.TargetFrozenActors);
@@ -482,15 +482,15 @@ namespace OpenRA.Mods.Common.Traits
return false; return false;
var target = Target.FromCell(self.World, location); 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 // 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 // If all are out of ammo, just use valid armament with highest range
armaments = armaments.OrderByDescending(x => x.MaxRange()); var a = ab.ChooseArmamentsForTarget(target, true)
var a = armaments.FirstOrDefault(x => !x.IsTraitPaused); .OrderBy(x => x.IsTraitPaused)
a ??= armaments.First(); .ThenByDescending(x => x.MaxRange())
.FirstOrDefault();
if (a == null)
return false;
cursor = !target.IsInRange(self.CenterPosition, a.MaxRange()) cursor = !target.IsInRange(self.CenterPosition, a.MaxRange())
? ab.Info.OutsideRangeCursor ?? a.Info.OutsideRangeCursor ? ab.Info.OutsideRangeCursor ?? a.Info.OutsideRangeCursor

View File

@@ -163,7 +163,7 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
fuzzyEngine.Rules.Add(fuzzyEngine.ParseRule(rule)); fuzzyEngine.Rules.Add(fuzzyEngine.ParseRule(rule));
} }
public bool CanAttack(IEnumerable<Actor> ownUnits, IEnumerable<Actor> enemyUnits) public bool CanAttack(IReadOnlyCollection<Actor> ownUnits, IReadOnlyCollection<Actor> enemyUnits)
{ {
double attackChance; double attackChance;
var inputValues = new Dictionary<FuzzyVariable, double>(); var inputValues = new Dictionary<FuzzyVariable, double>();
@@ -201,7 +201,7 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
return (int)((long)sumOfHp * normalizeByValue / sumOfMaxHp); return (int)((long)sumOfHp * normalizeByValue / sumOfMaxHp);
} }
static float RelativePower(IEnumerable<Actor> own, IEnumerable<Actor> enemy) static float RelativePower(IReadOnlyCollection<Actor> own, IReadOnlyCollection<Actor> enemy)
{ {
return RelativeValue(own, enemy, 100, SumOfValues<AttackBaseInfo>, a => return RelativeValue(own, enemy, 100, SumOfValues<AttackBaseInfo>, a =>
{ {
@@ -225,18 +225,18 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
}); });
} }
static float RelativeSpeed(IEnumerable<Actor> own, IEnumerable<Actor> enemy) static float RelativeSpeed(IReadOnlyCollection<Actor> own, IReadOnlyCollection<Actor> enemy)
{ {
return RelativeValue(own, enemy, 100, Average<MobileInfo>, (Actor a) => a.Info.TraitInfo<MobileInfo>().Speed); return RelativeValue(own, enemy, 100, Average<MobileInfo>, (Actor a) => a.Info.TraitInfo<MobileInfo>().Speed);
} }
static float RelativeValue(IEnumerable<Actor> own, IEnumerable<Actor> enemy, float normalizeByValue, static float RelativeValue(IReadOnlyCollection<Actor> own, IReadOnlyCollection<Actor> enemy, float normalizeByValue,
Func<IEnumerable<Actor>, Func<Actor, int>, float> relativeFunc, Func<Actor, int> getValue) Func<IReadOnlyCollection<Actor>, Func<Actor, int>, float> relativeFunc, Func<Actor, int> getValue)
{ {
if (!enemy.Any()) if (enemy.Count == 0)
return 999.0f; return 999.0f;
if (!own.Any()) if (own.Count == 0)
return 0.0f; return 0.0f;
var relative = relativeFunc(own, getValue) / relativeFunc(enemy, getValue) * normalizeByValue; var relative = relativeFunc(own, getValue) / relativeFunc(enemy, getValue) * normalizeByValue;

View File

@@ -22,9 +22,9 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
protected const int MissileUnitMultiplier = 3; protected const int MissileUnitMultiplier = 3;
protected static int CountAntiAirUnits(IEnumerable<Actor> units) protected static int CountAntiAirUnits(IReadOnlyCollection<Actor> units)
{ {
if (!units.Any()) if (units.Count == 0)
return 0; return 0;
var missileUnitsCount = 0; var missileUnitsCount = 0;

View File

@@ -80,7 +80,7 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
return false; return false;
} }
protected virtual bool ShouldFlee(Squad squad, Func<IEnumerable<Actor>, bool> flee) protected virtual bool ShouldFlee(Squad squad, Func<IReadOnlyCollection<Actor>, bool> flee)
{ {
if (!squad.IsValid) if (!squad.IsValid)
return false; return false;
@@ -95,8 +95,10 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads
if (u.Owner == squad.Bot.Player && u.Info.HasTraitInfo<BuildingInfo>()) if (u.Owner == squad.Bot.Player && u.Info.HasTraitInfo<BuildingInfo>())
return false; return false;
var enemyAroundUnit = units.Where(unit => squad.SquadManager.IsPreferredEnemyUnit(unit) && unit.Info.HasTraitInfo<AttackBaseInfo>()); var enemyAroundUnit = units
if (!enemyAroundUnit.Any()) .Where(unit => squad.SquadManager.IsPreferredEnemyUnit(unit) && unit.Info.HasTraitInfo<AttackBaseInfo>())
.ToList();
if (enemyAroundUnit.Count == 0)
return false; return false;
return flee(enemyAroundUnit); return flee(enemyAroundUnit);

View File

@@ -172,17 +172,18 @@ namespace OpenRA.Mods.Common.Traits
ActorInfo ChooseUnitToBuild(ProductionQueue queue) ActorInfo ChooseUnitToBuild(ProductionQueue queue)
{ {
var buildableThings = queue.BuildableItems(); var buildableThings = queue.BuildableItems().Select(b => b.Name).ToHashSet();
if (!buildableThings.Any()) if (buildableThings.Count == 0)
return null; return null;
var myUnits = player.World var myUnits = player.World
.ActorsHavingTrait<IPositionable>() .ActorsHavingTrait<IPositionable>()
.Where(a => a.Owner == player) .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)) 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 (myUnits.Count(a => a == unit.Key) * 100 < unit.Value * myUnits.Count)
if (HasAdequateAirUnitReloadBuildings(world.Map.Rules.Actors[unit.Key])) if (HasAdequateAirUnitReloadBuildings(world.Map.Rules.Actors[unit.Key]))
return world.Map.Rules.Actors[unit.Key]; return world.Map.Rules.Actors[unit.Key];

View File

@@ -80,9 +80,6 @@ namespace OpenRA.Mods.Common.Traits
return null; return null;
var allOfType = Exits(actor, productionType); var allOfType = Exits(actor, productionType);
if (!allOfType.Any())
return null;
foreach (var g in allOfType.GroupBy(e => e.Info.Priority)) foreach (var g in allOfType.GroupBy(e => e.Info.Priority))
{ {
var shuffled = g.Shuffle(world.SharedRandom); var shuffled = g.Shuffle(world.SharedRandom);

View File

@@ -54,13 +54,15 @@ namespace OpenRA.Mods.Common.Traits
public override void RulesetLoaded(Ruleset rules, ActorInfo ai) public override void RulesetLoaded(Ruleset rules, ActorInfo ai)
{ {
var locomotorInfos = rules.Actors[SystemActors.World].TraitInfos<LocomotorInfo>(); var locomotorInfos = rules.Actors[SystemActors.World].TraitInfos<LocomotorInfo>()
LocomotorInfo = locomotorInfos.FirstOrDefault(li => li.Name == Locomotor); .Where(li => li.Name == Locomotor).ToList();
if (LocomotorInfo == null) if (locomotorInfos.Count == 0)
throw new YamlException($"A locomotor named '{Locomotor}' doesn't exist."); 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}'."); throw new YamlException($"There is more than one locomotor named '{Locomotor}'.");
LocomotorInfo = locomotorInfos[0];
base.RulesetLoaded(rules, ai); base.RulesetLoaded(rules, ai);
} }
} }

View File

@@ -113,23 +113,32 @@ namespace OpenRA.Mods.Common.Traits
void INotifyParachute.OnLanded(Actor self) void INotifyParachute.OnLanded(Actor self)
{ {
// Check whether the crate landed on anything // Check whether the crate landed on anything
var landedOn = self.World.ActorMap.GetActorsAt(self.Location) var anyOtherActors = false;
.Where(a => a != self); Actor collector = null;
foreach (var otherActor in self.World.ActorMap.GetActorsAt(self.Location))
if (!landedOn.Any())
return;
var collector = landedOn.FirstOrDefault(a =>
{ {
if (self == otherActor)
continue;
anyOtherActors = true;
// Mobile is (currently) the only trait that supports crushing // Mobile is (currently) the only trait that supports crushing
var mi = a.Info.TraitInfoOrDefault<MobileInfo>(); var mi = otherActor.Info.TraitInfoOrDefault<MobileInfo>();
if (mi == null) if (mi == null)
return false; continue;
// Make sure that the actor can collect this crate type // Make sure that the actor can collect this crate type
// Crate can only be crushed if it is not in the air. // 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 // Destroy the crate if none of the units in the cell are valid collectors
if (collector != null) if (collector != null)
@@ -148,10 +157,11 @@ namespace OpenRA.Mods.Common.Traits
self.Dispose(); self.Dispose();
collected = true; 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 totalShares = shares.Sum(a => a.Shares);
var n = self.World.SharedRandom.Next(totalShares); var n = self.World.SharedRandom.Next(totalShares);

View File

@@ -76,7 +76,8 @@ namespace OpenRA.Mods.Common.Traits
void GrantCondition(Actor actor) void GrantCondition(Actor actor)
{ {
var externals = actor.TraitsImplementing<ExternalCondition>() var externals = actor.TraitsImplementing<ExternalCondition>()
.Where(t => t.Info.Condition == info.Condition); .Where(t => t.Info.Condition == info.Condition)
.ToList();
ExternalCondition external = null; ExternalCondition external = null;
for (var n = 0; n < info.Levels; n++) for (var n = 0; n < info.Levels; n++)

View File

@@ -104,13 +104,15 @@ namespace OpenRA.Mods.Common.Traits
public override void RulesetLoaded(Ruleset rules, ActorInfo ai) public override void RulesetLoaded(Ruleset rules, ActorInfo ai)
{ {
var locomotorInfos = rules.Actors[SystemActors.World].TraitInfos<LocomotorInfo>(); var locomotorInfos = rules.Actors[SystemActors.World].TraitInfos<LocomotorInfo>()
LocomotorInfo = locomotorInfos.FirstOrDefault(li => li.Name == Locomotor); .Where(li => li.Name == Locomotor).ToList();
if (LocomotorInfo == null) if (locomotorInfos.Count == 0)
throw new YamlException($"A locomotor named '{Locomotor}' doesn't exist."); 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}'."); 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. // We need to reset the reference to the locomotor between each worlds, otherwise we are reference the previous state.
locomotor = null; locomotor = null;

View File

@@ -133,15 +133,15 @@ namespace OpenRA.Mods.Common.Traits
public override TraitPair<Production> MostLikelyProducer() public override TraitPair<Production> MostLikelyProducer()
{ {
var productionActors = self.World.ActorsWithTrait<Production>() var productionActor = self.World.ActorsWithTrait<Production>()
.Where(x => x.Actor.Owner == self.Owner .Where(x => x.Actor.Owner == self.Owner
&& !x.Trait.IsTraitDisabled && x.Trait.Info.Produces.Contains(Info.Type)) && !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) .ThenByDescending(x => x.Actor.ActorID)
.ToList(); .FirstOrDefault();
var unpaused = productionActors.FirstOrDefault(a => !a.Trait.IsTraitPaused); return productionActor;
return unpaused.Trait != null ? unpaused : productionActors.FirstOrDefault();
} }
protected override bool BuildUnit(ActorInfo unit) protected override bool BuildUnit(ActorInfo unit)
@@ -159,14 +159,10 @@ namespace OpenRA.Mods.Common.Traits
.OrderByDescending(x => x.Actor.IsPrimaryBuilding()) .OrderByDescending(x => x.Actor.IsPrimaryBuilding())
.ThenByDescending(x => x.Actor.ActorID); .ThenByDescending(x => x.Actor.ActorID);
if (!producers.Any()) var anyProducers = false;
{
CancelProduction(unit.Name, 1);
return false;
}
foreach (var p in producers) foreach (var p in producers)
{ {
anyProducers = true;
if (p.Trait.IsTraitPaused) if (p.Trait.IsTraitPaused)
continue; continue;
@@ -184,6 +180,12 @@ namespace OpenRA.Mods.Common.Traits
} }
} }
if (!anyProducers)
{
CancelProduction(unit.Name, 1);
return false;
}
return false; return false;
} }

View File

@@ -83,15 +83,15 @@ namespace OpenRA.Mods.Common.Traits
public override TraitPair<Production> MostLikelyProducer() public override TraitPair<Production> MostLikelyProducer()
{ {
var productionActors = self.World.ActorsWithTrait<Production>() var productionActor = self.World.ActorsWithTrait<Production>()
.Where(x => x.Actor.Owner == self.Owner .Where(x => x.Actor.Owner == self.Owner
&& !x.Trait.IsTraitDisabled && x.Trait.Info.Produces.Contains(Info.Type)) && !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) .ThenByDescending(x => x.Actor.ActorID)
.ToList(); .FirstOrDefault();
var unpaused = productionActors.FirstOrDefault(a => !a.Trait.IsTraitPaused); return productionActor;
return unpaused.Trait != null ? unpaused : productionActors.FirstOrDefault();
} }
protected override bool BuildUnit(ActorInfo unit) protected override bool BuildUnit(ActorInfo unit)
@@ -109,14 +109,10 @@ namespace OpenRA.Mods.Common.Traits
.OrderByDescending(x => x.Actor.IsPrimaryBuilding()) .OrderByDescending(x => x.Actor.IsPrimaryBuilding())
.ThenByDescending(x => x.Actor.ActorID); .ThenByDescending(x => x.Actor.ActorID);
if (!producers.Any()) var anyProducers = false;
{
CancelProduction(unit.Name, 1);
return false;
}
foreach (var p in producers) foreach (var p in producers)
{ {
anyProducers = true;
if (p.Trait.IsTraitPaused) if (p.Trait.IsTraitPaused)
continue; continue;
@@ -134,6 +130,12 @@ namespace OpenRA.Mods.Common.Traits
} }
} }
if (!anyProducers)
{
CancelProduction(unit.Name, 1);
return false;
}
return false; return false;
} }

View File

@@ -81,13 +81,14 @@ namespace OpenRA.Mods.Common.Traits
return; return;
var myTeam = self.World.LobbyInfo.ClientWithIndex(self.Owner.ClientIndex).Team; 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<PlayerStatistics>())) .Select(p => (Player: p, PlayerStatistics: p.PlayerActor.TraitOrDefault<PlayerStatistics>()))
.OrderByDescending(p => p.PlayerStatistics?.Experience ?? 0) .OrderByDescending(p => p.PlayerStatistics?.Experience ?? 0)
.GroupBy(p => (self.World.LobbyInfo.ClientWithIndex(p.Player.ClientIndex) ?? new Session.Client()).Team) .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); mo.MarkCompleted(self.Owner, objectiveID);
return; return;

View File

@@ -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 // Returns the actor/trait that is most likely (but not necessarily guaranteed) to produce something in this queue
public virtual TraitPair<Production> MostLikelyProducer() public virtual TraitPair<Production> MostLikelyProducer()
{ {
var traits = productionTraits.Where(p => !p.IsTraitDisabled && p.Info.Produces.Contains(Info.Type)); var trait = productionTraits
var unpaused = traits.FirstOrDefault(a => !a.IsTraitPaused); .Where(p => !p.IsTraitDisabled && p.Info.Produces.Contains(Info.Type))
return new TraitPair<Production>(Actor, unpaused ?? traits.FirstOrDefault()); .OrderBy(p => p.IsTraitPaused)
.FirstOrDefault();
return new TraitPair<Production>(Actor, trait);
} }
// Builds a unit from the actor that holds this queue (1 queue per building) // Builds a unit from the actor that holds this queue (1 queue per building)

View File

@@ -89,13 +89,21 @@ namespace OpenRA.Mods.Common.Traits
if (!self.Owner.NonCombatant && self.Owner.HasNoRequiredUnits(shortGame)) if (!self.Owner.NonCombatant && self.Owner.HasNoRequiredUnits(shortGame))
mo.MarkFailed(self.Owner, objectiveID); mo.MarkFailed(self.Owner, objectiveID);
var others = self.World.Players.Where(p => !p.NonCombatant var allOthersLost = true;
&& !p.IsAlliedWith(self.Owner)); 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); mo.MarkCompleted(player, objectiveID);
if (others.Any(p => p.WinState == WinState.Won)) if (anyOtherWon)
mo.MarkFailed(player, objectiveID); mo.MarkFailed(player, objectiveID);
// See if any of the conditions are met to increase the count // See if any of the conditions are met to increase the count
@@ -119,13 +127,14 @@ namespace OpenRA.Mods.Common.Traits
return; return;
var myTeam = self.World.LobbyInfo.ClientWithIndex(self.Owner.ClientIndex).Team; 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<PlayerStatistics>())) .Select(p => (Player: p, PlayerStatistics: p.PlayerActor.TraitOrDefault<PlayerStatistics>()))
.OrderByDescending(p => p.PlayerStatistics?.Experience ?? 0) .OrderByDescending(p => p.PlayerStatistics?.Experience ?? 0)
.GroupBy(p => (self.World.LobbyInfo.ClientWithIndex(p.Player.ClientIndex) ?? new Session.Client()).Team) .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); mo.MarkCompleted(self.Owner, objectiveID);
return; return;

View File

@@ -44,10 +44,19 @@ namespace OpenRA.Mods.Common.Traits
if (rejectsOrdersTraits.Length == 0) if (rejectsOrdersTraits.Length == 0)
return true; return true;
var reject = rejectsOrdersTraits.SelectMany(t => t.Reject); foreach (var rejectsOrdersTrait in rejectsOrdersTraits)
var except = rejectsOrdersTraits.SelectMany(t => t.Except); 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;
} }
} }
} }

View File

@@ -51,29 +51,27 @@ namespace OpenRA.Mods.Common.Traits.Render
if (IsTraitDisabled) if (IsTraitDisabled)
return r; return r;
var renderables = r.ToList();
var height = self.World.Map.DistanceAboveTerrain(self.CenterPosition).Length; 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) .Select(ma => ((IModifyableRenderable)ma).WithTint(shadowColor, ((IModifyableRenderable)ma).TintModifiers | TintModifiers.ReplaceColor)
.WithAlpha(shadowAlpha) .WithAlpha(shadowAlpha)
.OffsetBy(info.Offset - new WVec(0, 0, height)) .OffsetBy(info.Offset - new WVec(0, 0, height))
.WithZOffset(ma.ZOffset + height + info.ZOffset) .WithZOffset(ma.ZOffset + height + info.ZOffset)
.AsDecoration()); .AsDecoration());
return shadowSprites.Concat(r); return shadowSprites.Concat(renderables);
} }
IEnumerable<Rectangle> IRenderModifier.ModifyScreenBounds(Actor self, WorldRenderer wr, IEnumerable<Rectangle> bounds) IEnumerable<Rectangle> IRenderModifier.ModifyScreenBounds(Actor self, WorldRenderer wr, IEnumerable<Rectangle> bounds)
{ {
foreach (var r in bounds)
yield return r;
if (IsTraitDisabled) if (IsTraitDisabled)
yield break; return bounds;
var boundsList = bounds.ToList();
var height = self.World.Map.DistanceAboveTerrain(self.CenterPosition).Length; var height = self.World.Map.DistanceAboveTerrain(self.CenterPosition).Length;
var offset = wr.ScreenPxOffset(info.Offset - new WVec(0, 0, height)); var offset = wr.ScreenPxOffset(info.Offset - new WVec(0, 0, height));
foreach (var r in bounds) return boundsList.Concat(boundsList.Select(r => new Rectangle(r.X + offset.X, r.Y + offset.Y, r.Width, r.Height)));
yield return new Rectangle(r.X + offset.X, r.Y + offset.Y, r.Width, r.Height);
} }
} }
} }

View File

@@ -205,8 +205,8 @@ namespace OpenRA.Mods.Common.Traits
actorShouldBeRemoved = removeActorPosition.Contains; actorShouldBeRemoved = removeActorPosition.Contains;
LargestActorRadius = map.Rules.Actors.SelectMany(a => a.Value.TraitInfos<HitShapeInfo>()).Max(h => h.Type.OuterRadius); LargestActorRadius = map.Rules.Actors.SelectMany(a => a.Value.TraitInfos<HitShapeInfo>()).Max(h => h.Type.OuterRadius);
var blockers = map.Rules.Actors.Where(a => a.Value.HasTraitInfo<IBlocksProjectilesInfo>()); var blockers = map.Rules.Actors.Where(a => a.Value.HasTraitInfo<IBlocksProjectilesInfo>()).ToList();
LargestBlockingActorRadius = blockers.Any() ? blockers.SelectMany(a => a.Value.TraitInfos<HitShapeInfo>()).Max(h => h.Type.OuterRadius) : WDist.Zero; LargestBlockingActorRadius = blockers.Count != 0 ? blockers.SelectMany(a => a.Value.TraitInfos<HitShapeInfo>()).Max(h => h.Type.OuterRadius) : WDist.Zero;
} }
void INotifyCreated.Created(Actor self) void INotifyCreated.Created(Actor self)

View File

@@ -28,7 +28,7 @@ namespace OpenRA.Mods.Common.Traits
/// </summary> /// </summary>
void ICreatePlayersInfo.CreateServerPlayers(MapPreview map, Session lobbyInfo, List<GameInformation.Player> players, MersenneTwister playerRandom) void ICreatePlayersInfo.CreateServerPlayers(MapPreview map, Session lobbyInfo, List<GameInformation.Player> players, MersenneTwister playerRandom)
{ {
var factions = map.WorldActorInfo.TraitInfos<FactionInfo>().ToArray(); var factions = map.WorldActorInfo.TraitInfos<FactionInfo>();
var assignSpawnLocations = map.WorldActorInfo.TraitInfoOrDefault<IAssignSpawnPointsInfo>(); var assignSpawnLocations = map.WorldActorInfo.TraitInfoOrDefault<IAssignSpawnPointsInfo>();
var spawnState = assignSpawnLocations?.InitializeState(map, lobbyInfo); var spawnState = assignSpawnLocations?.InitializeState(map, lobbyInfo);
@@ -41,7 +41,7 @@ namespace OpenRA.Mods.Common.Traits
} }
// Create the regular playable players. // Create the regular playable players.
var bots = map.PlayerActorInfo.TraitInfos<IBotInfo>().ToArray(); var bots = map.PlayerActorInfo.TraitInfos<IBotInfo>();
foreach (var kv in lobbyInfo.Slots) foreach (var kv in lobbyInfo.Slots)
{ {

View File

@@ -178,7 +178,7 @@ namespace OpenRA.Mods.Common.Traits
return reference.GetOrDefault<T>(info); return reference.GetOrDefault<T>(info);
} }
public IEnumerable<T> GetInits<T>() where T : ActorInit public IReadOnlyCollection<T> GetInits<T>() where T : ActorInit
{ {
return reference.GetAll<T>(); return reference.GetAll<T>();
} }

View File

@@ -136,7 +136,7 @@ namespace OpenRA.Mods.Common.Traits
public readonly LongBitSet<PlayerBitMask> Crushable; public readonly LongBitSet<PlayerBitMask> Crushable;
public readonly CellFlag CellFlag; public readonly CellFlag CellFlag;
public CellCache(LongBitSet<PlayerBitMask> immovable, CellFlag cellFlag, LongBitSet<PlayerBitMask> crushable = default) public CellCache(LongBitSet<PlayerBitMask> immovable, CellFlag cellFlag, LongBitSet<PlayerBitMask> crushable)
{ {
Immovable = immovable; Immovable = immovable;
Crushable = crushable; Crushable = crushable;
@@ -459,26 +459,17 @@ namespace OpenRA.Mods.Common.Traits
using (new PerfSample("locomotor_cache")) using (new PerfSample("locomotor_cache"))
{ {
var cache = blockingCache[cell.Layer]; var cache = blockingCache[cell.Layer];
var actors = actorMap.GetActorsAt(cell);
var cellFlag = CellFlag.HasFreeSpace; 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<PlayerBitMask>); var cellImmovablePlayers = default(LongBitSet<PlayerBitMask>);
var cellCrushablePlayers = world.AllPlayersMask; 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 actorImmovablePlayers = world.AllPlayersMask;
var actorCrushablePlayers = world.NoPlayersMask; var actorCrushablePlayers = world.NoPlayersMask;
@@ -493,11 +484,10 @@ namespace OpenRA.Mods.Common.Traits
if (isTransitOnly) if (isTransitOnly)
cellFlag |= CellFlag.HasTransitOnlyActor; cellFlag |= CellFlag.HasTransitOnlyActor;
if (crushables.Any()) foreach (var crushable in crushables)
{ {
cellFlag |= CellFlag.HasCrushableActor; 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) if (isMoving)

View File

@@ -10,7 +10,6 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace OpenRA.Mods.Common.UpdateRules.Rules namespace OpenRA.Mods.Common.UpdateRules.Rules
{ {
@@ -24,7 +23,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
public override IEnumerable<string> AfterUpdate(ModData modData) public override IEnumerable<string> AfterUpdate(ModData modData)
{ {
if (locations.Any()) if (locations.Count != 0)
yield return "Icon overlay logic has been split from ProducibleWithLevel to WithProductionIconOverlay trait.\n" + 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" + "If you have been using VeteranProductionIconOverlay trait, add WithProductionIconOverlay to following actors:\n" +
UpdateUtils.FormatMessageList(locations); UpdateUtils.FormatMessageList(locations);

View File

@@ -45,16 +45,16 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
foreach (var sequenceNode in imageNode.Value.Nodes) foreach (var sequenceNode in imageNode.Value.Nodes)
{ {
var useTilesetExtensionNode = sequenceNode.LastChildMatching("UseTilesetExtension"); var useTilesetExtensionNode = sequenceNode.LastChildMatching("UseTilesetExtension");
if (useTilesetExtensionNode != null && !tilesetExtensions.Any()) if (useTilesetExtensionNode != null && tilesetExtensions.Count == 0)
requiredMetadata.Add("TilesetExtensions"); requiredMetadata.Add("TilesetExtensions");
var useTilesetCodeNode = sequenceNode.LastChildMatching("UseTilesetCode"); var useTilesetCodeNode = sequenceNode.LastChildMatching("UseTilesetCode");
if (useTilesetCodeNode != null && !tilesetCodes.Any()) if (useTilesetCodeNode != null && tilesetCodes.Count == 0)
requiredMetadata.Add("TilesetCodes"); requiredMetadata.Add("TilesetCodes");
} }
} }
if (requiredMetadata.Any()) if (requiredMetadata.Count != 0)
{ {
yield return $"The ExplicitSequenceFilenames rule requires {requiredMetadata.JoinWith(", ")}\n" + yield return $"The ExplicitSequenceFilenames rule requires {requiredMetadata.JoinWith(", ")}\n" +
"to be defined under the SpriteSequenceFormat definition in mod.yaml.\n" + "to be defined under the SpriteSequenceFormat definition in mod.yaml.\n" +
@@ -317,7 +317,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
if (allSequencesHaveTilesetFilenames) if (allSequencesHaveTilesetFilenames)
sequenceNode.RemoveNodes("TilesetFilenames"); sequenceNode.RemoveNodes("TilesetFilenames");
if (!sequenceNode.Value.Nodes.Any()) if (sequenceNode.Value.Nodes.Count == 0)
imageNode.RemoveNode(sequenceNode); imageNode.RemoveNode(sequenceNode);
} }
@@ -328,13 +328,13 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
sequenceNode.RemoveNodes("TilesetFilenames"); sequenceNode.RemoveNodes("TilesetFilenames");
var tilesetFilenamesNode = sequenceNode.LastChildMatching("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); sequenceNode.RemoveNode(tilesetFilenamesNode);
} }
} }
foreach (var sequenceNode in imageNode.Value.Nodes.ToList()) 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); imageNode.RemoveNode(sequenceNode);
} }
@@ -424,7 +424,7 @@ namespace OpenRA.Mods.Common.UpdateRules.Rules
if (overrideNode.Value.Value == filenameNode.Value.Value) if (overrideNode.Value.Value == filenameNode.Value.Value)
tilesetFilenamesNode.Value.Nodes.Remove(overrideNode); tilesetFilenamesNode.Value.Nodes.Remove(overrideNode);
if (!tilesetFilenamesNode.Value.Nodes.Any()) if (tilesetFilenamesNode.Value.Nodes.Count == 0)
sequenceNode.RemoveNode(tilesetFilenamesNode); sequenceNode.RemoveNode(tilesetFilenamesNode);
sequenceNode.Value.Nodes.Insert(0, filenameNode); sequenceNode.Value.Nodes.Insert(0, filenameNode);

View File

@@ -114,7 +114,7 @@ namespace OpenRA.Mods.Common.UpdateRules
}), }),
}; };
public static IEnumerable<UpdateRule> FromSource(ObjectCreator objectCreator, string source, bool chain = true) public static IReadOnlyCollection<UpdateRule> FromSource(ObjectCreator objectCreator, string source, bool chain = true)
{ {
// Use reflection to identify types // Use reflection to identify types
var namedType = objectCreator.FindType(source); var namedType = objectCreator.FindType(source);
@@ -143,12 +143,12 @@ namespace OpenRA.Mods.Common.UpdateRules
this.chainToSource = chainToSource; this.chainToSource = chainToSource;
} }
IEnumerable<UpdateRule> Rules(bool chain = true) IReadOnlyCollection<UpdateRule> Rules(bool chain = true)
{ {
if (chainToSource != null && chain) if (chainToSource != null && chain)
{ {
var child = Paths.First(p => p.source == chainToSource); var child = Paths.First(p => p.source == chainToSource);
return rules.Concat(child.Rules(chain)); return rules.Concat(child.Rules(chain)).ToList();
} }
return rules; return rules;

View File

@@ -271,7 +271,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
if (isActivity) if (isActivity)
Console.WriteLine(" --- *Queued Activity*"); Console.WriteLine(" --- *Queued Activity*");
if (requiredTraits.Any()) if (requiredTraits.Length != 0)
Console.WriteLine($" --- **Requires {(requiredTraits.Length == 1 ? "Trait" : "Traits")}:** {requiredTraits.Select(GetDocumentationUrl).JoinWith(", ")}"); Console.WriteLine($" --- **Requires {(requiredTraits.Length == 1 ? "Trait" : "Traits")}:** {requiredTraits.Select(GetDocumentationUrl).JoinWith(", ")}");
if (memberInfo is MethodInfo methodInfo) if (memberInfo is MethodInfo methodInfo)

View File

@@ -38,7 +38,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
if (folder.OpenPackage(args[1], modData.ModFiles) is not IReadWritePackage package) if (folder.OpenPackage(args[1], modData.ModFiles) is not IReadWritePackage package)
throw new FileNotFoundException(args[1]); throw new FileNotFoundException(args[1]);
IEnumerable<UpdateRule> rules = null; IReadOnlyCollection<UpdateRule> rules = null;
if (args.Length > 2) if (args.Length > 2)
rules = UpdatePath.FromSource(modData.ObjectCreator, args[2]); rules = UpdatePath.FromSource(modData.ObjectCreator, args[2]);
@@ -77,9 +77,10 @@ namespace OpenRA.Mods.Common.UtilityCommands
} }
var other = UpdatePath.KnownRules(modData.ObjectCreator) 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:"); Console.WriteLine(" Other:");
foreach (var r in other) foreach (var r in other)

View File

@@ -32,7 +32,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
// HACK: The engine code assumes that Game.modData is set. // HACK: The engine code assumes that Game.modData is set.
var modData = Game.ModData = utility.ModData; var modData = Game.ModData = utility.ModData;
IEnumerable<UpdateRule> rules = null; IReadOnlyCollection<UpdateRule> rules = null;
if (args.Length > 1) if (args.Length > 1)
rules = UpdatePath.FromSource(modData.ObjectCreator, args[1]); rules = UpdatePath.FromSource(modData.ObjectCreator, args[1]);
@@ -71,9 +71,10 @@ namespace OpenRA.Mods.Common.UtilityCommands
} }
var other = UpdatePath.KnownRules(modData.ObjectCreator) 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:"); Console.WriteLine(" Other:");
foreach (var r in other) foreach (var r in other)
@@ -111,9 +112,9 @@ namespace OpenRA.Mods.Common.UtilityCommands
PrintSummary(rules, args.Contains("--detailed")); PrintSummary(rules, args.Contains("--detailed"));
} }
public static void PrintSummary(IEnumerable<UpdateRule> rules, bool detailed) public static void PrintSummary(IReadOnlyCollection<UpdateRule> rules, bool detailed)
{ {
var count = rules.Count(); var count = rules.Count;
if (count == 1) if (count == 1)
Console.WriteLine("Found 1 API change:"); Console.WriteLine("Found 1 API change:");
else else

View File

@@ -60,9 +60,9 @@ namespace OpenRA.Mods.Common.Warheads
if (RandomClusterCount != 0) if (RandomClusterCount != 0)
{ {
var randomTargetCells = CellsMatching(targetCell, true); var randomTargetCells = CellsMatching(targetCell, true).ToList();
var clusterCount = RandomClusterCount < 0 ? randomTargetCells.Count() : RandomClusterCount; var clusterCount = RandomClusterCount < 0 ? randomTargetCells.Count : RandomClusterCount;
if (randomTargetCells.Any()) if (randomTargetCells.Count != 0)
for (var i = 0; i < clusterCount; i++) for (var i = 0; i < clusterCount; i++)
FireProjectileAtCell(map, firedBy, target, randomTargetCells.Random(firedBy.World.SharedRandom), args); FireProjectileAtCell(map, firedBy, target, randomTargetCells.Random(firedBy.World.SharedRandom), args);
} }

View File

@@ -84,7 +84,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var actors = new List<KeyValuePair<ActorInfo, EncyclopediaInfo>>(); var actors = new List<KeyValuePair<ActorInfo, EncyclopediaInfo>>();
foreach (var actor in modData.DefaultRules.Actors.Values) foreach (var actor in modData.DefaultRules.Actors.Values)
{ {
if (!actor.TraitInfos<IRenderActorPreviewSpritesInfo>().Any()) if (actor.TraitInfos<IRenderActorPreviewSpritesInfo>().Count == 0)
continue; continue;
var statistics = actor.TraitInfoOrDefault<UpdatesPlayerStatisticsInfo>(); var statistics = actor.TraitInfoOrDefault<UpdatesPlayerStatisticsInfo>();

View File

@@ -113,7 +113,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
.Select(p => (Player: p, PlayerStatistics: p.PlayerActor.TraitOrDefault<PlayerStatistics>())) .Select(p => (Player: p, PlayerStatistics: p.PlayerActor.TraitOrDefault<PlayerStatistics>()))
.OrderByDescending(p => p.PlayerStatistics?.Experience ?? 0) .OrderByDescending(p => p.PlayerStatistics?.Experience ?? 0)
.GroupBy(p => (world.LobbyInfo.ClientWithIndex(p.Player.ClientIndex) ?? new Session.Client()).Team) .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) void KickAction(Session.Client client)
{ {
@@ -133,7 +134,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
foreach (var t in teams) foreach (var t in teams)
{ {
if (teams.Count() > 1) if (teams.Count > 1)
{ {
var teamHeader = ScrollItemWidget.Setup(teamTemplate, () => false, () => { }); var teamHeader = ScrollItemWidget.Setup(teamTemplate, () => false, () => { });
var team = t.Key > 0 var team = t.Key > 0

View File

@@ -114,9 +114,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
.GroupBy(p => (world.LobbyInfo.ClientWithIndex(p.Player.ClientIndex) ?? new Session.Client()).Team) .GroupBy(p => (world.LobbyInfo.ClientWithIndex(p.Player.ClientIndex) ?? new Session.Client()).Team)
.OrderBy(g => g.Key); .OrderBy(g => g.Key);
var noTeams = teams.Count() == 1; var teamsList = teams.ToList();
var noTeams = teamsList.Count == 1;
var totalPlayers = 0; var totalPlayers = 0;
foreach (var t in teams) foreach (var t in teamsList)
{ {
totalPlayers += t.Count(); totalPlayers += t.Count();
var label = noTeams ? TranslationProvider.GetString(Players) : t.Key > 0 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) if (e.Key >= Keycode.NUMBER_0 && e.Key <= Keycode.NUMBER_9)
{ {
var key = (int)e.Key - (int)Keycode.NUMBER_0; var key = (int)e.Key - (int)Keycode.NUMBER_0;
var team = teams.Where(t => t.Key == key).SelectMany(s => s); var team = teams.Where(t => t.Key == key).SelectMany(s => s).ToList();
if (!team.Any()) if (team.Count == 0)
return false; return false;
if (e.Modifiers == Modifiers.Shift) if (e.Modifiers == Modifiers.Shift)
team = team.Reverse(); team.Reverse();
selected = team.SkipWhile(t => t.Player != selected.Player).Skip(1).FirstOrDefault() ?? team.FirstOrDefault(); selected = team.SkipWhile(t => t.Player != selected.Player).Skip(1).FirstOrDefault() ?? team.FirstOrDefault();
selected.OnClick(); selected.OnClick();

View File

@@ -183,7 +183,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
selectedPackages = availablePackages.ToDictionary(x => x.Identifier, y => y.Required); selectedPackages = availablePackages.ToDictionary(x => x.Identifier, y => y.Required);
// Ignore source if content is already installed // Ignore source if content is already installed
if (availablePackages.Any()) if (availablePackages.Length != 0)
{ {
Game.RunAfterTick(() => Game.RunAfterTick(() =>
{ {
@@ -218,10 +218,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var options = new Dictionary<string, IEnumerable<string>>(); var options = new Dictionary<string, IEnumerable<string>>();
if (gameSources.Any()) if (gameSources.Count != 0)
options.Add(TranslationProvider.GetString(GameSources), gameSources); options.Add(TranslationProvider.GetString(GameSources), gameSources);
if (digitalInstalls.Any()) if (digitalInstalls.Count != 0)
options.Add(TranslationProvider.GetString(DigitalInstalls), digitalInstalls); options.Add(TranslationProvider.GetString(DigitalInstalls), digitalInstalls);
Game.RunAfterTick(() => Game.RunAfterTick(() =>

View File

@@ -322,18 +322,19 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (!int.TryParse(mapFilter, out var playerCountFilter)) if (!int.TryParse(mapFilter, out var playerCountFilter))
playerCountFilter = -1; playerCountFilter = -1;
var validMaps = tabMaps[tab] var maps = tabMaps[tab]
.Where(m => category == null || m.Categories.Contains(category)) .Where(m => category == null || m.Categories.Contains(category))
.Where(m => mapFilter == null || .Where(m => mapFilter == null ||
(m.Title != null && m.Title.Contains(mapFilter, StringComparison.CurrentCultureIgnoreCase)) || (m.Title != null && m.Title.Contains(mapFilter, StringComparison.CurrentCultureIgnoreCase)) ||
(m.Author != null && m.Author.Contains(mapFilter, StringComparison.CurrentCultureIgnoreCase)) || (m.Author != null && m.Author.Contains(mapFilter, StringComparison.CurrentCultureIgnoreCase)) ||
m.PlayerCount == playerCountFilter); m.PlayerCount == playerCountFilter);
IOrderedEnumerable<MapPreview> maps;
if (orderByFunc == null) if (orderByFunc == null)
maps = validMaps.OrderBy(m => m.Title); maps = maps.OrderBy(m => m.Title);
else else
maps = validMaps.OrderBy(orderByFunc).ThenBy(m => m.Title); maps = maps.OrderBy(orderByFunc).ThenBy(m => m.Title);
maps = maps.ToList();
scrollpanels[tab].RemoveChildren(); scrollpanels[tab].RemoveChildren();
foreach (var loop in maps) foreach (var loop in maps)

View File

@@ -715,10 +715,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var players = replay.GameInfo.Players var players = replay.GameInfo.Players
.GroupBy(p => p.Team) .GroupBy(p => p.Team)
.OrderBy(g => g.Key); .OrderBy(g => g.Key)
.ToList();
var teams = new Dictionary<string, IEnumerable<GameInformation.Player>>(); var teams = new Dictionary<string, IEnumerable<GameInformation.Player>>();
var noTeams = players.Count() == 1; var noTeams = players.Count == 1;
foreach (var p in players) foreach (var p in players)
{ {
var label = noTeams ? TranslationProvider.GetString(Players) : p.Key > 0 var label = noTeams ? TranslationProvider.GetString(Players) : p.Key > 0

View File

@@ -572,10 +572,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var players = server.Clients var players = server.Clients
.Where(c => !c.IsSpectator) .Where(c => !c.IsSpectator)
.GroupBy(p => p.Team) .GroupBy(p => p.Team)
.OrderBy(g => g.Key); .OrderBy(g => g.Key)
.ToList();
var teams = new Dictionary<string, IEnumerable<GameClient>>(); var teams = new Dictionary<string, IEnumerable<GameClient>>();
var noTeams = players.Count() == 1; var noTeams = players.Count == 1;
foreach (var p in players) foreach (var p in players)
{ {
var label = noTeams ? TranslationProvider.GetString(Players) : p.Key > 0 var label = noTeams ? TranslationProvider.GetString(Players) : p.Key > 0

View File

@@ -183,9 +183,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ {
var typesInGroup = hg.Value; var typesInGroup = hg.Value;
var keysInGroup = modData.Hotkeys.Definitions 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; continue;
var header = headerTemplate.Clone(); var header = headerTemplate.Clone();

View File

@@ -102,14 +102,15 @@ namespace OpenRA.Mods.Common.Widgets
var queues = world.ActorsWithTrait<ProductionQueue>() var queues = world.ActorsWithTrait<ProductionQueue>()
.Where(a => a.Actor.Owner == player) .Where(a => a.Actor.Owner == player)
.Select((a, i) => new { a.Trait, i }); .Select(a => a.Trait)
.ToList();
foreach (var queue in queues) foreach (var queue in queues)
if (!clocks.ContainsKey(queue.Trait)) if (!clocks.ContainsKey(queue))
clocks.Add(queue.Trait, new Animation(world, ClockAnimation)); clocks.Add(queue, new Animation(world, ClockAnimation));
var currentItemsByItem = queues var currentItemsByItem = queues
.Select(a => a.Trait.CurrentItem()) .Select(q => q.CurrentItem())
.Where(pi => pi != null) .Where(pi => pi != null)
.GroupBy(pr => pr.Item) .GroupBy(pr => pr.Item)
.OrderBy(g => g.First().Queue.Info.DisplayOrder) .OrderBy(g => g.First().Queue.Info.DisplayOrder)

View File

@@ -183,9 +183,9 @@ namespace OpenRA.Mods.Common.Widgets
public override void Draw() 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; return;
var rb = RenderBounds; var rb = RenderBounds;

View File

@@ -51,7 +51,7 @@ namespace OpenRA.Mods.D2k.Activities
swallow = self.Trait<AttackSwallow>(); swallow = self.Trait<AttackSwallow>();
} }
bool AttackTargets(Actor self, IEnumerable<Actor> targets) bool AttackTargets(Actor self, IReadOnlyCollection<Actor> targets)
{ {
var targetLocation = target.Actor.Location; var targetLocation = target.Actor.Location;
foreach (var t in targets) foreach (var t in targets)
@@ -129,9 +129,10 @@ namespace OpenRA.Mods.D2k.Activities
} }
var targets = self.World.ActorMap.GetActorsAt(targetLocation) 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); RevokeCondition(self);
return true; return true;

View File

@@ -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 // 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 // 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 templateInfo = (DefaultTerrainTemplateInfo)t.Value;
var templates = terrainInfo.Templates.Where(t => return templateInfo.Frames != null && templateInfo.Frames.Contains(tileIndex);
{ }).Value;
var templateInfo = (DefaultTerrainTemplateInfo)t.Value;
return templateInfo.Frames != null && templateInfo.Frames.Contains(tileIndex);
});
if (templates.Any())
template = templates.First().Value;
}
if (template == null) if (template == null)
{ {