diff API/Factories/Xml/WarFoundryXmlRaceFactory.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 077e9be48438
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/API/Factories/Xml/WarFoundryXmlRaceFactory.cs	Sun Apr 03 18:50:32 2011 +0000
@@ -0,0 +1,481 @@
+//  This file (WarFoundryXmlRaceFactory.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 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.IO;
+using System.Xml;
+using IBBoard.Xml;
+using IBBoard.IO;
+using IBBoard.Limits;
+using IBBoard.CustomMath;
+using ICSharpCode.SharpZipLib.Zip;
+using IBBoard.WarFoundry.API.Objects;
+
+namespace IBBoard.WarFoundry.API.Factories.Xml
+{
+	/// <summary>
+	/// A sub-factory for loading WarFoundry Race XML files
+	/// </summary>
+	public class WarFoundryXmlRaceFactory : AbstractStagedLoadedSubFactory
+	{
+		private Dictionary<Race, XmlDocument> extraData = new Dictionary<Race, XmlDocument>();
+		private WarFoundryXmlLimitParser limitParser = new WarFoundryXmlLimitParser();
+		
+		public WarFoundryXmlRaceFactory(WarFoundryXmlFactory factory) : base (factory)
+		{
+			//Do nothing special
+		}
+		
+		private void StoreExtraData(Race wfObject, XmlElement elem)
+		{
+			extraData[wfObject] = elem.OwnerDocument;
+		}
+	
+		private XmlDocument GetExtraData(Race obj)
+		{
+			XmlDocument extra = null;
+			extraData.TryGetValue(obj, out extra);
+			return extra;
+		}
+		
+		public Race CreateRaceFromElement(ZipFile file, XmlElement elem)
+		{
+			string id = elem.GetAttribute("id");
+			string subid = elem.GetAttribute("subid");
+			string systemID = elem.GetAttribute("system");
+			string name = elem.GetAttribute("name");
+            string armyDefaultName = elem.GetAttribute("defaultArmyName");
+			GameSystem gameSystem = WarFoundryLoader.GetDefault ().GetGameSystem (systemID);
+
+			if (gameSystem == null)
+			{
+				throw new InvalidFileException("Referenced game system, '"+systemID+"', did not exist");
+			}
+
+            Race race = new Race(id, subid, name, gameSystem, mainFactory);
+			race.ArmyDefaultName = armyDefaultName;
+			StoreExtraData(race, elem);
+			return race;
+		}
+		
+		public void CompleteLoading(Race race)
+		{
+			if (!WarFoundryXmlFactoryUtils.CanCompleteLoading(race))
+			{
+				return;
+			}
+			
+			race.SetAsLoading();			
+			XmlDocument extraData = GetExtraData(race);
+			
+			foreach (XmlElement node in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:categories/cat:cat"))
+			{
+				CreateCategoryFromElement(node, race);
+			}
+							
+			foreach (XmlElement node  in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:equipment/race:equipmentItem"))
+			{
+				CreateEquipmentItemFromElement(node, race);
+			}
+							
+			foreach (XmlElement node  in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:abilities/race:ability"))
+			{
+				CreateAbilityFromElement(node, race);
+			}
+							
+			foreach (XmlElement node  in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:memberTypes/race:memberType"))
+			{
+				CreateMemberTypeFromElement(node, race);
+			}
+			
+			foreach (XmlElement node in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:units/race:unit"))
+			{
+				GetUnitTypeForElement(node, race);
+			}
+			
+			race.SetAsFullyLoaded();
+		}
+
+		private Category CreateCategoryFromElement(XmlElement elem, Race parentRace)
+		{
+			Category cat = CreateCategoryFromElement(elem);
+			parentRace.AddCategory(cat);
+			return cat;
+		}
+
+		private UnitType GetUnitTypeFromDocument(XmlDocument doc, string id, Race parentRace)
+		{
+			XmlElement unitWithId = WarFoundryXmlFactoryUtils.SelectSingleElement (doc, "/race:race/race:units/race:unit[@id='" + id + "']");
+			
+			if (unitWithId == null)
+			{
+				throw new InvalidFileException("Could not find unit with ID "+id);
+			}
+			
+			return GetUnitTypeForElement(unitWithId, parentRace);
+		}
+						
+		private UnitType GetUnitTypeForElement(XmlElement elem, Race parentRace)
+		{
+			string id = elem.GetAttribute("id");
+			UnitType type = parentRace.GetUnitType(id);
+
+			if (type==null)
+			{
+				type = CreateUnitTypeFromElement(elem, id, parentRace);
+			}
+			
+			return type;
+		}
+
+		private UnitType CreateUnitTypeFromElement(XmlElement elem, string id, Race parentRace)
+		{
+			string name = elem.GetAttribute("typeName");
+			UnitType type = new UnitType(id, name, parentRace);
+			LoadCoreValuesForUnitType(elem, type);
+			LoadEquipmentSlotsForUnitType(elem, type);
+			LoadEquipmentForUnitType(elem, type);
+			LoadAbilitiesForUnitType(elem, type);
+			LoadContainedUnitsForUnitType(elem, type);
+			LoadRequirementsForUnitType(elem, type);
+			LoadExtraDataForUnitType(elem, type);
+			LoadNotesForUnitType(elem, type);
+			parentRace.AddUnitType(type);
+			return type;
+		}
+
+		private void LoadCoreValuesForUnitType(XmlElement elem, UnitType type)
+		{
+			try
+			{
+				type.MaxNumber = XmlTools.GetIntValueFromAttribute(elem, "maxNum");
+				type.MinNumber = XmlTools.GetIntValueFromAttribute(elem, "minNum");
+				type.MaxSize = XmlTools.GetIntValueFromAttribute(elem, "maxSize");
+				type.MinSize = XmlTools.GetIntValueFromAttribute(elem, "minSize");
+				type.BaseSize = XmlTools.GetIntValueFromAttribute(elem, "baseSize");
+				type.CostPerTrooper = XmlTools.GetDoubleValueFromAttribute(elem, "points");
+				type.BaseUnitCost = XmlTools.GetDoubleValueFromAttribute(elem, "basePoints");
+			}
+			catch (FormatException ex)
+			{
+				throw new InvalidFileException(ex.Message, ex);
+			}
+
+			Race race = type.Race;
+			string mainCatID = elem.GetAttribute("cat");
+			Category cat = race.GetCategory(mainCatID);
+			
+			if (cat == null)
+			{
+				throw new InvalidFileException(String.Format("Category with ID '{1}' did not exist for UnitType '{0}'", type.Name, mainCatID));
+			}
+			
+			type.MainCategory = cat;
+			
+			XmlNodeList unitCategories = WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitCategories/race:unitCategory");
+			
+			foreach (XmlElement unitCategory in unitCategories)
+			{
+				string catID = unitCategory.GetAttribute("catID");
+				Category unitCat = race.GetCategory(catID);
+				
+				if (unitCat == null)
+				{
+					throw new InvalidFileException(String.Format("Category with ID '{1}' did not exist for UnitType '{0}'", type.Name, catID));
+				}
+				
+				type.AddCategory(unitCat);
+			}
+			
+			XmlElement statsElement = WarFoundryXmlFactoryUtils.SelectSingleElement(elem, "race:stats");
+			
+			if (statsElement!=null)
+			{
+				Stats unitStats = ParseUnitStats(statsElement, type.GameSystem);
+				type.SetUnitStats(unitStats);
+			}
+			
+			XmlNodeList unitMemberReferences = WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitMembers/race:unitMember");
+			
+			foreach (XmlElement unitMemberRef in unitMemberReferences)
+			{
+				string typeID = unitMemberRef.GetAttribute("typeID");
+				UnitMemberType unitMemberType = race.GetUnitMemberType(typeID);
+				type.AddUnitMemberType(unitMemberType);
+			}
+		}
+
+		private void LoadEquipmentSlotsForUnitType(XmlElement elem, UnitType type)
+		{
+			foreach (XmlElement equipSlot in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:equipmentSlots/race:equipmentSlot"))
+			{
+				LoadEquipmentSlotForUnitType (type, equipSlot);
+			}
+		}
+
+		private void LoadEquipmentSlotForUnitType(UnitType type, XmlElement equipSlot)
+		{
+			string slotName = equipSlot.GetAttribute("name");
+			ILimit limit = GetMaxLimit(equipSlot);
+			
+			if (limit != null)
+			{
+				type.AddEquipmentSlot(slotName, limit);
+			}
+		}
+		
+		private ILimit GetMinLimit(XmlElement elem)
+		{
+			return limitParser.GetMinLimit(elem);
+		}
+
+		private ILimit GetMaxLimit(XmlElement elem)
+		{
+			return limitParser.GetMaxLimit(elem);
+		}
+
+		private void LoadEquipmentForUnitType(XmlElement elem, UnitType type)
+		{
+			foreach (XmlElement equip in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitEquipment/race:unitEquipmentItem"))
+			{
+				string id = equip.GetAttribute("id");
+				EquipmentItem equipItem = type.Race.GetEquipmentItem(id);
+				
+				if (equipItem!=null)
+				{
+					string mutexGroupString = equip.GetAttribute("exclusivityGroups");
+					string[] mutexGroups;
+
+					if (mutexGroupString == "")
+					{
+						mutexGroupString = equip.GetAttribute("exclusivityGroup");
+					}
+
+					if (mutexGroupString != "")
+					{
+						string[] groups = mutexGroupString.Split(',');
+						int groupCount = groups.Length;
+
+						for (int i = 0; i < groupCount; i++)
+						{
+							groups[i] = groups[i].Trim();
+						}
+
+						mutexGroups = groups;
+					}
+					else
+					{
+						mutexGroups = new string[0];
+					}
+
+					UnitEquipmentItem unitEquipItem = new UnitEquipmentItem(equipItem, type, mutexGroups);
+
+					string equipSlot = equip.GetAttribute("equipmentSlot");
+
+					if (equipSlot != "")
+					{
+						if (type.HasEquipmentSlot(equipSlot))
+						{
+							unitEquipItem.SlotName = equipSlot;
+						}
+						else
+						{
+							throw new InvalidFileException("Attribute 'equipmentSlot' of unit equipment item " + id + " for " + type.Name + " was not a valid slot name");
+						}
+					}
+
+					ILimit limit = GetMaxLimit(equip);
+
+					if (limit != null)
+					{
+						unitEquipItem.MaxLimit = limit;
+					}
+
+					limit = GetMinLimit(equip);
+
+					if (limit != null)
+					{
+						unitEquipItem.MinLimit = limit;
+					}
+					
+					unitEquipItem.RoundNumberUp = equip.GetAttribute("roundDirection").Equals("up");
+					
+					try
+					{
+						unitEquipItem.IsRequired = XmlTools.GetBoolValueFromAttribute(equip, "required");
+					}
+					catch(FormatException e)
+					{
+						throw new InvalidFileException("Attribute 'required' of unit equipment item " + id + " for " + type.Name + " was not a valid boolean", e);
+					}
+					
+					try
+					{
+						unitEquipItem.CostMultiplier = XmlTools.GetDoubleValueFromAttribute(equip, "costMultiplier");
+					}
+					catch (FormatException e)
+					{
+						throw new InvalidFileException("Attribute 'costMultiplier' of unit equipment item " + id + " for " + type.Name + " was not a valid decimal number", e);
+					}
+					
+					try
+					{
+						unitEquipItem.CostRoundType = (RoundType) Enum.Parse(typeof(RoundType), equip.GetAttribute("costRounding"));
+					}
+					catch (ArgumentException e)
+					{
+						throw new InvalidFileException("Attribute 'costRounding' of unit equipment item " + id + " for " + type.Name + " was not a valid rounding type", e);
+					}
+				}
+				else
+				{
+					throw new InvalidFileException("Equipment item with ID '" + id + "' was required by " + type.Name + " but was not found");
+				}
+			}		
+		}
+		
+		private void LoadAbilitiesForUnitType(XmlElement elem, UnitType type)
+		{
+			foreach (XmlElement abilityElem in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitAbilities/race:unitAbility"))
+			{
+				string id = abilityElem.GetAttribute("abilityID");
+				Ability ability = type.Race.GetAbility(id);
+				
+				if (ability == null)
+				{
+					throw new InvalidFileException("Ability for "+type.Name+ " with ID "+id+ " did not exist in race definition");
+				}
+
+				bool required = XmlTools.GetBoolValueFromAttribute(abilityElem, "required");
+				type.AddAbility(ability, required);
+			}
+		}
+		
+		private void LoadContainedUnitsForUnitType(XmlElement elem, UnitType type)
+		{
+			foreach (XmlElement containedUnitType in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:contains/race:containedUnit"))
+			{
+				string id = containedUnitType.GetAttribute("containedID");
+				UnitType containedType = GetUnitTypeFromDocument(elem.OwnerDocument, id, type.Race);
+
+				if (containedType!=null)
+				{
+					type.AddContainedUnitType(containedType);
+				}
+				else
+				{
+					throw new InvalidFileException("Unit type " + type.Name + " tried to contain undefined unit with ID "+id);
+				}
+			}
+		}
+
+		private void LoadRequirementsForUnitType(XmlElement elem, UnitType type)
+		{
+			//TODO: Load requirements
+		}
+		
+		private void LoadExtraDataForUnitType(XmlElement elem, UnitType type)
+		{
+			foreach (XmlElement extraData in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:extraData/race:data"))
+			{
+				string id = extraData.GetAttribute("id");
+				string data = extraData.InnerXml;
+				type.AddExtraData(id, data);
+			}
+		}
+		
+		private void LoadNotesForUnitType(XmlElement elem, UnitType type)
+		{
+			XmlNode node = WarFoundryXmlFactoryUtils.SelectSingleNode(elem, "race:notes");
+
+			if (node!=null)
+			{
+				type.Notes = node.InnerText;
+			}
+		}
+		
+		private Stats ParseUnitStats(XmlElement elem, GameSystem system)
+		{
+			if (elem == null)
+			{
+				return null;
+			}
+			
+			String statsID = elem.GetAttribute("statSet");
+			SystemStats statsSet;
+			
+			if (statsID == "")
+			{
+				statsSet = system.StandardSystemStats;
+			}
+			else
+			{
+				statsSet = system.GetSystemStatsForID(statsID);
+			}
+			
+			Stats stats = new Stats(statsSet);
+			
+			foreach (XmlElement stat in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:stat"))
+			{
+				String statName = stat.GetAttribute("name");
+				stats.SetStatValue(statName, stat.InnerText);
+			}
+			
+			return stats;
+		}
+		
+		private EquipmentItem CreateEquipmentItemFromElement(XmlElement elem, Race race)
+		{
+			string id = elem.GetAttribute("id");
+			EquipmentItem item = race.GetEquipmentItem(id);
+
+			if (item == null)
+			{
+				item = CreateEquipmentItemFromElement(elem, id, race);
+			}
+			
+			return item;
+		}
+
+		private EquipmentItem CreateEquipmentItemFromElement(XmlElement elem, string id, Race race)
+		{
+			string name = elem.GetAttribute("name");
+			EquipmentItem item = new EquipmentItem(id, name, race);
+			double cost = 0;
+			
+			try
+			{
+				cost = XmlTools.GetDoubleValueFromAttribute(elem, "cost");
+			}
+			catch(FormatException ex)
+			{
+				throw new InvalidFileException("Attribute 'cost' of equipment item "+id+" was not a valid number", ex);
+			}			
+						
+			//TODO: Parse equipment stats if there are any
+			item.Cost = cost;
+			race.AddEquipmentItem(item);			
+			return item;
+		}
+		
+		private Ability CreateAbilityFromElement(XmlElement elem, Race race)
+		{
+			string id = elem.GetAttribute("id");
+			string name = elem.GetAttribute("name");
+			Ability ability = new Ability(id, name);
+			XmlNode node = WarFoundryXmlFactoryUtils.SelectSingleNode(elem, "race:description");
+			ability.Description = (node == null) ? "" : node.InnerText;
+			race.AddAbility(ability);
+			return ability;
+		}		
+
+		private void CreateMemberTypeFromElement(XmlElement elem, Race race)
+		{
+			Stats stats = ParseUnitStats(WarFoundryXmlFactoryUtils.SelectSingleElement(elem, "race:stats"), race.GameSystem);
+			UnitMemberType unitMemberType = new UnitMemberType(elem.GetAttribute("id"), elem.GetAttribute("name"), stats);
+			race.AddUnitMemberType(unitMemberType);
+		}
+	}
+}