// 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;
using IBBoard.Limits;
using IBBoard.Logging;
using IBBoard.WarFoundry.API.Requirements;
using IBBoard.WarFoundry.API.Objects.Requirement;
namespace IBBoard.WarFoundry.API.Objects
{
///
/// A UnitType is a type for a , normally relating to an entry in an army list. The UnitType defines the name, cost, minimum and maximum limits of a unit, and the equipment units of the type can take.
///
public class UnitType : WarFoundryObject
{
private Category mainCat;
private Race race;
private int min, max, baseSize = 0;
private int minSize, maxSize;
private double baseUnitCost;
private double costPerTrooper;
private Stats stats;
private List requirements = new List();
private Dictionary equipment = new Dictionary();
private Dictionary> equipmentExclusionGroups = new Dictionary>();
private List equipmentKeyOrder = new List();
private Dictionary requiredAbilities = new Dictionary();
private Dictionary optionalAbilities = new Dictionary();
private String notes = "";
private List containedTypes = new List();
private Dictionary extraData = new Dictionary();
private Dictionary slotLimits = new Dictionary();
private Dictionary unitMemberTypes = new Dictionary();
private List cats = new List();
public UnitType(string id, string typeName, Race parentRace) : base(id, typeName)
{
race = parentRace;
}
public GameSystem GameSystem
{
get { return Race.GameSystem; }
}
///
/// Gets the that this unit belongs to.
///
public Race Race
{
get { return race; }
}
///
/// Gets or sets the default that this unit type is a member of.
/// If it is not already in the collection of categories then it will be added.
///
public virtual Category MainCategory
{
get
{
return mainCat;
}
set
{
mainCat = value;
AddCategory(value);
}
}
///
/// Gets the collection of objects that this UnitType can be a member of
///
public Category[] Categories
{
get
{
return cats.ToArray();
}
}
///
/// Adds a category to the set of categories that this unit can be taken from. The first category added will automatically become the MainCategory.
///
///
/// A that this unit can be taken from
///
public void AddCategory(Category cat)
{
if (!cats.Contains(cat))
{
cats.Add(cat);
if (MainCategory == null)
{
MainCategory = cat;
}
}
}
///
/// Gets or sets the minimum size of each unit of this type. Note: This should be set AFTER MaxSize, otherwise an unintended default value may be set for the minimum
///
public int MinSize
{
get { return minSize; }
set
{
minSize = (value >= 0 ? value : 0);
CheckMinimumSize();
}
}
///
/// Gets or sets the maximum size of each unit of this type. Note: This should be set BEFORE MinSize, otherwise an unintended default value may be set for the minimum
///
public int MaxSize
{
get { return maxSize; }
set
{
maxSize = (value >= 0 ? value : WarFoundryCore.INFINITY);
CheckMinimumSize();
}
}
///
/// Gets or sets the minimum number of units of this type that must be taken in an army. Note: This should be set AFTER MaxNumber, otherwise an unintended default value may be set for the minimum
///
public int MinNumber
{
get { return min; }
set
{
min = (value >= 0 ? value : 0);
CheckMinimumNumber();
}
}
///
/// Gets or sets the maximum number of units of this type that can be taken in an army. Note: This should be set BEFORE MinNumber, otherwise an unintended default value may be set for the minimum
///
public int MaxNumber
{
get { return max; }
set
{
max = (value >= 0 ? value : WarFoundryCore.INFINITY);
CheckMinimumNumber();
}
}
///
/// Makes sure that the minimum number isn't more than the maximum number, hence the warning on the properties
///
private void CheckMinimumNumber()
{
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);
}
}
///
/// Makes sure that the minimum unit size isn't more than the maximum unit size, hence the warning on the properties
///
private void CheckMinimumSize()
{
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);
}
}
////
/// Gets or sets the "base size" of a unit, which is the number of troopers the unit has in it for its "base cost". For a lot of units this value will be 0 as the cost is worked out based on the total number of members.
///
public int BaseSize
{
get { return baseSize; }
set { baseSize = (value >= 0 ? value : 0); }
}
///
/// The number of points that a "base unit" of BaseSize
models costs. Additional models are charged at CostPerTrooper
each.
///
public double BaseUnitCost
{
get { return baseUnitCost; }
set { baseUnitCost = (value >= 0 ? value : 0); }
}
////
/// The cost of an individual trooper. This value is the cost for a basic trooper without weapons, which are added on top of the cost before calculating a unit cost.
///
public double CostPerTrooper
{
get { return costPerTrooper; }
set { costPerTrooper = (value >= 0 ? value : 0); }
}
protected override string DefaultName()
{
throw new InvalidOperationException("Unit type with id "+id+" did not have a name specified");
}
///
/// The array of s for each of the unit's stat lines
///
public Stat[][] UnitStatsArrays
{
get
{
Stat[][] statsArray;
if (stats != null)
{
statsArray = new Stat[][]{ stats.StatsArray };
}
else if (unitMemberTypes.Count > 0)
{
int memTypeCount = unitMemberTypes.Count;
statsArray = new Stat[memTypeCount][];
int i = 0;
foreach (UnitMemberType memType in unitMemberTypes.Values)
{
statsArray[i] = memType.StatsArray;
i++;
}
}
else
{
SystemStats systemStats = GameSystem.StandardSystemStats;
Stats tempStats = new Stats(systemStats);
statsArray = new Stat[][]{ tempStats.StatsArray };
}
return statsArray;
}
}
public string[] UnitStatsArrayIDs
{
get
{
string[] ids;
if (stats != null)
{
ids = new string[]{ stats.StatsID };
}
else if (unitMemberTypes.Count > 0)
{
ids = new string[unitMemberTypes.Count];
int i = 0;
foreach (UnitMemberType memType in unitMemberTypes.Values)
{
ids[i] = memType.StatsID;
i++;
}
}
else
{
ids = new string[]{ GameSystem.StandardSystemStatsID };
}
return ids;
}
}
////
/// The array of s for each of the unit's stat lines including an additional column that contains the unit type name
///
public Stat[][] UnitStatsArraysWithName
{
get
{
Stat[][] statsArray;
if (stats != null)
{
statsArray = new Stat[][]{ ExtendStatsArrayWithName(stats.StatsArray) };
}
else if (unitMemberTypes.Count > 0)
{
int memTypeCount = unitMemberTypes.Count;
statsArray = new Stat[memTypeCount][];
int i = 0;
foreach (UnitMemberType memType in unitMemberTypes.Values)
{
statsArray[i] = memType.StatsArrayWithName;
i++;
}
}
else
{
SystemStats systemStats = GameSystem.StandardSystemStats;
Stats tempStats = new Stats(systemStats);
statsArray = new Stat[][]{ ExtendStatsArrayWithName(tempStats.StatsArray) };
}
return statsArray;
}
}
public Stat[] ExtendStatsArrayWithName(Stat[] statsArray)
{
Stat[] extendedStats = new Stat[statsArray.Length+1];
extendedStats[0] = new Stat(new StatSlot("name"), Name);
statsArray.CopyTo(extendedStats, 1);
return extendedStats;
}
public void SetUnitStats(Stats newStats)
{
stats = newStats;
}
public string GetStatValue(string statName)
{
return stats.GetStatValue(statName.ToLower());
}
internal void AddEquipmentItem(UnitEquipmentItem item)
{
if (!equipment.ContainsKey(item.ID))
{
equipment.Add(item.ID, item);
equipmentKeyOrder.Add(item.ID);
AddToMutexGroups(item);
}
}
private void AddToMutexGroups(UnitEquipmentItem item)
{
string[] mutexGroups = item.MutexGroups;
foreach (string mutexGroup in mutexGroups)
{
List items = DictionaryUtils.GetValue(equipmentExclusionGroups, mutexGroup);
if (items == null)
{
items = new List();
equipmentExclusionGroups.Add(mutexGroup, items);
}
items.Add(item);
}
}
///
/// Gets a for the given ID string, or null
if nothing exists for that ID
///
///
/// The ID of the UnitEquipmentItem to get
///
///
/// The for the given ID string, or null
if nothing exists for that ID
///
public UnitEquipmentItem GetEquipmentItem(string id)
{
return DictionaryUtils.GetValue(equipment, id);
}
///
/// Gets a for the given , or null
if the unit can't take that EquipmentItem
///
///
/// The to get the
///
///
/// The that definies the UnitType's restrictions for taking the
///
public UnitEquipmentItem GetEquipmentItem(EquipmentItem item)
{
return GetEquipmentItem(item.ID);
}
///
/// Gets an array of all available s for this UnitType
///
///
/// An array of all available s for this UnitType
///
public UnitEquipmentItem[] GetEquipmentItems()
{
return DictionaryUtils.ToArray(equipment);
}
public UnitEquipmentItem[] GetEquipmentItemsByExclusionGroup(string group)
{
return GetEquipmentItemsByExclusionGroups(new string[] { group });
}
public UnitEquipmentItem[] GetEquipmentItemsByExclusionGroups(string[] groups)
{
List list = new List();
foreach (string group in groups)
{
List groupList = DictionaryUtils.GetValue(equipmentExclusionGroups, group);
if (groupList != null)
{
list.AddRange(groupList);
}
}
return list.ToArray();
}
public bool IsRatioLimitedEquipmentItem(EquipmentItem item)
{
UnitEquipmentItem equip = GetEquipmentItem(item);
return equip != null && equip.IsRatioLimit;
}
public bool IsAbsoluteLimitedEquipmentItem(EquipmentItem item)
{
UnitEquipmentItem equip = GetEquipmentItem(item);
return equip != null && !equip.IsRatioLimit;
}
public ICollection GetRequiredAbilities()
{
return requiredAbilities.Values;
}
public ICollection GetOptionalAbilities()
{
return optionalAbilities.Values;
}
public void AddAbility(Ability ability, bool isRequired)
{
string id = ability.ID;
if (!requiredAbilities.ContainsKey(id) && !optionalAbilities.ContainsKey(id))
{
if (isRequired)
{
requiredAbilities[id] = ability;
}
else
{
optionalAbilities[id] = ability;
}
}
}
public void AddRequirement(IRequirement requirement)
{
requirements.Add(requirement);
}
public IRequirement[] GetRequirements()
{
return requirements.ToArray();
}
[Obsolete]
public List CanAddToArmy(Army army)
{
List failures = new List();
return failures;
}
[Obsolete]
public List CanRemoveFromArmy(Army army)
{
List failures = new List();
return failures;
}
public string Notes
{
get { return notes; }
set { notes = value; }
}
public bool CanContainUnit(Unit unit)
{
return CanContainUnitType(unit.UnitType);
}
public bool CanContainUnitType(UnitType unitType)
{
return containedTypes.Contains(unitType);
}
public UnitType[] ContainedUnitTypes
{
get { return containedTypes.ToArray(); }
}
public void AddContainedUnitType(UnitType containedType)
{
containedTypes.Add(containedType);
}
public void AddExtraData(string id, string data)
{
extraData[id] = data;
}
public string GetExtraData(string id)
{
return DictionaryUtils.GetValue(extraData, id);
}
public string StatsID
{
get
{
return stats.StatsID;
}
}
public void AddEquipmentSlot(string slotName, ILimit slotLimit)
{
slotLimits.Add(slotName, slotLimit);
}
public bool HasEquipmentSlot(string slotName)
{
return slotLimits.ContainsKey(slotName);
}
///
/// Gets the maximum limit on the number of items allowed in a single slot
///
/// The name of the equipment slot to get the limit for
/// The limit of the number of items allowed in a slot, or an infinite limit if the slot is the default one or has not been specified
public ILimit GetEquipmentSlotLimit(string slotName)
{
ILimit slotLimit = null;
if (HasEquipmentSlot(slotName))
{
slotLimit = DictionaryUtils.GetValue(slotLimits, slotName);
}
if (slotLimit == null)
{
slotLimit = new UnlimitedLimit();
}
return slotLimit;
}
public void AddUnitMemberType(UnitMemberType unitMemberType)
{
unitMemberTypes.Add(unitMemberType.ID, unitMemberType);
}
}
}