diff API/Objects/Unit.cs @ 337:3c4a6403a88c

* Fix capitalisation so that new files are in the namespace no-open-ticket
author IBBoard <dev@ibboard.co.uk>
date Sun, 03 Apr 2011 18:50:32 +0000
parents
children 7179c585d31d
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/API/Objects/Unit.cs	Sun Apr 03 18:50:32 2011 +0000
@@ -0,0 +1,544 @@
+// This file (Unit.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 2007, 2008, 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;
+using System.Xml;
+using IBBoard.Lang;
+using IBBoard.Limits;
+using IBBoard.WarFoundry.API.Util;
+
+namespace IBBoard.WarFoundry.API.Objects
+{
+	/// <summary>
+	/// Summary description for UnitInstance.
+	/// </summary>
+	public class Unit : WarFoundryObject, ICostedWarFoundryObject
+	{
+		private UnitType type;
+		private int size;
+		private Unit parentUnit;
+		private double points;
+		private ArmyCategory cat;
+		private Dictionary<UnitEquipmentItem, AbstractUnitEquipmentItemSelection> equipment = new Dictionary<UnitEquipmentItem, AbstractUnitEquipmentItemSelection>();
+		private Dictionary<string, List<AbstractUnitEquipmentItemSelection>> equipmentSlots = new Dictionary<string, List<AbstractUnitEquipmentItemSelection>>();
+		private List<Unit> containedUnits = new List<Unit>();
+
+		public event DoubleValChangedDelegate PointsValueChanged;
+		public event IntValChangedDelegate UnitSizeChanged;
+		public event DoubleValChangedDelegate UnitEquipmentAmountChanged;
+
+		public Unit(UnitType unitType, ArmyCategory parentArmyCat) : this(unitType, unitType.MinSize, parentArmyCat)
+		{
+			//Do nothing extra
+		}
+
+		public Unit(UnitType unitType, int startSize, ArmyCategory parentArmyCat) : this("", "", startSize, unitType, parentArmyCat)
+		{
+			SetInitialEquipment();
+			UnitSizeChanged += new IntValChangedDelegate(RefreshUnitEquipmentAmounts);
+		}	
+
+		public Unit(string id, string name, int startSize, UnitType unitType, ArmyCategory parentArmyCat) : base(id, name)
+		{
+			Category = parentArmyCat;
+			type = unitType;
+			Size = startSize;
+			CalcCost();
+			UnitEquipmentAmountChanged += new DoubleValChangedDelegate(UnitEquipmentAmountChangedHandler);
+			UnitSizeChanged += new IntValChangedDelegate(UnitSizeChangedHandler);
+			Translation.TranslationChanged += HandleTranslationChanged;
+		}
+
+		private void UnitEquipmentAmountChangedHandler(WarFoundryObject obj, double oldVal, double newVal)
+		{
+			CalcCost();
+		}
+
+		private void UnitSizeChangedHandler(WarFoundryObject obj, int oldVal, int newVal)
+		{
+			CalcCost();
+			
+			if (HasDefaultName())
+			{
+				OnNameChanged("", Name);
+			}
+		}
+
+		protected override string DefaultName()
+		{
+			if (type != null)
+			{
+				if (size == 1)
+				{
+					return type.Name;
+				}
+				else
+				{
+					return String.Format(Translation.GetTranslation("defaultUnitName"), size, type.Name);
+				}
+			}
+			else
+			{
+				return "Unknown Unit";
+			}
+		}
+
+		private void HandleTranslationChanged()
+		{
+			if (type != null && HasDefaultName() && size != 1)
+			{
+				OnNameChanged(null, DefaultName());
+			}
+		}
+
+		private void SetInitialEquipment()
+		{
+			foreach (UnitEquipmentItem unitEquip in UnitType.GetEquipmentItems())
+			{
+				if (unitEquip.IsRequired)
+				{
+					if (CanEquipWithItem(unitEquip))
+					{
+						ILimit minLimit = unitEquip.MinLimit;
+						
+						if (minLimit is IPercentageLimit)
+						{
+							SetEquipmentRatio(unitEquip, UnitEquipmentUtil.GetMinEquipmentPercentage(this, unitEquip));
+						}
+						else
+						{
+							SetEquipmentAmount(unitEquip, UnitEquipmentUtil.GetMinEquipmentCount(this, unitEquip));
+						}
+					}
+				}
+			}
+		}
+
+		private void CalcCost()
+		{
+			double oldpoints = points;
+			points = type.CostPerTrooper * AdditionalTroopers + type.BaseUnitCost;
+
+			foreach (AbstractUnitEquipmentItemSelection equipSelection in equipment.Values)
+			{
+				points += equipSelection.TotalCost;
+			}
+
+			if (oldpoints != points)
+			{
+				OnPointsValueChanged(oldpoints, points);
+			}
+		}
+
+		public int AdditionalTroopers
+		{
+			get { return Math.Max(Size - type.BaseSize, 0); }
+		}
+
+		public int Size
+		{
+			get { return size; }
+			set
+			{
+				if (value != size)
+				{
+					int oldValue = size;
+					size = (value > 0 ? value : 1);
+					OnUnitSizeChanged(oldValue, size);
+				}
+			}
+		}
+
+		public UnitType UnitType
+		{
+			get { return type; }
+		}
+
+		public Army Army
+		{
+			get { return (Category == null ? null : Category.ParentArmy); }
+		}
+
+		public Race Race
+		{
+			get { return UnitType.Race; }
+		}
+
+		public ArmyCategory Category
+		{
+			get
+			{
+				return cat;
+			}
+			set { cat = value; }
+		}
+
+		public double Points
+		{
+			get
+			{
+				if (points == 0)
+				{
+					CalcCost();
+				}
+
+				return points;
+			}
+		}
+
+		public Unit[] ContainedUnits
+		{
+			get { return containedUnits.ToArray(); }
+		}
+
+		public void AddContainedUnit(Unit unit)
+		{
+			if (UnitType.CanContainUnit(unit))
+			{
+				if (!containedUnits.Contains(unit))
+				{
+					containedUnits.Add(unit);
+				}
+				
+				unit.ParentUnit = this;
+			}
+			else
+			{
+				throw new InvalidContainershipException(this, unit);
+			}
+		}
+
+		public void RemoveContainedUnit(Unit unit)
+		{
+			containedUnits.Remove(unit);
+		}
+
+		public Unit ParentUnit
+		{
+			get { return parentUnit; }
+			set
+			{
+				if (!(parentUnit == value || (parentUnit != null && parentUnit.Equals(value))))
+				{
+					parentUnit = value;
+					
+					if (value != null)
+					{
+						value.AddContainedUnit(this);
+					}
+				}
+			}
+		}
+
+		public UnitEquipmentItem[] GetEquipment()
+		{
+			return DictionaryUtils.ToKeyArray(equipment);
+		}
+
+		public EquipmentItem[] GetRequiredEquipment()
+		{
+			List<EquipmentItem> list = new List<EquipmentItem>();
+
+			foreach (UnitEquipmentItem item in GetEquipment())
+			{
+				if (item.IsRequired)
+				{
+					list.Add(item.EquipmentItem);
+				}
+			}
+
+			return list.ToArray();
+		}
+
+		internal AbstractUnitEquipmentItemSelection GetEquipmentSelection(UnitEquipmentItem item)
+		{
+			return DictionaryUtils.GetValue(equipment, item);
+		}
+
+		public void SetEquipmentAmount(UnitEquipmentItem equip, int amount)
+		{
+			if (amount < 1 && amount != WarFoundryCore.INFINITY)
+			{
+				amount = 0;
+			}
+			
+			if (amount == 0)
+			{
+				RemoveEquipmentItem(equip);
+			}
+			else
+			{
+				AbstractUnitEquipmentItemSelection currSelection = DictionaryUtils.GetValue(equipment, equip);
+				double oldAmount = (currSelection == null ? 0 : currSelection.AmountTaken);
+	
+				if (amount != oldAmount)
+				{
+					if (oldAmount == 0)
+					{
+						AddEquipmentAmount(equip, amount);
+					}
+					else if (currSelection is UnitEquipmentNumericSelection)
+					{
+						//A UnitEquipmentItem shouldn't change its IsRatio value, so assume we already have the right sub-type
+						currSelection.AmountTaken = amount;
+					}
+					else
+					{
+						RemoveEquipmentItem(equip);
+						AddEquipmentAmount(equip, amount);
+					}
+	
+					OnUnitEquipmentAmountChanged(equip, oldAmount, amount);
+				}
+			}
+		}
+
+		private void AddEquipmentAmount(UnitEquipmentItem equip, int amount)
+		{
+			AbstractUnitEquipmentItemSelection newItem = new UnitEquipmentNumericSelection(this, equip, amount);			
+			equipment[equip] = newItem;
+			List<AbstractUnitEquipmentItemSelection> selections = DictionaryUtils.GetValue(equipmentSlots, equip.SlotName);
+			
+			if (selections == null)
+			{
+				selections = new List<AbstractUnitEquipmentItemSelection>();
+				equipmentSlots[equip.SlotName] = selections;
+			}
+			
+			selections.Add(newItem);
+		}
+
+		public void SetEquipmentRatio(UnitEquipmentItem equip, double ratio)
+		{
+			if (!equip.IsRatioLimit)
+			{
+				throw new InvalidOperationException("Equipment with ID " + equip.ID + " for unit of type " + UnitType.ID + " has an absolute limit, not a ratio limit");
+			}
+			
+			if (ratio > 100)
+			{
+				ratio = 100;
+			}
+			else if (ratio < 0)
+			{
+				ratio = 0;
+			}
+			
+			if (ratio == 0)
+			{
+				RemoveEquipmentItem(equip);
+			}
+			else
+			{
+				AbstractUnitEquipmentItemSelection currSelection = DictionaryUtils.GetValue(equipment, equip);
+				double oldRatio = (currSelection == null ? 0 : currSelection.AmountTaken);
+	
+				if (ratio != oldRatio)
+				{
+					if (oldRatio == 0)
+					{
+						AddEquipmentRatio(equip, ratio);
+					}
+					else if (currSelection is UnitEquipmentRatioSelection)
+					{
+						currSelection.AmountTaken = ratio;
+					}
+					else
+					{
+						RemoveEquipmentItem(equip);
+						AddEquipmentRatio(equip, ratio);
+					}
+	
+					OnUnitEquipmentAmountChanged(equip, oldRatio, ratio);
+				}
+			}
+		}
+
+		private void AddEquipmentRatio(UnitEquipmentItem equip, double ratio)
+		{
+			UnitEquipmentRatioSelection newItem = new UnitEquipmentRatioSelection(this, equip, ratio);
+			equipment[equip] = newItem;
+			List<AbstractUnitEquipmentItemSelection> selections = DictionaryUtils.GetValue(equipmentSlots, equip.SlotName);
+			
+			if (selections == null)
+			{
+				selections = new List<AbstractUnitEquipmentItemSelection>();
+				equipmentSlots[equip.SlotName] = selections;
+			}
+			
+			selections.Add(newItem);
+		}
+
+		private void RemoveEquipmentItem(UnitEquipmentItem equip)
+		{
+			double oldAmount = UnitEquipmentUtil.GetEquipmentAmount(this, equip);
+		
+			if (oldAmount != 0)
+			{
+				AbstractUnitEquipmentItemSelection selection = DictionaryUtils.GetValue(equipment, equip);
+				equipment.Remove(equip);
+				List<AbstractUnitEquipmentItemSelection> slotSelections = DictionaryUtils.GetValue(equipmentSlots, equip.SlotName);
+				slotSelections.Remove(selection);
+				OnUnitEquipmentAmountChanged(equip, oldAmount, 0);
+			}
+		}
+
+		public bool CanEquipWithItem(UnitEquipmentItem item)
+		{
+			string[] mutexes = item.MutexGroups;
+			bool canEquip = false;
+
+			if (mutexes.Length == 0)
+			{
+				canEquip = true;
+			}
+			else
+			{
+				canEquip = UnitEquipmentUtil.GetBlockingEquipmentItems(this, item).Count == 0;
+			}
+
+			return canEquip;
+		}
+
+		public bool CanEquipWithItem(string equipID)
+		{
+			return CanEquipWithItem(UnitType.GetEquipmentItem(equipID));
+		}
+
+		private void OnPointsValueChanged(double oldValue, double newValue)
+		{
+			if (PointsValueChanged != null)
+			{
+				PointsValueChanged(this, oldValue, newValue);
+			}
+		}
+
+		private void OnUnitSizeChanged(int oldValue, int newValue)
+		{
+			if (UnitSizeChanged != null)
+			{
+				UnitSizeChanged(this, oldValue, newValue);
+			}
+		}
+
+		private void OnUnitEquipmentAmountChanged(UnitEquipmentItem equip, double oldValue, double newValue)
+		{
+			if (UnitEquipmentAmountChanged != null)
+			{
+				UnitEquipmentAmountChanged(equip, oldValue, newValue);
+			}
+		}
+
+		public Stat[][] UnitStatsArrays
+		{
+			get { return UnitType.UnitStatsArrays; }
+		}
+
+		public Stat[][] UnitStatsArraysWithName
+		{
+			get { return UnitType.UnitStatsArraysWithName; }
+		}
+
+		public string[] UnitStatsArrayIDs
+		{
+			get
+			{
+				return UnitType.UnitStatsArrayIDs;
+			}
+		}
+
+		public string GetStatValue(string statName)
+		{
+			return UnitType.GetStatValue(statName);
+		}
+
+		public int GetEquipmentAmountInSlot(string slotName)
+		{
+			int amount = 0;
+
+			List<AbstractUnitEquipmentItemSelection> selections = GetEquipmentSlotSelections(slotName);
+			
+			if (selections != null)
+			{
+				amount = GetSelectionTotal(selections);
+			}			
+			
+			return amount;
+		}
+
+		internal List<AbstractUnitEquipmentItemSelection> GetEquipmentSlotSelections(string slotName)
+		{
+			return DictionaryUtils.GetValue(equipmentSlots, slotName);
+		}
+
+		/// <summary>
+		/// Gets the total amount of items taken for the item's slot, excluding the provided item
+		/// </summary>
+		/// <param name="item">the item to exclude from the count</param>
+		/// <returns>the total number of items</returns>
+		public int GetEquipmentAmountInSlotExcludingItem(UnitEquipmentItem item)
+		{
+			int amount = 0;
+
+			List<AbstractUnitEquipmentItemSelection> selections = DictionaryUtils.GetValue(equipmentSlots, item.SlotName);
+
+			if (selections != null)
+			{
+				selections = new List<AbstractUnitEquipmentItemSelection>(selections);
+				RemoveSelectionFromList(item, selections);
+				amount = GetSelectionTotal(selections);
+			}
+
+			return amount;
+		}
+
+		private void RemoveSelectionFromList(UnitEquipmentItem item, List<AbstractUnitEquipmentItemSelection> selections)
+		{
+			AbstractUnitEquipmentItemSelection selection = GetEquipmentSelection(item);
+
+			if (selection != null)
+			{
+				selections.Remove(selection);
+			}
+		}
+
+		private int GetSelectionTotal(List<AbstractUnitEquipmentItemSelection> selections)
+		{
+			int amount = 0;
+			
+			foreach (AbstractUnitEquipmentItemSelection selection in selections)
+			{
+				amount += selection.NumberTaken;
+			}
+			
+			return amount;
+		}
+		
+		/// <summary>
+		/// Default stub implementation of getting the unit's abilities - defaults to just the unit type's required abilities until we get a way to modify them
+		/// </summary>
+		public ICollection<Ability> Abilities
+		{
+			get
+			{
+				return UnitType.GetRequiredAbilities();
+			}
+		}
+
+		private void RefreshUnitEquipmentAmounts(WarFoundryObject obj, int oldValue, int newValue)
+		{
+			foreach (UnitEquipmentItem item in equipment.Keys)
+			{
+				AbstractUnitEquipmentItemSelection selection = equipment[item];
+				
+				if (selection is UnitEquipmentRatioSelection)
+				{
+					OnUnitEquipmentAmountChanged(item, selection.AmountTaken, selection.AmountTaken);
+				}
+			}
+		}	
+	}
+}