comparison API/Objects/UnitType.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 d34ae0057a39
comparison
equal deleted inserted replaced
336:3631c1493c7f 337:3c4a6403a88c
1 // This file (UnitType.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 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.Xml;
8 using IBBoard.Limits;
9 using IBBoard.Logging;
10 using IBBoard.WarFoundry.API.Requirements;
11
12 namespace IBBoard.WarFoundry.API.Objects
13 {
14 /// <summary>
15 /// A UnitType is a type for a <see cref=" Unit"/>, normally relating to an entry in an army list. The UnitType defines the name, cost, minimum and maximum limits of a unit, and the equipment units of the type can take.
16 /// </summary>
17 public class UnitType : WarFoundryObject
18 {
19 private Category mainCat;
20 private Race race;
21 private int min, max, baseSize = 0;
22 private int minSize, maxSize;
23 private double baseUnitCost;
24 private double costPerTrooper;
25 private Stats stats;
26 private List<UnitRequirement> requirements = new List<UnitRequirement>();
27 private Dictionary<string, UnitEquipmentItem> equipment = new Dictionary<string, UnitEquipmentItem>();
28 private Dictionary<string, List<UnitEquipmentItem>> equipmentExclusionGroups = new Dictionary<string, List<UnitEquipmentItem>>();
29 private List<string> equipmentKeyOrder = new List<string>();
30 private Dictionary<string, Ability> requiredAbilities = new Dictionary<string, Ability>();
31 private Dictionary<string, Ability> optionalAbilities = new Dictionary<string, Ability>();
32 private String notes = "";
33 private List<UnitType> containedTypes = new List<UnitType>();
34 private Dictionary<string, string> extraData = new Dictionary<string, string>();
35 private Dictionary<string, ILimit> slotLimits = new Dictionary<string, ILimit>();
36 private Dictionary<string, UnitMemberType> unitMemberTypes = new Dictionary<string, UnitMemberType>();
37 private List<Category> cats = new List<Category>();
38
39
40 public UnitType(string id, string typeName, Race parentRace) : base(id, typeName)
41 {
42 race = parentRace;
43 }
44
45 public GameSystem GameSystem
46 {
47 get { return Race.GameSystem; }
48 }
49
50 /// <value>
51 /// Gets the <see cref=" Race"/> that this unit belongs to.
52 /// </value>
53 public Race Race
54 {
55 get { return race; }
56 }
57
58 /// <value>
59 /// Gets or sets the default <see cref=" Category"/> that this unit type is a member of.
60 /// If it is not already in the collection of categories then it will be added.
61 /// </value>
62 public virtual Category MainCategory
63 {
64 get
65 {
66 return mainCat;
67 }
68 set
69 {
70 mainCat = value;
71 AddCategory(value);
72 }
73 }
74 /// <summary>
75 /// Gets the collection of <see cref="Category"/> objects that this UnitType can be a member of
76 /// </summary>
77 public Category[] Categories
78 {
79 get
80 {
81 return cats.ToArray();
82 }
83 }
84
85 /// <summary>
86 /// Adds a category to the set of categories that this unit can be taken from. The first category added will automatically become the MainCategory.
87 /// </summary>
88 /// <param name="cat">
89 /// A <see cref="Category"/> that this unit can be taken from
90 /// </param>
91 public void AddCategory(Category cat)
92 {
93 if (!cats.Contains(cat))
94 {
95 cats.Add(cat);
96
97 if (MainCategory == null)
98 {
99 MainCategory = cat;
100 }
101 }
102 }
103
104 /// <value>
105 /// Gets or sets the minimum size of each unit of this type. Note: This should be set AFTER MaxSize, otherwise an unintended default value may be set for the minimum
106 /// </value>
107 public int MinSize
108 {
109 get { return minSize; }
110 set
111 {
112 minSize = (value >= 0 ? value : 0);
113 CheckMinimumSize();
114 }
115 }
116
117 /// <value>
118 /// Gets or sets the maximum size of each unit of this type. Note: This should be set BEFORE MinSize, otherwise an unintended default value may be set for the minimum
119 /// </value>
120 public int MaxSize
121 {
122 get { return maxSize; }
123 set
124 {
125 maxSize = (value >= 0 ? value : WarFoundryCore.INFINITY);
126 CheckMinimumSize();
127 }
128 }
129
130 /// <value>
131 /// Gets or sets the minimum number of units of this type that must be taken in an army. Note: This should be set AFTER MaxNumber, otherwise an unintended default value may be set for the minimum
132 /// </value>
133 public int MinNumber
134 {
135 get { return min; }
136 set
137 {
138 min = (value >= 0 ? value : 0);
139 CheckMinimumNumber();
140 }
141 }
142
143 /// <value>
144 /// Gets or sets the maximum number of units of this type that can be taken in an army. Note: This should be set BEFORE MinNumber, otherwise an unintended default value may be set for the minimum
145 /// </value>
146 public int MaxNumber
147 {
148 get { return max; }
149 set
150 {
151 max = (value >= 0 ? value : WarFoundryCore.INFINITY);
152 CheckMinimumNumber();
153 }
154 }
155
156 /// <summary>
157 /// Makes sure that the minimum number isn't more than the maximum number, hence the warning on the properties
158 /// </summary>
159 private void CheckMinimumNumber()
160 {
161 if (MinNumber > MaxNumber && MaxNumber!=WarFoundryCore.INFINITY)
162 {
163 MinNumber = MaxNumber;
164 LogNotifier.WarnFormat(GetType(), "Unit type {0} ({1}) had a minimum number greater than their maximum number.", Name, ID);
165 }
166 }
167
168 /// <summary>
169 /// Makes sure that the minimum unit size isn't more than the maximum unit size, hence the warning on the properties
170 /// </summary>
171 private void CheckMinimumSize()
172 {
173 if (MinSize > MaxSize && MaxSize!=WarFoundryCore.INFINITY)
174 {
175 MinSize = MaxSize;
176 LogNotifier.WarnFormat(GetType(), "Unit type {0} ({1}) had a minimum size greater than their maximum size.", Name, ID);
177 }
178 }
179
180 //// <value>
181 /// Gets or sets the "base size" of a unit, which is the number of troopers the unit has in it for its "base cost". For a lot of units this value will be 0 as the cost is worked out based on the total number of members.
182 /// </value>
183 public int BaseSize
184 {
185 get { return baseSize; }
186 set { baseSize = (value >= 0 ? value : 0); }
187 }
188
189 /// <value>
190 /// The number of points that a "base unit" of <code>BaseSize</code> models costs. Additional models are charged at <code>CostPerTrooper</code> each.
191 /// </value>
192 public double BaseUnitCost
193 {
194 get { return baseUnitCost; }
195 set { baseUnitCost = (value >= 0 ? value : 0); }
196 }
197
198 //// <value>
199 /// The cost of an individual trooper. This value is the cost for a basic trooper without weapons, which are added on top of the cost before calculating a unit cost.
200 /// </value>
201 public double CostPerTrooper
202 {
203 get { return costPerTrooper; }
204 set { costPerTrooper = (value >= 0 ? value : 0); }
205 }
206
207 protected override string DefaultName()
208 {
209 throw new InvalidOperationException("Unit type with id "+id+" did not have a name specified");
210 }
211
212 /// <value>
213 /// The array of <see cref="Stat"/>s for each of the unit's stat lines
214 /// </value>
215 public Stat[][] UnitStatsArrays
216 {
217 get
218 {
219 Stat[][] statsArray;
220
221 if (stats != null)
222 {
223 statsArray = new Stat[][]{ stats.StatsArray };
224 }
225 else if (unitMemberTypes.Count > 0)
226 {
227 int memTypeCount = unitMemberTypes.Count;
228 statsArray = new Stat[memTypeCount][];
229 int i = 0;
230
231 foreach (UnitMemberType memType in unitMemberTypes.Values)
232 {
233 statsArray[i] = memType.StatsArray;
234 i++;
235 }
236 }
237 else
238 {
239 SystemStats systemStats = GameSystem.StandardSystemStats;
240 Stats tempStats = new Stats(systemStats);
241 statsArray = new Stat[][]{ tempStats.StatsArray };
242 }
243
244 return statsArray;
245 }
246 }
247
248 public string[] UnitStatsArrayIDs
249 {
250 get
251 {
252 string[] ids;
253
254 if (stats != null)
255 {
256 ids = new string[]{ stats.StatsID };
257 }
258 else if (unitMemberTypes.Count > 0)
259 {
260 ids = new string[unitMemberTypes.Count];
261 int i = 0;
262
263 foreach (UnitMemberType memType in unitMemberTypes.Values)
264 {
265 ids[i] = memType.StatsID;
266 i++;
267 }
268 }
269 else
270 {
271 ids = new string[]{ GameSystem.StandardSystemStatsID };
272 }
273
274 return ids;
275 }
276 }
277
278 //// <value>
279 /// The array of <see cref="Stat"/>s for each of the unit's stat lines including an additional column that contains the unit type name
280 /// </value>
281 public Stat[][] UnitStatsArraysWithName
282 {
283 get
284 {
285 Stat[][] statsArray;
286
287 if (stats != null)
288 {
289 statsArray = new Stat[][]{ ExtendStatsArrayWithName(stats.StatsArray) };
290 }
291 else if (unitMemberTypes.Count > 0)
292 {
293 int memTypeCount = unitMemberTypes.Count;
294 statsArray = new Stat[memTypeCount][];
295 int i = 0;
296
297 foreach (UnitMemberType memType in unitMemberTypes.Values)
298 {
299 statsArray[i] = memType.StatsArrayWithName;
300 i++;
301 }
302 }
303 else
304 {
305 SystemStats systemStats = GameSystem.StandardSystemStats;
306 Stats tempStats = new Stats(systemStats);
307 statsArray = new Stat[][]{ ExtendStatsArrayWithName(tempStats.StatsArray) };
308 }
309
310 return statsArray;
311 }
312 }
313
314 public Stat[] ExtendStatsArrayWithName(Stat[] statsArray)
315 {
316 Stat[] extendedStats = new Stat[statsArray.Length+1];
317 extendedStats[0] = new Stat(new StatSlot("name"), Name);
318 statsArray.CopyTo(extendedStats, 1);
319 return extendedStats;
320 }
321
322 public void SetUnitStats(Stats newStats)
323 {
324 stats = newStats;
325 }
326
327 public string GetStatValue(string statName)
328 {
329 return stats.GetStatValue(statName.ToLower());
330 }
331
332 internal void AddEquipmentItem(UnitEquipmentItem item)
333 {
334 if (!equipment.ContainsKey(item.ID))
335 {
336 equipment.Add(item.ID, item);
337 equipmentKeyOrder.Add(item.ID);
338 AddToMutexGroups(item);
339 }
340 }
341
342 private void AddToMutexGroups(UnitEquipmentItem item)
343 {
344 string[] mutexGroups = item.MutexGroups;
345
346 foreach (string mutexGroup in mutexGroups)
347 {
348 List<UnitEquipmentItem> items = DictionaryUtils.GetValue(equipmentExclusionGroups, mutexGroup);
349
350 if (items == null)
351 {
352 items = new List<UnitEquipmentItem>();
353 equipmentExclusionGroups.Add(mutexGroup, items);
354 }
355
356 items.Add(item);
357 }
358 }
359
360 /// <summary>
361 /// Gets a <see cref="UnitEquipmentItem"/> for the given ID string, or <code>null</code> if nothing exists for that ID
362 /// </summary>
363 /// <param name="id">
364 /// The ID of the UnitEquipmentItem to get
365 /// </param>
366 /// <returns>
367 /// The <see cref="UnitEquipmentItem"/> for the given ID string, or <code>null</code> if nothing exists for that ID
368 /// </returns>
369 public UnitEquipmentItem GetEquipmentItem(string id)
370 {
371 return DictionaryUtils.GetValue(equipment, id);
372 }
373
374 /// <summary>
375 /// Gets a <see cref=" UnitEquipmentItem"/> for the given <see cref=" EquipmentItem"/>, or <code>null</code> if the unit can't take that <code>EquipmentItem</code>
376 /// </summary>
377 /// <param name="item">
378 /// The <see cref="EquipmentItem"/> to get the <see cref=" UnitEquipmentItem"/>
379 /// </param>
380 /// <returns>
381 /// The <see cref="UnitEquipmentItem"/> that definies the UnitType's restrictions for taking the <see cref=" EquipmentItem"/>
382 /// </returns>
383 public UnitEquipmentItem GetEquipmentItem(EquipmentItem item)
384 {
385 return GetEquipmentItem(item.ID);
386 }
387
388 /// <summary>
389 /// Gets an array of all available <see cref="UnitEquipmentItem"/>s for this UnitType
390 /// </summary>
391 /// <returns>
392 /// An array of all available <see cref="UnitEquipmentItem"/>s for this UnitType
393 /// </returns>
394 public UnitEquipmentItem[] GetEquipmentItems()
395 {
396 return DictionaryUtils.ToArray<string, UnitEquipmentItem>(equipment);
397 }
398
399 public UnitEquipmentItem[] GetEquipmentItemsByExclusionGroup(string group)
400 {
401 return GetEquipmentItemsByExclusionGroups(new string[] { group });
402 }
403
404 public UnitEquipmentItem[] GetEquipmentItemsByExclusionGroups(string[] groups)
405 {
406 List<UnitEquipmentItem> list = new List<UnitEquipmentItem>();
407
408 foreach (string group in groups)
409 {
410 List<UnitEquipmentItem> groupList = DictionaryUtils.GetValue(equipmentExclusionGroups, group);
411
412 if (groupList != null)
413 {
414 list.AddRange(groupList);
415 }
416 }
417
418 return list.ToArray();
419 }
420
421 public bool IsRatioLimitedEquipmentItem(EquipmentItem item)
422 {
423 UnitEquipmentItem equip = GetEquipmentItem(item);
424 return equip != null && equip.IsRatioLimit;
425 }
426
427 public bool IsAbsoluteLimitedEquipmentItem(EquipmentItem item)
428 {
429 UnitEquipmentItem equip = GetEquipmentItem(item);
430 return equip != null && !equip.IsRatioLimit;
431 }
432
433 public ICollection<Ability> GetRequiredAbilities()
434 {
435 return requiredAbilities.Values;
436 }
437
438 public ICollection<Ability> GetOptionalAbilities()
439 {
440 return optionalAbilities.Values;
441 }
442
443 public void AddAbility(Ability ability, bool isRequired)
444 {
445 string id = ability.ID;
446
447 if (!requiredAbilities.ContainsKey(id) && !optionalAbilities.ContainsKey(id))
448 {
449 if (isRequired)
450 {
451 requiredAbilities[id] = ability;
452 }
453 else
454 {
455 optionalAbilities[id] = ability;
456 }
457 }
458 }
459
460 public void AddRequirement(UnitRequirement requirement)
461 {
462 requirements.Add(requirement);
463 }
464
465 public UnitRequirement[] Requirements
466 {
467 get { return requirements.ToArray(); }
468 }
469
470 public List<FailedUnitRequirement> CanAddToArmy(Army army)
471 {
472 List<FailedUnitRequirement> failures = new List<FailedUnitRequirement>();
473
474 if (requirements!=null && requirements.Count > 0)
475 {
476 foreach (UnitRequirement requirement in requirements)
477 {
478 FailedUnitRequirement failure = (FailedUnitRequirement)requirement.CanAddToWarFoundryObject(army);
479
480 if (failure!=null)
481 {
482 failures.Add(failure);
483 }
484 }
485 }
486
487 return failures;
488 }
489
490 public List<FailedUnitRequirement> CanRemoveFromArmy(Army army)
491 {
492 List<FailedUnitRequirement> failures = new List<FailedUnitRequirement>();
493
494 if (requirements!=null && requirements.Count > 0)
495 {
496 foreach (UnitRequirement requirement in requirements)
497 {
498 FailedUnitRequirement failure = (FailedUnitRequirement)requirement.CanRemoveFromWarFoundryObject(army);
499
500 if (failure!=null)
501 {
502 failures.Add(failure);
503 }
504 }
505 }
506
507 return failures;
508 }
509
510 public string Notes
511 {
512 get { return notes; }
513 set { notes = value; }
514 }
515
516 public bool CanContainUnit(Unit unit)
517 {
518 return CanContainUnitType(unit.UnitType);
519 }
520
521 public bool CanContainUnitType(UnitType unitType)
522 {
523 return containedTypes.Contains(unitType);
524 }
525
526 public UnitType[] ContainedUnitTypes
527 {
528 get { return containedTypes.ToArray(); }
529 }
530
531 public void AddContainedUnitType(UnitType containedType)
532 {
533 containedTypes.Add(containedType);
534 }
535
536 public void AddExtraData(string id, string data)
537 {
538 extraData[id] = data;
539 }
540
541 public string GetExtraData(string id)
542 {
543 return DictionaryUtils.GetValue(extraData, id);
544 }
545
546 public string StatsID
547 {
548 get
549 {
550 return stats.StatsID;
551 }
552 }
553
554 public void AddEquipmentSlot(string slotName, ILimit slotLimit)
555 {
556 slotLimits.Add(slotName, slotLimit);
557 }
558
559 public bool HasEquipmentSlot(string slotName)
560 {
561 return slotLimits.ContainsKey(slotName);
562 }
563
564 /// <summary>
565 /// Gets the maximum limit on the number of items allowed in a single slot
566 /// </summary>
567 /// <param name="slotName">The name of the equipment slot to get the limit for</param>
568 /// <returns>The limit of the number of items allowed in a slot, or an infinite limit if the slot is the default one or has not been specified</returns>
569 public ILimit GetEquipmentSlotLimit(string slotName)
570 {
571 ILimit slotLimit = null;
572
573 if (HasEquipmentSlot(slotName))
574 {
575 slotLimit = DictionaryUtils.GetValue(slotLimits, slotName);
576 }
577
578 if (slotLimit == null)
579 {
580 slotLimit = new UnlimitedLimit();
581 }
582
583 return slotLimit;
584 }
585
586 public void AddUnitMemberType(UnitMemberType unitMemberType)
587 {
588 unitMemberTypes.Add(unitMemberType.ID, unitMemberType);
589 }
590 }
591 }