view API/Objects/UnitType.cs @ 367:1a70ca80ef41

Re #27: Define unit requirements * Remove remaining references to old Requirements namespace
author IBBoard <dev@ibboard.co.uk>
date Sat, 28 May 2011 15:51:54 +0000
parents 50d0d3b39a0b
children 71fceea2725b
line source
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.
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.Objects.Requirement;
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 = 0;
22 private int max = WarFoundryCore.INFINITY;
23 private int baseSize = 0;
24 private int minSize, maxSize;
25 private double baseUnitCost;
26 private double costPerTrooper;
27 private Stats stats;
28 private List<IRequirement> requirements = new List<IRequirement>();
29 private Dictionary<string, UnitEquipmentItem> equipment = new Dictionary<string, UnitEquipmentItem>();
30 private Dictionary<string, List<UnitEquipmentItem>> equipmentExclusionGroups = new Dictionary<string, List<UnitEquipmentItem>>();
31 private List<string> equipmentKeyOrder = new List<string>();
32 private Dictionary<string, Ability> requiredAbilities = new Dictionary<string, Ability>();
33 private Dictionary<string, Ability> optionalAbilities = new Dictionary<string, Ability>();
34 private String notes = "";
35 private List<UnitType> containedTypes = new List<UnitType>();
36 private Dictionary<string, string> extraData = new Dictionary<string, string>();
37 private Dictionary<string, ILimit> slotLimits = new Dictionary<string, ILimit>();
38 private Dictionary<string, UnitMemberType> unitMemberTypes = new Dictionary<string, UnitMemberType>();
39 private List<Category> cats = new List<Category>();
42 public UnitType(string id, string typeName, Race parentRace) : base(id, typeName)
43 {
44 race = parentRace;
45 }
47 public override bool Equals (object obj)
48 {
49 if (obj == null)
50 {
51 return false;
52 }
53 else if (!(obj is UnitType))
54 {
55 return false;
56 }
57 else
58 {
59 UnitType other = (UnitType)obj;
61 if (!ID.Equals(other.ID) || !Name.Equals(other.Name) || !Race.Equals(other.Race))
62 {
63 return false;
64 }
65 else
66 {
67 return true;
68 }
69 }
70 }
72 public GameSystem GameSystem
73 {
74 get { return Race.GameSystem; }
75 }
77 /// <value>
78 /// Gets the <see cref=" Race"/> that this unit belongs to.
79 /// </value>
80 public Race Race
81 {
82 get { return race; }
83 }
85 /// <value>
86 /// Gets or sets the default <see cref=" Category"/> that this unit type is a member of.
87 /// If it is not already in the collection of categories then it will be added.
88 /// </value>
89 public virtual Category MainCategory
90 {
91 get
92 {
93 return mainCat;
94 }
95 set
96 {
97 mainCat = value;
98 AddCategory(value);
99 }
100 }
101 /// <summary>
102 /// Gets the collection of <see cref="Category"/> objects that this UnitType can be a member of
103 /// </summary>
104 public Category[] Categories
105 {
106 get
107 {
108 return cats.ToArray();
109 }
110 }
112 /// <summary>
113 /// Adds a category to the set of categories that this unit can be taken from. The first category added will automatically become the MainCategory.
114 /// </summary>
115 /// <param name="cat">
116 /// A <see cref="Category"/> that this unit can be taken from
117 /// </param>
118 public void AddCategory(Category cat)
119 {
120 if (!cats.Contains(cat))
121 {
122 cats.Add(cat);
124 if (MainCategory == null)
125 {
126 MainCategory = cat;
127 }
128 }
129 }
131 /// <value>
132 /// 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
133 /// </value>
134 public int MinSize
135 {
136 get { return minSize; }
137 set
138 {
139 minSize = (value >= 0 ? value : 0);
140 CheckMinimumSize();
141 }
142 }
144 /// <value>
145 /// 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
146 /// </value>
147 public int MaxSize
148 {
149 get { return maxSize; }
150 set
151 {
152 maxSize = (value >= 0 ? value : WarFoundryCore.INFINITY);
153 CheckMinimumSize();
154 }
155 }
157 /// <value>
158 /// 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
159 /// </value>
160 public int MinNumber
161 {
162 get { return min; }
163 set
164 {
165 min = (value >= 0 ? value : 0);
166 CheckMinimumNumber();
167 }
168 }
170 /// <value>
171 /// 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
172 /// </value>
173 public int MaxNumber
174 {
175 get { return max; }
176 set
177 {
178 max = (value >= 0 ? value : WarFoundryCore.INFINITY);
179 CheckMinimumNumber();
180 }
181 }
183 /// <summary>
184 /// Makes sure that the minimum number isn't more than the maximum number, hence the warning on the properties
185 /// </summary>
186 private void CheckMinimumNumber()
187 {
188 if (MinNumber > MaxNumber && MaxNumber!=WarFoundryCore.INFINITY)
189 {
190 MinNumber = MaxNumber;
191 LogNotifier.WarnFormat(GetType(), "Unit type {0} ({1}) had a minimum number greater than their maximum number.", Name, ID);
192 }
193 }
195 /// <summary>
196 /// Makes sure that the minimum unit size isn't more than the maximum unit size, hence the warning on the properties
197 /// </summary>
198 private void CheckMinimumSize()
199 {
200 if (MinSize > MaxSize && MaxSize!=WarFoundryCore.INFINITY)
201 {
202 MinSize = MaxSize;
203 LogNotifier.WarnFormat(GetType(), "Unit type {0} ({1}) had a minimum size greater than their maximum size.", Name, ID);
204 }
205 }
207 //// <value>
208 /// 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.
209 /// </value>
210 public int BaseSize
211 {
212 get { return baseSize; }
213 set { baseSize = (value >= 0 ? value : 0); }
214 }
216 /// <value>
217 /// The number of points that a "base unit" of <code>BaseSize</code> models costs. Additional models are charged at <code>CostPerTrooper</code> each.
218 /// </value>
219 public double BaseUnitCost
220 {
221 get { return baseUnitCost; }
222 set { baseUnitCost = (value >= 0 ? value : 0); }
223 }
225 //// <value>
226 /// 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.
227 /// </value>
228 public double CostPerTrooper
229 {
230 get { return costPerTrooper; }
231 set { costPerTrooper = (value >= 0 ? value : 0); }
232 }
234 protected override string DefaultName()
235 {
236 throw new InvalidOperationException("Unit type with id "+id+" did not have a name specified");
237 }
239 /// <value>
240 /// The array of <see cref="Stat"/>s for each of the unit's stat lines
241 /// </value>
242 public Stat[][] UnitStatsArrays
243 {
244 get
245 {
246 Stat[][] statsArray;
248 if (stats != null)
249 {
250 statsArray = new Stat[][]{ stats.StatsArray };
251 }
252 else if (unitMemberTypes.Count > 0)
253 {
254 int memTypeCount = unitMemberTypes.Count;
255 statsArray = new Stat[memTypeCount][];
256 int i = 0;
258 foreach (UnitMemberType memType in unitMemberTypes.Values)
259 {
260 statsArray[i] = memType.StatsArray;
261 i++;
262 }
263 }
264 else
265 {
266 SystemStats systemStats = GameSystem.StandardSystemStats;
267 Stats tempStats = new Stats(systemStats);
268 statsArray = new Stat[][]{ tempStats.StatsArray };
269 }
271 return statsArray;
272 }
273 }
275 public string[] UnitStatsArrayIDs
276 {
277 get
278 {
279 string[] ids;
281 if (stats != null)
282 {
283 ids = new string[]{ stats.StatsID };
284 }
285 else if (unitMemberTypes.Count > 0)
286 {
287 ids = new string[unitMemberTypes.Count];
288 int i = 0;
290 foreach (UnitMemberType memType in unitMemberTypes.Values)
291 {
292 ids[i] = memType.StatsID;
293 i++;
294 }
295 }
296 else
297 {
298 ids = new string[]{ GameSystem.StandardSystemStatsID };
299 }
301 return ids;
302 }
303 }
305 //// <value>
306 /// 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
307 /// </value>
308 public Stat[][] UnitStatsArraysWithName
309 {
310 get
311 {
312 Stat[][] statsArray;
314 if (stats != null)
315 {
316 statsArray = new Stat[][]{ ExtendStatsArrayWithName(stats.StatsArray) };
317 }
318 else if (unitMemberTypes.Count > 0)
319 {
320 int memTypeCount = unitMemberTypes.Count;
321 statsArray = new Stat[memTypeCount][];
322 int i = 0;
324 foreach (UnitMemberType memType in unitMemberTypes.Values)
325 {
326 statsArray[i] = memType.StatsArrayWithName;
327 i++;
328 }
329 }
330 else
331 {
332 SystemStats systemStats = GameSystem.StandardSystemStats;
333 Stats tempStats = new Stats(systemStats);
334 statsArray = new Stat[][]{ ExtendStatsArrayWithName(tempStats.StatsArray) };
335 }
337 return statsArray;
338 }
339 }
341 public Stat[] ExtendStatsArrayWithName(Stat[] statsArray)
342 {
343 Stat[] extendedStats = new Stat[statsArray.Length+1];
344 extendedStats[0] = new Stat(new StatSlot("name"), Name);
345 statsArray.CopyTo(extendedStats, 1);
346 return extendedStats;
347 }
349 public void SetUnitStats(Stats newStats)
350 {
351 stats = newStats;
352 }
354 public string GetStatValue(string statName)
355 {
356 return stats.GetStatValue(statName.ToLower());
357 }
359 internal void AddEquipmentItem(UnitEquipmentItem item)
360 {
361 if (!equipment.ContainsKey(item.ID))
362 {
363 equipment.Add(item.ID, item);
364 equipmentKeyOrder.Add(item.ID);
365 AddToMutexGroups(item);
366 }
367 }
369 private void AddToMutexGroups(UnitEquipmentItem item)
370 {
371 string[] mutexGroups = item.MutexGroups;
373 foreach (string mutexGroup in mutexGroups)
374 {
375 List<UnitEquipmentItem> items = DictionaryUtils.GetValue(equipmentExclusionGroups, mutexGroup);
377 if (items == null)
378 {
379 items = new List<UnitEquipmentItem>();
380 equipmentExclusionGroups.Add(mutexGroup, items);
381 }
383 items.Add(item);
384 }
385 }
387 /// <summary>
388 /// Gets a <see cref="UnitEquipmentItem"/> for the given ID string, or <code>null</code> if nothing exists for that ID
389 /// </summary>
390 /// <param name="id">
391 /// The ID of the UnitEquipmentItem to get
392 /// </param>
393 /// <returns>
394 /// The <see cref="UnitEquipmentItem"/> for the given ID string, or <code>null</code> if nothing exists for that ID
395 /// </returns>
396 public UnitEquipmentItem GetEquipmentItem(string id)
397 {
398 return DictionaryUtils.GetValue(equipment, id);
399 }
401 /// <summary>
402 /// 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>
403 /// </summary>
404 /// <param name="item">
405 /// The <see cref="EquipmentItem"/> to get the <see cref=" UnitEquipmentItem"/>
406 /// </param>
407 /// <returns>
408 /// The <see cref="UnitEquipmentItem"/> that definies the UnitType's restrictions for taking the <see cref=" EquipmentItem"/>
409 /// </returns>
410 public UnitEquipmentItem GetEquipmentItem(EquipmentItem item)
411 {
412 return GetEquipmentItem(item.ID);
413 }
415 /// <summary>
416 /// Gets an array of all available <see cref="UnitEquipmentItem"/>s for this UnitType
417 /// </summary>
418 /// <returns>
419 /// An array of all available <see cref="UnitEquipmentItem"/>s for this UnitType
420 /// </returns>
421 public UnitEquipmentItem[] GetEquipmentItems()
422 {
423 return DictionaryUtils.ToArray<string, UnitEquipmentItem>(equipment);
424 }
426 public UnitEquipmentItem[] GetEquipmentItemsByExclusionGroup(string group)
427 {
428 return GetEquipmentItemsByExclusionGroups(new string[] { group });
429 }
431 public UnitEquipmentItem[] GetEquipmentItemsByExclusionGroups(string[] groups)
432 {
433 List<UnitEquipmentItem> list = new List<UnitEquipmentItem>();
435 foreach (string group in groups)
436 {
437 List<UnitEquipmentItem> groupList = DictionaryUtils.GetValue(equipmentExclusionGroups, group);
439 if (groupList != null)
440 {
441 list.AddRange(groupList);
442 }
443 }
445 return list.ToArray();
446 }
448 public bool IsRatioLimitedEquipmentItem(EquipmentItem item)
449 {
450 UnitEquipmentItem equip = GetEquipmentItem(item);
451 return equip != null && equip.IsRatioLimit;
452 }
454 public bool IsAbsoluteLimitedEquipmentItem(EquipmentItem item)
455 {
456 UnitEquipmentItem equip = GetEquipmentItem(item);
457 return equip != null && !equip.IsRatioLimit;
458 }
460 public ICollection<Ability> GetRequiredAbilities()
461 {
462 return requiredAbilities.Values;
463 }
465 public ICollection<Ability> GetOptionalAbilities()
466 {
467 return optionalAbilities.Values;
468 }
470 public void AddAbility(Ability ability, bool isRequired)
471 {
472 string id = ability.ID;
474 if (!requiredAbilities.ContainsKey(id) && !optionalAbilities.ContainsKey(id))
475 {
476 if (isRequired)
477 {
478 requiredAbilities[id] = ability;
479 }
480 else
481 {
482 optionalAbilities[id] = ability;
483 }
484 }
485 }
487 public void AddRequirement(IRequirement requirement)
488 {
489 requirements.Add(requirement);
490 }
492 public IRequirement[] GetRequirements()
493 {
494 List<IRequirement> reqs = new List<IRequirement>(requirements);
496 if (MinNumber != 0)
497 {
498 RequiresAtLeastNUnitsRequirement req = new RequiresAtLeastNUnitsRequirement();
499 req.AddUnitTypeRequirement(this, MinNumber);
500 reqs.Add(req);
501 }
503 if (MaxNumber != WarFoundryCore.INFINITY)
504 {
505 RequiresNoMoreThanNOfUnitTypeRequirement req = new RequiresNoMoreThanNOfUnitTypeRequirement();
506 req.AddUnitTypeRequirement(this, MaxNumber);
507 reqs.Add(req);
508 }
510 return reqs.ToArray();
511 }
513 public string Notes
514 {
515 get { return notes; }
516 set { notes = value; }
517 }
519 public bool CanContainUnit(Unit unit)
520 {
521 return CanContainUnitType(unit.UnitType);
522 }
524 public bool CanContainUnitType(UnitType unitType)
525 {
526 return containedTypes.Contains(unitType);
527 }
529 public UnitType[] ContainedUnitTypes
530 {
531 get { return containedTypes.ToArray(); }
532 }
534 public void AddContainedUnitType(UnitType containedType)
535 {
536 containedTypes.Add(containedType);
537 }
539 public void AddExtraData(string id, string data)
540 {
541 extraData[id] = data;
542 }
544 public string GetExtraData(string id)
545 {
546 return DictionaryUtils.GetValue(extraData, id);
547 }
549 public string StatsID
550 {
551 get
552 {
553 return stats.StatsID;
554 }
555 }
557 public void AddEquipmentSlot(string slotName, ILimit slotLimit)
558 {
559 slotLimits.Add(slotName, slotLimit);
560 }
562 public bool HasEquipmentSlot(string slotName)
563 {
564 return slotLimits.ContainsKey(slotName);
565 }
567 /// <summary>
568 /// Gets the maximum limit on the number of items allowed in a single slot
569 /// </summary>
570 /// <param name="slotName">The name of the equipment slot to get the limit for</param>
571 /// <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>
572 public ILimit GetEquipmentSlotLimit(string slotName)
573 {
574 ILimit slotLimit = null;
576 if (HasEquipmentSlot(slotName))
577 {
578 slotLimit = DictionaryUtils.GetValue(slotLimits, slotName);
579 }
581 if (slotLimit == null)
582 {
583 slotLimit = new UnlimitedLimit();
584 }
586 return slotLimit;
587 }
589 public void AddUnitMemberType(UnitMemberType unitMemberType)
590 {
591 unitMemberTypes.Add(unitMemberType.ID, unitMemberType);
592 }
593 }