# HG changeset patch # User IBBoard # Date 1343417472 -3600 # Node ID 248df19652f95cc30d800bd276c21804545ad019 # Parent 6e5b39caeb4e7ca89aa166aa9e211c22641ae5ad Re #410: Create "N units per M models in parent unit" requirement * Add null adding context * Add initial skeleton of "N units per M models in parent unit" requirement * Update use of context * Standardise some of "is applicable" testing diff -r 6e5b39caeb4e -r 248df19652f9 API/Objects/Requirement/AbstractUnitRequirement.cs --- a/API/Objects/Requirement/AbstractUnitRequirement.cs Sun Jul 15 21:01:47 2012 +0100 +++ b/API/Objects/Requirement/AbstractUnitRequirement.cs Fri Jul 27 20:31:12 2012 +0100 @@ -74,21 +74,97 @@ return hash; } - protected virtual bool IsApplicable(IWarFoundryObject toObjectAdded, Army toArmy, AddingContext context) + /// + /// A wrapper method that supplies a default "null context" to determines whether this requirement is applicable to the specified object and/or the specified army + /// + /// + /// true if this instance is applicable to the specified toObjectAdded or toArmy (or both); otherwise, false. + /// + /// + /// The WarFoundry object being added + /// + /// + /// The army the object is being added to + /// + protected bool IsApplicable(IWarFoundryObject toObjectAdded, Army toArmy) { - return IsApplicable(toArmy, context) || IsApplicable(toObjectAdded, context); + return IsApplicable(toObjectAdded, toArmy, new NullAddingContext()); } + /// + /// Determines whether this requirement is independantly applicable to the specified object or the specified army. + /// + /// + /// true if this instance is applicable to the specified toObjectAdded or toArmy (or both); otherwise, false. + /// + /// + /// The WarFoundry object being added + /// + /// + /// The army the object is being added to + /// + /// + /// The context for adding the object + /// + protected bool IsApplicable(IWarFoundryObject toObjectAdded, Army toArmy, AddingContext context) + { + return IsApplicableToBoth(toObjectAdded, toArmy, context) || IsApplicable(toArmy, context) || IsApplicable(toObjectAdded, context); + } + + /// + /// Determines whether this requirement is applicable the specified army in the given context + /// + /// + /// true if this requirement is applicable to the specified army in the given context; otherwise, false. + /// + /// + /// The army that an object is being added to + /// + /// + /// The context for adding the object + /// protected virtual bool IsApplicable(Army toArmy, AddingContext context) { return false; } + /// + /// Determines whether this requirement is applicable the specified object in the given context. + /// + /// + /// true if this requirement is applicable the specified object in the context; otherwise, false. + /// + /// + /// The object being added to an army + /// + /// + /// The context for adding the object + /// protected virtual bool IsApplicable(IWarFoundryObject toObject, AddingContext context) { return false; } + /// + /// Determines whether this instance is applicable to both the specified object and army in the given context. + /// + /// + /// true if this instance is applicable to both the specified object and army in the given context; otherwise, false. + /// + /// + /// The object being added to an army + /// + /// + /// The army that an object is being added to + /// + /// + /// The context for adding the object + /// + protected virtual bool IsApplicableToBoth(IWarFoundryObject toObjectAdded, Army toArmy, AddingContext context) + { + return false; + } + public string GetValidationMessage(Army army) { string message = ""; @@ -113,12 +189,17 @@ public string GetAllowsAddingMessage(IWarFoundryObject toAdd, Army toArmy) { + return GetAllowsAddingMessage(toAdd, toArmy, new NullAddingContext()); + } + + public string GetAllowsAddingMessage(IWarFoundryObject toAdd, Army toArmy, AddingContext context) + { string message = ""; - Validation result = AllowsAdding(toAdd, toArmy); + Validation result = AllowsAdding(toAdd, toArmy, context); if (!Validates.AsOkay(result)) { - message = GetAllowsAddingFailedMessage(toAdd, toArmy); + message = GetAllowsAddingFailedMessage(toAdd, toArmy, context); } return message; @@ -126,12 +207,12 @@ protected virtual string AllowsAddingFailedMessage { get { return ValidationFailedMessage; } } - protected string GetAllowsAddingFailedMessage(IWarFoundryObject toAdd, Army toArmy) + protected string GetAllowsAddingFailedMessage(IWarFoundryObject toAdd, Army toArmy, AddingContext context) { - return String.Format(AllowsAddingFailedMessage, GetFailedAddingRequirementsString(toAdd, toArmy)); + return String.Format(AllowsAddingFailedMessage, GetFailedAddingRequirementsString(toAdd, toArmy, context)); } - protected abstract string GetFailedAddingRequirementsString(IWarFoundryObject toAdd, Army toArmy); + protected abstract string GetFailedAddingRequirementsString(IWarFoundryObject toAdd, Army toArmy, AddingContext context); /// /// Checks whether the supplied WarFoundryObject can be added to the supplied army. @@ -147,7 +228,7 @@ /// public Validation AllowsAdding(IWarFoundryObject wfObject, Army toArmy) { - return AllowsAdding(wfObject, toArmy, null); + return AllowsAdding(wfObject, toArmy, new NullAddingContext()); } /// @@ -243,6 +324,19 @@ string[] names = namesList.ToArray(); return String.Join(" or ", names); } + + protected bool IsUnitTypeLimited(UnitType unitType) + { + foreach (UnitCountRequirementData requirement in requiredTypes) + { + if (Arrays.Contains(requirement.UnitTypes, unitType)) + { + return true; + } + } + + return false; + } } } diff -r 6e5b39caeb4e -r 248df19652f9 API/Objects/Requirement/Context/NullAddingContext.cs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/API/Objects/Requirement/Context/NullAddingContext.cs Fri Jul 27 20:31:12 2012 +0100 @@ -0,0 +1,13 @@ +// This file (NullAddingContext.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2012 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.Context +{ + public class NullAddingContext : AddingContext + { + //Do nothing special + } +} + diff -r 6e5b39caeb4e -r 248df19652f9 API/Objects/Requirement/IRequirement.cs --- a/API/Objects/Requirement/IRequirement.cs Sun Jul 15 21:01:47 2012 +0100 +++ b/API/Objects/Requirement/IRequirement.cs Fri Jul 27 20:31:12 2012 +0100 @@ -2,6 +2,7 @@ // // 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 IBBoard.WarFoundry.API.Objects.Requirement.Context; namespace IBBoard.WarFoundry.API.Objects.Requirement { @@ -67,7 +68,10 @@ /// /// The army the object will be added to /// - string GetAllowsAddingMessage(IWarFoundryObject toAdd, Army toArmy); + /// + /// The context of the addition + /// + string GetAllowsAddingMessage(IWarFoundryObject toAdd, Army toArmy, AddingContext context); } } diff -r 6e5b39caeb4e -r 248df19652f9 API/Objects/Requirement/RequirementHandler.cs --- a/API/Objects/Requirement/RequirementHandler.cs Sun Jul 15 21:01:47 2012 +0100 +++ b/API/Objects/Requirement/RequirementHandler.cs Fri Jul 27 20:31:12 2012 +0100 @@ -3,6 +3,7 @@ // 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.Requirement.Context; namespace IBBoard.WarFoundry.API.Objects.Requirement { @@ -41,12 +42,22 @@ public static Validation AllowsAdding(UnitType unitType, Army army) { - ICollection ignored; - return AllowsAdding(unitType, army, out ignored); + return AllowsAdding(unitType, army, new NullAddingContext()); } + public static Validation AllowsAdding(UnitType unitType, Army army, AddingContext context) + { + ICollection ignored; + return AllowsAdding(unitType, army, context, out ignored); + } + public static Validation AllowsAdding(UnitType unitType, Army army, out ICollection failureMessages) { + return AllowsAdding(unitType, army, new NullAddingContext(), out failureMessages); + } + + public static Validation AllowsAdding(UnitType unitType, Army army, AddingContext context, out ICollection failureMessages) + { Validation result = Validation.Passed; failureMessages = new List(); @@ -55,7 +66,7 @@ if (!Validates.AsOkay(requirement.AllowsAdding(unitType, army))) { result = Validation.Failed; - failureMessages.Add(requirement.GetAllowsAddingMessage(unitType, army)); + failureMessages.Add(requirement.GetAllowsAddingMessage(unitType, army, context)); } } diff -r 6e5b39caeb4e -r 248df19652f9 API/Objects/Requirement/RequiresAtLeastNUnitsRequirement.cs --- a/API/Objects/Requirement/RequiresAtLeastNUnitsRequirement.cs Sun Jul 15 21:01:47 2012 +0100 +++ b/API/Objects/Requirement/RequiresAtLeastNUnitsRequirement.cs Fri Jul 27 20:31:12 2012 +0100 @@ -189,7 +189,7 @@ return failures; } - protected override string GetFailedAddingRequirementsString(IWarFoundryObject toAdd, Army toArmy) + protected override string GetFailedAddingRequirementsString(IWarFoundryObject toAdd, Army toArmy, AddingContext context) { return String.Join("; ", GetFailedAddingRequirements(toAdd, toArmy).ToArray()); } diff -r 6e5b39caeb4e -r 248df19652f9 API/Objects/Requirement/RequiresNUnitsForMObjectsRequirement.cs --- a/API/Objects/Requirement/RequiresNUnitsForMObjectsRequirement.cs Sun Jul 15 21:01:47 2012 +0100 +++ b/API/Objects/Requirement/RequiresNUnitsForMObjectsRequirement.cs Fri Jul 27 20:31:12 2012 +0100 @@ -27,7 +27,7 @@ return hash; } - protected override string GetFailedAddingRequirementsString(IWarFoundryObject toAdd, Army toArmy) + protected override string GetFailedAddingRequirementsString(IWarFoundryObject toAdd, Army toArmy, AddingContext context) { return String.Join("; ", GetFailedAddingRequirements(toAdd, toArmy).ToArray()); } diff -r 6e5b39caeb4e -r 248df19652f9 API/Objects/Requirement/RequiresNoMoreThanNOfUnitTypeRequirement.cs --- a/API/Objects/Requirement/RequiresNoMoreThanNOfUnitTypeRequirement.cs Sun Jul 15 21:01:47 2012 +0100 +++ b/API/Objects/Requirement/RequiresNoMoreThanNOfUnitTypeRequirement.cs Fri Jul 27 20:31:12 2012 +0100 @@ -67,7 +67,7 @@ foreach (UnitCountRequirementData limit in ConstraintTypes) { - if (GetUnitTypeCount(toArmy, limit.UnitType, wfObject) > limit.Count) + if (GetUnitTypeCount(toArmy, limit.UnitType, wfObject, context) > limit.Count) { canAdd = Validation.Failed; break; @@ -77,16 +77,21 @@ return canAdd; } - private int GetUnitTypeCount(Army toArmy, UnitType unitType, WarFoundryObject wfObject) + private int GetUnitTypeCount(Army toArmy, UnitType unitType, IWarFoundryObject wfObject, AddingContext context) { - return toArmy.GetUnitTypeCount(unitType) + GetCountFromObject(wfObject, unitType); + return toArmy.GetUnitTypeCount(unitType) + GetCountFromObject(wfObject, unitType) + GetCountFromContext(context); } - private int GetCountFromObject(WarFoundryObject wfObject, UnitType limitedType) + private int GetCountFromObject(IWarFoundryObject wfObject, UnitType limitedType, AddingContext context) { return (limitedType.Equals(wfObject) || (wfObject is Unit && ((Unit)wfObject).UnitType.Equals(limitedType))) ? 1 : 0; } + protected virtual int GetCountFromContext(AddingContext context) + { + return 0; + } + /// /// Adds a requirement for there to be no more than maxCount of a given UnitType /// @@ -177,7 +182,7 @@ return failures; } - protected override string GetFailedAddingRequirementsString(IWarFoundryObject toAdd, Army toArmy) + protected override string GetFailedAddingRequirementsString(IWarFoundryObject toAdd, Army toArmy, AddingContext context) { return String.Join("; ", GetFailedAddingRequirements(toAdd, toArmy).ToArray()); } diff -r 6e5b39caeb4e -r 248df19652f9 API/Objects/Requirement/UnitRequiresAtLeastNUnitsRequirement.cs --- a/API/Objects/Requirement/UnitRequiresAtLeastNUnitsRequirement.cs Sun Jul 15 21:01:47 2012 +0100 +++ b/API/Objects/Requirement/UnitRequiresAtLeastNUnitsRequirement.cs Fri Jul 27 20:31:12 2012 +0100 @@ -17,17 +17,12 @@ { } - protected override bool IsApplicable(IWarFoundryObject toObject, Army toArmy, AddingContext context) - { - return IsApplicable(toObject, context) || IsApplicableForRequiredType(toObject, toArmy); - } - protected override bool IsApplicable(IWarFoundryObject toObject, AddingContext context) { return AllowedObject.Equals(toObject) || (toObject is Unit && AllowedObject.Equals(((Unit)toObject).UnitType)); } - private bool IsApplicableForRequiredType(IWarFoundryObject toObject, Army toArmy) + protected override bool IsApplicableToBoth(IWarFoundryObject toObject, Army toArmy, AddingContext context) { bool isApplicable = false; UnitType addedType = toObject as UnitType; diff -r 6e5b39caeb4e -r 248df19652f9 API/Objects/Requirement/UnitRequiresNParentModelsForMUnitsRequirement.cs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/API/Objects/Requirement/UnitRequiresNParentModelsForMUnitsRequirement.cs Fri Jul 27 20:31:12 2012 +0100 @@ -0,0 +1,183 @@ +// This file (UnitRequiresNoMoreThanNUnitsPerMModelsInParentUnitRequirement.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2012 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 IBBoard.WarFoundry.API.Objects.Requirement.Context; + +namespace IBBoard.WarFoundry.API.Objects.Requirement +{ + public class UnitRequiresNParentModelsForMUnitsRequirement : AbstractUnitRequirement + { + public static readonly string N_PER_M_IN_PARENT_REQUIREMENT_ID = "RequiresNoMoreThanNUnitsPerMModelsInParent"; + + public UnitRequiresNParentModelsForMUnitsRequirement(UnitType allowedObject, params UnitType[] limitedUnitTypes) : base(allowedObject, limitedUnitTypes) + { + //Do nothing special + } + + public override string RequirementID + { + get + { + return N_PER_M_IN_PARENT_REQUIREMENT_ID; + } + } + + protected override bool IsApplicable(IWarFoundryObject toObject, AddingContext context) + { + return GetCountFromContext(context) > 0 && IsApplicable(toObject); + } + + private bool IsApplicable(IWarFoundryObject toObject) + { + bool isApplicable = false; + UnitType unitType = GetUnitTypeFromObject(toObject); + + if (unitType != null) + { + isApplicable = this.AllowedObject.Equals(unitType) || IsApplicable(unitType); + } + + return isApplicable; + } + + private bool IsApplicable(UnitType unitType) + { + bool isApplicable = false; + foreach (UnitCountRequirementData requirement in ConstraintTypes) + { + if (requirement.UnitType.Equals(unitType)) + { + isApplicable = true; + break; + } + } + return isApplicable; + } + + protected override int GetObjectCountFromArmy(Army toArmy, UnitType obj) + { + return 0; + } + + protected override int GetObjectCountFromObject(IWarFoundryObject wfObject) + { + return base.GetObjectCountFromObject(wfObject); + } + + private int GetCountFromContext(AddingContext context) + { + AddingToParentContext parentContext = context as AddingToParentContext; + + if (parentContext == null || !IsUnitTypeLimited(parentContext.ParentUnit.UnitType)) + { + return 0; + } + else + { + return parentContext.ParentUnit.Size; + } + } + + public override void AddUnitTypeRequirement(UnitType unitType) + { + this.ConstraintTypes.Add(new UnitCountRequirementData(unitType, 1, 1)); + } + + protected override string GetFailedRequirementsString(Army army) + { + return ""; + } + + protected override string GetFailedAddingRequirementsString(IWarFoundryObject toAdd, Army toArmy, AddingContext context) + { + UnitType unitType = GetUnitTypeFromObject(toAdd); + AddingToParentContext parentContext = context as AddingToParentContext; + + if (unitType != null && parentContext != null) + { + return GetFailedAddingRequirementsString(unitType, parentContext.ParentUnit, toArmy); + } + else + { + return ""; + } + } + + private string GetFailedAddingRequirementsString(UnitType unitType, Unit parentUnit, Army toArmy) + { + int allowedTypeCount = GetChildCountFromUnit(parentUnit); + + return String.Format("Army can only contain {0} × {1} as a sub-unit of each {2}, would have {3}", allowedTypeCount, unitType.Name, parentUnit.UnitType.Name, (allowedTypeCount + 1)); + } + + protected override string AllowsAddingFailedMessage + { + get + { + return "{0}"; + } + } + + protected override Validation CheckAllowsAdding(IWarFoundryObject wfObject, Army toArmy, AddingContext context) + { + AddingToParentContext parentContext = context as AddingToParentContext; + return parentContext == null ? Validation.Passed : CheckAllowsAdding(wfObject, toArmy, parentContext); + } + + private Validation CheckAllowsAdding(IWarFoundryObject obj, Army toArmy, AddingToParentContext context) + { + Validation canAdd = Validation.Passed; + Unit parentUnit = context.ParentUnit; + //Get count and add one because we're checking if this unit can be added, so it hasn't been added yet + int allowedTypeCount = GetChildCountFromUnit(parentUnit) + 1; + + foreach (UnitCountRequirementData limit in ConstraintTypes) + { + int limitedTypeCount = parentUnit.Size; + + if (!IsValidByRequirement(limit, allowedTypeCount, limitedTypeCount)) + { + canAdd = Validation.Failed; + break; + } + } + + return canAdd; + } + + private int GetChildCountFromUnit(Unit parentUnit) + { + int count = 0; + + foreach (Unit unit in parentUnit.ContainedUnits) + { + if (unit.UnitType.Equals(AllowedObject)) + { + count++; + } + } + + return count; + } + + private bool IsValidByRequirement(UnitCountRequirementData limit, int allowedTypeCount, int limitedTypeCount) + { + //Round down limited units to get only whole amounts + double normalisedLimited = Math.Floor(limitedTypeCount / (limit.Count * 1.0)); + double normalisedAllowed = allowedTypeCount / (limit.AllowsCount * 1.0); + return normalisedLimited >= 1 && normalisedAllowed <= normalisedLimited; + } + + public override Validation ValidatesArmy(Army army) + { + throw new System.NotImplementedException(); + } + + protected override int GetObjectCountFromArmy(Army toArmy) + { + return 0; + } + } +} + diff -r 6e5b39caeb4e -r 248df19652f9 API/Objects/Requirement/UnitRequiresNUnitsForMUnitsRequirement.cs --- a/API/Objects/Requirement/UnitRequiresNUnitsForMUnitsRequirement.cs Sun Jul 15 21:01:47 2012 +0100 +++ b/API/Objects/Requirement/UnitRequiresNUnitsForMUnitsRequirement.cs Fri Jul 27 20:31:12 2012 +0100 @@ -21,17 +21,12 @@ //Do nothing } - protected override bool IsApplicable(IWarFoundryObject toObject, Army toArmy, AddingContext context) - { - return IsApplicable(toObject, context) || IsApplicableForRequiredType(toObject, toArmy); - } - protected override bool IsApplicable(IWarFoundryObject toObject, AddingContext context) { return AllowedObject.Equals(toObject) || (toObject is Unit && AllowedObject.Equals(((Unit)toObject).UnitType)); } - private bool IsApplicableForRequiredType(IWarFoundryObject toObject, Army toArmy) + protected override bool IsApplicableToBoth(IWarFoundryObject toObject, Army toArmy, AddingContext context) { bool isApplicable = false; UnitType addedType = toObject as UnitType; diff -r 6e5b39caeb4e -r 248df19652f9 API/Objects/Requirement/UnitRequiresNoMoreThanNOfUnitTypeRequirement.cs --- a/API/Objects/Requirement/UnitRequiresNoMoreThanNOfUnitTypeRequirement.cs Sun Jul 15 21:01:47 2012 +0100 +++ b/API/Objects/Requirement/UnitRequiresNoMoreThanNOfUnitTypeRequirement.cs Fri Jul 27 20:31:12 2012 +0100 @@ -18,17 +18,12 @@ //Do nothing special } - protected override bool IsApplicable(IWarFoundryObject toObject, Army toArmy, AddingContext context) - { - return base.IsApplicable(toObject, toArmy, context) || IsApplicableForRequiredType(toObject, toArmy); - } - protected override bool IsApplicable(IWarFoundryObject toObject, AddingContext context) { return AllowedObject.Equals(toObject) || (toObject is Unit && AllowedObject.Equals(((Unit)toObject).UnitType)); } - private bool IsApplicableForRequiredType(IWarFoundryObject toObject, Army toArmy) + protected override bool IsApplicableToBoth(IWarFoundryObject toObject, Army toArmy, AddingContext context) { bool isApplicable = false; UnitType addedType = toObject as UnitType; diff -r 6e5b39caeb4e -r 248df19652f9 API/Objects/UnitType.cs --- a/API/Objects/UnitType.cs Sun Jul 15 21:01:47 2012 +0100 +++ b/API/Objects/UnitType.cs Fri Jul 27 20:31:12 2012 +0100 @@ -19,7 +19,8 @@ private int min = 0; private int max = WarFoundryCore.INFINITY; private int baseSize = 0; - private int minSize, maxSize; + private int minSize = 1; + private int maxSize = WarFoundryCore.INFINITY; private double baseUnitCost; private double costPerTrooper; private Stats stats; diff -r 6e5b39caeb4e -r 248df19652f9 IBBoard.WarFoundry.API.csproj --- a/IBBoard.WarFoundry.API.csproj Sun Jul 15 21:01:47 2012 +0100 +++ b/IBBoard.WarFoundry.API.csproj Fri Jul 27 20:31:12 2012 +0100 @@ -162,6 +162,8 @@ + +