// 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; using System.Collections.Generic; using IBBoard.Collections; namespace IBBoard.WarFoundry.API.Objects.Requirement { public class UnitRequiresNParentModelsForMUnitsRequirement : AbstractUnitRequirement { public static readonly string REQUIREMENT_ID = "RequiresNoMoreThanNUnitsPerMModelsInParent"; public UnitRequiresNParentModelsForMUnitsRequirement(UnitType allowedObject, params UnitType[] limitedUnitTypes) : base(allowedObject, limitedUnitTypes) { //Do nothing special } public override string RequirementID { get { return 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) { AddUnitTypeRequirement(unitType, 1); } public void AddUnitTypeRequirement(UnitType unitType, int allowedCount) { AddUnitTypeRequirement(unitType, allowedCount, 1); } public void AddUnitTypeRequirement(UnitType unitType, int allowedCount, int reqsCount) { ConstraintTypes.Add(new UnitCountRequirementData(unitType, reqsCount, allowedCount)); } 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) + 1; UnitCountRequirementData parentRequirement = null; foreach (UnitCountRequirementData limit in ConstraintTypes) { if (Arrays.Contains(limit.UnitTypes, parentUnit.UnitType)) { parentRequirement = limit; break; } } string txt = "Units of type {0} can only have {1} x {2} for every {3} models, would have {4} for {5}"; return String.Format(txt, parentUnit.UnitType.Name, parentRequirement.AllowsCount, unitType.Name, parentRequirement.Count, allowedTypeCount, parentUnit.Size); } 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) { bool isApplicable = false; Validation result = Validation.Passed; foreach (Unit unit in army.GetUnits(AllowedObject)) { Unit parentUnit = unit.ParentUnit; isApplicable = isApplicable || (parentUnit != null && IsUnitTypeLimited(parentUnit.UnitType)); if (isApplicable && !ValidatesUnitInArmy(unit, parentUnit, army)) { result = Validation.Failed; break; } } return isApplicable ? result : Validation.NotApplicable; } private bool ValidatesUnitInArmy(Unit unit, Unit parentUnit, Army army) { bool isValid = true; int allowedTypeCount = GetChildCountFromUnit(parentUnit); foreach (UnitCountRequirementData limit in ConstraintTypes) { int limitedTypeCount = parentUnit.Size; if (!IsValidByRequirement(limit, allowedTypeCount, limitedTypeCount)) { isValid = false; break; } } return isValid; } protected override string ValidationFailedMessage { get { List parentsList = new List(); foreach (UnitCountRequirementData limit in ConstraintTypes) { parentsList.Add(String.Format("{0} × {1} for every {2} model in a parent unit of {3}", limit.AllowsCount, AllowedObject.Name, limit.Count, limit.UnitType.Name)); } return String.Format("Army can only contain {0}: {{0}}.", String.Join(", ", parentsList.ToArray())); } } protected override string GetFailedRequirementsString(Army army) { return String.Join("; ", GetFailedValidationRequirements(army).ToArray()); } private List GetFailedValidationRequirements(Army army) { List failures = new List(); SimpleSet parentUnits = new SimpleSet(); foreach (Unit unit in army.GetUnits(AllowedObject)) { Unit parentUnit = unit.ParentUnit; if (parentUnit != null && !parentUnits.Contains(parentUnit) && IsUnitTypeLimited(parentUnit.UnitType) && !ValidatesUnitInArmy(unit, parentUnit, army)) { parentUnits.Add(parentUnit); int allowedTypeCount = GetChildCountFromUnit(parentUnit); failures.Add(String.Format("{0} has {1} for {2}", parentUnit.Name, allowedTypeCount, parentUnit.Size)); } } return failures; } protected override int GetObjectCountFromArmy(Army toArmy) { return 0; } } }