Mercurial > repos > IBBoard.WarFoundry.API
view api/Factories/Xml/WarFoundryXmlRaceFactory.cs @ 232:f5009a00a50d
Re #236: Handle null game system in race
* Fix error message text
no-open-ticket
author | IBBoard <dev@ibboard.co.uk> |
---|---|
date | Thu, 24 Dec 2009 14:57:32 +0000 |
parents | d1c90159547a |
children | 91ee6259677f |
line wrap: on
line source
// 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>(); 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"); 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); 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: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); } string mainCatID = elem.GetAttribute("cat"); Category cat = type.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; XmlElement statsElement = WarFoundryXmlFactoryUtils.SelectSingleElement(elem, "race:stats"); Stats unitStats = ParseUnitStats(statsElement, type.GameSystem); type.SetUnitStats(unitStats); } private void LoadEquipmentSlotsForUnitType(XmlElement elem, UnitType type) { foreach (XmlElement equipSlot in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:equipmentSlots/race:equipmentSlot")) { LoadEquipmentSlotForUnitType (type, equipSlot); } } private static void LoadEquipmentSlotForUnitType (UnitType type, XmlElement equipSlot) { string slotName = equipSlot.GetAttribute ("name"); AbstractLimit limit = GetMaxLimit (equipSlot); if (limit!=null) { type.AddEquipmentSlot (slotName, limit); } } private static AbstractLimit GetMaxLimit (XmlElement equipSlot) { XmlElement limitElem = WarFoundryXmlFactoryUtils.SelectSingleElement(equipSlot, "race:maxLimit/*[1]"); return GetLimitFromElement(limitElem); } private static AbstractLimit GetLimitFromElement(XmlElement limitElem) { AbstractLimit limit = null; if (limitElem != null) { switch (limitElem.LocalName) { case "percentageLimit": double limitPercent = XmlTools.GetDoubleValueFromAttribute(limitElem, "limit"); bool roundUp = limitElem.GetAttribute("round").Equals("up"); limit = new SimpleRoundedPercentageLimit(limitPercent, roundUp); break; case "sizeConstrainedLimit": limit = new NumericSizeConstrainedLimit(XmlTools.GetIntValueFromAttribute(limitElem, "limit")); break; case "absoluteLimit": limit = new AbsoluteNumericLimit(XmlTools.GetIntValueFromAttribute(limitElem, "limit")); break; case "unitSizeLimit": limit = new SimpleRoundedPercentageLimit(100); break; default: //TODO: Warn of missing handler for when we've extended the limit list break; } } return limit; } 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"); } } AbstractLimit 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 static AbstractLimit GetMinLimit(XmlElement elem) { XmlElement limitElem = WarFoundryXmlFactoryUtils.SelectSingleElement(elem, "race:minLimit/*[1]"); return GetLimitFromElement(limitElem); } 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) { } 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) { 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; ArmourType armourType; try { cost = XmlTools.GetDoubleValueFromAttribute(elem, "cost"); } catch(FormatException ex) { throw new InvalidFileException("Attribute 'cost' of equipment item "+id+" was not a valid number", ex); } try { armourType = (ArmourType)Enum.Parse(typeof(ArmourType), elem.GetAttribute("armourType")); } catch(ArgumentException ex) { throw new InvalidFileException("Attribute 'armourType' of equipment "+id+" was not a valid value from the enumeration", ex); } //TODO: Parse equipment stats if there are any item.Cost = cost; item.ItemArmourType = armourType; 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 = elem.SelectSingleNode("description", WarFoundryXmlFactoryUtils.GetNamespaceManager()); ability.Description = (node == null) ? "" : node.InnerText; race.AddAbility(ability); return ability; } } }