view API/Objects/UnitType.cs @ 348:d34ae0057a39

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