// 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 description for UnitInstance.
///
public class Unit : WarFoundryObject, ICostedWarFoundryObject
{
private UnitType type;
private int size;
private Unit parentUnit;
private double points;
private ArmyCategory cat;
private Dictionary equipment = new Dictionary();
private Dictionary> equipmentSlots = new Dictionary>();
private List containedUnits = new List();
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 list = new List();
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 selections = DictionaryUtils.GetValue(equipmentSlots, equip.SlotName);
if (selections == null)
{
selections = new List();
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 selections = DictionaryUtils.GetValue(equipmentSlots, equip.SlotName);
if (selections == null)
{
selections = new List();
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 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 selections = GetEquipmentSlotSelections(slotName);
if (selections != null)
{
amount = GetSelectionTotal(selections);
}
return amount;
}
internal List GetEquipmentSlotSelections(string slotName)
{
return DictionaryUtils.GetValue(equipmentSlots, slotName);
}
///
/// Gets the total amount of items taken for the item's slot, excluding the provided item
///
/// the item to exclude from the count
/// the total number of items
public int GetEquipmentAmountInSlotExcludingItem(UnitEquipmentItem item)
{
int amount = 0;
List selections = DictionaryUtils.GetValue(equipmentSlots, item.SlotName);
if (selections != null)
{
selections = new List(selections);
RemoveSelectionFromList(item, selections);
amount = GetSelectionTotal(selections);
}
return amount;
}
private void RemoveSelectionFromList(UnitEquipmentItem item, List selections)
{
AbstractUnitEquipmentItemSelection selection = GetEquipmentSelection(item);
if (selection != null)
{
selections.Remove(selection);
}
}
private int GetSelectionTotal(List selections)
{
int amount = 0;
foreach (AbstractUnitEquipmentItemSelection selection in selections)
{
amount += selection.RawNumberTaken;
}
return amount;
}
///
/// 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
///
public ICollection 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);
}
}
}
}
}