Mercurial > repos > IBDev-IBBoard.WarFoundry.API
view API/Factories/Xml/WarFoundryXmlRaceFactory.cs @ 376:e50682387d63
Re #353: Handle unloaded units being referenced outside factory
* Remove inheritance prior to creation of more generalised sub-factory inheritance
* Create helper class that contains the only common code from old inheritance to keep duplication down
author | IBBoard <dev@ibboard.co.uk> |
---|---|
date | Sat, 02 Jul 2011 19:58:28 +0000 |
parents | 13793f3a2a2e |
children | c3b0b28bad48 |
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; using IBBoard.WarFoundry.API.Objects.Requirement; using IBBoard.WarFoundry.API.Factories.Requirement; namespace IBBoard.WarFoundry.API.Factories.Xml { /// <summary> /// A sub-factory for loading WarFoundry Race XML files /// </summary> public class WarFoundryXmlRaceFactory { private Dictionary<Race, XmlDocument> extraData = new Dictionary<Race, XmlDocument>(); private WarFoundryXmlLimitParser limitParser = new WarFoundryXmlLimitParser(); private WarFoundryXmlFactory mainFactory; public WarFoundryXmlRaceFactory(WarFoundryXmlFactory factory) { this.mainFactory = factory; } 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 = CategoryLoader.CreateFromElement(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) { foreach (XmlElement extraData in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:requirements/race:requirement")) { string name = extraData.GetAttribute("requirementName"); UnitRequiresAtLeastNUnitsRequirementFactory reqFactory = GetRequirementFactory(name); if (reqFactory != null) { string data = WarFoundryXmlFactoryUtils.SelectSingleElement(extraData, "race:data").InnerText; UnitRequiresAtLeastNUnitsRequirement req = reqFactory.CreateRequirement(type, data); type.AddRequirement(req); } } } private UnitRequiresAtLeastNUnitsRequirementFactory GetRequirementFactory (string name) { UnitRequiresAtLeastNUnitsRequirementFactory factory = null; if ("RequiresAtLeastNUnits".Equals(name)) { factory = new UnitRequiresAtLeastNUnitsRequirementFactory(); } return factory; } 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); } } }