changeset 455:afc6410e4efc

Re #379: Fix validation of requirements to check for unit * Move to "Unit" requirements, since we assume things depend on units * Rename some classes to more meaningful names from unit-based change * Rename "requires N for M" requirement as we can make it more flexible
author IBBoard <dev@ibboard.co.uk>
date Wed, 22 Feb 2012 20:45:39 +0000
parents def5d333c5e8
children 52baffdd2ab9
files API/Factories/Requirement/UnitRequiresNUnitsForMUnitsRequirementFactory.cs API/Objects/Requirement/AbstractRequirement.cs API/Objects/Requirement/AbstractUnitRequirement.cs API/Objects/Requirement/IRequirement.cs API/Objects/Requirement/RequiresAtLeastNUnitsRequirement.cs API/Objects/Requirement/RequiresNUnitsForMObjectsRequirement.cs API/Objects/Requirement/RequiresNUnitsForMUnitsRequirement.cs API/Objects/Requirement/RequiresNoMoreThanNOfUnitTypeRequirement.cs API/Objects/Requirement/UnitRequiresAtLeastNUnitsRequirement.cs API/Objects/Requirement/UnitRequiresNUnitsForMUnitsRequirement.cs API/Objects/Requirement/UnitRequiresNoMoreThanNOfUnitTypeRequirement.cs API/Objects/UnitType.cs IBBoard.WarFoundry.API.csproj
diffstat 13 files changed, 597 insertions(+), 499 deletions(-) [+]
line wrap: on
line diff
--- a/API/Factories/Requirement/UnitRequiresNUnitsForMUnitsRequirementFactory.cs	Tue Jan 31 20:58:09 2012 +0000
+++ b/API/Factories/Requirement/UnitRequiresNUnitsForMUnitsRequirementFactory.cs	Wed Feb 22 20:45:39 2012 +0000
@@ -19,19 +19,19 @@
 		{
 			get
 			{
-				return RequiresNUnitsForMUnitsRequirement.REQUIREMENT_ID;
+				return UnitRequiresNUnitsForMUnitsRequirement.REQUIREMENT_ID;
 			}
 		}
 
 		public IRequirement CreateRequirement<SOURCE_FILE_TYPE, ENTRY_TYPE>(UnitType type, string data, IRaceFactory<SOURCE_FILE_TYPE, ENTRY_TYPE> raceFactory)
 		{
-			RequiresNUnitsForMUnitsRequirement req = new RequiresNUnitsForMUnitsRequirement(type);
+			UnitRequiresNUnitsForMUnitsRequirement req = new UnitRequiresNUnitsForMUnitsRequirement(type);
 			Race race = type.Race;
 			AddRequirements(req, race, data, raceFactory);
 			return req;
 		}
 
-		private void AddRequirements<SOURCE_FILE_TYPE, ENTRY_TYPE>(RequiresNUnitsForMUnitsRequirement req, Race race, string data, IRaceFactory<SOURCE_FILE_TYPE, ENTRY_TYPE> raceFactory)
+		private void AddRequirements<SOURCE_FILE_TYPE, ENTRY_TYPE>(UnitRequiresNUnitsForMUnitsRequirement req, Race race, string data, IRaceFactory<SOURCE_FILE_TYPE, ENTRY_TYPE> raceFactory)
 		{
 			foreach (string requirement in data.Split('|'))
 			{
--- a/API/Objects/Requirement/AbstractRequirement.cs	Tue Jan 31 20:58:09 2012 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-// This file (AbstractRequirement.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2011 IBBoard
-// 
-// The file and the library/program it is in are licensed and distributed, without warranty, under the GNU Affero GPL license, either version 3 of the License or (at your option) any later version. Please see COPYING for more information and the full license.
-using System;
-
-namespace IBBoard.WarFoundry.API.Objects.Requirement
-{
-	public abstract class AbstractRequirement : IRequirement
-	{
-		public abstract string RequirementID { get; }
-
-		public override bool Equals(object obj)
-		{
-			if (obj == null)
-			{
-				return false;
-			}
-			else if (obj.GetType().Equals(this.GetType()))
-			{
-				return TypeEquals(obj);
-			}
-			else
-			{
-				return false;
-			}
-		}
-
-		public override abstract int GetHashCode();
-
-		/// <summary>
-		/// Type-specific equality checking - must be implemented by each class
-		/// </summary>
-		/// <returns>
-		/// <code>true</code> if this object is equal to <code>obj</code>, else <code>false</code>
-		/// </returns>
-		/// <param name='obj'>
-		/// The object to compare to
-		/// </param>
-		protected abstract bool TypeEquals(object obj);
-
-		protected virtual bool IsApplicable(WarFoundryObject toObjectAdded, Army toArmy)
-		{
-			return IsApplicable(toArmy) || IsApplicable(toObjectAdded);
-		}
-
-		protected virtual bool IsApplicable(Army toArmy)
-		{
-			return true;
-		}
-
-		protected virtual bool IsApplicable(WarFoundryObject toObject)
-		{
-			return true;
-		}
-
-		public string GetValidationMessage(Army army)
-		{
-			string message = "";
-
-			Validation result = ValidatesArmy(army);
-			if (!Validates.AsOkay(result))
-			{
-				message = GetValidationFailedMessage(army);
-			}
-
-			return message;
-		}
-
-		protected virtual string ValidationFailedMessage { get { return "Army must contain: {0}."; } }
-
-		private string GetValidationFailedMessage(Army army)
-		{
-			return String.Format(ValidationFailedMessage, GetFailedRequirementsString(army));
-		}
-
-		protected abstract string GetFailedRequirementsString(Army army);
-
-		public string GetAllowsAddingMessage(UnitType toAdd, Army toArmy)
-		{
-			string message = "";
-
-			Validation result = AllowsAdding(toAdd, toArmy);
-			if (!Validates.AsOkay(result))
-			{
-				message = GetAllowsAddingFailedMessage(toAdd, toArmy);
-			}
-
-			return message;
-		}
-
-		protected virtual string AllowsAddingFailedMessage { get { return ValidationFailedMessage; } }
-
-		protected string GetAllowsAddingFailedMessage(UnitType toAdd, Army toArmy)
-		{
-			return String.Format(AllowsAddingFailedMessage, GetFailedAddingRequirementsString(toAdd, toArmy));
-		}
-
-		protected abstract string GetFailedAddingRequirementsString(UnitType toAdd, Army toArmy);
-
-		public abstract  Validation AllowsAdding(WarFoundryObject wfObject, Army toArmy);
-
-		public abstract  Validation ValidatesArmy(Army army);
-	}
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/API/Objects/Requirement/AbstractUnitRequirement.cs	Wed Feb 22 20:45:39 2012 +0000
@@ -0,0 +1,164 @@
+// This file (AbstractRequirement.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2011 IBBoard
+// 
+// The file and the library/program it is in are licensed and distributed, without warranty, under the GNU Affero GPL license, either version 3 of the License or (at your option) any later version. Please see COPYING for more information and the full license.
+using System;
+using System.Collections.Generic;
+
+namespace IBBoard.WarFoundry.API.Objects.Requirement
+{
+	public abstract class AbstractUnitRequirement<OBJECT_TYPE> : IRequirement where OBJECT_TYPE : IWarFoundryObject
+	{
+		private List<UnitCountRequirementData> requiredTypes;
+		private OBJECT_TYPE allowedObject;
+
+		public AbstractUnitRequirement(OBJECT_TYPE allowedObject, params UnitType[] requiredUnitTypes)
+		{
+			this.allowedObject = allowedObject;
+			requiredTypes = new List<UnitCountRequirementData>();
+
+			foreach (UnitType unitType in requiredUnitTypes)
+			{
+				AddUnitTypeRequirement(unitType);
+			}
+		}
+
+		public abstract string RequirementID { get; }
+
+		protected List<UnitCountRequirementData> RequiredTypes { get { return requiredTypes; } }
+
+		public abstract void AddUnitTypeRequirement(UnitType unitType);
+
+		public override bool Equals(object obj)
+		{
+			if (obj == null)
+			{
+				return false;
+			}
+			else if (obj.GetType().Equals(this.GetType()))
+			{
+				return TypeEquals(obj);
+			}
+			else
+			{
+				return false;
+			}
+		}
+
+		public override abstract int GetHashCode();
+
+		/// <summary>
+		/// Type-specific equality checking - must be implemented by each class
+		/// </summary>
+		/// <returns>
+		/// <code>true</code> if this object is equal to <code>obj</code>, else <code>false</code>
+		/// </returns>
+		/// <param name='obj'>
+		/// The object to compare to
+		/// </param>
+		protected abstract bool TypeEquals(object obj);
+
+		protected virtual bool IsApplicable(IWarFoundryObject toObjectAdded, Army toArmy)
+		{
+			return IsApplicable(toArmy) || IsApplicable(toObjectAdded);
+		}
+
+		protected virtual bool IsApplicable(Army toArmy)
+		{
+			return true;
+		}
+
+		protected virtual bool IsApplicable(IWarFoundryObject toObject)
+		{
+			return true;
+		}
+
+		public string GetValidationMessage(Army army)
+		{
+			string message = "";
+
+			Validation result = ValidatesArmy(army);
+			if (!Validates.AsOkay(result))
+			{
+				message = GetValidationFailedMessage(army);
+			}
+
+			return message;
+		}
+
+		protected virtual string ValidationFailedMessage { get { return "Army must contain: {0}."; } }
+
+		private string GetValidationFailedMessage(Army army)
+		{
+			return String.Format(ValidationFailedMessage, GetFailedRequirementsString(army));
+		}
+
+		protected abstract string GetFailedRequirementsString(Army army);
+
+		public string GetAllowsAddingMessage(IWarFoundryObject toAdd, Army toArmy)
+		{
+			string message = "";
+
+			Validation result = AllowsAdding(toAdd, toArmy);
+			if (!Validates.AsOkay(result))
+			{
+				message = GetAllowsAddingFailedMessage(toAdd, toArmy);
+			}
+
+			return message;
+		}
+
+		protected virtual string AllowsAddingFailedMessage { get { return ValidationFailedMessage; } }
+
+		protected string GetAllowsAddingFailedMessage(IWarFoundryObject toAdd, Army toArmy)
+		{
+			return String.Format(AllowsAddingFailedMessage, GetFailedAddingRequirementsString(toAdd, toArmy));
+		}
+
+		protected abstract string GetFailedAddingRequirementsString(IWarFoundryObject toAdd, Army toArmy);
+
+		public abstract  Validation AllowsAdding(IWarFoundryObject wfObject, Army toArmy);
+
+		public abstract  Validation ValidatesArmy(Army army);
+
+		protected UnitType GetUnitTypeFromObject(IWarFoundryObject toObject)
+		{
+			UnitType unitType = null;
+
+			if (toObject is UnitType)
+			{
+				unitType = (UnitType)toObject;
+			}
+			else if (toObject is Unit)
+			{
+				unitType = ((Unit)toObject).UnitType;
+			}
+
+			return unitType;
+		}
+
+		protected int GetUnitTypeCount(Army toArmy, UnitType unitType, IWarFoundryObject wfObject)
+		{
+			return toArmy.GetUnitTypeCount(unitType) + GetCountFromObject(wfObject, unitType);
+		}
+
+		protected int GetCountFromObject(IWarFoundryObject wfObject, UnitType limitedType)
+		{
+			return (limitedType.Equals(wfObject) || (wfObject is Unit && ((Unit)wfObject).UnitType.Equals(limitedType))) ? 1 : 0;
+		}
+
+		protected int GetObjectCount(Army toArmy, IWarFoundryObject wfObject)
+		{
+			return GetObjectCountFromArmy(toArmy) + GetObjectCountFromObject(wfObject);
+		}
+
+		protected abstract int GetObjectCountFromArmy(Army toArmy);
+
+		protected virtual int GetObjectCountFromObject(IWarFoundryObject wfObject)
+		{
+			return allowedObject.Equals(wfObject) ? 1 : 0;
+		}
+
+		public OBJECT_TYPE AllowedObject { get { return allowedObject; } }
+	}
+}
+
--- a/API/Objects/Requirement/IRequirement.cs	Tue Jan 31 20:58:09 2012 +0000
+++ b/API/Objects/Requirement/IRequirement.cs	Wed Feb 22 20:45:39 2012 +0000
@@ -6,7 +6,7 @@
 namespace IBBoard.WarFoundry.API.Objects.Requirement
 {
 	/// <summary>
-	/// Base interface for a Requirement that constrains the units/equipment that can be taken in an army
+	/// Base interface for a Requirement that constrains the IWarFoundryObjects that can be taken in an army
 	/// </summary>
 	public interface IRequirement
 	{
@@ -20,7 +20,7 @@
 		string RequirementID { get; }
 
 		/// <summary>
-		/// Checks whether the supplied WarFoundryObject can be added to the supplied army.
+		/// Checks whether the supplied IWarFoundryObject can be added to the supplied army.
 		/// </summary>
 		/// <returns>
 		/// A <code>Validation</code> enum to show the result of the validation
@@ -31,7 +31,7 @@
 		/// <param name='toArmy'>
 		/// The army to add the object to.
 		/// </param>
-		Validation AllowsAdding(WarFoundryObject wfObject, Army toArmy);
+		Validation AllowsAdding(IWarFoundryObject wfObject, Army toArmy);
 
 		/// <summary>
 		/// Checks whether the supplied army is currently valid according to this requirement.
@@ -56,18 +56,18 @@
 		string GetValidationMessage(Army army);
 
 		/// <summary>
-		/// Gets the validation message for adding a unit to the army
+		/// Gets the validation message for adding an IWarFoundryObject to the army
 		/// </summary>
 		/// <returns>
-		/// A validation message, if the type cannot be added, else an empty string.
+		/// A validation message, if the object cannot be added, else an empty string.
 		/// </returns>
 		/// <param name='toAdd'>
-		/// The unit type to try to add
+		/// The IWarFoundryObject to try to add
 		/// </param>
 		/// <param name='toArmy'>
-		/// The army the unit will be added to
+		/// The army the object will be added to
 		/// </param>
-		string GetAllowsAddingMessage(UnitType toAdd, Army toArmy);
+		string GetAllowsAddingMessage(IWarFoundryObject toAdd, Army toArmy);
 	}
 }
 
--- a/API/Objects/Requirement/RequiresAtLeastNUnitsRequirement.cs	Tue Jan 31 20:58:09 2012 +0000
+++ b/API/Objects/Requirement/RequiresAtLeastNUnitsRequirement.cs	Wed Feb 22 20:45:39 2012 +0000
@@ -13,19 +13,13 @@
 	///
 	/// The definition for how this requirement is built from a data file is defined in the <see cref="UnitRequiresAtLeastNUnitsRequirementFactory"/> class.
 	/// </summary>
-	public class RequiresAtLeastNUnitsRequirement : AbstractRequirement
+	public class RequiresAtLeastNUnitsRequirement<OBJECT_TYPE> : AbstractUnitRequirement<OBJECT_TYPE> where OBJECT_TYPE : IWarFoundryObject
 	{
 		public static readonly string REQUIREMENT_ID = "RequiresAtLeastNUnits";
-		private List<UnitCountRequirementData> requiredTypes;
 
-		public RequiresAtLeastNUnitsRequirement(params UnitType[] requiredUnitTypes)
+		public RequiresAtLeastNUnitsRequirement(OBJECT_TYPE requirementOn, params UnitType[] requiredUnitTypes) : base(requirementOn, requiredUnitTypes)
 		{
-			requiredTypes = new List<UnitCountRequirementData>();
-
-			foreach (UnitType unitType in requiredUnitTypes)
-			{
-				AddUnitTypeRequirement(unitType);
-			}
+			//Do nothing
 		}
 
 		public override string RequirementID
@@ -38,8 +32,8 @@
 
 		protected override bool TypeEquals(object obj)
 		{
-			RequiresAtLeastNUnitsRequirement otherReq = (RequiresAtLeastNUnitsRequirement)obj;
-			if (!Collections.Collections.AreEqual(requiredTypes, otherReq.requiredTypes))
+			RequiresAtLeastNUnitsRequirement<OBJECT_TYPE> otherReq = (RequiresAtLeastNUnitsRequirement<OBJECT_TYPE>)obj;
+			if (!Collections.Collections.AreEqual(RequiredTypes, otherReq.RequiredTypes))
 			{
 				return false;
 			}
@@ -53,7 +47,7 @@
 		{
 			int hash = 0;
 
-			foreach (UnitCountRequirementData req in requiredTypes)
+			foreach (UnitCountRequirementData req in RequiredTypes)
 			{
 				hash += req.UnitType.GetHashCode();
 			}
@@ -73,7 +67,7 @@
 		/// <param name='toArmy'>
 		/// The army to add the object to.
 		/// </param>
-		public override Validation AllowsAdding(WarFoundryObject wfObject, Army toArmy)
+		public override Validation AllowsAdding(IWarFoundryObject wfObject, Army toArmy)
 		{
 			return IsApplicable(wfObject, toArmy) ? CheckAllowsAdding(wfObject, toArmy) : Validation.NotApplicable;
 		}
@@ -83,7 +77,7 @@
 			return false;
 		}
 
-		protected override bool IsApplicable(WarFoundryObject toObject)
+		protected override bool IsApplicable(IWarFoundryObject toObject)
 		{
 			bool isApplicable = false;
 			UnitType unitType = GetUnitTypeFromObject(toObject);
@@ -96,26 +90,10 @@
 			return isApplicable;
 		}
 
-		protected UnitType GetUnitTypeFromObject(WarFoundryObject toObject)
-		{
-			UnitType unitType = null;
-
-			if (toObject is UnitType)
-			{
-				unitType = (UnitType)toObject;
-			}
-			else if (toObject is Unit)
-			{
-				unitType = ((Unit)toObject).UnitType;
-			}
-
-			return unitType;
-		}
-
 		private bool IsApplicable(UnitType unitType)
 		{
 			bool isApplicable = false;
-			foreach (UnitCountRequirementData requirement in requiredTypes)
+			foreach (UnitCountRequirementData requirement in RequiredTypes)
 			{
 				if (requirement.UnitType.Equals(unitType))
 				{
@@ -126,11 +104,11 @@
 			return isApplicable;
 		}
 
-		private Validation CheckAllowsAdding(WarFoundryObject wfObject, Army toArmy)
+		private Validation CheckAllowsAdding(IWarFoundryObject wfObject, Army toArmy)
 		{
 			Validation isValid = Validation.Passed;
 			
-			foreach (UnitCountRequirementData requirement in requiredTypes)
+			foreach (UnitCountRequirementData requirement in RequiredTypes)
 			{
 				if (GetUnitTypeCount(toArmy, requirement.UnitType, wfObject) < requirement.Count)
 				{
@@ -142,16 +120,6 @@
 			return isValid;
 		}
 
-		private int GetUnitTypeCount(Army toArmy, UnitType unitType, WarFoundryObject wfObject)
-		{
-			return toArmy.GetUnitTypeCount(unitType) + GetCountFromObject(wfObject, unitType);
-		}
-
-		private int GetCountFromObject(WarFoundryObject wfObject, UnitType limitedType)
-		{
-			return (limitedType.Equals(wfObject) || (wfObject is Unit && ((Unit)wfObject).UnitType.Equals(limitedType))) ? 1 : 0;
-		}
-
 		/// <summary>
 		/// Adds a requirement for there to be at least minCount of a given UnitType
 		/// </summary>
@@ -163,7 +131,7 @@
 		/// </param>
 		public void AddUnitTypeRequirement(UnitType unitType, int minCount)
 		{
-			requiredTypes.Add(new UnitCountRequirementData(unitType, minCount));
+			RequiredTypes.Add(new UnitCountRequirementData(unitType, minCount));
 		}
 
 		/// <summary>
@@ -172,7 +140,7 @@
 		/// <param name='unitType'>
 		/// The unit type to require.
 		/// </param>
-		public void AddUnitTypeRequirement(UnitType unitType)
+		public override void AddUnitTypeRequirement(UnitType unitType)
 		{
 			AddUnitTypeRequirement(unitType, 1);
 		}
@@ -190,7 +158,7 @@
 		{
 			Validation isValid = Validation.Passed;
 
-			foreach (UnitCountRequirementData requirement in requiredTypes)
+			foreach (UnitCountRequirementData requirement in RequiredTypes)
 			{
 				if (toValidate.GetUnitTypeCount(requirement.UnitType) < requirement.Count)
 				{
@@ -211,7 +179,7 @@
 		{
 			List<string> failures = new List<string>();
 
-			foreach (UnitCountRequirementData requirement in requiredTypes)
+			foreach (UnitCountRequirementData requirement in RequiredTypes)
 			{
 				int unitCount = army.GetUnitTypeCount(requirement.UnitType);
 
@@ -224,18 +192,18 @@
 			return failures;
 		}
 
-		protected override string GetFailedAddingRequirementsString(UnitType toAdd, Army toArmy)
+		protected override string GetFailedAddingRequirementsString(IWarFoundryObject toAdd, Army toArmy)
 		{
 			return String.Join("; ", GetFailedAddingRequirements(toAdd, toArmy).ToArray());
 		}
 
-		private List<string> GetFailedAddingRequirements(UnitType unitType, Army army)
+		private List<string> GetFailedAddingRequirements(IWarFoundryObject toAdd, Army army)
 		{
 			List<string> failures = new List<string>();
 
-			foreach (UnitCountRequirementData requirement in requiredTypes)
+			foreach (UnitCountRequirementData requirement in RequiredTypes)
 			{
-				int unitCount = GetUnitTypeCount(army, requirement.UnitType, unitType);
+				int unitCount = GetUnitTypeCount(army, requirement.UnitType, toAdd);
 
 				if (unitCount < requirement.Count)
 				{
@@ -245,6 +213,11 @@
 
 			return failures;
 		}
+
+		protected override int GetObjectCountFromArmy(Army toArmy)
+		{
+			return 0;
+		}
 	}
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/API/Objects/Requirement/RequiresNUnitsForMObjectsRequirement.cs	Wed Feb 22 20:45:39 2012 +0000
@@ -0,0 +1,281 @@
+// This file(RequiresNUnitsForMUnitsRequirement.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2011 IBBoard
+// 
+// The file and the library/program it is in are licensed and distributed, without warranty, under the GNU Affero GPL license, either version 3 of the License or(at your option) any later version. Please see COPYING for more information and the full license.
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace IBBoard.WarFoundry.API.Objects.Requirement
+{
+	public abstract class RequiresNUnitsForMObjectsRequirement<OBJECT_TYPE> : AbstractUnitRequirement<OBJECT_TYPE> where OBJECT_TYPE : IWarFoundryObject
+	{
+		public RequiresNUnitsForMObjectsRequirement(OBJECT_TYPE allowedObject, params UnitType[] requiredUnitTypes) : base(allowedObject, requiredUnitTypes)
+		{
+			//Do nothing
+		}
+
+		public override int GetHashCode()
+		{
+			int hash = 0;
+
+			foreach (UnitCountRequirementData req in RequiredTypes)
+			{
+				hash += req.UnitType.GetHashCode();
+			}
+
+			return hash;
+		}
+
+		protected override bool TypeEquals(object obj)
+		{
+			RequiresNUnitsForMObjectsRequirement<OBJECT_TYPE> otherReq = (RequiresNUnitsForMObjectsRequirement<OBJECT_TYPE>)obj;
+			if (!Collections.Collections.AreEqual(RequiredTypes, otherReq.RequiredTypes))
+			{
+				return false;
+			}
+			else
+			{
+				return true;
+			}
+		}
+
+		protected override string GetFailedAddingRequirementsString(IWarFoundryObject toAdd, Army toArmy)
+		{
+			return String.Join("; ", GetFailedAddingRequirements(toAdd, toArmy).ToArray());
+		}
+
+		private List<string> GetFailedAddingRequirements(IWarFoundryObject obj, Army toArmy)
+		{
+			List<string> failures = new List<string>();
+			int allowedObjectCount = GetAllowedObjectCount(toArmy, AllowedObject, obj);
+
+			foreach (UnitCountRequirementData limit in RequiredTypes)
+			{
+				int limitedTypeCount = GetUnitTypesCount(toArmy, limit.UnitTypes, obj);
+
+				if (!IsValidByRequirement(obj, toArmy, limit, allowedObjectCount))
+				{
+					string unitTypeList = GetUnitTypeList(limit);
+					failures.Add(String.Format("{0} × {1} for every {2} × {3} (would have {4} for {5})", limit.Count, unitTypeList, limit.AllowsCount, AllowedObject.Name, limitedTypeCount, allowedObjectCount));
+				}
+			}
+
+			return failures;
+		}
+
+		private string GetUnitTypeList(UnitCountRequirementData limit)
+		{
+			List<String> namesList = new List<String>();
+
+			foreach (UnitType unitType in limit.UnitTypes)
+			{
+				namesList.Add(unitType.Name);
+			}
+
+			string[] names = namesList.ToArray();
+			return String.Join(" or ", names);
+		}
+
+		private bool IsValidByRequirement(IWarFoundryObject wfObject, Army toArmy, UnitCountRequirementData limit, int allowedTypeCount)
+		{
+			int limitedTypeCount = GetUnitTypesCount(toArmy, limit.UnitTypes, wfObject);
+			return IsValidByRequirement(limit, allowedTypeCount, limitedTypeCount);
+		}
+
+		private bool IsValidByRequirement(UnitCountRequirementData limit, int allowedTypeCount, int limitedTypeCount)
+		{
+			int wholeNumLimited = (limitedTypeCount / limit.Count);
+			double allowedRatio = (limit.AllowsCount / (limit.Count * 1.0));
+			return allowedRatio * wholeNumLimited >= allowedTypeCount;
+		}
+
+		public override Validation AllowsAdding(IWarFoundryObject wfObject, Army toArmy)
+		{
+			Validation canAdd = Validation.NotApplicable;
+			UnitType addedUnitType = (wfObject is Unit) ? ((Unit)wfObject).UnitType : wfObject as UnitType;
+			bool typeFound = (wfObject == (IWarFoundryObject)AllowedObject);
+			int allowedTypeCount = GetAllowedObjectCount(toArmy, AllowedObject, wfObject);
+
+			foreach (UnitCountRequirementData limit in RequiredTypes)
+			{
+				typeFound |= (addedUnitType == limit.UnitType);
+
+				if (!IsValidByRequirement(wfObject, toArmy, limit, allowedTypeCount))
+				{
+					canAdd = Validation.Failed;
+					break;
+				}
+			}
+
+			if (typeFound && canAdd == Validation.NotApplicable)
+			{
+				canAdd = Validation.Passed;
+			}
+
+			return canAdd;
+		}
+
+		private int GetUnitTypesCount(Army toArmy, UnitType[] unitTypes, IWarFoundryObject wfObject)
+		{
+			int count = 0;
+
+			foreach (UnitType unitType in unitTypes)
+			{
+				count += GetUnitTypeCount(toArmy, unitType, wfObject);
+			}
+
+			return count;
+		}
+
+		private int GetAllowedObjectCount(Army toArmy, OBJECT_TYPE obj, IWarFoundryObject wfObject)
+		{
+			return GetObjectCountFromArmy(toArmy, obj) + GetCountFromObject(wfObject, obj);
+		}
+
+		protected abstract int GetObjectCountFromArmy(Army toArmy, OBJECT_TYPE obj);
+
+		private int GetCountFromObject(IWarFoundryObject wfObject, OBJECT_TYPE limitedType)
+		{
+			return (limitedType.Equals(wfObject) || (wfObject is Unit && ((Unit)wfObject).UnitType.Equals(limitedType))) ? 1 : 0;
+		}
+
+		public override Validation ValidatesArmy(Army army)
+		{
+			Validation canAdd = Validation.NotApplicable;
+			int allowedTypeCount = GetObjectCountFromArmy(army, AllowedObject);
+			bool typeFound = (allowedTypeCount > 0);
+
+			foreach (UnitCountRequirementData limit in RequiredTypes)
+			{
+				int limitedTypeCount = GetUnitTypesCount(army, limit.UnitTypes);
+				typeFound |= (limitedTypeCount > 0);
+
+				if (!IsValidByRequirement(limit, allowedTypeCount, limitedTypeCount))
+				{
+					canAdd = Validation.Failed;
+					break;
+				}
+			}
+
+			if (typeFound && canAdd == Validation.NotApplicable)
+			{
+				canAdd = Validation.Passed;
+			}
+
+			return canAdd;
+		}
+
+		protected override string GetFailedRequirementsString(Army army)
+		{
+			return String.Join("; ", GetFailedValidationRequirements(army).ToArray());
+		}
+
+		private List<string> GetFailedValidationRequirements(Army army)
+		{
+			List<string> failures = new List<string>();
+			int allowedTypeCount = GetAllowedObjectCount(army);
+
+			foreach (UnitCountRequirementData limit in RequiredTypes)
+			{
+				int limitedTypeCount = GetUnitTypesCount(army, limit.UnitTypes);
+
+				if (!IsValidByRequirement(limit, allowedTypeCount, limitedTypeCount))
+				{
+					string unitTypeList = GetUnitTypeList(limit);
+					failures.Add(String.Format("{0} × {1} for every {2} × {3} (have {4} for {5})", limit.Count, unitTypeList, limit.AllowsCount, AllowedObject.Name, limitedTypeCount, allowedTypeCount));
+				}
+			}
+
+			return failures;
+		}
+
+		protected abstract int GetAllowedObjectCount(Army army);
+
+		private int GetUnitTypesCount(Army army, UnitType[] unitTypes)
+		{
+			return GetUnitTypesCount(army, unitTypes, null);
+		}
+
+		/// <summary>
+		/// Adds a requirement for there to be at least minCount of a given UnitType, allowing allowedCount of this UnitType
+		/// </summary>
+		/// <param name='unitType'>
+		/// The unit type to require.
+		/// </param>
+		/// <param name='minCount'>
+		/// The minimum number of that type that must exist.
+		/// </param>
+		/// <param name='allowedCount'>
+		/// The number of units allowed for every minCount units of the supplied unit type.
+		/// </param>
+		public void AddUnitTypeRequirement(UnitType unitType, int minCount, int allowedCount)
+		{
+			RequiredTypes.Add(new UnitCountRequirementData(unitType, minCount, allowedCount));
+		}
+
+		/// <summary>
+		/// Adds a requirement for there to be at least one of a given UnitType, allowing allowedCount of this UnitType
+		/// </summary>
+		/// <param name='unitType'>
+		/// The unit type to require.
+		/// </param>
+		/// <param name='allowedCount'>
+		/// The number of units allowed for each unit of the supplied unit type.
+		/// </param>
+		public void AddUnitTypeRequirement(UnitType unitType, int allowedCount)
+		{
+			AddUnitTypeRequirement(allowedCount, unitType);
+		}
+
+		public override void AddUnitTypeRequirement(UnitType unitType)
+		{
+			AddUnitTypeRequirement(1, unitType);
+		}
+
+		/// <summary>
+		/// Adds a requirement for there to be one or more of the given UnitTypes, allowing one of this UnitType. If multiple unit types
+		/// are supplied here then the number is additive (so 1 x unitType1 and 1 x unitType2 allows two of this UnitType).
+		/// </summary>
+		/// <param name='unitType'>
+		/// The unit type or types to require.
+		/// </param>
+		public void AddUnitTypeRequirement(params UnitType[] unitTypes)
+		{
+			AddUnitTypeRequirement(1, unitTypes);
+		}
+
+		/// <summary>
+		/// Adds a requirement for there to be one or more of the given UnitTypes, allowing allowsAmount of this UnitType. If multiple unit types
+		/// are supplied here then the number is additive (so 1 x unitType1 and 1 x unitType2 allows two of this UnitType).
+		/// </summary>
+		/// <param name="allowsAmount">
+		/// The number of units to be allowed for each 1 of unitType
+		/// </param>
+		/// <param name='unitType'>
+		/// The unit type or types to require.
+		/// </param>
+		public void AddUnitTypeRequirement(int allowsAmount, params UnitType[] unitTypes)
+		{
+			RequiredTypes.Add(new UnitCountRequirementData(unitTypes, 1, allowsAmount));
+		}
+
+		/// <summary>
+		/// Adds a requirement for there to be minCount or more of the given UnitTypes, allowing allowsAmount of this UnitType. If multiple unit types
+		/// are supplied here then the number is additive (so 1 x unitType1 and 1 x unitType2 allows two of this UnitType).
+		/// </summary>
+		/// <param name='minCount'>
+		/// The minimum number of that type that must exist.
+		/// </param>
+		/// <param name="allowsAmount">
+		/// The number of units to be allowed for each 1 of unitType
+		/// </param>
+		/// <param name='unitType'>
+		/// The unit type or types to require.
+		/// </param>
+		public void AddUnitTypeRequirement(int minCount, int allowsAmount, params UnitType[] unitTypes)
+		{
+			RequiredTypes.Add(new UnitCountRequirementData(unitTypes, minCount, allowsAmount));
+		}
+	}
+}
+
--- a/API/Objects/Requirement/RequiresNUnitsForMUnitsRequirement.cs	Tue Jan 31 20:58:09 2012 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,290 +0,0 @@
-// This file(RequiresNUnitsForMUnitsRequirement.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2011 IBBoard
-// 
-// The file and the library/program it is in are licensed and distributed, without warranty, under the GNU Affero GPL license, either version 3 of the License or(at your option) any later version. Please see COPYING for more information and the full license.
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace IBBoard.WarFoundry.API.Objects.Requirement
-{
-	public class RequiresNUnitsForMUnitsRequirement : AbstractRequirement
-	{
-		public static readonly string REQUIREMENT_ID = "RequiresNUnitsForMUnits";
-		private List<UnitCountRequirementData> requiredTypes;
-		private UnitType allowedType;
-
-		public RequiresNUnitsForMUnitsRequirement(UnitType allowedType, params UnitType[] requiredUnitTypes)
-		{
-			this.allowedType = allowedType;
-			requiredTypes = new List<UnitCountRequirementData>();
-
-			foreach (UnitType unitType in requiredUnitTypes)
-			{
-				AddUnitTypeRequirement(unitType);
-			}
-		}
-
-		public override int GetHashCode()
-		{
-			int hash = 0;
-
-			foreach (UnitCountRequirementData req in requiredTypes)
-			{
-				hash += req.UnitType.GetHashCode();
-			}
-
-			return hash;
-		}
-
-		protected override bool TypeEquals(object obj)
-		{
-			RequiresNUnitsForMUnitsRequirement otherReq = (RequiresNUnitsForMUnitsRequirement)obj;
-			if (!Collections.Collections.AreEqual(requiredTypes, otherReq.requiredTypes))
-			{
-				return false;
-			}
-			else
-			{
-				return true;
-			}
-		}
-
-		protected override string GetFailedAddingRequirementsString(UnitType toAdd, Army toArmy)
-		{
-			return String.Join("; ", GetFailedAddingRequirements(toAdd, toArmy).ToArray());
-		}
-
-		private List<string> GetFailedAddingRequirements(UnitType unitType, Army toArmy)
-		{
-			List<string> failures = new List<string>();
-			int allowedTypeCount = GetUnitTypeCount(toArmy, allowedType, unitType);
-
-			foreach (UnitCountRequirementData limit in requiredTypes)
-			{
-				int limitedTypeCount = GetUnitTypesCount(toArmy, limit.UnitTypes, unitType);
-
-				if (!IsValidByRequirement(unitType, toArmy, limit, allowedTypeCount))
-				{
-					string unitTypeList = GetUnitTypeList(limit);
-					failures.Add(String.Format("{0} × {1} for every {2} × {3} (would have {4} for {5})", limit.Count, unitTypeList, limit.AllowsCount, allowedType.Name, limitedTypeCount, allowedTypeCount));
-				}
-			}
-
-			return failures;
-		}
-
-		private string GetUnitTypeList(UnitCountRequirementData limit)
-		{
-			List<String> namesList = new List<String>();
-
-			foreach (UnitType unitType in limit.UnitTypes)
-			{
-				namesList.Add(unitType.Name);
-			}
-
-			string[] names = namesList.ToArray();
-			return String.Join(" or ", names);
-		}
-
-		private bool IsValidByRequirement(WarFoundryObject wfObject, Army toArmy, UnitCountRequirementData limit, int allowedTypeCount)
-		{
-			int limitedTypeCount = GetUnitTypesCount(toArmy, limit.UnitTypes, wfObject);
-			return IsValidByRequirement(limit, allowedTypeCount, limitedTypeCount);
-		}
-
-		private bool IsValidByRequirement(UnitCountRequirementData limit, int allowedTypeCount, int limitedTypeCount)
-		{
-			int wholeNumLimited = (limitedTypeCount / limit.Count);
-			double allowedRatio = (limit.AllowsCount / (limit.Count * 1.0));
-			return allowedRatio * wholeNumLimited >= allowedTypeCount;
-		}
-
-		public override Validation AllowsAdding(WarFoundryObject wfObject, Army toArmy)
-		{
-			Validation canAdd = Validation.NotApplicable;
-			UnitType addedUnitType = (wfObject is Unit) ? ((Unit)wfObject).UnitType : wfObject as UnitType;
-			bool typeFound = (addedUnitType == allowedType);
-			int allowedTypeCount = GetUnitTypeCount(toArmy, allowedType, wfObject);
-
-			foreach (UnitCountRequirementData limit in requiredTypes)
-			{
-				typeFound |= (addedUnitType == limit.UnitType);
-
-				if (!IsValidByRequirement(wfObject, toArmy, limit, allowedTypeCount))
-				{
-					canAdd = Validation.Failed;
-					break;
-				}
-			}
-
-			if (typeFound && canAdd == Validation.NotApplicable)
-			{
-				canAdd = Validation.Passed;
-			}
-
-			return canAdd;
-		}
-
-		private int GetUnitTypesCount(Army toArmy, UnitType[] unitTypes, WarFoundryObject wfObject)
-		{
-			int count = 0;
-
-			foreach (UnitType unitType in unitTypes)
-			{
-				count += GetUnitTypeCount(toArmy, unitType, wfObject);
-			}
-
-			return count;
-		}
-
-		private int GetUnitTypeCount(Army toArmy, UnitType unitType, WarFoundryObject wfObject)
-		{
-			return toArmy.GetUnitTypeCount(unitType) + GetCountFromObject(wfObject, unitType);
-		}
-
-		private int GetCountFromObject(WarFoundryObject wfObject, UnitType limitedType)
-		{
-			return (limitedType.Equals(wfObject) || (wfObject is Unit && ((Unit)wfObject).UnitType.Equals(limitedType))) ? 1 : 0;
-		}
-
-		public override Validation ValidatesArmy(Army army)
-		{
-			Validation canAdd = Validation.NotApplicable;
-			int allowedTypeCount = army.GetUnitTypeCount(allowedType);
-			bool typeFound = (allowedTypeCount > 0);
-
-			foreach (UnitCountRequirementData limit in requiredTypes)
-			{
-				int limitedTypeCount = GetUnitTypesCount(army, limit.UnitTypes);
-				typeFound |= (limitedTypeCount > 0);
-
-				if (!IsValidByRequirement(limit, allowedTypeCount, limitedTypeCount))
-				{
-					canAdd = Validation.Failed;
-					break;
-				}
-			}
-
-			if (typeFound && canAdd == Validation.NotApplicable)
-			{
-				canAdd = Validation.Passed;
-			}
-
-			return canAdd;
-		}
-
-		protected override string GetFailedRequirementsString(Army army)
-		{
-			return String.Join("; ", GetFailedValidationRequirements(army).ToArray());
-		}
-
-		private List<string> GetFailedValidationRequirements(Army army)
-		{
-			List<string> failures = new List<string>();
-			int allowedTypeCount = army.GetUnitTypeCount(allowedType);
-
-			foreach (UnitCountRequirementData limit in requiredTypes)
-			{
-				int limitedTypeCount = GetUnitTypesCount(army, limit.UnitTypes);
-
-				if (!IsValidByRequirement(limit, allowedTypeCount, limitedTypeCount))
-				{
-					string unitTypeList = GetUnitTypeList(limit);
-					failures.Add(String.Format("{0} × {1} for every {2} × {3} (have {4} for {5})", limit.Count, unitTypeList, limit.AllowsCount, allowedType.Name, limitedTypeCount, allowedTypeCount));
-				}
-			}
-
-			return failures;
-		}
-
-		private int GetUnitTypesCount(Army army, UnitType[] unitTypes)
-		{
-			return GetUnitTypesCount(army, unitTypes, null);
-		}
-
-		public override string RequirementID
-		{
-			get
-			{
-				return REQUIREMENT_ID;
-			}
-		}
-
-		/// <summary>
-		/// Adds a requirement for there to be at least minCount of a given UnitType, allowing allowedCount of this UnitType
-		/// </summary>
-		/// <param name='unitType'>
-		/// The unit type to require.
-		/// </param>
-		/// <param name='minCount'>
-		/// The minimum number of that type that must exist.
-		/// </param>
-		/// <param name='allowedCount'>
-		/// The number of units allowed for every minCount units of the supplied unit type.
-		/// </param>
-		public void AddUnitTypeRequirement(UnitType unitType, int minCount, int allowedCount)
-		{
-			requiredTypes.Add(new UnitCountRequirementData(unitType, minCount, allowedCount));
-		}
-
-		/// <summary>
-		/// Adds a requirement for there to be at least one of a given UnitType, allowing allowedCount of this UnitType
-		/// </summary>
-		/// <param name='unitType'>
-		/// The unit type to require.
-		/// </param>
-		/// <param name='allowedCount'>
-		/// The number of units allowed for each unit of the supplied unit type.
-		/// </param>
-		public void AddUnitTypeRequirement(UnitType unitType, int allowedCount)
-		{
-			AddUnitTypeRequirement(allowedCount, unitType);
-		}
-
-		/// <summary>
-		/// Adds a requirement for there to be one or more of the given UnitTypes, allowing one of this UnitType. If multiple unit types
-		/// are supplied here then the number is additive (so 1 x unitType1 and 1 x unitType2 allows two of this UnitType).
-		/// </summary>
-		/// <param name='unitType'>
-		/// The unit type or types to require.
-		/// </param>
-		public void AddUnitTypeRequirement(params UnitType[] unitTypes)
-		{
-			AddUnitTypeRequirement(1, unitTypes);
-		}
-
-		/// <summary>
-		/// Adds a requirement for there to be one or more of the given UnitTypes, allowing allowsAmount of this UnitType. If multiple unit types
-		/// are supplied here then the number is additive (so 1 x unitType1 and 1 x unitType2 allows two of this UnitType).
-		/// </summary>
-		/// <param name="allowsAmount">
-		/// The number of units to be allowed for each 1 of unitType
-		/// </param>
-		/// <param name='unitType'>
-		/// The unit type or types to require.
-		/// </param>
-		public void AddUnitTypeRequirement(int allowsAmount, params UnitType[] unitTypes)
-		{
-			requiredTypes.Add(new UnitCountRequirementData(unitTypes, 1, allowsAmount));
-		}
-
-		/// <summary>
-		/// Adds a requirement for there to be minCount or more of the given UnitTypes, allowing allowsAmount of this UnitType. If multiple unit types
-		/// are supplied here then the number is additive (so 1 x unitType1 and 1 x unitType2 allows two of this UnitType).
-		/// </summary>
-		/// <param name='minCount'>
-		/// The minimum number of that type that must exist.
-		/// </param>
-		/// <param name="allowsAmount">
-		/// The number of units to be allowed for each 1 of unitType
-		/// </param>
-		/// <param name='unitType'>
-		/// The unit type or types to require.
-		/// </param>
-		public void AddUnitTypeRequirement(int minCount, int allowsAmount, params UnitType[] unitTypes)
-		{
-			requiredTypes.Add(new UnitCountRequirementData(unitTypes, minCount, allowsAmount));
-		}
-	}
-}
-
--- a/API/Objects/Requirement/RequiresNoMoreThanNOfUnitTypeRequirement.cs	Tue Jan 31 20:58:09 2012 +0000
+++ b/API/Objects/Requirement/RequiresNoMoreThanNOfUnitTypeRequirement.cs	Wed Feb 22 20:45:39 2012 +0000
@@ -11,19 +11,14 @@
 	/// <summary>
 	/// A requirement where a WarFoundryObject cannot be taken in an army if more than N of a UnitType will be in the army.
 	/// </summary>
-	public class RequiresNoMoreThanNOfUnitTypeRequirement : AbstractRequirement
+	public class RequiresNoMoreThanNOfUnitTypeRequirement<OBJECT_TYPE> : AbstractUnitRequirement<OBJECT_TYPE> where OBJECT_TYPE : IWarFoundryObject
 	{
 		public static readonly string REQUIREMENT_ID = "RequiresNoMoreThanNUnits";
 		private List<UnitCountRequirementData> limitedTypes;
 
-		public RequiresNoMoreThanNOfUnitTypeRequirement(params UnitType[] limitedUnitTypes)
+		public RequiresNoMoreThanNOfUnitTypeRequirement(params UnitType[] limitedUnitTypes) : base(default(OBJECT_TYPE), limitedUnitTypes)
 		{
 			limitedTypes = new List<UnitCountRequirementData>();
-
-			foreach (UnitType unitType in limitedUnitTypes)
-			{
-				AddUnitTypeRequirement(unitType);
-			}
 		}
 
 		public override string RequirementID
@@ -46,7 +41,7 @@
 		/// <param name='toArmy'>
 		/// The army to add the object to.
 		/// </param>
-		public override Validation AllowsAdding(WarFoundryObject wfObject, Army toArmy)
+		public override Validation AllowsAdding(IWarFoundryObject wfObject, Army toArmy)
 		{
 			Validation canAdd = Validation.Passed;
 			
@@ -92,7 +87,7 @@
 		/// <param name='unitType'>
 		/// The unit type to limit.
 		/// </param>
-		public void AddUnitTypeRequirement(UnitType unitType)
+		public override void AddUnitTypeRequirement(UnitType unitType)
 		{
 			AddUnitTypeRequirement(unitType, 0);
 		}
@@ -124,7 +119,7 @@
 
 		protected override bool TypeEquals(object obj)
 		{
-			RequiresNoMoreThanNOfUnitTypeRequirement other = (RequiresNoMoreThanNOfUnitTypeRequirement)obj;
+			RequiresNoMoreThanNOfUnitTypeRequirement<OBJECT_TYPE> other = (RequiresNoMoreThanNOfUnitTypeRequirement<OBJECT_TYPE>)obj;
 			return Collections.Collections.AreEqual(limitedTypes, other.limitedTypes);
 		}
 
@@ -170,18 +165,18 @@
 			return failures;
 		}
 
-		protected override string GetFailedAddingRequirementsString(UnitType toAdd, Army toArmy)
+		protected override string GetFailedAddingRequirementsString(IWarFoundryObject toAdd, Army toArmy)
 		{
 			return String.Join("; ", GetFailedAddingRequirements(toAdd, toArmy).ToArray());
 		}
 
-		private List<string> GetFailedAddingRequirements(UnitType unitType, Army army)
+		private List<string> GetFailedAddingRequirements(IWarFoundryObject toAdd, Army army)
 		{
 			List<string> failures = new List<string>();
 
 			foreach (UnitCountRequirementData requirement in limitedTypes)
 			{
-				int unitCount = GetUnitTypeCount(army, requirement.UnitType, unitType);
+				int unitCount = GetUnitTypeCount(army, requirement.UnitType, toAdd);
 
 				if (unitCount > requirement.Count)
 				{
@@ -191,6 +186,11 @@
 
 			return failures;
 		}
+
+		protected override int GetObjectCountFromArmy(Army toArmy)
+		{
+			return 0;
+		}
 	}
 }
 
--- a/API/Objects/Requirement/UnitRequiresAtLeastNUnitsRequirement.cs	Tue Jan 31 20:58:09 2012 +0000
+++ b/API/Objects/Requirement/UnitRequiresAtLeastNUnitsRequirement.cs	Wed Feb 22 20:45:39 2012 +0000
@@ -10,28 +10,25 @@
 	/// <summary>
 	/// A requirement where a UnitType requires at least N units of one or more unit types before any number of that object can be taken in an army.
 	/// </summary>
-	public class UnitRequiresAtLeastNUnitsRequirement : RequiresAtLeastNUnitsRequirement
+	public class UnitRequiresAtLeastNUnitsRequirement : RequiresAtLeastNUnitsRequirement<UnitType>
 	{
-		private UnitType requirementOnType;
-
-		public UnitRequiresAtLeastNUnitsRequirement(UnitType requirementOn) : base()
+		public UnitRequiresAtLeastNUnitsRequirement(UnitType requirementOn) : base(requirementOn)
 		{
-			requirementOnType = requirementOn;
 		}
 
-		protected override bool IsApplicable(WarFoundryObject toObjectAdded)
+		protected override bool IsApplicable(IWarFoundryObject toObjectAdded)
 		{
 			return base.IsApplicable(toObjectAdded) || IsRequirementOnType(toObjectAdded);
 		}
 
-		private bool IsRequirementOnType(WarFoundryObject toObjectAdded)
+		private bool IsRequirementOnType(IWarFoundryObject toObjectAdded)
 		{
-			return requirementOnType.Equals(toObjectAdded) || (toObjectAdded is Unit && requirementOnType.Equals(((Unit)toObjectAdded).UnitType));
+			return AllowedObject.Equals(toObjectAdded) || (toObjectAdded is Unit && AllowedObject.Equals(((Unit)toObjectAdded).UnitType));
 		}
 
 		protected override bool IsApplicable(Army toArmy)
 		{
-			return toArmy.GetUnitTypeCount(requirementOnType) > 0;
+			return toArmy.GetUnitTypeCount(AllowedObject) > 0;
 		}
 
 		public override Validation ValidatesArmy(Army toArmy)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/API/Objects/Requirement/UnitRequiresNUnitsForMUnitsRequirement.cs	Wed Feb 22 20:45:39 2012 +0000
@@ -0,0 +1,79 @@
+// This file (UnitRequiresAtLeastNUnitsRequirement.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2011 IBBoard
+// 
+// The file and the library/program it is in are licensed and distributed, without warranty, under the GNU Affero GPL license, either version 3 of the License or (at your option) any later version. Please see COPYING for more information and the full license.
+using System;
+using System.Collections.Generic;
+using IBBoard.WarFoundry.API.Objects;
+
+namespace IBBoard.WarFoundry.API.Objects.Requirement
+{
+	/// <summary>
+	/// A requirement where N of a UnitType can only be taken if there are M of a unit type in the army.
+	/// </summary>
+	public class UnitRequiresNUnitsForMUnitsRequirement : RequiresNUnitsForMObjectsRequirement<UnitType>
+	{
+		//Note: We're sticking with the old requirement name to prevent breakage
+		public static readonly string REQUIREMENT_ID = "RequiresNUnitsForMUnits";
+
+		public UnitRequiresNUnitsForMUnitsRequirement(UnitType requirementOn, params UnitType[] requiredUnitTypes) : base(requirementOn, requiredUnitTypes)
+		{
+			//Do nothing
+		}
+
+		/// <summary>
+		/// Checks whether the supplied WarFoundryObject can be added to the supplied army.
+		/// </summary>
+		/// <returns>
+		/// A <code>Validation</code> enum to show the result of the validation
+		/// </returns>
+		/// <param name='wfObject'>
+		/// The object that we want to add. This may be involved in the check, or it may not affect the evaluation of the requirement
+		/// </param>
+		/// <param name='toArmy'>
+		/// The army to add the object to.
+		/// </param>
+		public override Validation AllowsAdding(IWarFoundryObject wfObject, Army toArmy)
+		{
+			return IsApplicable(wfObject, toArmy) ? base.AllowsAdding(wfObject, toArmy) : Validation.NotApplicable;
+		}
+
+		protected override bool IsApplicable(IWarFoundryObject toObject, Army toArmy)
+		{
+			return IsApplicable(toArmy) || IsApplicable(toObject);
+		}
+
+		protected override bool IsApplicable(Army toArmy)
+		{
+			return toArmy.GetUnitTypeCount(AllowedObject) > 0;
+		}
+
+		protected override bool IsApplicable(IWarFoundryObject toObject)
+		{
+			return AllowedObject.Equals(toObject) || (toObject is Unit && AllowedObject.Equals(((Unit)toObject).UnitType));
+		}
+
+		public override string RequirementID
+		{
+			get
+			{
+				return REQUIREMENT_ID;
+			}
+		}
+
+		protected override int GetObjectCountFromArmy(Army toArmy)
+		{
+			return GetObjectCountFromArmy(toArmy, AllowedObject);
+		}
+
+		protected override int GetObjectCountFromArmy(Army toArmy, UnitType unitType)
+		{
+			return toArmy.GetUnitTypeCount(unitType);
+		}
+
+		protected override int GetAllowedObjectCount(Army army)
+		{
+			return army.GetUnitTypeCount(AllowedObject);
+		}
+	}
+}
+
--- a/API/Objects/Requirement/UnitRequiresNoMoreThanNOfUnitTypeRequirement.cs	Tue Jan 31 20:58:09 2012 +0000
+++ b/API/Objects/Requirement/UnitRequiresNoMoreThanNOfUnitTypeRequirement.cs	Wed Feb 22 20:45:39 2012 +0000
@@ -10,7 +10,7 @@
 	/// <summary>
 	/// A requirement where a UnitType can only be taken if there are no more than N units of one or more unit in an army.
 	/// </summary>
-	public class UnitRequiresNoMoreThanNOfUnitTypeRequirement : RequiresNoMoreThanNOfUnitTypeRequirement
+	public class UnitRequiresNoMoreThanNOfUnitTypeRequirement : RequiresNoMoreThanNOfUnitTypeRequirement<UnitType>
 	{
 		private UnitType requirementOnType;
 
@@ -31,12 +31,12 @@
 		/// <param name='toArmy'>
 		/// The army to add the object to.
 		/// </param>
-		public override Validation AllowsAdding(WarFoundryObject wfObject, Army toArmy)
+		public override Validation AllowsAdding(IWarFoundryObject wfObject, Army toArmy)
 		{
 			return IsApplicable(wfObject, toArmy) ? base.AllowsAdding(wfObject, toArmy) : Validation.NotApplicable;
 		}
 
-		protected override bool IsApplicable(WarFoundryObject toObject, Army toArmy)
+		protected override bool IsApplicable(IWarFoundryObject toObject, Army toArmy)
 		{
 			return IsApplicable(toArmy) || IsApplicable(toObject);
 		}
@@ -46,7 +46,7 @@
 			return toArmy.GetUnitTypeCount(requirementOnType) > 0;
 		}
 
-		protected override bool IsApplicable(WarFoundryObject toObject)
+		protected override bool IsApplicable(IWarFoundryObject toObject)
 		{
 			return requirementOnType.Equals(toObject) || (toObject is Unit && requirementOnType.Equals(((Unit)toObject).UnitType));
 		}
--- a/API/Objects/UnitType.cs	Tue Jan 31 20:58:09 2012 +0000
+++ b/API/Objects/UnitType.cs	Wed Feb 22 20:45:39 2012 +0000
@@ -1,7 +1,6 @@
 // This file (UnitType.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
 //
 // The file and the library/program it is in are licensed and distributed, without warranty, under the GNU Affero GPL license, either version 3 of the License or (at your option) any later version. Please see COPYING for more information and the full license.
-
 using System;
 using System.Collections.Generic;
 using System.Xml;
@@ -37,14 +36,13 @@
 		private Dictionary<string, ILimit> slotLimits = new Dictionary<string, ILimit>();
 		private Dictionary<string, UnitMemberType> unitMemberTypes = new Dictionary<string, UnitMemberType>();
 		private List<Category> cats = new List<Category>();
-			
 
 		public UnitType(string id, string typeName, Race parentRace) : base(id, typeName)
 		{
 			race = parentRace;
 		}
 
-		public override bool Equals (object obj)
+		public override bool Equals(object obj)
 		{
 			if (obj == null)
 			{
@@ -190,7 +188,7 @@
 		/// </summary>
 		private void CheckMinimumNumber()
 		{
-			if (MinNumber > MaxNumber && MaxNumber!=WarFoundryCore.INFINITY)
+			if (MinNumber > MaxNumber && MaxNumber != WarFoundryCore.INFINITY)
 			{
 				MinNumber = MaxNumber;
 				LogNotifier.WarnFormat(GetType(), "Unit type {0} ({1}) had a minimum number greater than their maximum number.", Name, ID);
@@ -202,7 +200,7 @@
 		/// </summary>
 		private void CheckMinimumSize()
 		{
-			if (MinSize > MaxSize && MaxSize!=WarFoundryCore.INFINITY)
+			if (MinSize > MaxSize && MaxSize != WarFoundryCore.INFINITY)
 			{
 				MinSize = MaxSize;
 				LogNotifier.WarnFormat(GetType(), "Unit type {0} ({1}) had a minimum size greater than their maximum size.", Name, ID);
@@ -238,7 +236,7 @@
 
 		protected override string DefaultName()
 		{
-			throw new InvalidOperationException("Unit type with id "+id+" did not have a name specified");
+			throw new InvalidOperationException("Unit type with id " + id + " did not have a name specified");
 		}
 		
 		/// <value>
@@ -279,7 +277,7 @@
 		
 		public string[] UnitStatsArrayIDs
 		{
-			get 
+			get
 			{
 				string[] ids;
 				
@@ -345,7 +343,7 @@
 		
 		public Stat[] ExtendStatsArrayWithName(Stat[] statsArray)
 		{
-			Stat[] extendedStats = new Stat[statsArray.Length+1];
+			Stat[] extendedStats = new Stat[statsArray.Length + 1];
 			extendedStats[0] = new Stat(new StatSlot("name"), Name);
 			statsArray.CopyTo(extendedStats, 1);
 			return extendedStats;
@@ -500,16 +498,16 @@
 
 			if (MinNumber != 0)
 			{
-				RequiresAtLeastNUnitsRequirement req = new RequiresAtLeastNUnitsRequirement();
-				req.AddUnitTypeRequirement(this, MinNumber);
-				reqs.Add(req);
+				//RequiresAtLeastNUnitsRequirement<Army> req = new RequiresAtLeastNUnitsRequirement<Army>();
+				//req.AddUnitTypeRequirement(this, MinNumber);
+				//reqs.Add(req);
 			}
 
 			if (MaxNumber != WarFoundryCore.INFINITY)
 			{
-				RequiresNoMoreThanNOfUnitTypeRequirement req = new RequiresNoMoreThanNOfUnitTypeRequirement();
-				req.AddUnitTypeRequirement(this, MaxNumber);
-				reqs.Add(req);
+				//RequiresNoMoreThanNOfUnitTypeRequirement<IWarFoundryObject> req = new RequiresNoMoreThanNOfUnitTypeRequirement<IWarFoundryObject>();
+				//req.AddUnitTypeRequirement(this, MaxNumber);
+				//reqs.Add(req);
 			}
 
 			return reqs.ToArray();
--- a/IBBoard.WarFoundry.API.csproj	Tue Jan 31 20:58:09 2012 +0000
+++ b/IBBoard.WarFoundry.API.csproj	Wed Feb 22 20:45:39 2012 +0000
@@ -176,7 +176,6 @@
     <Compile Include="API\Objects\Requirement\UnitRequiresNoMoreThanNOfUnitTypeRequirement.cs" />
     <Compile Include="API\Objects\Requirement\IRequirement.cs" />
     <Compile Include="API\Objects\Requirement\RequirementHandler.cs" />
-    <Compile Include="API\Objects\Requirement\AbstractRequirement.cs" />
     <Compile Include="API\Factories\Requirement\UnitRequiresAtLeastNUnitsRequirementFactory.cs" />
     <Compile Include="API\Factories\Requirement\InvalidRequirementException.cs" />
     <Compile Include="API\Factories\Xml\CategoryLoader.cs" />
@@ -191,8 +190,10 @@
     <None Include="data\SampleSystem.system">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>
-    <Compile Include="API\Objects\Requirement\RequiresNUnitsForMUnitsRequirement.cs" />
     <Compile Include="API\Factories\Requirement\UnitRequiresNUnitsForMUnitsRequirementFactory.cs" />
+    <Compile Include="API\Objects\Requirement\UnitRequiresNUnitsForMUnitsRequirement.cs" />
+    <Compile Include="API\Objects\Requirement\RequiresNUnitsForMObjectsRequirement.cs" />
+    <Compile Include="API\Objects\Requirement\AbstractUnitRequirement.cs" />
   </ItemGroup>
   <ItemGroup>
     <Reference Include="System.Xml" />