changeset 469:2ba1f24eb427

Re #359: Add "only contained" attribute to unit types * Add attribute to schema and load in factory Also: * Reformat line endings to be consistent
author IBBoard <dev@ibboard.co.uk>
date Sat, 14 Apr 2012 20:04:31 +0100
parents 95c1b68a600b
children 426b8c5e283c
files API/Factories/Xml/WarFoundryXmlRaceFactory.cs schemas/race.xsd
diffstat 2 files changed, 477 insertions(+), 475 deletions(-) [+]
line diff
     1.1 --- a/API/Factories/Xml/WarFoundryXmlRaceFactory.cs	Sat Apr 14 16:57:34 2012 +0100
     1.2 +++ b/API/Factories/Xml/WarFoundryXmlRaceFactory.cs	Sat Apr 14 20:04:31 2012 +0100
     1.3 @@ -1,111 +1,111 @@
     1.4 -//  This file (WarFoundryXmlRaceFactory.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
     1.5 -// 
     1.6 -// 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.
     1.7 -
     1.8 -using System;
     1.9 -using System.Collections.Generic;
    1.10 -using System.IO;
    1.11 -using System.Xml;
    1.12 -using IBBoard.Xml;
    1.13 -using IBBoard.IO;
    1.14 -using IBBoard.Limits;
    1.15 -using IBBoard.CustomMath;
    1.16 -using ICSharpCode.SharpZipLib.Zip;
    1.17 +//  This file (WarFoundryXmlRaceFactory.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
    1.18 +// 
    1.19 +// 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.
    1.20 +
    1.21 +using System;
    1.22 +using System.Collections.Generic;
    1.23 +using System.IO;
    1.24 +using System.Xml;
    1.25 +using IBBoard.Xml;
    1.26 +using IBBoard.IO;
    1.27 +using IBBoard.Limits;
    1.28 +using IBBoard.CustomMath;
    1.29 +using ICSharpCode.SharpZipLib.Zip;
    1.30  using IBBoard.WarFoundry.API.Objects;
    1.31  using IBBoard.WarFoundry.API.Objects.Requirement;
    1.32 -using IBBoard.WarFoundry.API.Factories.Requirement;
    1.33 -
    1.34 -namespace IBBoard.WarFoundry.API.Factories.Xml
    1.35 -{
    1.36 -	/// <summary>
    1.37 -	/// A sub-factory for loading WarFoundry Race XML files
    1.38 -	/// </summary>
    1.39 -	public class WarFoundryXmlRaceFactory : IRaceFactory<XmlDocument, XmlElement>
    1.40 -	{
    1.41 -		private Dictionary<Race, XmlDocument> extraData = new Dictionary<Race, XmlDocument>();
    1.42 +using IBBoard.WarFoundry.API.Factories.Requirement;
    1.43 +
    1.44 +namespace IBBoard.WarFoundry.API.Factories.Xml
    1.45 +{
    1.46 +	/// <summary>
    1.47 +	/// A sub-factory for loading WarFoundry Race XML files
    1.48 +	/// </summary>
    1.49 +	public class WarFoundryXmlRaceFactory : IRaceFactory<XmlDocument, XmlElement>
    1.50 +	{
    1.51 +		private Dictionary<Race, XmlDocument> extraData = new Dictionary<Race, XmlDocument>();
    1.52  		private WarFoundryXmlLimitParser limitParser = new WarFoundryXmlLimitParser();
    1.53 -		private WarFoundryXmlFactory mainFactory;
    1.54 -		
    1.55 -		public WarFoundryXmlRaceFactory(WarFoundryXmlFactory factory)
    1.56 -		{
    1.57 -			this.mainFactory = factory;
    1.58 -		}
    1.59 -		
    1.60 -		private void StoreExtraData(Race wfObject, XmlElement elem)
    1.61 -		{
    1.62 -			extraData[wfObject] = elem.OwnerDocument;
    1.63 -		}
    1.64 -	
    1.65 -		private XmlDocument GetExtraData(Race obj)
    1.66 -		{
    1.67 -			XmlDocument extra = null;
    1.68 -			extraData.TryGetValue(obj, out extra);
    1.69 -			return extra;
    1.70 +		private WarFoundryXmlFactory mainFactory;
    1.71 +		
    1.72 +		public WarFoundryXmlRaceFactory(WarFoundryXmlFactory factory)
    1.73 +		{
    1.74 +			this.mainFactory = factory;
    1.75 +		}
    1.76 +		
    1.77 +		private void StoreExtraData(Race wfObject, XmlElement elem)
    1.78 +		{
    1.79 +			extraData[wfObject] = elem.OwnerDocument;
    1.80 +		}
    1.81 +	
    1.82 +		private XmlDocument GetExtraData(Race obj)
    1.83 +		{
    1.84 +			XmlDocument extra = null;
    1.85 +			extraData.TryGetValue(obj, out extra);
    1.86 +			return extra;
    1.87  		}
    1.88  
    1.89  		public Race CreateRace(XmlElement elem)
    1.90  		{
    1.91 -			string id = elem.GetAttribute("id");
    1.92 -			string subid = elem.GetAttribute("subid");
    1.93 -			string systemID = elem.GetAttribute("system");
    1.94 -			string name = elem.GetAttribute("name");
    1.95 -            string armyDefaultName = elem.GetAttribute("defaultArmyName");
    1.96 -			GameSystem gameSystem = WarFoundryLoader.GetDefault ().GetGameSystem (systemID);
    1.97 -
    1.98 -			if (gameSystem == null)
    1.99 -			{
   1.100 -				throw new InvalidFileException("Referenced game system, '"+systemID+"', did not exist");
   1.101 -			}
   1.102 -
   1.103 -            Race race = new Race(id, subid, name, gameSystem, mainFactory);
   1.104 -			race.ArmyDefaultName = armyDefaultName;
   1.105 -			StoreExtraData(race, elem);
   1.106 -			return race;
   1.107 -		}
   1.108 -		
   1.109 -		public void CompleteLoading(Race race)
   1.110 -		{
   1.111 -			if (!WarFoundryXmlFactoryUtils.CanCompleteLoading(race))
   1.112 -			{
   1.113 -				return;
   1.114 -			}
   1.115 -			
   1.116 -			race.SetAsLoading();			
   1.117 -			XmlDocument extraData = GetExtraData(race);
   1.118 -			
   1.119 -			foreach (XmlElement node in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:categories/cat:cat"))
   1.120 -			{
   1.121 -				CreateCategoryFromElement(node, race);
   1.122 -			}
   1.123 -							
   1.124 -			foreach (XmlElement node  in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:equipment/race:equipmentItem"))
   1.125 -			{
   1.126 -				CreateEquipmentItemFromElement(node, race);
   1.127 -			}
   1.128 -							
   1.129 -			foreach (XmlElement node  in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:abilities/race:ability"))
   1.130 -			{
   1.131 -				CreateAbilityFromElement(node, race);
   1.132 -			}
   1.133 -							
   1.134 -			foreach (XmlElement node  in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:memberTypes/race:memberType"))
   1.135 -			{
   1.136 -				CreateMemberTypeFromElement(node, race);
   1.137 -			}
   1.138 -			
   1.139 -			foreach (XmlElement node in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:units/race:unit"))
   1.140 -			{
   1.141 -				GetUnitTypeForElement(node, race);
   1.142 -			}
   1.143 -			
   1.144 -			race.SetAsFullyLoaded();
   1.145 -		}
   1.146 -
   1.147 -		private Category CreateCategoryFromElement(XmlElement elem, Race parentRace)
   1.148 -		{
   1.149 -			Category cat = CategoryLoader.CreateFromElement(elem);
   1.150 -			parentRace.AddCategory(cat);
   1.151 -			return cat;
   1.152 +			string id = elem.GetAttribute("id");
   1.153 +			string subid = elem.GetAttribute("subid");
   1.154 +			string systemID = elem.GetAttribute("system");
   1.155 +			string name = elem.GetAttribute("name");
   1.156 +            string armyDefaultName = elem.GetAttribute("defaultArmyName");
   1.157 +			GameSystem gameSystem = WarFoundryLoader.GetDefault ().GetGameSystem (systemID);
   1.158 +
   1.159 +			if (gameSystem == null)
   1.160 +			{
   1.161 +				throw new InvalidFileException("Referenced game system, '"+systemID+"', did not exist");
   1.162 +			}
   1.163 +
   1.164 +            Race race = new Race(id, subid, name, gameSystem, mainFactory);
   1.165 +			race.ArmyDefaultName = armyDefaultName;
   1.166 +			StoreExtraData(race, elem);
   1.167 +			return race;
   1.168 +		}
   1.169 +		
   1.170 +		public void CompleteLoading(Race race)
   1.171 +		{
   1.172 +			if (!WarFoundryXmlFactoryUtils.CanCompleteLoading(race))
   1.173 +			{
   1.174 +				return;
   1.175 +			}
   1.176 +			
   1.177 +			race.SetAsLoading();			
   1.178 +			XmlDocument extraData = GetExtraData(race);
   1.179 +			
   1.180 +			foreach (XmlElement node in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:categories/cat:cat"))
   1.181 +			{
   1.182 +				CreateCategoryFromElement(node, race);
   1.183 +			}
   1.184 +							
   1.185 +			foreach (XmlElement node  in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:equipment/race:equipmentItem"))
   1.186 +			{
   1.187 +				CreateEquipmentItemFromElement(node, race);
   1.188 +			}
   1.189 +							
   1.190 +			foreach (XmlElement node  in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:abilities/race:ability"))
   1.191 +			{
   1.192 +				CreateAbilityFromElement(node, race);
   1.193 +			}
   1.194 +							
   1.195 +			foreach (XmlElement node  in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:memberTypes/race:memberType"))
   1.196 +			{
   1.197 +				CreateMemberTypeFromElement(node, race);
   1.198 +			}
   1.199 +			
   1.200 +			foreach (XmlElement node in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:units/race:unit"))
   1.201 +			{
   1.202 +				GetUnitTypeForElement(node, race);
   1.203 +			}
   1.204 +			
   1.205 +			race.SetAsFullyLoaded();
   1.206 +		}
   1.207 +
   1.208 +		private Category CreateCategoryFromElement(XmlElement elem, Race parentRace)
   1.209 +		{
   1.210 +			Category cat = CategoryLoader.CreateFromElement(elem);
   1.211 +			parentRace.AddCategory(cat);
   1.212 +			return cat;
   1.213  		}
   1.214  
   1.215  		
   1.216 @@ -124,275 +124,276 @@
   1.217  			}
   1.218  
   1.219  			return type;
   1.220 -		}
   1.221 -
   1.222 -		private UnitType GetUnitTypeFromDocument(XmlDocument doc, string id, Race parentRace)
   1.223 -		{
   1.224 -			XmlElement unitWithId = WarFoundryXmlFactoryUtils.SelectSingleElement (doc, "/race:race/race:units/race:unit[@id='" + id + "']");
   1.225 -			
   1.226 -			if (unitWithId == null)
   1.227 -			{
   1.228 -				throw new InvalidFileException("Could not find unit with ID "+id);
   1.229 -			}
   1.230 -			
   1.231 -			return GetUnitTypeForElement(unitWithId, parentRace);
   1.232 -		}
   1.233 -						
   1.234 -		private UnitType GetUnitTypeForElement(XmlElement elem, Race parentRace)
   1.235 -		{
   1.236 -			string id = elem.GetAttribute("id");
   1.237 -			UnitType type = parentRace.GetUnitType(id);
   1.238 -
   1.239 -			if (type==null)
   1.240 -			{
   1.241 -				type = CreateUnitTypeFromElement(elem, id, parentRace);
   1.242 -			}
   1.243 -			
   1.244 -			return type;
   1.245 -		}
   1.246 -
   1.247 -		private UnitType CreateUnitTypeFromElement(XmlElement elem, string id, Race parentRace)
   1.248 -		{
   1.249 -			string name = elem.GetAttribute("typeName");
   1.250 -			UnitType type = new UnitType(id, name, parentRace);
   1.251 -			LoadCoreValuesForUnitType(elem, type);
   1.252 -			parentRace.AddUnitType(type);
   1.253 -			LoadEquipmentSlotsForUnitType(elem, type);
   1.254 -			LoadEquipmentForUnitType(elem, type);
   1.255 -			LoadAbilitiesForUnitType(elem, type);
   1.256 -			LoadContainedUnitsForUnitType(elem, type);
   1.257 -			LoadRequirementsForUnitType(elem, type);
   1.258 -			LoadExtraDataForUnitType(elem, type);
   1.259 -			LoadNotesForUnitType(elem, type);
   1.260 -			return type;
   1.261 -		}
   1.262 -
   1.263 -		private void LoadCoreValuesForUnitType(XmlElement elem, UnitType type)
   1.264 -		{
   1.265 -			try
   1.266 -			{
   1.267 -				type.MaxNumber = XmlTools.GetIntValueFromAttribute(elem, "maxNum");
   1.268 -				type.MinNumber = XmlTools.GetIntValueFromAttribute(elem, "minNum");
   1.269 -				type.MaxSize = XmlTools.GetIntValueFromAttribute(elem, "maxSize");
   1.270 -				type.MinSize = XmlTools.GetIntValueFromAttribute(elem, "minSize");
   1.271 -				type.BaseSize = XmlTools.GetIntValueFromAttribute(elem, "baseSize");
   1.272 -				type.CostPerTrooper = XmlTools.GetDoubleValueFromAttribute(elem, "points");
   1.273 -				type.BaseUnitCost = XmlTools.GetDoubleValueFromAttribute(elem, "basePoints");
   1.274 -			}
   1.275 -			catch (FormatException ex)
   1.276 -			{
   1.277 -				throw new InvalidFileException(ex.Message, ex);
   1.278 -			}
   1.279 -
   1.280 -			Race race = type.Race;
   1.281 -			string mainCatID = elem.GetAttribute("cat");
   1.282 -			Category cat = race.GetCategory(mainCatID);
   1.283 -			
   1.284 -			if (cat == null)
   1.285 -			{
   1.286 -				throw new InvalidFileException(String.Format("Category with ID '{1}' did not exist for UnitType '{0}'", type.Name, mainCatID));
   1.287 -			}
   1.288 -			
   1.289 -			type.MainCategory = cat;
   1.290 -			
   1.291 -			XmlNodeList unitCategories = WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitCategories/race:unitCategory");
   1.292 -			
   1.293 -			foreach (XmlElement unitCategory in unitCategories)
   1.294 -			{
   1.295 -				string catID = unitCategory.GetAttribute("catID");
   1.296 -				Category unitCat = race.GetCategory(catID);
   1.297 -				
   1.298 -				if (unitCat == null)
   1.299 -				{
   1.300 -					throw new InvalidFileException(String.Format("Category with ID '{1}' did not exist for UnitType '{0}'", type.Name, catID));
   1.301 -				}
   1.302 -				
   1.303 -				type.AddCategory(unitCat);
   1.304 -			}
   1.305 -			
   1.306 -			XmlElement statsElement = WarFoundryXmlFactoryUtils.SelectSingleElement(elem, "race:stats");
   1.307 -			
   1.308 -			if (statsElement!=null)
   1.309 -			{
   1.310 -				Stats unitStats = ParseUnitStats(statsElement, type.GameSystem);
   1.311 -				type.SetUnitStats(unitStats);
   1.312 -			}
   1.313 -			
   1.314 -			XmlNodeList unitMemberReferences = WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitMembers/race:unitMember");
   1.315 -			
   1.316 -			foreach (XmlElement unitMemberRef in unitMemberReferences)
   1.317 -			{
   1.318 -				string typeID = unitMemberRef.GetAttribute("typeID");
   1.319 -				UnitMemberType unitMemberType = race.GetUnitMemberType(typeID);
   1.320 -				type.AddUnitMemberType(unitMemberType);
   1.321 -			}
   1.322 -		}
   1.323 -
   1.324 -		private void LoadEquipmentSlotsForUnitType(XmlElement elem, UnitType type)
   1.325 -		{
   1.326 -			foreach (XmlElement equipSlot in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:equipmentSlots/race:equipmentSlot"))
   1.327 -			{
   1.328 -				LoadEquipmentSlotForUnitType (type, equipSlot);
   1.329 -			}
   1.330 -		}
   1.331 -
   1.332 -		private void LoadEquipmentSlotForUnitType(UnitType type, XmlElement equipSlot)
   1.333 -		{
   1.334 -			string slotName = equipSlot.GetAttribute("name");
   1.335 -			ILimit limit = GetMaxLimit(equipSlot);
   1.336 -			
   1.337 -			if (limit != null)
   1.338 -			{
   1.339 -				type.AddEquipmentSlot(slotName, limit);
   1.340 -			}
   1.341 -		}
   1.342 -		
   1.343 -		private ILimit GetMinLimit(XmlElement elem)
   1.344 -		{
   1.345 -			return limitParser.GetMinLimit(elem);
   1.346 -		}
   1.347 -
   1.348 -		private ILimit GetMaxLimit(XmlElement elem)
   1.349 -		{
   1.350 -			return limitParser.GetMaxLimit(elem);
   1.351 -		}
   1.352 -
   1.353 -		private void LoadEquipmentForUnitType(XmlElement elem, UnitType type)
   1.354 -		{
   1.355 -			foreach (XmlElement equip in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitEquipment/race:unitEquipmentItem"))
   1.356 -			{
   1.357 -				string id = equip.GetAttribute("id");
   1.358 -				EquipmentItem equipItem = type.Race.GetEquipmentItem(id);
   1.359 -				
   1.360 -				if (equipItem!=null)
   1.361 -				{
   1.362 -					string mutexGroupString = equip.GetAttribute("exclusivityGroups");
   1.363 -					string[] mutexGroups;
   1.364 -
   1.365 -					if (mutexGroupString == "")
   1.366 -					{
   1.367 -						mutexGroupString = equip.GetAttribute("exclusivityGroup");
   1.368 -					}
   1.369 -
   1.370 -					if (mutexGroupString != "")
   1.371 -					{
   1.372 -						string[] groups = mutexGroupString.Split(',');
   1.373 -						int groupCount = groups.Length;
   1.374 -
   1.375 -						for (int i = 0; i < groupCount; i++)
   1.376 -						{
   1.377 -							groups[i] = groups[i].Trim();
   1.378 -						}
   1.379 -
   1.380 -						mutexGroups = groups;
   1.381 -					}
   1.382 -					else
   1.383 -					{
   1.384 -						mutexGroups = new string[0];
   1.385 -					}
   1.386 -
   1.387 -					UnitEquipmentItem unitEquipItem = new UnitEquipmentItem(equipItem, type, mutexGroups);
   1.388 -
   1.389 -					string equipSlot = equip.GetAttribute("equipmentSlot");
   1.390 -
   1.391 -					if (equipSlot != "")
   1.392 -					{
   1.393 -						if (type.HasEquipmentSlot(equipSlot))
   1.394 -						{
   1.395 -							unitEquipItem.SlotName = equipSlot;
   1.396 -						}
   1.397 -						else
   1.398 -						{
   1.399 -							throw new InvalidFileException("Attribute 'equipmentSlot' of unit equipment item " + id + " for " + type.Name + " was not a valid slot name");
   1.400 -						}
   1.401 -					}
   1.402 -
   1.403 -					ILimit limit = GetMaxLimit(equip);
   1.404 -
   1.405 -					if (limit != null)
   1.406 -					{
   1.407 -						unitEquipItem.MaxLimit = limit;
   1.408 -					}
   1.409 -
   1.410 -					limit = GetMinLimit(equip);
   1.411 -
   1.412 -					if (limit != null)
   1.413 -					{
   1.414 -						unitEquipItem.MinLimit = limit;
   1.415 -					}
   1.416 -					
   1.417 -					unitEquipItem.RoundNumberUp = equip.GetAttribute("roundDirection").Equals("up");
   1.418 -					
   1.419 -					try
   1.420 -					{
   1.421 -						unitEquipItem.IsRequired = XmlTools.GetBoolValueFromAttribute(equip, "required");
   1.422 -					}
   1.423 -					catch(FormatException e)
   1.424 -					{
   1.425 -						throw new InvalidFileException("Attribute 'required' of unit equipment item " + id + " for " + type.Name + " was not a valid boolean", e);
   1.426 -					}
   1.427 -					
   1.428 -					try
   1.429 -					{
   1.430 -						unitEquipItem.CostMultiplier = XmlTools.GetDoubleValueFromAttribute(equip, "costMultiplier");
   1.431 -					}
   1.432 -					catch (FormatException e)
   1.433 -					{
   1.434 -						throw new InvalidFileException("Attribute 'costMultiplier' of unit equipment item " + id + " for " + type.Name + " was not a valid decimal number", e);
   1.435 -					}
   1.436 -					
   1.437 -					try
   1.438 -					{
   1.439 -						unitEquipItem.CostRoundType = (RoundType) Enum.Parse(typeof(RoundType), equip.GetAttribute("costRounding"));
   1.440 -					}
   1.441 -					catch (ArgumentException e)
   1.442 -					{
   1.443 -						throw new InvalidFileException("Attribute 'costRounding' of unit equipment item " + id + " for " + type.Name + " was not a valid rounding type", e);
   1.444 -					}
   1.445 -				}
   1.446 -				else
   1.447 -				{
   1.448 -					throw new InvalidFileException("Equipment item with ID '" + id + "' was required by " + type.Name + " but was not found");
   1.449 -				}
   1.450 -			}		
   1.451 -		}
   1.452 -		
   1.453 -		private void LoadAbilitiesForUnitType(XmlElement elem, UnitType type)
   1.454 -		{
   1.455 -			foreach (XmlElement abilityElem in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitAbilities/race:unitAbility"))
   1.456 -			{
   1.457 -				string id = abilityElem.GetAttribute("abilityID");
   1.458 -				Ability ability = type.Race.GetAbility(id);
   1.459 -				
   1.460 -				if (ability == null)
   1.461 -				{
   1.462 -					throw new InvalidFileException("Ability for "+type.Name+ " with ID "+id+ " did not exist in race definition");
   1.463 -				}
   1.464 -
   1.465 -				bool required = XmlTools.GetBoolValueFromAttribute(abilityElem, "required");
   1.466 -				type.AddAbility(ability, required);
   1.467 -			}
   1.468 -		}
   1.469 -		
   1.470 -		private void LoadContainedUnitsForUnitType(XmlElement elem, UnitType type)
   1.471 -		{
   1.472 -			foreach (XmlElement containedUnitType in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:contains/race:containedUnit"))
   1.473 -			{
   1.474 -				string id = containedUnitType.GetAttribute("containedID");
   1.475 -				UnitType containedType = GetUnitTypeFromDocument(elem.OwnerDocument, id, type.Race);
   1.476 -
   1.477 -				if (containedType!=null)
   1.478 -				{
   1.479 -					type.AddContainedUnitType(containedType);
   1.480 -				}
   1.481 -				else
   1.482 -				{
   1.483 -					throw new InvalidFileException("Unit type " + type.Name + " tried to contain undefined unit with ID "+id);
   1.484 -				}
   1.485 -			}
   1.486 -		}
   1.487 -
   1.488 -		private void LoadRequirementsForUnitType(XmlElement elem, UnitType type)
   1.489 +		}
   1.490 +
   1.491 +		private UnitType GetUnitTypeFromDocument(XmlDocument doc, string id, Race parentRace)
   1.492 +		{
   1.493 +			XmlElement unitWithId = WarFoundryXmlFactoryUtils.SelectSingleElement (doc, "/race:race/race:units/race:unit[@id='" + id + "']");
   1.494 +			
   1.495 +			if (unitWithId == null)
   1.496 +			{
   1.497 +				throw new InvalidFileException("Could not find unit with ID "+id);
   1.498 +			}
   1.499 +			
   1.500 +			return GetUnitTypeForElement(unitWithId, parentRace);
   1.501 +		}
   1.502 +						
   1.503 +		private UnitType GetUnitTypeForElement(XmlElement elem, Race parentRace)
   1.504 +		{
   1.505 +			string id = elem.GetAttribute("id");
   1.506 +			UnitType type = parentRace.GetUnitType(id);
   1.507 +
   1.508 +			if (type==null)
   1.509 +			{
   1.510 +				type = CreateUnitTypeFromElement(elem, id, parentRace);
   1.511 +			}
   1.512 +			
   1.513 +			return type;
   1.514 +		}
   1.515 +
   1.516 +		private UnitType CreateUnitTypeFromElement(XmlElement elem, string id, Race parentRace)
   1.517 +		{
   1.518 +			string name = elem.GetAttribute("typeName");
   1.519 +			UnitType type = new UnitType(id, name, parentRace);
   1.520 +			LoadCoreValuesForUnitType(elem, type);
   1.521 +			parentRace.AddUnitType(type);
   1.522 +			LoadEquipmentSlotsForUnitType(elem, type);
   1.523 +			LoadEquipmentForUnitType(elem, type);
   1.524 +			LoadAbilitiesForUnitType(elem, type);
   1.525 +			LoadContainedUnitsForUnitType(elem, type);
   1.526 +			LoadRequirementsForUnitType(elem, type);
   1.527 +			LoadExtraDataForUnitType(elem, type);
   1.528 +			LoadNotesForUnitType(elem, type);
   1.529 +			return type;
   1.530 +		}
   1.531 +
   1.532 +		private void LoadCoreValuesForUnitType(XmlElement elem, UnitType type)
   1.533 +		{
   1.534 +			try
   1.535 +			{
   1.536 +				type.MaxNumber = XmlTools.GetIntValueFromAttribute(elem, "maxNum");
   1.537 +				type.MinNumber = XmlTools.GetIntValueFromAttribute(elem, "minNum");
   1.538 +				type.MaxSize = XmlTools.GetIntValueFromAttribute(elem, "maxSize");
   1.539 +				type.MinSize = XmlTools.GetIntValueFromAttribute(elem, "minSize");
   1.540 +				type.BaseSize = XmlTools.GetIntValueFromAttribute(elem, "baseSize");
   1.541 +				type.CostPerTrooper = XmlTools.GetDoubleValueFromAttribute(elem, "points");
   1.542 +				type.BaseUnitCost = XmlTools.GetDoubleValueFromAttribute(elem, "basePoints");
   1.543 +				type.IsContainedOnly = XmlTools.GetBoolValueFromAttribute(elem, "containedOnly");
   1.544 +			}
   1.545 +			catch (FormatException ex)
   1.546 +			{
   1.547 +				throw new InvalidFileException(ex.Message, ex);
   1.548 +			}
   1.549 +
   1.550 +			Race race = type.Race;
   1.551 +			string mainCatID = elem.GetAttribute("cat");
   1.552 +			Category cat = race.GetCategory(mainCatID);
   1.553 +			
   1.554 +			if (cat == null)
   1.555 +			{
   1.556 +				throw new InvalidFileException(String.Format("Category with ID '{1}' did not exist for UnitType '{0}'", type.Name, mainCatID));
   1.557 +			}
   1.558 +			
   1.559 +			type.MainCategory = cat;
   1.560 +			
   1.561 +			XmlNodeList unitCategories = WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitCategories/race:unitCategory");
   1.562 +			
   1.563 +			foreach (XmlElement unitCategory in unitCategories)
   1.564 +			{
   1.565 +				string catID = unitCategory.GetAttribute("catID");
   1.566 +				Category unitCat = race.GetCategory(catID);
   1.567 +				
   1.568 +				if (unitCat == null)
   1.569 +				{
   1.570 +					throw new InvalidFileException(String.Format("Category with ID '{1}' did not exist for UnitType '{0}'", type.Name, catID));
   1.571 +				}
   1.572 +				
   1.573 +				type.AddCategory(unitCat);
   1.574 +			}
   1.575 +			
   1.576 +			XmlElement statsElement = WarFoundryXmlFactoryUtils.SelectSingleElement(elem, "race:stats");
   1.577 +			
   1.578 +			if (statsElement!=null)
   1.579 +			{
   1.580 +				Stats unitStats = ParseUnitStats(statsElement, type.GameSystem);
   1.581 +				type.SetUnitStats(unitStats);
   1.582 +			}
   1.583 +			
   1.584 +			XmlNodeList unitMemberReferences = WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitMembers/race:unitMember");
   1.585 +			
   1.586 +			foreach (XmlElement unitMemberRef in unitMemberReferences)
   1.587 +			{
   1.588 +				string typeID = unitMemberRef.GetAttribute("typeID");
   1.589 +				UnitMemberType unitMemberType = race.GetUnitMemberType(typeID);
   1.590 +				type.AddUnitMemberType(unitMemberType);
   1.591 +			}
   1.592 +		}
   1.593 +
   1.594 +		private void LoadEquipmentSlotsForUnitType(XmlElement elem, UnitType type)
   1.595 +		{
   1.596 +			foreach (XmlElement equipSlot in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:equipmentSlots/race:equipmentSlot"))
   1.597 +			{
   1.598 +				LoadEquipmentSlotForUnitType (type, equipSlot);
   1.599 +			}
   1.600 +		}
   1.601 +
   1.602 +		private void LoadEquipmentSlotForUnitType(UnitType type, XmlElement equipSlot)
   1.603 +		{
   1.604 +			string slotName = equipSlot.GetAttribute("name");
   1.605 +			ILimit limit = GetMaxLimit(equipSlot);
   1.606 +			
   1.607 +			if (limit != null)
   1.608 +			{
   1.609 +				type.AddEquipmentSlot(slotName, limit);
   1.610 +			}
   1.611 +		}
   1.612 +		
   1.613 +		private ILimit GetMinLimit(XmlElement elem)
   1.614 +		{
   1.615 +			return limitParser.GetMinLimit(elem);
   1.616 +		}
   1.617 +
   1.618 +		private ILimit GetMaxLimit(XmlElement elem)
   1.619 +		{
   1.620 +			return limitParser.GetMaxLimit(elem);
   1.621 +		}
   1.622 +
   1.623 +		private void LoadEquipmentForUnitType(XmlElement elem, UnitType type)
   1.624 +		{
   1.625 +			foreach (XmlElement equip in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitEquipment/race:unitEquipmentItem"))
   1.626 +			{
   1.627 +				string id = equip.GetAttribute("id");
   1.628 +				EquipmentItem equipItem = type.Race.GetEquipmentItem(id);
   1.629 +				
   1.630 +				if (equipItem!=null)
   1.631 +				{
   1.632 +					string mutexGroupString = equip.GetAttribute("exclusivityGroups");
   1.633 +					string[] mutexGroups;
   1.634 +
   1.635 +					if (mutexGroupString == "")
   1.636 +					{
   1.637 +						mutexGroupString = equip.GetAttribute("exclusivityGroup");
   1.638 +					}
   1.639 +
   1.640 +					if (mutexGroupString != "")
   1.641 +					{
   1.642 +						string[] groups = mutexGroupString.Split(',');
   1.643 +						int groupCount = groups.Length;
   1.644 +
   1.645 +						for (int i = 0; i < groupCount; i++)
   1.646 +						{
   1.647 +							groups[i] = groups[i].Trim();
   1.648 +						}
   1.649 +
   1.650 +						mutexGroups = groups;
   1.651 +					}
   1.652 +					else
   1.653 +					{
   1.654 +						mutexGroups = new string[0];
   1.655 +					}
   1.656 +
   1.657 +					UnitEquipmentItem unitEquipItem = new UnitEquipmentItem(equipItem, type, mutexGroups);
   1.658 +
   1.659 +					string equipSlot = equip.GetAttribute("equipmentSlot");
   1.660 +
   1.661 +					if (equipSlot != "")
   1.662 +					{
   1.663 +						if (type.HasEquipmentSlot(equipSlot))
   1.664 +						{
   1.665 +							unitEquipItem.SlotName = equipSlot;
   1.666 +						}
   1.667 +						else
   1.668 +						{
   1.669 +							throw new InvalidFileException("Attribute 'equipmentSlot' of unit equipment item " + id + " for " + type.Name + " was not a valid slot name");
   1.670 +						}
   1.671 +					}
   1.672 +
   1.673 +					ILimit limit = GetMaxLimit(equip);
   1.674 +
   1.675 +					if (limit != null)
   1.676 +					{
   1.677 +						unitEquipItem.MaxLimit = limit;
   1.678 +					}
   1.679 +
   1.680 +					limit = GetMinLimit(equip);
   1.681 +
   1.682 +					if (limit != null)
   1.683 +					{
   1.684 +						unitEquipItem.MinLimit = limit;
   1.685 +					}
   1.686 +					
   1.687 +					unitEquipItem.RoundNumberUp = equip.GetAttribute("roundDirection").Equals("up");
   1.688 +					
   1.689 +					try
   1.690 +					{
   1.691 +						unitEquipItem.IsRequired = XmlTools.GetBoolValueFromAttribute(equip, "required");
   1.692 +					}
   1.693 +					catch(FormatException e)
   1.694 +					{
   1.695 +						throw new InvalidFileException("Attribute 'required' of unit equipment item " + id + " for " + type.Name + " was not a valid boolean", e);
   1.696 +					}
   1.697 +					
   1.698 +					try
   1.699 +					{
   1.700 +						unitEquipItem.CostMultiplier = XmlTools.GetDoubleValueFromAttribute(equip, "costMultiplier");
   1.701 +					}
   1.702 +					catch (FormatException e)
   1.703 +					{
   1.704 +						throw new InvalidFileException("Attribute 'costMultiplier' of unit equipment item " + id + " for " + type.Name + " was not a valid decimal number", e);
   1.705 +					}
   1.706 +					
   1.707 +					try
   1.708 +					{
   1.709 +						unitEquipItem.CostRoundType = (RoundType) Enum.Parse(typeof(RoundType), equip.GetAttribute("costRounding"));
   1.710 +					}
   1.711 +					catch (ArgumentException e)
   1.712 +					{
   1.713 +						throw new InvalidFileException("Attribute 'costRounding' of unit equipment item " + id + " for " + type.Name + " was not a valid rounding type", e);
   1.714 +					}
   1.715 +				}
   1.716 +				else
   1.717 +				{
   1.718 +					throw new InvalidFileException("Equipment item with ID '" + id + "' was required by " + type.Name + " but was not found");
   1.719 +				}
   1.720 +			}		
   1.721 +		}
   1.722 +		
   1.723 +		private void LoadAbilitiesForUnitType(XmlElement elem, UnitType type)
   1.724 +		{
   1.725 +			foreach (XmlElement abilityElem in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitAbilities/race:unitAbility"))
   1.726 +			{
   1.727 +				string id = abilityElem.GetAttribute("abilityID");
   1.728 +				Ability ability = type.Race.GetAbility(id);
   1.729 +				
   1.730 +				if (ability == null)
   1.731 +				{
   1.732 +					throw new InvalidFileException("Ability for "+type.Name+ " with ID "+id+ " did not exist in race definition");
   1.733 +				}
   1.734 +
   1.735 +				bool required = XmlTools.GetBoolValueFromAttribute(abilityElem, "required");
   1.736 +				type.AddAbility(ability, required);
   1.737 +			}
   1.738 +		}
   1.739 +		
   1.740 +		private void LoadContainedUnitsForUnitType(XmlElement elem, UnitType type)
   1.741 +		{
   1.742 +			foreach (XmlElement containedUnitType in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:contains/race:containedUnit"))
   1.743 +			{
   1.744 +				string id = containedUnitType.GetAttribute("containedID");
   1.745 +				UnitType containedType = GetUnitTypeFromDocument(elem.OwnerDocument, id, type.Race);
   1.746 +
   1.747 +				if (containedType!=null)
   1.748 +				{
   1.749 +					type.AddContainedUnitType(containedType);
   1.750 +				}
   1.751 +				else
   1.752 +				{
   1.753 +					throw new InvalidFileException("Unit type " + type.Name + " tried to contain undefined unit with ID "+id);
   1.754 +				}
   1.755 +			}
   1.756 +		}
   1.757 +
   1.758 +		private void LoadRequirementsForUnitType(XmlElement elem, UnitType type)
   1.759  		{
   1.760  			foreach (XmlElement extraData in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:requirements/race:requirement"))
   1.761  			{
   1.762 @@ -404,109 +405,109 @@
   1.763  					IRequirement req = reqFactory.CreateRequirement(type, data, this);
   1.764  					type.AddRequirement(req);
   1.765  				}
   1.766 -			}
   1.767 +			}
   1.768  		}
   1.769 -		
   1.770 -		private void LoadExtraDataForUnitType(XmlElement elem, UnitType type)
   1.771 -		{
   1.772 -			foreach (XmlElement extraData in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:extraData/race:data"))
   1.773 -			{
   1.774 -				string id = extraData.GetAttribute("id");
   1.775 -				string data = extraData.InnerXml;
   1.776 -				type.AddExtraData(id, data);
   1.777 -			}
   1.778 -		}
   1.779 -		
   1.780 -		private void LoadNotesForUnitType(XmlElement elem, UnitType type)
   1.781 -		{
   1.782 -			XmlNode node = WarFoundryXmlFactoryUtils.SelectSingleNode(elem, "race:notes");
   1.783 -
   1.784 -			if (node!=null)
   1.785 -			{
   1.786 -				type.Notes = node.InnerText;
   1.787 -			}
   1.788 -		}
   1.789 -		
   1.790 -		private Stats ParseUnitStats(XmlElement elem, GameSystem system)
   1.791 -		{
   1.792 -			if (elem == null)
   1.793 -			{
   1.794 -				return null;
   1.795 -			}
   1.796 -			
   1.797 -			String statsID = elem.GetAttribute("statSet");
   1.798 -			SystemStats statsSet;
   1.799 -			
   1.800 -			if (statsID == "")
   1.801 -			{
   1.802 -				statsSet = system.StandardSystemStats;
   1.803 -			}
   1.804 -			else
   1.805 -			{
   1.806 -				statsSet = system.GetSystemStatsForID(statsID);
   1.807 -			}
   1.808 -			
   1.809 -			Stats stats = new Stats(statsSet);
   1.810 -			
   1.811 -			foreach (XmlElement stat in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:stat"))
   1.812 -			{
   1.813 -				String statName = stat.GetAttribute("name");
   1.814 -				stats.SetStatValue(statName, stat.InnerText);
   1.815 -			}
   1.816 -			
   1.817 -			return stats;
   1.818 -		}
   1.819 -		
   1.820 -		private EquipmentItem CreateEquipmentItemFromElement(XmlElement elem, Race race)
   1.821 -		{
   1.822 -			string id = elem.GetAttribute("id");
   1.823 -			EquipmentItem item = race.GetEquipmentItem(id);
   1.824 -
   1.825 -			if (item == null)
   1.826 -			{
   1.827 -				item = CreateEquipmentItemFromElement(elem, id, race);
   1.828 -			}
   1.829 -			
   1.830 -			return item;
   1.831 -		}
   1.832 -
   1.833 -		private EquipmentItem CreateEquipmentItemFromElement(XmlElement elem, string id, Race race)
   1.834 -		{
   1.835 -			string name = elem.GetAttribute("name");
   1.836 -			EquipmentItem item = new EquipmentItem(id, name, race);
   1.837 -			double cost = 0;
   1.838 -			
   1.839 -			try
   1.840 -			{
   1.841 -				cost = XmlTools.GetDoubleValueFromAttribute(elem, "cost");
   1.842 -			}
   1.843 -			catch(FormatException ex)
   1.844 -			{
   1.845 -				throw new InvalidFileException("Attribute 'cost' of equipment item "+id+" was not a valid number", ex);
   1.846 -			}			
   1.847 -						
   1.848 -			//TODO: Parse equipment stats if there are any
   1.849 -			item.Cost = cost;
   1.850 -			race.AddEquipmentItem(item);			
   1.851 -			return item;
   1.852 -		}
   1.853 -		
   1.854 -		private Ability CreateAbilityFromElement(XmlElement elem, Race race)
   1.855 -		{
   1.856 -			string id = elem.GetAttribute("id");
   1.857 -			string name = elem.GetAttribute("name");
   1.858 -			Ability ability = new Ability(id, name);
   1.859 -			XmlNode node = WarFoundryXmlFactoryUtils.SelectSingleNode(elem, "race:description");
   1.860 -			ability.Description = (node == null) ? "" : node.InnerText;
   1.861 -			race.AddAbility(ability);
   1.862 -			return ability;
   1.863 -		}		
   1.864 -
   1.865 -		private void CreateMemberTypeFromElement(XmlElement elem, Race race)
   1.866 -		{
   1.867 -			Stats stats = ParseUnitStats(WarFoundryXmlFactoryUtils.SelectSingleElement(elem, "race:stats"), race.GameSystem);
   1.868 -			UnitMemberType unitMemberType = new UnitMemberType(elem.GetAttribute("id"), elem.GetAttribute("name"), stats);
   1.869 -			race.AddUnitMemberType(unitMemberType);
   1.870 -		}
   1.871 -	}
   1.872 -}
   1.873 +		
   1.874 +		private void LoadExtraDataForUnitType(XmlElement elem, UnitType type)
   1.875 +		{
   1.876 +			foreach (XmlElement extraData in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:extraData/race:data"))
   1.877 +			{
   1.878 +				string id = extraData.GetAttribute("id");
   1.879 +				string data = extraData.InnerXml;
   1.880 +				type.AddExtraData(id, data);
   1.881 +			}
   1.882 +		}
   1.883 +		
   1.884 +		private void LoadNotesForUnitType(XmlElement elem, UnitType type)
   1.885 +		{
   1.886 +			XmlNode node = WarFoundryXmlFactoryUtils.SelectSingleNode(elem, "race:notes");
   1.887 +
   1.888 +			if (node!=null)
   1.889 +			{
   1.890 +				type.Notes = node.InnerText;
   1.891 +			}
   1.892 +		}
   1.893 +		
   1.894 +		private Stats ParseUnitStats(XmlElement elem, GameSystem system)
   1.895 +		{
   1.896 +			if (elem == null)
   1.897 +			{
   1.898 +				return null;
   1.899 +			}
   1.900 +			
   1.901 +			String statsID = elem.GetAttribute("statSet");
   1.902 +			SystemStats statsSet;
   1.903 +			
   1.904 +			if (statsID == "")
   1.905 +			{
   1.906 +				statsSet = system.StandardSystemStats;
   1.907 +			}
   1.908 +			else
   1.909 +			{
   1.910 +				statsSet = system.GetSystemStatsForID(statsID);
   1.911 +			}
   1.912 +			
   1.913 +			Stats stats = new Stats(statsSet);
   1.914 +			
   1.915 +			foreach (XmlElement stat in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:stat"))
   1.916 +			{
   1.917 +				String statName = stat.GetAttribute("name");
   1.918 +				stats.SetStatValue(statName, stat.InnerText);
   1.919 +			}
   1.920 +			
   1.921 +			return stats;
   1.922 +		}
   1.923 +		
   1.924 +		private EquipmentItem CreateEquipmentItemFromElement(XmlElement elem, Race race)
   1.925 +		{
   1.926 +			string id = elem.GetAttribute("id");
   1.927 +			EquipmentItem item = race.GetEquipmentItem(id);
   1.928 +
   1.929 +			if (item == null)
   1.930 +			{
   1.931 +				item = CreateEquipmentItemFromElement(elem, id, race);
   1.932 +			}
   1.933 +			
   1.934 +			return item;
   1.935 +		}
   1.936 +
   1.937 +		private EquipmentItem CreateEquipmentItemFromElement(XmlElement elem, string id, Race race)
   1.938 +		{
   1.939 +			string name = elem.GetAttribute("name");
   1.940 +			EquipmentItem item = new EquipmentItem(id, name, race);
   1.941 +			double cost = 0;
   1.942 +			
   1.943 +			try
   1.944 +			{
   1.945 +				cost = XmlTools.GetDoubleValueFromAttribute(elem, "cost");
   1.946 +			}
   1.947 +			catch(FormatException ex)
   1.948 +			{
   1.949 +				throw new InvalidFileException("Attribute 'cost' of equipment item "+id+" was not a valid number", ex);
   1.950 +			}			
   1.951 +						
   1.952 +			//TODO: Parse equipment stats if there are any
   1.953 +			item.Cost = cost;
   1.954 +			race.AddEquipmentItem(item);			
   1.955 +			return item;
   1.956 +		}
   1.957 +		
   1.958 +		private Ability CreateAbilityFromElement(XmlElement elem, Race race)
   1.959 +		{
   1.960 +			string id = elem.GetAttribute("id");
   1.961 +			string name = elem.GetAttribute("name");
   1.962 +			Ability ability = new Ability(id, name);
   1.963 +			XmlNode node = WarFoundryXmlFactoryUtils.SelectSingleNode(elem, "race:description");
   1.964 +			ability.Description = (node == null) ? "" : node.InnerText;
   1.965 +			race.AddAbility(ability);
   1.966 +			return ability;
   1.967 +		}		
   1.968 +
   1.969 +		private void CreateMemberTypeFromElement(XmlElement elem, Race race)
   1.970 +		{
   1.971 +			Stats stats = ParseUnitStats(WarFoundryXmlFactoryUtils.SelectSingleElement(elem, "race:stats"), race.GameSystem);
   1.972 +			UnitMemberType unitMemberType = new UnitMemberType(elem.GetAttribute("id"), elem.GetAttribute("name"), stats);
   1.973 +			race.AddUnitMemberType(unitMemberType);
   1.974 +		}
   1.975 +	}
   1.976 +}
     2.1 --- a/schemas/race.xsd	Sat Apr 14 16:57:34 2012 +0100
     2.2 +++ b/schemas/race.xsd	Sat Apr 14 20:04:31 2012 +0100
     2.3 @@ -273,6 +273,7 @@
     2.4  	<xs:attribute name="maxSize" type="core:positiveOrInfiniteInteger" default="-1"/>
     2.5  	<xs:attribute name="minNum" type="xs:nonNegativeInteger" default="0"/>
     2.6  	<xs:attribute name="maxNum" type="core:positiveOrInfiniteInteger" default="-1"/>
     2.7 +	<xs:attribute name="containedOnly" type="xs:boolean" default="false" />
     2.8  	<xs:anyAttribute processContents="lax"/>
     2.9  </xs:complexType>
    2.10