comparison 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
comparison
equal deleted inserted replaced
336:3631c1493c7f 337:3c4a6403a88c
1 // This file (WarFoundryXmlRaceFactory.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
2 //
3 // 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.
4
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 using System.Xml;
9 using IBBoard.Xml;
10 using IBBoard.IO;
11 using IBBoard.Limits;
12 using IBBoard.CustomMath;
13 using ICSharpCode.SharpZipLib.Zip;
14 using IBBoard.WarFoundry.API.Objects;
15
16 namespace IBBoard.WarFoundry.API.Factories.Xml
17 {
18 /// <summary>
19 /// A sub-factory for loading WarFoundry Race XML files
20 /// </summary>
21 public class WarFoundryXmlRaceFactory : AbstractStagedLoadedSubFactory
22 {
23 private Dictionary<Race, XmlDocument> extraData = new Dictionary<Race, XmlDocument>();
24 private WarFoundryXmlLimitParser limitParser = new WarFoundryXmlLimitParser();
25
26 public WarFoundryXmlRaceFactory(WarFoundryXmlFactory factory) : base (factory)
27 {
28 //Do nothing special
29 }
30
31 private void StoreExtraData(Race wfObject, XmlElement elem)
32 {
33 extraData[wfObject] = elem.OwnerDocument;
34 }
35
36 private XmlDocument GetExtraData(Race obj)
37 {
38 XmlDocument extra = null;
39 extraData.TryGetValue(obj, out extra);
40 return extra;
41 }
42
43 public Race CreateRaceFromElement(ZipFile file, XmlElement elem)
44 {
45 string id = elem.GetAttribute("id");
46 string subid = elem.GetAttribute("subid");
47 string systemID = elem.GetAttribute("system");
48 string name = elem.GetAttribute("name");
49 string armyDefaultName = elem.GetAttribute("defaultArmyName");
50 GameSystem gameSystem = WarFoundryLoader.GetDefault ().GetGameSystem (systemID);
51
52 if (gameSystem == null)
53 {
54 throw new InvalidFileException("Referenced game system, '"+systemID+"', did not exist");
55 }
56
57 Race race = new Race(id, subid, name, gameSystem, mainFactory);
58 race.ArmyDefaultName = armyDefaultName;
59 StoreExtraData(race, elem);
60 return race;
61 }
62
63 public void CompleteLoading(Race race)
64 {
65 if (!WarFoundryXmlFactoryUtils.CanCompleteLoading(race))
66 {
67 return;
68 }
69
70 race.SetAsLoading();
71 XmlDocument extraData = GetExtraData(race);
72
73 foreach (XmlElement node in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:categories/cat:cat"))
74 {
75 CreateCategoryFromElement(node, race);
76 }
77
78 foreach (XmlElement node in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:equipment/race:equipmentItem"))
79 {
80 CreateEquipmentItemFromElement(node, race);
81 }
82
83 foreach (XmlElement node in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:abilities/race:ability"))
84 {
85 CreateAbilityFromElement(node, race);
86 }
87
88 foreach (XmlElement node in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:memberTypes/race:memberType"))
89 {
90 CreateMemberTypeFromElement(node, race);
91 }
92
93 foreach (XmlElement node in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:units/race:unit"))
94 {
95 GetUnitTypeForElement(node, race);
96 }
97
98 race.SetAsFullyLoaded();
99 }
100
101 private Category CreateCategoryFromElement(XmlElement elem, Race parentRace)
102 {
103 Category cat = CreateCategoryFromElement(elem);
104 parentRace.AddCategory(cat);
105 return cat;
106 }
107
108 private UnitType GetUnitTypeFromDocument(XmlDocument doc, string id, Race parentRace)
109 {
110 XmlElement unitWithId = WarFoundryXmlFactoryUtils.SelectSingleElement (doc, "/race:race/race:units/race:unit[@id='" + id + "']");
111
112 if (unitWithId == null)
113 {
114 throw new InvalidFileException("Could not find unit with ID "+id);
115 }
116
117 return GetUnitTypeForElement(unitWithId, parentRace);
118 }
119
120 private UnitType GetUnitTypeForElement(XmlElement elem, Race parentRace)
121 {
122 string id = elem.GetAttribute("id");
123 UnitType type = parentRace.GetUnitType(id);
124
125 if (type==null)
126 {
127 type = CreateUnitTypeFromElement(elem, id, parentRace);
128 }
129
130 return type;
131 }
132
133 private UnitType CreateUnitTypeFromElement(XmlElement elem, string id, Race parentRace)
134 {
135 string name = elem.GetAttribute("typeName");
136 UnitType type = new UnitType(id, name, parentRace);
137 LoadCoreValuesForUnitType(elem, type);
138 LoadEquipmentSlotsForUnitType(elem, type);
139 LoadEquipmentForUnitType(elem, type);
140 LoadAbilitiesForUnitType(elem, type);
141 LoadContainedUnitsForUnitType(elem, type);
142 LoadRequirementsForUnitType(elem, type);
143 LoadExtraDataForUnitType(elem, type);
144 LoadNotesForUnitType(elem, type);
145 parentRace.AddUnitType(type);
146 return type;
147 }
148
149 private void LoadCoreValuesForUnitType(XmlElement elem, UnitType type)
150 {
151 try
152 {
153 type.MaxNumber = XmlTools.GetIntValueFromAttribute(elem, "maxNum");
154 type.MinNumber = XmlTools.GetIntValueFromAttribute(elem, "minNum");
155 type.MaxSize = XmlTools.GetIntValueFromAttribute(elem, "maxSize");
156 type.MinSize = XmlTools.GetIntValueFromAttribute(elem, "minSize");
157 type.BaseSize = XmlTools.GetIntValueFromAttribute(elem, "baseSize");
158 type.CostPerTrooper = XmlTools.GetDoubleValueFromAttribute(elem, "points");
159 type.BaseUnitCost = XmlTools.GetDoubleValueFromAttribute(elem, "basePoints");
160 }
161 catch (FormatException ex)
162 {
163 throw new InvalidFileException(ex.Message, ex);
164 }
165
166 Race race = type.Race;
167 string mainCatID = elem.GetAttribute("cat");
168 Category cat = race.GetCategory(mainCatID);
169
170 if (cat == null)
171 {
172 throw new InvalidFileException(String.Format("Category with ID '{1}' did not exist for UnitType '{0}'", type.Name, mainCatID));
173 }
174
175 type.MainCategory = cat;
176
177 XmlNodeList unitCategories = WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitCategories/race:unitCategory");
178
179 foreach (XmlElement unitCategory in unitCategories)
180 {
181 string catID = unitCategory.GetAttribute("catID");
182 Category unitCat = race.GetCategory(catID);
183
184 if (unitCat == null)
185 {
186 throw new InvalidFileException(String.Format("Category with ID '{1}' did not exist for UnitType '{0}'", type.Name, catID));
187 }
188
189 type.AddCategory(unitCat);
190 }
191
192 XmlElement statsElement = WarFoundryXmlFactoryUtils.SelectSingleElement(elem, "race:stats");
193
194 if (statsElement!=null)
195 {
196 Stats unitStats = ParseUnitStats(statsElement, type.GameSystem);
197 type.SetUnitStats(unitStats);
198 }
199
200 XmlNodeList unitMemberReferences = WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitMembers/race:unitMember");
201
202 foreach (XmlElement unitMemberRef in unitMemberReferences)
203 {
204 string typeID = unitMemberRef.GetAttribute("typeID");
205 UnitMemberType unitMemberType = race.GetUnitMemberType(typeID);
206 type.AddUnitMemberType(unitMemberType);
207 }
208 }
209
210 private void LoadEquipmentSlotsForUnitType(XmlElement elem, UnitType type)
211 {
212 foreach (XmlElement equipSlot in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:equipmentSlots/race:equipmentSlot"))
213 {
214 LoadEquipmentSlotForUnitType (type, equipSlot);
215 }
216 }
217
218 private void LoadEquipmentSlotForUnitType(UnitType type, XmlElement equipSlot)
219 {
220 string slotName = equipSlot.GetAttribute("name");
221 ILimit limit = GetMaxLimit(equipSlot);
222
223 if (limit != null)
224 {
225 type.AddEquipmentSlot(slotName, limit);
226 }
227 }
228
229 private ILimit GetMinLimit(XmlElement elem)
230 {
231 return limitParser.GetMinLimit(elem);
232 }
233
234 private ILimit GetMaxLimit(XmlElement elem)
235 {
236 return limitParser.GetMaxLimit(elem);
237 }
238
239 private void LoadEquipmentForUnitType(XmlElement elem, UnitType type)
240 {
241 foreach (XmlElement equip in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitEquipment/race:unitEquipmentItem"))
242 {
243 string id = equip.GetAttribute("id");
244 EquipmentItem equipItem = type.Race.GetEquipmentItem(id);
245
246 if (equipItem!=null)
247 {
248 string mutexGroupString = equip.GetAttribute("exclusivityGroups");
249 string[] mutexGroups;
250
251 if (mutexGroupString == "")
252 {
253 mutexGroupString = equip.GetAttribute("exclusivityGroup");
254 }
255
256 if (mutexGroupString != "")
257 {
258 string[] groups = mutexGroupString.Split(',');
259 int groupCount = groups.Length;
260
261 for (int i = 0; i < groupCount; i++)
262 {
263 groups[i] = groups[i].Trim();
264 }
265
266 mutexGroups = groups;
267 }
268 else
269 {
270 mutexGroups = new string[0];
271 }
272
273 UnitEquipmentItem unitEquipItem = new UnitEquipmentItem(equipItem, type, mutexGroups);
274
275 string equipSlot = equip.GetAttribute("equipmentSlot");
276
277 if (equipSlot != "")
278 {
279 if (type.HasEquipmentSlot(equipSlot))
280 {
281 unitEquipItem.SlotName = equipSlot;
282 }
283 else
284 {
285 throw new InvalidFileException("Attribute 'equipmentSlot' of unit equipment item " + id + " for " + type.Name + " was not a valid slot name");
286 }
287 }
288
289 ILimit limit = GetMaxLimit(equip);
290
291 if (limit != null)
292 {
293 unitEquipItem.MaxLimit = limit;
294 }
295
296 limit = GetMinLimit(equip);
297
298 if (limit != null)
299 {
300 unitEquipItem.MinLimit = limit;
301 }
302
303 unitEquipItem.RoundNumberUp = equip.GetAttribute("roundDirection").Equals("up");
304
305 try
306 {
307 unitEquipItem.IsRequired = XmlTools.GetBoolValueFromAttribute(equip, "required");
308 }
309 catch(FormatException e)
310 {
311 throw new InvalidFileException("Attribute 'required' of unit equipment item " + id + " for " + type.Name + " was not a valid boolean", e);
312 }
313
314 try
315 {
316 unitEquipItem.CostMultiplier = XmlTools.GetDoubleValueFromAttribute(equip, "costMultiplier");
317 }
318 catch (FormatException e)
319 {
320 throw new InvalidFileException("Attribute 'costMultiplier' of unit equipment item " + id + " for " + type.Name + " was not a valid decimal number", e);
321 }
322
323 try
324 {
325 unitEquipItem.CostRoundType = (RoundType) Enum.Parse(typeof(RoundType), equip.GetAttribute("costRounding"));
326 }
327 catch (ArgumentException e)
328 {
329 throw new InvalidFileException("Attribute 'costRounding' of unit equipment item " + id + " for " + type.Name + " was not a valid rounding type", e);
330 }
331 }
332 else
333 {
334 throw new InvalidFileException("Equipment item with ID '" + id + "' was required by " + type.Name + " but was not found");
335 }
336 }
337 }
338
339 private void LoadAbilitiesForUnitType(XmlElement elem, UnitType type)
340 {
341 foreach (XmlElement abilityElem in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitAbilities/race:unitAbility"))
342 {
343 string id = abilityElem.GetAttribute("abilityID");
344 Ability ability = type.Race.GetAbility(id);
345
346 if (ability == null)
347 {
348 throw new InvalidFileException("Ability for "+type.Name+ " with ID "+id+ " did not exist in race definition");
349 }
350
351 bool required = XmlTools.GetBoolValueFromAttribute(abilityElem, "required");
352 type.AddAbility(ability, required);
353 }
354 }
355
356 private void LoadContainedUnitsForUnitType(XmlElement elem, UnitType type)
357 {
358 foreach (XmlElement containedUnitType in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:contains/race:containedUnit"))
359 {
360 string id = containedUnitType.GetAttribute("containedID");
361 UnitType containedType = GetUnitTypeFromDocument(elem.OwnerDocument, id, type.Race);
362
363 if (containedType!=null)
364 {
365 type.AddContainedUnitType(containedType);
366 }
367 else
368 {
369 throw new InvalidFileException("Unit type " + type.Name + " tried to contain undefined unit with ID "+id);
370 }
371 }
372 }
373
374 private void LoadRequirementsForUnitType(XmlElement elem, UnitType type)
375 {
376 //TODO: Load requirements
377 }
378
379 private void LoadExtraDataForUnitType(XmlElement elem, UnitType type)
380 {
381 foreach (XmlElement extraData in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:extraData/race:data"))
382 {
383 string id = extraData.GetAttribute("id");
384 string data = extraData.InnerXml;
385 type.AddExtraData(id, data);
386 }
387 }
388
389 private void LoadNotesForUnitType(XmlElement elem, UnitType type)
390 {
391 XmlNode node = WarFoundryXmlFactoryUtils.SelectSingleNode(elem, "race:notes");
392
393 if (node!=null)
394 {
395 type.Notes = node.InnerText;
396 }
397 }
398
399 private Stats ParseUnitStats(XmlElement elem, GameSystem system)
400 {
401 if (elem == null)
402 {
403 return null;
404 }
405
406 String statsID = elem.GetAttribute("statSet");
407 SystemStats statsSet;
408
409 if (statsID == "")
410 {
411 statsSet = system.StandardSystemStats;
412 }
413 else
414 {
415 statsSet = system.GetSystemStatsForID(statsID);
416 }
417
418 Stats stats = new Stats(statsSet);
419
420 foreach (XmlElement stat in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:stat"))
421 {
422 String statName = stat.GetAttribute("name");
423 stats.SetStatValue(statName, stat.InnerText);
424 }
425
426 return stats;
427 }
428
429 private EquipmentItem CreateEquipmentItemFromElement(XmlElement elem, Race race)
430 {
431 string id = elem.GetAttribute("id");
432 EquipmentItem item = race.GetEquipmentItem(id);
433
434 if (item == null)
435 {
436 item = CreateEquipmentItemFromElement(elem, id, race);
437 }
438
439 return item;
440 }
441
442 private EquipmentItem CreateEquipmentItemFromElement(XmlElement elem, string id, Race race)
443 {
444 string name = elem.GetAttribute("name");
445 EquipmentItem item = new EquipmentItem(id, name, race);
446 double cost = 0;
447
448 try
449 {
450 cost = XmlTools.GetDoubleValueFromAttribute(elem, "cost");
451 }
452 catch(FormatException ex)
453 {
454 throw new InvalidFileException("Attribute 'cost' of equipment item "+id+" was not a valid number", ex);
455 }
456
457 //TODO: Parse equipment stats if there are any
458 item.Cost = cost;
459 race.AddEquipmentItem(item);
460 return item;
461 }
462
463 private Ability CreateAbilityFromElement(XmlElement elem, Race race)
464 {
465 string id = elem.GetAttribute("id");
466 string name = elem.GetAttribute("name");
467 Ability ability = new Ability(id, name);
468 XmlNode node = WarFoundryXmlFactoryUtils.SelectSingleNode(elem, "race:description");
469 ability.Description = (node == null) ? "" : node.InnerText;
470 race.AddAbility(ability);
471 return ability;
472 }
473
474 private void CreateMemberTypeFromElement(XmlElement elem, Race race)
475 {
476 Stats stats = ParseUnitStats(WarFoundryXmlFactoryUtils.SelectSingleElement(elem, "race:stats"), race.GameSystem);
477 UnitMemberType unitMemberType = new UnitMemberType(elem.GetAttribute("id"), elem.GetAttribute("name"), stats);
478 race.AddUnitMemberType(unitMemberType);
479 }
480 }
481 }