diff --git a/Makefile b/Makefile index a1b2913b3c..b34ad1e63c 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,7 @@ SDK ?= CSC = mcs $(SDK) CSFLAGS = -nologo -warn:4 -codepage:utf8 -langversion:5 -unsafe -warnaserror DEFINE = TRACE -COMMON_LIBS = System.dll System.Core.dll System.Data.dll System.Data.DataSetExtensions.dll System.Drawing.dll System.Xml.dll thirdparty/download/ICSharpCode.SharpZipLib.dll thirdparty/download/FuzzyLogicLibrary.dll thirdparty/download/MaxMind.Db.dll thirdparty/download/Eluant.dll thirdparty/download/rix0rrr.BeaconLib.dll +COMMON_LIBS = System.dll System.Core.dll System.Data.dll System.Data.DataSetExtensions.dll System.Drawing.dll System.Numerics.dll System.Xml.dll thirdparty/download/ICSharpCode.SharpZipLib.dll thirdparty/download/FuzzyLogicLibrary.dll thirdparty/download/MaxMind.Db.dll thirdparty/download/Eluant.dll thirdparty/download/rix0rrr.BeaconLib.dll NUNIT_LIBS_PATH := NUNIT_LIBS := $(NUNIT_LIBS_PATH)nunit.framework.dll diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index b0a8d64414..23436144f5 100644 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -339,21 +339,23 @@ namespace OpenRA return defaultVisibility.IsVisible(this, player); } - public IEnumerable GetAllTargetTypes() + public BitSet GetAllTargetTypes() { // PERF: Avoid LINQ. + var targetTypes = new BitSet(); foreach (var targetable in Targetables) - foreach (var targetType in targetable.TargetTypes) - yield return targetType; + targetTypes = targetTypes.Union(targetable.TargetTypes); + return targetTypes; } - public IEnumerable GetEnabledTargetTypes() + public BitSet GetEnabledTargetTypes() { // PERF: Avoid LINQ. + var targetTypes = new BitSet(); foreach (var targetable in Targetables) if (targetable.IsTraitEnabled()) - foreach (var targetType in targetable.TargetTypes) - yield return targetType; + targetTypes = targetTypes.Union(targetable.TargetTypes); + return targetTypes; } public bool IsTargetableBy(Actor byActor) diff --git a/OpenRA.Game/FieldLoader.cs b/OpenRA.Game/FieldLoader.cs index f09fd1f25d..4b8fedb96a 100644 --- a/OpenRA.Game/FieldLoader.cs +++ b/OpenRA.Game/FieldLoader.cs @@ -607,14 +607,13 @@ namespace OpenRA return InvalidValueAction(value, fieldType, fieldName); } - else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Bits<>)) + else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(BitSet<>)) { if (value != null) { var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - var argTypes = new Type[] { typeof(string[]) }; - var argValues = new object[] { parts }; - return fieldType.GetConstructor(argTypes).Invoke(argValues); + var ctor = fieldType.GetConstructor(new[] { typeof(string[]) }); + return ctor.Invoke(new object[] { parts.Select(p => p.Trim()).ToArray() }); } return InvalidValueAction(value, fieldType, fieldName); diff --git a/OpenRA.Game/FieldSaver.cs b/OpenRA.Game/FieldSaver.cs index 8e4caf3a77..8b1780c7f4 100644 --- a/OpenRA.Game/FieldSaver.cs +++ b/OpenRA.Game/FieldSaver.cs @@ -18,6 +18,7 @@ using System.Globalization; using System.Linq; using System.Reflection; using OpenRA.Graphics; +using OpenRA.Primitives; namespace OpenRA { @@ -98,6 +99,11 @@ namespace OpenRA return "{0},{1},{2},{3}".F(r.X, r.Y, r.Width, r.Height); } + if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(BitSet<>)) + { + return ((IEnumerable)v).Select(FormatValue).JoinWith(", "); + } + if (t.IsArray && t.GetArrayRank() == 1) { return ((Array)v).Cast().Select(FormatValue).JoinWith(", "); diff --git a/OpenRA.Game/GameRules/ActorInfo.cs b/OpenRA.Game/GameRules/ActorInfo.cs index 8476b89ecf..bead7a4a63 100644 --- a/OpenRA.Game/GameRules/ActorInfo.cs +++ b/OpenRA.Game/GameRules/ActorInfo.cs @@ -162,5 +162,14 @@ namespace OpenRA 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 BitSet GetAllTargetTypes() + { + // PERF: Avoid LINQ. + var targetTypes = new BitSet(); + foreach (var targetable in TraitInfos()) + targetTypes = targetTypes.Union(targetable.GetTargetTypes()); + return targetTypes; + } } } diff --git a/OpenRA.Game/GameRules/WeaponInfo.cs b/OpenRA.Game/GameRules/WeaponInfo.cs index 2b88556bdc..55ab9d5e8d 100644 --- a/OpenRA.Game/GameRules/WeaponInfo.cs +++ b/OpenRA.Game/GameRules/WeaponInfo.cs @@ -13,6 +13,7 @@ using System; using System.Collections.Generic; using System.Linq; using OpenRA.Effects; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.GameRules @@ -64,10 +65,10 @@ namespace OpenRA.GameRules public readonly int Burst = 1; [Desc("What types of targets are affected.")] - public readonly HashSet ValidTargets = new HashSet { "Ground", "Water" }; + public readonly BitSet ValidTargets = new BitSet("Ground", "Water"); [Desc("What types of targets are unaffected.", "Overrules ValidTargets.")] - public readonly HashSet InvalidTargets = new HashSet(); + public readonly BitSet InvalidTargets; [Desc("Delay in ticks between firing shots from the same ammo magazine. If one entry, it will be used for all bursts.", "If multiple entries, their number needs to match Burst - 1.")] @@ -116,7 +117,7 @@ namespace OpenRA.GameRules return retList; } - public bool IsValidTarget(IEnumerable targetTypes) + public bool IsValidTarget(BitSet targetTypes) { return ValidTargets.Overlaps(targetTypes) && !InvalidTargets.Overlaps(targetTypes); } diff --git a/OpenRA.Game/Map/TileSet.cs b/OpenRA.Game/Map/TileSet.cs index 3629f5bc98..0d2dae30e6 100644 --- a/OpenRA.Game/Map/TileSet.cs +++ b/OpenRA.Game/Map/TileSet.cs @@ -15,6 +15,7 @@ using System.IO; using System.Linq; using OpenRA.FileSystem; using OpenRA.Primitives; +using OpenRA.Traits; namespace OpenRA { @@ -60,7 +61,7 @@ namespace OpenRA static readonly TerrainTypeInfo Default = new TerrainTypeInfo(); public readonly string Type; - public readonly HashSet TargetTypes = new HashSet(); + public readonly BitSet TargetTypes; public readonly HashSet AcceptsSmudgeType = new HashSet(); public readonly Color Color; public readonly bool RestrictPlayerColor = false; diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index d618498159..94cc61b8e9 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -98,6 +98,7 @@ ..\thirdparty\download\MaxMind.Db.dll False + @@ -272,7 +273,7 @@ - + diff --git a/OpenRA.Game/Primitives/BitSet.cs b/OpenRA.Game/Primitives/BitSet.cs new file mode 100644 index 0000000000..ee24dc1332 --- /dev/null +++ b/OpenRA.Game/Primitives/BitSet.cs @@ -0,0 +1,151 @@ +#region Copyright & License Information +/* + * Copyright 2007-2018 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using BitSetIndex = System.Numerics.BigInteger; + +namespace OpenRA.Primitives +{ + static class BitSetAllocator where T : class + { + static readonly Cache Bits = new Cache(Allocate); + static BitSetIndex nextBits = 1; + + static BitSetIndex Allocate(string value) + { + lock (Bits) + { + var bits = nextBits; + nextBits <<= 1; + return bits; + } + } + + public static BitSetIndex GetBits(string[] values) + { + BitSetIndex bits = 0; + lock (Bits) + foreach (var value in values) + bits |= Bits[value]; + + return bits; + } + + public static IEnumerable GetStrings(BitSetIndex bits) + { + var values = new List(); + lock (Bits) + foreach (var kvp in Bits) + if ((kvp.Value & bits) != 0) + values.Add(kvp.Key); + + return values; + } + + public static bool BitsContainString(BitSetIndex bits, string value) + { + BitSetIndex valueBit; + lock (Bits) + if (!Bits.TryGetValue(value, out valueBit)) + return false; + return (bits & valueBit) != 0; + } + } + + public struct BitSet : IEnumerable, IEquatable> where T : class + { + readonly BitSetIndex bits; + + public BitSet(params string[] values) : this(BitSetAllocator.GetBits(values)) { } + BitSet(BitSetIndex bits) { this.bits = bits; } + + public override string ToString() + { + return BitSetAllocator.GetStrings(bits).JoinWith(","); + } + + public static bool operator ==(BitSet me, BitSet other) { return me.bits == other.bits; } + public static bool operator !=(BitSet me, BitSet other) { return !(me == other); } + + public bool Equals(BitSet other) { return other == this; } + public override bool Equals(object obj) { return obj is BitSet && Equals((BitSet)obj); } + public override int GetHashCode() { return bits.GetHashCode(); } + + public bool IsEmpty { get { return bits == 0; } } + + public bool IsProperSubsetOf(BitSet other) + { + return IsSubsetOf(other) && !SetEquals(other); + } + + public bool IsProperSupersetOf(BitSet other) + { + return IsSupersetOf(other) && !SetEquals(other); + } + + public bool IsSubsetOf(BitSet other) + { + return (bits | other.bits) == other.bits; + } + + public bool IsSupersetOf(BitSet other) + { + return (bits | other.bits) == bits; + } + + public bool Overlaps(BitSet other) + { + return (bits & other.bits) != 0; + } + + public bool SetEquals(BitSet other) + { + return bits == other.bits; + } + + public bool Contains(string value) + { + return BitSetAllocator.BitsContainString(bits, value); + } + + public IEnumerator GetEnumerator() + { + return BitSetAllocator.GetStrings(bits).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public BitSet Except(BitSet other) + { + return new BitSet(bits & ~other.bits); + } + + public BitSet Intersect(BitSet other) + { + return new BitSet(bits & other.bits); + } + + public BitSet SymmetricExcept(BitSet other) + { + return new BitSet(bits ^ other.bits); + } + + public BitSet Union(BitSet other) + { + return new BitSet(bits | other.bits); + } + } +} diff --git a/OpenRA.Game/Primitives/Bits.cs b/OpenRA.Game/Primitives/Bits.cs deleted file mode 100644 index efe60f8b07..0000000000 --- a/OpenRA.Game/Primitives/Bits.cs +++ /dev/null @@ -1,69 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2018 The OpenRA Developers (see AUTHORS) - * This file is part of OpenRA, which is free software. It is made - * available to you under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. For more - * information, see COPYING. - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace OpenRA.Primitives -{ - static class BitAllocator where T : struct - { - static int nextVal = 1; - static Cache bits = new Cache(_ => Allocate()); - - static int Allocate() - { - if (nextVal == 0) - throw new InvalidOperationException( - "Too many values in BitAllocator<{0}>".F(typeof(T).Name)); - - var val = nextVal; - nextVal <<= 1; - return val; - } - - public static int GetValue(string[] val) - { - return val.Select(a => bits[a]).Aggregate(0, (a, b) => a | b); - } - - public static IEnumerable GetStrings(int val) - { - for (var i = 0; i < 32; i++) - { - var x = 1 << i; - if ((val & x) != 0) - yield return bits.Single(a => a.Value == x).Key; - } - } - } - - public struct Bits : IEquatable> where T : struct - { - public int Value; - - public Bits(string[] val) { Value = BitAllocator.GetValue(val); } - public Bits(Bits other) { Value = other.Value; } - - public override string ToString() - { - return BitAllocator.GetStrings(Value).JoinWith(","); - } - - public static bool operator ==(Bits me, Bits other) { return me.Value == other.Value; } - public static bool operator !=(Bits me, Bits other) { return !(me == other); } - - public bool Equals(Bits other) { return other == this; } - public override bool Equals(object obj) { return obj is Bits && Equals((Bits)obj); } - public override int GetHashCode() { return Value.GetHashCode(); } - } -} diff --git a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs index 5190746cba..4724de23da 100644 --- a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs +++ b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs @@ -36,7 +36,7 @@ namespace OpenRA.Traits readonly Shroud shroud; public Player Owner { get; private set; } - public HashSet TargetTypes { get; private set; } + public BitSet TargetTypes { get; private set; } public ITooltipInfo TooltipInfo { get; private set; } public Player TooltipOwner { get; private set; } @@ -84,7 +84,6 @@ namespace OpenRA.Traits footprint.Select(p => shroud.Contains(p).ToString()).JoinWith("|"))); CenterPosition = actor.CenterPosition; - TargetTypes = new HashSet(); tooltips = actor.TraitsImplementing().ToArray(); health = actor.TraitOrDefault(); @@ -101,10 +100,7 @@ namespace OpenRA.Traits public void RefreshState() { Owner = actor.Owner; - - // PERF: Reuse collection to avoid allocations. - TargetTypes.Clear(); - TargetTypes.UnionWith(actor.GetEnabledTargetTypes()); + TargetTypes = actor.GetEnabledTargetTypes(); if (health != null) { diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index ad32fe1c65..5cc6623a31 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -365,15 +365,20 @@ namespace OpenRA.Traits bool SpatiallyPartitionable { get; } } + /// + /// Indicates target types as defined on are present in a . + /// + public sealed class TargetableType { TargetableType() { } } + public interface ITargetableInfo : ITraitInfoInterface { - HashSet GetTargetTypes(); + BitSet GetTargetTypes(); } public interface ITargetable { // Check IsTraitEnabled or !IsTraitDisabled first - HashSet TargetTypes { get; } + BitSet TargetTypes { get; } bool TargetableBy(Actor self, Actor byActor); bool RequiresForceFire { get; } } diff --git a/OpenRA.Mods.Cnc/Traits/Disguise.cs b/OpenRA.Mods.Cnc/Traits/Disguise.cs index af33e413b4..53c56def73 100644 --- a/OpenRA.Mods.Cnc/Traits/Disguise.cs +++ b/OpenRA.Mods.Cnc/Traits/Disguise.cs @@ -16,6 +16,7 @@ using System.Linq; using OpenRA.Mods.Common.Orders; using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits.Render; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Cnc.Traits @@ -84,7 +85,7 @@ namespace OpenRA.Mods.Cnc.Traits public readonly Stance ValidStances = Stance.Ally | Stance.Neutral | Stance.Enemy; [Desc("Target types of actors that this actor disguise as.")] - public readonly HashSet TargetTypes = new HashSet { "Disguise" }; + public readonly BitSet TargetTypes = new BitSet("Disguise"); [Desc("Triggers which cause the actor to drop it's disguise. Possible values: None, Attack, Damaged,", "Unload, Infiltrate, Demolish, Move.")] @@ -317,7 +318,7 @@ namespace OpenRA.Mods.Cnc.Traits if (!info.ValidStances.HasStance(stance)) return false; - return info.TargetTypes.Overlaps(target.Info.TraitInfos().SelectMany(ti => ti.GetTargetTypes())); + return info.TargetTypes.Overlaps(target.Info.GetAllTargetTypes()); } } } diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForCash.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForCash.cs index 3e7575bbbb..2c95ad520b 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForCash.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForCash.cs @@ -13,6 +13,7 @@ using System; using System.Collections.Generic; using OpenRA.Mods.Common.Effects; using OpenRA.Mods.Common.Traits; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Cnc.Traits @@ -20,7 +21,7 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("This structure can be infiltrated causing funds to be stolen.")] class InfiltrateForCashInfo : ITraitInfo { - public readonly HashSet Types = new HashSet(); + public readonly BitSet Types; [Desc("Percentage of the victim's resources that will be stolen.")] public readonly int Percentage = 100; @@ -47,7 +48,7 @@ namespace OpenRA.Mods.Cnc.Traits public InfiltrateForCash(InfiltrateForCashInfo info) { this.info = info; } - void INotifyInfiltrated.Infiltrated(Actor self, Actor infiltrator, HashSet types) + void INotifyInfiltrated.Infiltrated(Actor self, Actor infiltrator, BitSet types) { if (!info.Types.Overlaps(types)) return; diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForDecoration.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForDecoration.cs index 8b0ebedc2f..0e4e23176a 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForDecoration.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForDecoration.cs @@ -13,6 +13,7 @@ using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits.Render; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Cnc.Traits @@ -20,7 +21,7 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("Reveals a decoration sprite to the indicated players when infiltrated.")] class InfiltrateForDecorationInfo : WithDecorationInfo { - public readonly HashSet Types = new HashSet(); + public readonly BitSet Types; public override object Create(ActorInitializer init) { return new InfiltrateForDecoration(init.Self, this); } } @@ -36,7 +37,7 @@ namespace OpenRA.Mods.Cnc.Traits this.info = info; } - void INotifyInfiltrated.Infiltrated(Actor self, Actor infiltrator, HashSet types) + void INotifyInfiltrated.Infiltrated(Actor self, Actor infiltrator, BitSet types) { if (!info.Types.Overlaps(types)) return; diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForExploration.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForExploration.cs index 8e8d07d395..55a09bef56 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForExploration.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForExploration.cs @@ -12,6 +12,7 @@ using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Traits; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Cnc.Traits @@ -19,7 +20,7 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("Steal and reset the owner's exploration.")] class InfiltrateForExplorationInfo : ITraitInfo { - public readonly HashSet Types = new HashSet(); + public readonly BitSet Types; public object Create(ActorInitializer init) { return new InfiltrateForExploration(init.Self, this); } } @@ -33,7 +34,7 @@ namespace OpenRA.Mods.Cnc.Traits this.info = info; } - void INotifyInfiltrated.Infiltrated(Actor self, Actor infiltrator, HashSet types) + void INotifyInfiltrated.Infiltrated(Actor self, Actor infiltrator, BitSet types) { if (!info.Types.Overlaps(types)) return; diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForPowerOutage.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForPowerOutage.cs index 768e6c9ac9..fe36ec2d97 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForPowerOutage.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForPowerOutage.cs @@ -11,13 +11,14 @@ using System.Collections.Generic; using OpenRA.Mods.Common.Traits; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Cnc.Traits { class InfiltrateForPowerOutageInfo : ITraitInfo { - public readonly HashSet Types = new HashSet(); + public readonly BitSet Types; public readonly int Duration = 25 * 20; @@ -35,7 +36,7 @@ namespace OpenRA.Mods.Cnc.Traits playerPower = self.Owner.PlayerActor.Trait(); } - void INotifyInfiltrated.Infiltrated(Actor self, Actor infiltrator, HashSet types) + void INotifyInfiltrated.Infiltrated(Actor self, Actor infiltrator, BitSet types) { if (!info.Types.Overlaps(types)) return; diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForSupportPower.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForSupportPower.cs index 1175b96162..7e3db3fb92 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForSupportPower.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/InfiltrateForSupportPower.cs @@ -20,7 +20,7 @@ namespace OpenRA.Mods.Cnc.Traits { [ActorReference, FieldLoader.Require] public readonly string Proxy = null; - public readonly HashSet Types = new HashSet(); + public readonly BitSet Types; public object Create(ActorInitializer init) { return new InfiltrateForSupportPower(this); } } @@ -34,7 +34,7 @@ namespace OpenRA.Mods.Cnc.Traits this.info = info; } - void INotifyInfiltrated.Infiltrated(Actor self, Actor infiltrator, HashSet types) + void INotifyInfiltrated.Infiltrated(Actor self, Actor infiltrator, BitSet types) { if (!info.Types.Overlaps(types)) return; diff --git a/OpenRA.Mods.Cnc/Traits/Infiltration/Infiltrates.cs b/OpenRA.Mods.Cnc/Traits/Infiltration/Infiltrates.cs index b0e5d6ded7..9ad770adf2 100644 --- a/OpenRA.Mods.Cnc/Traits/Infiltration/Infiltrates.cs +++ b/OpenRA.Mods.Cnc/Traits/Infiltration/Infiltrates.cs @@ -17,13 +17,14 @@ using OpenRA.Mods.Common; using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Orders; using OpenRA.Mods.Common.Traits; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Cnc.Traits { public class InfiltratesInfo : ConditionalTraitInfo { - public readonly HashSet Types = new HashSet(); + public readonly BitSet Types; [VoiceReference] public readonly string Voice = "Action"; @@ -72,14 +73,14 @@ namespace OpenRA.Mods.Cnc.Traits if (IsTraitDisabled) return false; - IEnumerable targetTypes = null; + var targetTypes = new BitSet(); if (order.Target.Type == TargetType.FrozenActor) targetTypes = order.Target.FrozenActor.TargetTypes; if (order.Target.Type == TargetType.Actor) targetTypes = order.TargetActor.GetEnabledTargetTypes(); - return targetTypes != null && Info.Types.Overlaps(targetTypes); + return Info.Types.Overlaps(targetTypes); } public string VoicePhraseForOrder(Actor self, Order order) @@ -133,7 +134,7 @@ namespace OpenRA.Mods.Cnc.Traits if (!info.ValidStances.HasStance(stance)) return false; - return info.Types.Overlaps(target.Info.TraitInfos().SelectMany(ti => ti.GetTargetTypes())); + return info.Types.Overlaps(target.Info.GetAllTargetTypes()); } } } diff --git a/OpenRA.Mods.Cnc/Traits/MadTank.cs b/OpenRA.Mods.Cnc/Traits/MadTank.cs index 8b534deb2e..e832f97c6a 100644 --- a/OpenRA.Mods.Cnc/Traits/MadTank.cs +++ b/OpenRA.Mods.Cnc/Traits/MadTank.cs @@ -114,7 +114,7 @@ namespace OpenRA.Mods.Cnc.Traits { get { - yield return new TargetTypeOrderTargeter(new HashSet { "DetonateAttack" }, "DetonateAttack", 5, "attack", true, false) { ForceAttack = false }; + yield return new TargetTypeOrderTargeter(new BitSet("DetonateAttack"), "DetonateAttack", 5, "attack", true, false) { ForceAttack = false }; yield return new DeployOrderTargeter("Detonate", 5); } } diff --git a/OpenRA.Mods.Common/AI/States/AirStates.cs b/OpenRA.Mods.Common/AI/States/AirStates.cs index b6b47cd6dd..7736af9781 100644 --- a/OpenRA.Mods.Common/AI/States/AirStates.cs +++ b/OpenRA.Mods.Common/AI/States/AirStates.cs @@ -13,13 +13,14 @@ using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Traits; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Common.AI { abstract class AirStateBase : StateBase { - static readonly string[] AirTargetTypes = new[] { "Air" }; + static readonly BitSet AirTargetTypes = new BitSet("Air"); protected const int MissileUnitMultiplier = 3; diff --git a/OpenRA.Mods.Common/AI/States/GroundStates.cs b/OpenRA.Mods.Common/AI/States/GroundStates.cs index eb08773eb5..04100ec3cf 100644 --- a/OpenRA.Mods.Common/AI/States/GroundStates.cs +++ b/OpenRA.Mods.Common/AI/States/GroundStates.cs @@ -105,7 +105,7 @@ namespace OpenRA.Mods.Common.AI else { var enemies = owner.World.FindActorsInCircle(leader.CenterPosition, WDist.FromCells(owner.Bot.Info.AttackScanRadius)) - .Where(a => !a.IsDead && leader.Owner.Stances[a.Owner] == Stance.Enemy && a.GetEnabledTargetTypes().Any()); + .Where(a => !a.IsDead && leader.Owner.Stances[a.Owner] == Stance.Enemy && !a.GetEnabledTargetTypes().IsEmpty); var target = enemies.ClosestTo(leader.CenterPosition); if (target != null) { diff --git a/OpenRA.Mods.Common/AI/States/NavyStates.cs b/OpenRA.Mods.Common/AI/States/NavyStates.cs index a383200750..fa57a2cc92 100644 --- a/OpenRA.Mods.Common/AI/States/NavyStates.cs +++ b/OpenRA.Mods.Common/AI/States/NavyStates.cs @@ -132,7 +132,7 @@ namespace OpenRA.Mods.Common.AI else { var enemies = owner.World.FindActorsInCircle(leader.CenterPosition, WDist.FromCells(owner.Bot.Info.AttackScanRadius)) - .Where(a => !a.IsDead && leader.Owner.Stances[a.Owner] == Stance.Enemy && a.GetEnabledTargetTypes().Any()); + .Where(a => !a.IsDead && leader.Owner.Stances[a.Owner] == Stance.Enemy && !a.GetEnabledTargetTypes().IsEmpty); var target = enemies.ClosestTo(leader.CenterPosition); if (target != null) { diff --git a/OpenRA.Mods.Common/AI/States/StateBase.cs b/OpenRA.Mods.Common/AI/States/StateBase.cs index edb270f966..662fcc6896 100644 --- a/OpenRA.Mods.Common/AI/States/StateBase.cs +++ b/OpenRA.Mods.Common/AI/States/StateBase.cs @@ -65,7 +65,7 @@ namespace OpenRA.Mods.Common.AI return false; var targetTypes = target.GetEnabledTargetTypes(); - if (!targetTypes.Any()) + if (targetTypes.IsEmpty) return false; var arms = a.TraitsImplementing(); diff --git a/OpenRA.Mods.Common/AI/SupportPowerDecision.cs b/OpenRA.Mods.Common/AI/SupportPowerDecision.cs index cd43972b7a..84c4c03f84 100644 --- a/OpenRA.Mods.Common/AI/SupportPowerDecision.cs +++ b/OpenRA.Mods.Common/AI/SupportPowerDecision.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using OpenRA.Mods.Common.Traits; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Common.AI @@ -122,7 +123,7 @@ namespace OpenRA.Mods.Common.AI public readonly Stance Against = Stance.Enemy; [Desc("What types should the desired targets of this power be?")] - public readonly HashSet Types = new HashSet { "Air", "Ground", "Water" }; + public readonly BitSet Types = new BitSet("Air", "Ground", "Water"); [Desc("How attractive are these types of targets?")] public readonly int Attractiveness = 100; diff --git a/OpenRA.Mods.Common/Lint/CheckDeathTypes.cs b/OpenRA.Mods.Common/Lint/CheckDeathTypes.cs index 693519bd72..a3c6b4604f 100644 --- a/OpenRA.Mods.Common/Lint/CheckDeathTypes.cs +++ b/OpenRA.Mods.Common/Lint/CheckDeathTypes.cs @@ -35,8 +35,8 @@ namespace OpenRA.Mods.Common.Lint if (!deathTypes.Any() || spawnActorOnAnyDeathType) continue; - var targetable = actorInfo.Value.TraitInfos().SelectMany(x => x.GetTargetTypes()).ToList(); - if (!targetable.Any()) + var targetable = actorInfo.Value.GetAllTargetTypes(); + if (targetable.IsEmpty) continue; foreach (var weaponInfo in rules.Weapons) diff --git a/OpenRA.Mods.Common/Orders/UnitOrderTargeter.cs b/OpenRA.Mods.Common/Orders/UnitOrderTargeter.cs index a20603e1c1..165b6f236e 100644 --- a/OpenRA.Mods.Common/Orders/UnitOrderTargeter.cs +++ b/OpenRA.Mods.Common/Orders/UnitOrderTargeter.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Common.Orders @@ -67,9 +68,9 @@ namespace OpenRA.Mods.Common.Orders public class TargetTypeOrderTargeter : UnitOrderTargeter { - readonly HashSet targetTypes; + readonly BitSet targetTypes; - public TargetTypeOrderTargeter(HashSet targetTypes, string order, int priority, string cursor, bool targetEnemyUnits, bool targetAllyUnits) + public TargetTypeOrderTargeter(BitSet targetTypes, string order, int priority, string cursor, bool targetEnemyUnits, bool targetAllyUnits) : base(order, priority, cursor, targetEnemyUnits, targetAllyUnits) { this.targetTypes = targetTypes; diff --git a/OpenRA.Mods.Common/Scripting/ScriptTriggers.cs b/OpenRA.Mods.Common/Scripting/ScriptTriggers.cs index 2630ffc22d..70706590d1 100644 --- a/OpenRA.Mods.Common/Scripting/ScriptTriggers.cs +++ b/OpenRA.Mods.Common/Scripting/ScriptTriggers.cs @@ -13,6 +13,7 @@ using System; using System.Collections.Generic; using Eluant; using OpenRA.Mods.Common.Traits; +using OpenRA.Primitives; using OpenRA.Scripting; using OpenRA.Traits; @@ -302,7 +303,7 @@ namespace OpenRA.Mods.Common.Scripting OnCapturedInternal(self); } - void INotifyInfiltrated.Infiltrated(Actor self, Actor infiltrator, HashSet types) + void INotifyInfiltrated.Infiltrated(Actor self, Actor infiltrator, BitSet types) { if (world.Disposing) return; diff --git a/OpenRA.Mods.Common/Traits/Attack/AttackSuicides.cs b/OpenRA.Mods.Common/Traits/Attack/AttackSuicides.cs index e5a09c6fef..60cdb5c060 100644 --- a/OpenRA.Mods.Common/Traits/Attack/AttackSuicides.cs +++ b/OpenRA.Mods.Common/Traits/Attack/AttackSuicides.cs @@ -13,6 +13,7 @@ using System.Collections.Generic; using System.Drawing; using OpenRA.Activities; using OpenRA.Mods.Common.Orders; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -45,7 +46,7 @@ namespace OpenRA.Mods.Common.Traits if (IsTraitDisabled) yield break; - yield return new TargetTypeOrderTargeter(new HashSet { "DetonateAttack" }, "DetonateAttack", 5, "attack", true, false) { ForceAttack = false }; + yield return new TargetTypeOrderTargeter(new BitSet("DetonateAttack"), "DetonateAttack", 5, "attack", true, false) { ForceAttack = false }; yield return new DeployOrderTargeter("Detonate", 5); } } diff --git a/OpenRA.Mods.Common/Traits/AutoTarget.cs b/OpenRA.Mods.Common/Traits/AutoTarget.cs index c82193f055..0ac5d52459 100644 --- a/OpenRA.Mods.Common/Traits/AutoTarget.cs +++ b/OpenRA.Mods.Common/Traits/AutoTarget.cs @@ -284,7 +284,7 @@ namespace OpenRA.Mods.Common.Traits continue; // Check whether we can auto-target this actor - var targetTypes = actor.GetEnabledTargetTypes().ToArray(); + var targetTypes = actor.GetEnabledTargetTypes(); var validPriorities = activePriorities.Where(ati => { // Already have a higher priority target diff --git a/OpenRA.Mods.Common/Traits/AutoTargetPriority.cs b/OpenRA.Mods.Common/Traits/AutoTargetPriority.cs index 7c7301255e..159a3f2115 100644 --- a/OpenRA.Mods.Common/Traits/AutoTargetPriority.cs +++ b/OpenRA.Mods.Common/Traits/AutoTargetPriority.cs @@ -9,7 +9,7 @@ */ #endregion -using System.Collections.Generic; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -18,10 +18,10 @@ namespace OpenRA.Mods.Common.Traits public class AutoTargetPriorityInfo : ConditionalTraitInfo, Requires { [Desc("Target types that can be AutoTargeted.")] - public readonly HashSet ValidTargets = new HashSet { "Ground", "Water", "Air" }; + public readonly BitSet ValidTargets = new BitSet("Ground", "Water", "Air"); [Desc("Target types that can't be AutoTargeted.", "Overrules ValidTargets.")] - public readonly HashSet InvalidTargets = new HashSet(); + public readonly BitSet InvalidTargets; [Desc("ValidTargets with larger priorities will be AutoTargeted before lower priorities.")] public readonly int Priority = 1; diff --git a/OpenRA.Mods.Common/Traits/Crates/DuplicateUnitCrateAction.cs b/OpenRA.Mods.Common/Traits/Crates/DuplicateUnitCrateAction.cs index bdeae1b5b0..d9fb73c145 100644 --- a/OpenRA.Mods.Common/Traits/Crates/DuplicateUnitCrateAction.cs +++ b/OpenRA.Mods.Common/Traits/Crates/DuplicateUnitCrateAction.cs @@ -33,7 +33,7 @@ namespace OpenRA.Mods.Common.Traits public readonly int MaxRadius = 4; [Desc("The list of unit target types we are allowed to duplicate.")] - public readonly HashSet ValidTargets = new HashSet { "Ground", "Water" }; + public readonly BitSet ValidTargets = new BitSet("Ground", "Water"); [Desc("Which factions this crate action can occur for.")] public readonly HashSet ValidFactions = new HashSet(); diff --git a/OpenRA.Mods.Common/Traits/Targetable.cs b/OpenRA.Mods.Common/Traits/Targetable.cs index df39a350c8..10c48af022 100644 --- a/OpenRA.Mods.Common/Traits/Targetable.cs +++ b/OpenRA.Mods.Common/Traits/Targetable.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using System.Linq; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -19,8 +20,8 @@ namespace OpenRA.Mods.Common.Traits public class TargetableInfo : ConditionalTraitInfo, ITargetableInfo { [Desc("Target type. Used for filtering (in)valid targets.")] - public readonly HashSet TargetTypes = new HashSet(); - public HashSet GetTargetTypes() { return TargetTypes; } + public readonly BitSet TargetTypes; + public BitSet GetTargetTypes() { return TargetTypes; } public bool RequiresForceFire = false; @@ -53,7 +54,7 @@ namespace OpenRA.Mods.Common.Traits return cloaks.All(c => c.IsTraitDisabled || c.IsVisible(self, viewer.Owner)); } - public virtual HashSet TargetTypes { get { return Info.TargetTypes; } } + public virtual BitSet TargetTypes { get { return Info.TargetTypes; } } public bool RequiresForceFire { get { return Info.RequiresForceFire; } } } diff --git a/OpenRA.Mods.Common/TraitsInterfaces.cs b/OpenRA.Mods.Common/TraitsInterfaces.cs index e3fb2ba686..744211fbfe 100644 --- a/OpenRA.Mods.Common/TraitsInterfaces.cs +++ b/OpenRA.Mods.Common/TraitsInterfaces.cs @@ -118,7 +118,7 @@ namespace OpenRA.Mods.Common.Traits public interface ISeedableResource { void Seed(Actor self); } [RequireExplicitImplementation] - public interface INotifyInfiltrated { void Infiltrated(Actor self, Actor infiltrator, HashSet types); } + public interface INotifyInfiltrated { void Infiltrated(Actor self, Actor infiltrator, BitSet types); } [RequireExplicitImplementation] public interface INotifyBlockingMove { void OnNotifyBlockingMove(Actor self, Actor blocking); } diff --git a/OpenRA.Mods.Common/Warheads/CreateEffectWarhead.cs b/OpenRA.Mods.Common/Warheads/CreateEffectWarhead.cs index 61fe5f5ca1..f3cafcebf9 100644 --- a/OpenRA.Mods.Common/Warheads/CreateEffectWarhead.cs +++ b/OpenRA.Mods.Common/Warheads/CreateEffectWarhead.cs @@ -14,6 +14,7 @@ using System.Linq; using OpenRA.GameRules; using OpenRA.Mods.Common.Effects; using OpenRA.Mods.Common.Traits; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Common.Warheads @@ -48,7 +49,7 @@ namespace OpenRA.Mods.Common.Warheads [Desc("Whether to consider actors in determining whether the explosion should happen. If false, only terrain will be considered.")] public readonly bool ImpactActors = true; - static readonly string[] TargetTypeAir = new string[] { "Air" }; + static readonly BitSet TargetTypeAir = new BitSet("Air"); public ImpactType GetImpactType(World world, CPos cell, WPos pos, Actor firedBy) { diff --git a/OpenRA.Mods.Common/Warheads/Warhead.cs b/OpenRA.Mods.Common/Warheads/Warhead.cs index 4f507fe469..69dd3be345 100644 --- a/OpenRA.Mods.Common/Warheads/Warhead.cs +++ b/OpenRA.Mods.Common/Warheads/Warhead.cs @@ -12,6 +12,7 @@ using System; using System.Collections.Generic; using System.Drawing; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Common.Warheads @@ -35,10 +36,10 @@ namespace OpenRA.Mods.Common.Warheads public abstract class Warhead : IWarhead { [Desc("What types of targets are affected.")] - public readonly HashSet ValidTargets = new HashSet { "Ground", "Water" }; + public readonly BitSet ValidTargets = new BitSet("Ground", "Water"); [Desc("What types of targets are unaffected.", "Overrules ValidTargets.")] - public readonly HashSet InvalidTargets = new HashSet(); + public readonly BitSet InvalidTargets; [Desc("What diplomatic stances are affected.")] public readonly Stance ValidStances = Stance.Ally | Stance.Neutral | Stance.Enemy; @@ -54,7 +55,7 @@ namespace OpenRA.Mods.Common.Warheads [Desc("The color used for this warhead's visualization in the world's `WarheadDebugOverlay` trait.")] public readonly Color DebugOverlayColor = Color.Red; - public bool IsValidTarget(IEnumerable targetTypes) + public bool IsValidTarget(BitSet targetTypes) { return ValidTargets.Overlaps(targetTypes) && !InvalidTargets.Overlaps(targetTypes); }