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