view api/Objects/Unit.cs @ 151:1d13820b3d96

Fixes #176: Bug when saving recently edited army * Add loaded file cleanup to AbstractWarFoundryFactory * Add override of method with Zip reference closing to WarFoundryXmlFactory WarFoundry now no longer ends up with trailing handles to files, although why they only caused problems in some situations is unknown Also: * Some line ending fixes (curse cross-platform development and different line terminators!)
author IBBoard <dev@ibboard.co.uk>
date Sat, 26 Sep 2009 18:48:36 +0000
parents c01366bd1627
children 0c0e14f03785
line wrap: on
line source

// 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;

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 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) : this("", "", startSize, unitType, parentArmyCat)
		{
			SetInitialEquipment();
		}		
		
		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);
		}
		
		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 SetInitialEquipment()
		{
			foreach (UnitEquipmentItem unitEquip in UnitType.GetEquipmentItems())
			{
				if (unitEquip.IsRequired)
				{
					if (CanEquipWithItem(unitEquip))
					{
						if (unitEquip.IsRatioLimit)
						{
							SetEquipmentRatio(unitEquip, unitEquip.MinPercentage);
						}
						else
						{
							SetEquipmentAmount(unitEquip, unitEquip.MinNumber);
						}
					}
				}
			}
		}

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

		[Obsolete("Use Points instead")]
		public double PointsValue
		{
			get { return Points; }
		}
		
		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[] 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)
		{
			double amount = 0;
			AbstractUnitEquipmentItemSelection selection = DictionaryUtils.GetValue(equipment, item);
			
			if (selection != null)
			{
				amount = selection.AmountTaken;
			}
			
			return amount;
		}

		public double GetEquipmentAmount(string equipID)
		{
			return GetEquipmentAmount(UnitType.GetEquipmentItem(equipID));
		}

		public bool GetEquipmentAmountIsRatio(UnitEquipmentItem item)
		{
			return IsEquipmentAmountRatio(GetEquipmentSelection(item));
		}

		private AbstractUnitEquipmentItemSelection GetEquipmentSelection(UnitEquipmentItem item)
		{
			return DictionaryUtils.GetValue(equipment, item);
		}

		private bool IsEquipmentAmountRatio(AbstractUnitEquipmentItemSelection selection)
		{
			return (selection is UnitEquipmentRatioSelection);
		}

		public bool GetEquipmentAmountIsRatio(string itemID)
		{
			return GetEquipmentAmountIsRatio(UnitType.GetEquipmentItem(itemID));
		}

		public string GetEquipmentAmountString(string equipID)
		{
			return GetEquipmentAmountString(UnitType.GetEquipmentItem(equipID));
		}

		public string GetEquipmentAmountString(UnitEquipmentItem item)
		{
			String amountString = "";
			AbstractUnitEquipmentItemSelection selection = GetEquipmentSelection(item);
				
			if (IsEquipmentAmountRatio(selection))
			{
				amountString = UnitEquipmentRatioSelection.GetEquipmentAmountString(GetEquipmentAmount(item));
			}
			else
			{
				amountString = UnitEquipmentNumericSelection.GetEquipmentAmountString(GetEquipmentAmount(item));
			}

			return amountString;
		}
		
		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
					{
						equipment.Remove(equip);
						AddEquipmentAmount(equip, amount);
					}
	
					OnUnitEquipmentAmountChanged(equip, oldAmount, amount);
				}
			}
		}
		
		private void AddEquipmentAmount(UnitEquipmentItem equip, int amount)
		{
			AbstractUnitEquipmentItemSelection newItem = null;
			
			if (equip.IsRatioLimit)
			{
				newItem = new UnitEquipmentNumericForRatioSelection(this, equip, amount);
			}
			else
			{
				newItem = new UnitEquipmentNumericSelection(this, equip, amount);
			}
			
			equipment[equip] = 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
					{
						equipment.Remove(equip);
						AddEquipmentRatio(equip, ratio);
					}
	
					OnUnitEquipmentAmountChanged(equip, oldRatio, ratio);
				}
			}
		}
		
		private void AddEquipmentRatio(UnitEquipmentItem equip, double ratio)
		{
			equipment[equip] = new UnitEquipmentRatioSelection(this, equip, ratio);
		}
		
		private void RemoveEquipmentItem(UnitEquipmentItem equip)
		{
			double oldAmount = GetEquipmentAmount(equip);
		
			if (oldAmount != 0)
			{
				equipment.Remove(equip);
				OnUnitEquipmentAmountChanged(equip, oldAmount, 0);
			}
		}
		
		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);
		}
	}
}