view api/Objects/Unit.cs @ 88:110a7b0bcab5

Re #50: Complete core loading from XML * Fix attribute loading for UnitEquipmentItem to read from correct XmlElement * Stop infinite recursion when getting number of a given weapon
author IBBoard <dev@ibboard.co.uk>
date Sat, 01 Aug 2009 20:03:54 +0000
parents 46ad6f478203
children bd2aaa9a61df
line wrap: on
line source

// This file (Unit.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard.
//
// The file and the library/program it is in are licensed under the GNU LGPL license, either version 3 of the License or (at your option) any later version. Please see COPYING.LGPL for more information and the full license.

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using IBBoard.Lang;

namespace IBBoard.WarFoundry.API.Objects
{
	/// <summary>
	/// Summary description for UnitInstance.
	/// </summary>
	public class Unit : WarFoundryObject
	{
		private UnitType type;
		private int size;
		private Unit parentUnit;
		private double points;
		private ArmyCategory cat;
		private Dictionary<UnitEquipmentItem, double> equipment = new Dictionary<UnitEquipmentItem, double>();
		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) { }

		public Unit(UnitType unitType, int startSize, ArmyCategory parentArmyCat)
		{
			Category = parentArmyCat;
			type = unitType;
			Size = startSize;
			SetInitialEquipment();
			CalcCost();
		}

		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 SetInitialEquipment()
		{
			foreach (UnitEquipmentItem unitEquip in UnitType.GetEquipmentItems())
			{
				if (unitEquip.IsRequired)
				{
					if (CanEquipWithItem(unitEquip))
					{
						equipment[unitEquip] = unitEquip.MinNumber;
					}
				}
			}
		}

		private void CalcCost()
		{
			String oldName = HasDefaultName() ? Name : null;
			double oldpoints = points;
			points = type.CostPerTrooper * AdditionalTroopers + type.BaseUnitCost;

			foreach (UnitEquipmentItem unitEquipItem in equipment.Keys)
			{
				double count = equipment[unitEquipItem];
				
				if (unitEquipItem.IsRatioLimit)
				{
					if (unitEquipItem.RoundNumberUp)
					{
						points += Math.Ceiling(size * count) * unitEquipItem.Cost;
					}
					else
					{
						points += Math.Floor(size * count) * unitEquipItem.Cost;
					}
				}
				else
				{
					if (count == WarFoundryCore.INFINITY)
					{
						points += size * unitEquipItem.Cost;
					}
					else
					{
						points += count * unitEquipItem.Cost;
					}
				}
			}

			if (oldpoints!=points)
			{
				OnPointsValueChanged(oldpoints, points);
			}

			if (oldName!=null)
			{
				OnNameChanged(oldName, Name);
			}
		}
		
		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);
					CalcCost();
					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 PointsValue
		{
			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[] GetAllowedOptionalEquipment()
		{
			List<UnitEquipmentItem> list = new List<UnitEquipmentItem>();

			foreach (UnitEquipmentItem item in UnitType.GetEquipmentItems())
			{
				if (!item.IsRequired)
				{
					list.Add(item);
				}
			}

			return list.ToArray();
		}

		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();
		}

		public double GetEquipmentAmount(UnitEquipmentItem item)
		{
			return DictionaryUtils.GetValue(equipment, item);
		}

		public double GetEquipmentAmount(string equipID)
		{
			return GetEquipmentAmount(UnitType.GetEquipmentItem(equipID));
		}
		
		public void SetEquipmentAmount(UnitEquipmentItem equip, int amount)
		{
			if (equip.IsRatioLimit)
			{
				throw new InvalidOperationException("Equipment with ID "+equip.ID+" for unit of type "+UnitType.ID+" has a ratio limit, not an absolute limit");
			}
			
			if (amount <1 && amount != WarFoundryCore.INFINITY)
			{
				amount = 0;
			}
			
			SetEquipmentAmount(equip, amount);
		}
		
		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 > 1)
			{
				ratio = 1;
			}
			else if (ratio < 0)
			{
				ratio = 0;
			}
			
			SetEquipmentAmount(equip, ratio);
		}
		
		private void SetEquipmentAmount(UnitEquipmentItem equip, double amount)
		{
			double oldAmount = 0;

			if (equipment.ContainsKey(equip))
			{
				oldAmount = equipment[equip];
			}

			if (amount!=oldAmount)
			{
				if (amount != 0)
				{
					equipment[equip] = amount;
				}
				else
				{
					equipment.Remove(equip);
				}

				OnUnitEquipmentAmountChanged(equip, oldAmount, amount);
				CalcCost();
			}
		}
		
		public bool CanEquipWithItem(UnitEquipmentItem item)
		{
			string mutex = item.MutexGroup;

			if (mutex == "")
			{
				return true;
			}

			foreach (UnitEquipmentItem unitItem in GetEquipment())
			{
				if (unitItem.MutexGroup == mutex)
				{
					return false;
				}
			}

			return true;
		}

		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[] UnitStatsArray
		{
			get { return UnitType.UnitStatsArray; }
		}

		public Stat[] UnitStatsArrayWithName
		{
			get { return UnitType.UnitStatsArrayWithName; }
		}

		public string GetStatValue(string statName)
		{
			return UnitType.GetStatValue(statName);
		}
	}
}