comparison API/Objects/Unit.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 7179c585d31d
comparison
equal deleted inserted replaced
336:3631c1493c7f 337:3c4a6403a88c
1 // This file (Unit.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 2007, 2008, 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.Text;
8 using System.Xml;
9 using IBBoard.Lang;
10 using IBBoard.Limits;
11 using IBBoard.WarFoundry.API.Util;
12
13 namespace IBBoard.WarFoundry.API.Objects
14 {
15 /// <summary>
16 /// Summary description for UnitInstance.
17 /// </summary>
18 public class Unit : WarFoundryObject, ICostedWarFoundryObject
19 {
20 private UnitType type;
21 private int size;
22 private Unit parentUnit;
23 private double points;
24 private ArmyCategory cat;
25 private Dictionary<UnitEquipmentItem, AbstractUnitEquipmentItemSelection> equipment = new Dictionary<UnitEquipmentItem, AbstractUnitEquipmentItemSelection>();
26 private Dictionary<string, List<AbstractUnitEquipmentItemSelection>> equipmentSlots = new Dictionary<string, List<AbstractUnitEquipmentItemSelection>>();
27 private List<Unit> containedUnits = new List<Unit>();
28
29 public event DoubleValChangedDelegate PointsValueChanged;
30 public event IntValChangedDelegate UnitSizeChanged;
31 public event DoubleValChangedDelegate UnitEquipmentAmountChanged;
32
33 public Unit(UnitType unitType, ArmyCategory parentArmyCat) : this(unitType, unitType.MinSize, parentArmyCat)
34 {
35 //Do nothing extra
36 }
37
38 public Unit(UnitType unitType, int startSize, ArmyCategory parentArmyCat) : this("", "", startSize, unitType, parentArmyCat)
39 {
40 SetInitialEquipment();
41 UnitSizeChanged += new IntValChangedDelegate(RefreshUnitEquipmentAmounts);
42 }
43
44 public Unit(string id, string name, int startSize, UnitType unitType, ArmyCategory parentArmyCat) : base(id, name)
45 {
46 Category = parentArmyCat;
47 type = unitType;
48 Size = startSize;
49 CalcCost();
50 UnitEquipmentAmountChanged += new DoubleValChangedDelegate(UnitEquipmentAmountChangedHandler);
51 UnitSizeChanged += new IntValChangedDelegate(UnitSizeChangedHandler);
52 Translation.TranslationChanged += HandleTranslationChanged;
53 }
54
55 private void UnitEquipmentAmountChangedHandler(WarFoundryObject obj, double oldVal, double newVal)
56 {
57 CalcCost();
58 }
59
60 private void UnitSizeChangedHandler(WarFoundryObject obj, int oldVal, int newVal)
61 {
62 CalcCost();
63
64 if (HasDefaultName())
65 {
66 OnNameChanged("", Name);
67 }
68 }
69
70 protected override string DefaultName()
71 {
72 if (type != null)
73 {
74 if (size == 1)
75 {
76 return type.Name;
77 }
78 else
79 {
80 return String.Format(Translation.GetTranslation("defaultUnitName"), size, type.Name);
81 }
82 }
83 else
84 {
85 return "Unknown Unit";
86 }
87 }
88
89 private void HandleTranslationChanged()
90 {
91 if (type != null && HasDefaultName() && size != 1)
92 {
93 OnNameChanged(null, DefaultName());
94 }
95 }
96
97 private void SetInitialEquipment()
98 {
99 foreach (UnitEquipmentItem unitEquip in UnitType.GetEquipmentItems())
100 {
101 if (unitEquip.IsRequired)
102 {
103 if (CanEquipWithItem(unitEquip))
104 {
105 ILimit minLimit = unitEquip.MinLimit;
106
107 if (minLimit is IPercentageLimit)
108 {
109 SetEquipmentRatio(unitEquip, UnitEquipmentUtil.GetMinEquipmentPercentage(this, unitEquip));
110 }
111 else
112 {
113 SetEquipmentAmount(unitEquip, UnitEquipmentUtil.GetMinEquipmentCount(this, unitEquip));
114 }
115 }
116 }
117 }
118 }
119
120 private void CalcCost()
121 {
122 double oldpoints = points;
123 points = type.CostPerTrooper * AdditionalTroopers + type.BaseUnitCost;
124
125 foreach (AbstractUnitEquipmentItemSelection equipSelection in equipment.Values)
126 {
127 points += equipSelection.TotalCost;
128 }
129
130 if (oldpoints != points)
131 {
132 OnPointsValueChanged(oldpoints, points);
133 }
134 }
135
136 public int AdditionalTroopers
137 {
138 get { return Math.Max(Size - type.BaseSize, 0); }
139 }
140
141 public int Size
142 {
143 get { return size; }
144 set
145 {
146 if (value != size)
147 {
148 int oldValue = size;
149 size = (value > 0 ? value : 1);
150 OnUnitSizeChanged(oldValue, size);
151 }
152 }
153 }
154
155 public UnitType UnitType
156 {
157 get { return type; }
158 }
159
160 public Army Army
161 {
162 get { return (Category == null ? null : Category.ParentArmy); }
163 }
164
165 public Race Race
166 {
167 get { return UnitType.Race; }
168 }
169
170 public ArmyCategory Category
171 {
172 get
173 {
174 return cat;
175 }
176 set { cat = value; }
177 }
178
179 public double Points
180 {
181 get
182 {
183 if (points == 0)
184 {
185 CalcCost();
186 }
187
188 return points;
189 }
190 }
191
192 public Unit[] ContainedUnits
193 {
194 get { return containedUnits.ToArray(); }
195 }
196
197 public void AddContainedUnit(Unit unit)
198 {
199 if (UnitType.CanContainUnit(unit))
200 {
201 if (!containedUnits.Contains(unit))
202 {
203 containedUnits.Add(unit);
204 }
205
206 unit.ParentUnit = this;
207 }
208 else
209 {
210 throw new InvalidContainershipException(this, unit);
211 }
212 }
213
214 public void RemoveContainedUnit(Unit unit)
215 {
216 containedUnits.Remove(unit);
217 }
218
219 public Unit ParentUnit
220 {
221 get { return parentUnit; }
222 set
223 {
224 if (!(parentUnit == value || (parentUnit != null && parentUnit.Equals(value))))
225 {
226 parentUnit = value;
227
228 if (value != null)
229 {
230 value.AddContainedUnit(this);
231 }
232 }
233 }
234 }
235
236 public UnitEquipmentItem[] GetEquipment()
237 {
238 return DictionaryUtils.ToKeyArray(equipment);
239 }
240
241 public EquipmentItem[] GetRequiredEquipment()
242 {
243 List<EquipmentItem> list = new List<EquipmentItem>();
244
245 foreach (UnitEquipmentItem item in GetEquipment())
246 {
247 if (item.IsRequired)
248 {
249 list.Add(item.EquipmentItem);
250 }
251 }
252
253 return list.ToArray();
254 }
255
256 internal AbstractUnitEquipmentItemSelection GetEquipmentSelection(UnitEquipmentItem item)
257 {
258 return DictionaryUtils.GetValue(equipment, item);
259 }
260
261 public void SetEquipmentAmount(UnitEquipmentItem equip, int amount)
262 {
263 if (amount < 1 && amount != WarFoundryCore.INFINITY)
264 {
265 amount = 0;
266 }
267
268 if (amount == 0)
269 {
270 RemoveEquipmentItem(equip);
271 }
272 else
273 {
274 AbstractUnitEquipmentItemSelection currSelection = DictionaryUtils.GetValue(equipment, equip);
275 double oldAmount = (currSelection == null ? 0 : currSelection.AmountTaken);
276
277 if (amount != oldAmount)
278 {
279 if (oldAmount == 0)
280 {
281 AddEquipmentAmount(equip, amount);
282 }
283 else if (currSelection is UnitEquipmentNumericSelection)
284 {
285 //A UnitEquipmentItem shouldn't change its IsRatio value, so assume we already have the right sub-type
286 currSelection.AmountTaken = amount;
287 }
288 else
289 {
290 RemoveEquipmentItem(equip);
291 AddEquipmentAmount(equip, amount);
292 }
293
294 OnUnitEquipmentAmountChanged(equip, oldAmount, amount);
295 }
296 }
297 }
298
299 private void AddEquipmentAmount(UnitEquipmentItem equip, int amount)
300 {
301 AbstractUnitEquipmentItemSelection newItem = new UnitEquipmentNumericSelection(this, equip, amount);
302 equipment[equip] = newItem;
303 List<AbstractUnitEquipmentItemSelection> selections = DictionaryUtils.GetValue(equipmentSlots, equip.SlotName);
304
305 if (selections == null)
306 {
307 selections = new List<AbstractUnitEquipmentItemSelection>();
308 equipmentSlots[equip.SlotName] = selections;
309 }
310
311 selections.Add(newItem);
312 }
313
314 public void SetEquipmentRatio(UnitEquipmentItem equip, double ratio)
315 {
316 if (!equip.IsRatioLimit)
317 {
318 throw new InvalidOperationException("Equipment with ID " + equip.ID + " for unit of type " + UnitType.ID + " has an absolute limit, not a ratio limit");
319 }
320
321 if (ratio > 100)
322 {
323 ratio = 100;
324 }
325 else if (ratio < 0)
326 {
327 ratio = 0;
328 }
329
330 if (ratio == 0)
331 {
332 RemoveEquipmentItem(equip);
333 }
334 else
335 {
336 AbstractUnitEquipmentItemSelection currSelection = DictionaryUtils.GetValue(equipment, equip);
337 double oldRatio = (currSelection == null ? 0 : currSelection.AmountTaken);
338
339 if (ratio != oldRatio)
340 {
341 if (oldRatio == 0)
342 {
343 AddEquipmentRatio(equip, ratio);
344 }
345 else if (currSelection is UnitEquipmentRatioSelection)
346 {
347 currSelection.AmountTaken = ratio;
348 }
349 else
350 {
351 RemoveEquipmentItem(equip);
352 AddEquipmentRatio(equip, ratio);
353 }
354
355 OnUnitEquipmentAmountChanged(equip, oldRatio, ratio);
356 }
357 }
358 }
359
360 private void AddEquipmentRatio(UnitEquipmentItem equip, double ratio)
361 {
362 UnitEquipmentRatioSelection newItem = new UnitEquipmentRatioSelection(this, equip, ratio);
363 equipment[equip] = newItem;
364 List<AbstractUnitEquipmentItemSelection> selections = DictionaryUtils.GetValue(equipmentSlots, equip.SlotName);
365
366 if (selections == null)
367 {
368 selections = new List<AbstractUnitEquipmentItemSelection>();
369 equipmentSlots[equip.SlotName] = selections;
370 }
371
372 selections.Add(newItem);
373 }
374
375 private void RemoveEquipmentItem(UnitEquipmentItem equip)
376 {
377 double oldAmount = UnitEquipmentUtil.GetEquipmentAmount(this, equip);
378
379 if (oldAmount != 0)
380 {
381 AbstractUnitEquipmentItemSelection selection = DictionaryUtils.GetValue(equipment, equip);
382 equipment.Remove(equip);
383 List<AbstractUnitEquipmentItemSelection> slotSelections = DictionaryUtils.GetValue(equipmentSlots, equip.SlotName);
384 slotSelections.Remove(selection);
385 OnUnitEquipmentAmountChanged(equip, oldAmount, 0);
386 }
387 }
388
389 public bool CanEquipWithItem(UnitEquipmentItem item)
390 {
391 string[] mutexes = item.MutexGroups;
392 bool canEquip = false;
393
394 if (mutexes.Length == 0)
395 {
396 canEquip = true;
397 }
398 else
399 {
400 canEquip = UnitEquipmentUtil.GetBlockingEquipmentItems(this, item).Count == 0;
401 }
402
403 return canEquip;
404 }
405
406 public bool CanEquipWithItem(string equipID)
407 {
408 return CanEquipWithItem(UnitType.GetEquipmentItem(equipID));
409 }
410
411 private void OnPointsValueChanged(double oldValue, double newValue)
412 {
413 if (PointsValueChanged != null)
414 {
415 PointsValueChanged(this, oldValue, newValue);
416 }
417 }
418
419 private void OnUnitSizeChanged(int oldValue, int newValue)
420 {
421 if (UnitSizeChanged != null)
422 {
423 UnitSizeChanged(this, oldValue, newValue);
424 }
425 }
426
427 private void OnUnitEquipmentAmountChanged(UnitEquipmentItem equip, double oldValue, double newValue)
428 {
429 if (UnitEquipmentAmountChanged != null)
430 {
431 UnitEquipmentAmountChanged(equip, oldValue, newValue);
432 }
433 }
434
435 public Stat[][] UnitStatsArrays
436 {
437 get { return UnitType.UnitStatsArrays; }
438 }
439
440 public Stat[][] UnitStatsArraysWithName
441 {
442 get { return UnitType.UnitStatsArraysWithName; }
443 }
444
445 public string[] UnitStatsArrayIDs
446 {
447 get
448 {
449 return UnitType.UnitStatsArrayIDs;
450 }
451 }
452
453 public string GetStatValue(string statName)
454 {
455 return UnitType.GetStatValue(statName);
456 }
457
458 public int GetEquipmentAmountInSlot(string slotName)
459 {
460 int amount = 0;
461
462 List<AbstractUnitEquipmentItemSelection> selections = GetEquipmentSlotSelections(slotName);
463
464 if (selections != null)
465 {
466 amount = GetSelectionTotal(selections);
467 }
468
469 return amount;
470 }
471
472 internal List<AbstractUnitEquipmentItemSelection> GetEquipmentSlotSelections(string slotName)
473 {
474 return DictionaryUtils.GetValue(equipmentSlots, slotName);
475 }
476
477 /// <summary>
478 /// Gets the total amount of items taken for the item's slot, excluding the provided item
479 /// </summary>
480 /// <param name="item">the item to exclude from the count</param>
481 /// <returns>the total number of items</returns>
482 public int GetEquipmentAmountInSlotExcludingItem(UnitEquipmentItem item)
483 {
484 int amount = 0;
485
486 List<AbstractUnitEquipmentItemSelection> selections = DictionaryUtils.GetValue(equipmentSlots, item.SlotName);
487
488 if (selections != null)
489 {
490 selections = new List<AbstractUnitEquipmentItemSelection>(selections);
491 RemoveSelectionFromList(item, selections);
492 amount = GetSelectionTotal(selections);
493 }
494
495 return amount;
496 }
497
498 private void RemoveSelectionFromList(UnitEquipmentItem item, List<AbstractUnitEquipmentItemSelection> selections)
499 {
500 AbstractUnitEquipmentItemSelection selection = GetEquipmentSelection(item);
501
502 if (selection != null)
503 {
504 selections.Remove(selection);
505 }
506 }
507
508 private int GetSelectionTotal(List<AbstractUnitEquipmentItemSelection> selections)
509 {
510 int amount = 0;
511
512 foreach (AbstractUnitEquipmentItemSelection selection in selections)
513 {
514 amount += selection.NumberTaken;
515 }
516
517 return amount;
518 }
519
520 /// <summary>
521 /// Default stub implementation of getting the unit's abilities - defaults to just the unit type's required abilities until we get a way to modify them
522 /// </summary>
523 public ICollection<Ability> Abilities
524 {
525 get
526 {
527 return UnitType.GetRequiredAbilities();
528 }
529 }
530
531 private void RefreshUnitEquipmentAmounts(WarFoundryObject obj, int oldValue, int newValue)
532 {
533 foreach (UnitEquipmentItem item in equipment.Keys)
534 {
535 AbstractUnitEquipmentItemSelection selection = equipment[item];
536
537 if (selection is UnitEquipmentRatioSelection)
538 {
539 OnUnitEquipmentAmountChanged(item, selection.AmountTaken, selection.AmountTaken);
540 }
541 }
542 }
543 }
544 }