source: branches/AlphaChannel/AI Template/Project/Lib/UnitBase.cs

Last change on this file was 191, checked in by chronos, 5 years ago
  • Added: AI Template directory with manual for development of custom AI from original game.
File size: 25.6 KB
Line 
1using System;
2using System.Collections.Generic;
3using AI;
4
5namespace CevoAILib
6{
7 enum Job
8 {
9 None = 0, BuildRoad = 1, BuildRailRoad = 2, ClearOrDrain = 3, Irrigate = 4, BuildFarmland = 5, Afforest = 6, BuildMine = 7,
10 BuildCanal = 8, Transform = 9, BuildFortress = 10, CleanUp = 11, BuildBase = 12, Pillage = 13, BuildCity = 14
11 }
12
13 enum SpyMission { SabotageProduction = 1, StealMaps = 2, CollectThirdNationKnowledge = 3, PrepareDossier = 4, PrepareMilitaryReport = 5 }
14
15 struct BattleOutcome
16 {
17 public readonly int EndHealthOfAttacker;
18 public readonly int EndHealthOfDefender;
19
20 public BattleOutcome(int endHealthOfAttacker, int endHealthOfDefender)
21 {
22 this.EndHealthOfAttacker = endHealthOfAttacker;
23 this.EndHealthOfDefender = endHealthOfDefender;
24 }
25
26 public override string ToString()
27 {
28 return string.Format("A{0} D{1}", EndHealthOfAttacker, EndHealthOfDefender);
29 }
30 }
31
32 /// <summary>
33 /// basic unit information as available for both own and foreign units
34 /// </summary>
35 interface IUnitInfo
36 {
37 Nation Nation { get; }
38 ModelBase Model { get; }
39 Location Location { get; }
40 bool AreOtherUnitsPresent { get; }
41 bool IsLoaded { get; }
42 int Speed { get; } // usually same as Model.Speed
43 bool IsTerrainResistant { get; }
44 int Experience { get; }
45 int ExperienceLevel { get; }
46 int Health { get; }
47 bool IsFortified { get; }
48 int Load { get; }
49 int Fuel { get; }
50 Job Job { get; }
51 }
52
53 /// <summary>
54 /// own unit, abstract base class
55 /// </summary>
56 unsafe abstract class AUnit : IUnitInfo
57 {
58 /// <summary>
59 /// movement points an own or foreign unit has per turn, considering damage and wonders
60 /// </summary>
61 /// <param name="unit">the unit</param>
62 /// <returns>movement points</returns>
63 public static int UnitSpeed(IUnitInfo unit)
64 {
65 if (unit.Model.Domain == ModelDomain.Sea)
66 {
67 int speed = unit.Model.Speed;
68 if (unit.Nation.HasWonder(Building.MagellansExpedition))
69 speed += 200;
70 if (unit.Health < 100)
71 speed = ((speed - 250) * unit.Health / 5000) * 50 + 250;
72 return speed;
73 }
74 else
75 return unit.Model.Speed;
76 }
77
78 protected readonly Empire theEmpire;
79 public readonly int ID;
80 protected readonly Model model;
81
82 public AUnit(Empire empire, int indexInSharedMemory)
83 {
84 this.theEmpire = empire;
85 IndexInSharedMemory = indexInSharedMemory;
86 ID = address[3] & 0xFFFF; // save to be able to find unit back
87 model = theEmpire.Models[(address[3] >> 16) & 0xFFFF];
88 }
89
90 public override string ToString()
91 {
92 return string.Format("{0}@{1}", model, address[0]);
93 }
94
95 #region IUnitInfo members
96 public Nation Nation { get { return theEmpire.Us; } }
97 public ModelBase Model { get { return model; } }
98 public Location Location { get { return new Location(theEmpire, address[0]); } }
99
100 /// <summary>
101 /// whether other units are present at the same location
102 /// </summary>
103 public bool AreOtherUnitsPresent { get { return (address[7] & Protocol.unMulti) != 0; } }
104
105 /// <summary>
106 /// whether unit is loaded to a ship or plane
107 /// </summary>
108 public bool IsLoaded { get { return ((uint)address[4] & 0x80000000) == 0; } } // sign bit
109
110 /// <summary>
111 /// movement points this unit has per turn, considering damage and wonders
112 /// </summary>
113 public int Speed { get { return UnitSpeed(this); } }
114
115 /// <summary>
116 /// whether this unit passes hostile terrain without damage
117 /// </summary>
118 public bool IsTerrainResistant { get { return model.IsTerrainResistant || theEmpire.Us.HasWonder(Building.HangingGardens); } }
119
120 /// <summary>
121 /// Experience points collected.
122 /// </summary>
123 public int Experience { get { return (address[6] >> 8) & 0xFF; } }
124
125 /// <summary>
126 /// Experience level as applied in combat. 0 = Green ... 4 = Elite.
127 /// </summary>
128 public int ExperienceLevel { get { return Experience / Cevo.ExperienceLevelCost; } }
129
130 public int Health { get { return (sbyte)((address[5] >> 16) & 0xFF); } }
131 public bool IsFortified { get { return (address[7] & Protocol.unFortified) != 0; } }
132
133 /// <summary>
134 /// total number of units loaded to this unit
135 /// </summary>
136 public int Load { get { return TroopLoad + AirLoad; } }
137
138 /// <summary>
139 /// Fuel remaining, not necessarily the same as Model.Fuel.
140 /// </summary>
141 public int Fuel { get { return (sbyte)((address[5] >> 24) & 0xFF); } }
142
143 /// <summary>
144 /// settler job this unit is currently doing
145 /// </summary>
146 public Job Job { get { return (Job)(address[6] & 0xFF); } }
147 #endregion
148
149 public bool Exists { get { return indexInSharedMemory >= 0; } }
150 public bool IsConscripts { get { return (address[7] & Protocol.unConscripts) != 0; } }
151 public int MovementLeft { get { return (short)(address[5] & 0xFFFF); } }
152 public bool MustPauseForMountains { get { return (address[7] & Protocol.unMountainDelay) != 0; } }
153 public bool WasWithdrawn { get { return (address[7] & Protocol.unWithdrawn) != 0; } }
154 public bool CausesUnrest { get { return Location.MayCauseUnrest && !model.IsCivil; } }
155 public int TroopLoad { get { return (address[6] >> 16) & 0xFF; } }
156 public int AirLoad { get { return (address[6] >> 24) & 0xFF; } }
157 public bool AreBombsLoaded { get { return (address[7] & Protocol.unBombsLoaded) != 0; } }
158
159 /// <summary>
160 /// home city, null if none
161 /// </summary>
162 public City Home
163 {
164 get
165 {
166 int homeCityIndexInSharedMemory = (short)(address[4] & 0xFFFF);
167 if (homeCityIndexInSharedMemory >= 0)
168 return theEmpire.CityLookup[homeCityIndexInSharedMemory];
169 else
170 return null;
171 }
172 }
173
174 /// <summary>
175 /// ship or aircraft by which this unit is currently transported, null if not transported
176 /// </summary>
177 public Unit Transport
178 {
179 get
180 {
181 int transportIndexInSharedMemory = (short)((address[4] >> 16) & 0xFFFF);
182 if (transportIndexInSharedMemory >= 0)
183 return theEmpire.UnitLookup[transportIndexInSharedMemory];
184 else
185 return null;
186 }
187 }
188
189 /// <summary>
190 /// persistent custom value
191 /// </summary>
192 public int Status
193 {
194 get { return address[1]; }
195 set { address[1] = value; }
196 }
197
198 #region effective methods
199 /// <summary>
200 /// Move unit to a certain location.
201 /// Moves along shortest possible path considering all known information.
202 /// Only does the part of the move that is possible within this turn.
203 /// If move has to be continued next turn the return value has the Error property Incomplete.
204 /// Operation breaks even if it could be continued within the turn if a new foreign unit or city is spotted,
205 /// in this case the result has the NewUnitOrCitySpotted property set.
206 /// Hostile terrain is considered to find a compromise between damage and reaching the target fast.
207 /// </summary>
208 /// <param name="target">target location</param>
209 /// <returns>result of operation</returns>
210 public PlayResult MoveTo__Turn(Location target)
211 {
212 if (target == Location)
213 return PlayResult.NoChange;
214 else
215 return MoveTo(target, false);
216 }
217
218 /// <summary>
219 /// Move unit adjacent to a certain location.
220 /// Moves along shortest possible path considering all known information.
221 /// Only does the part of the move that is possible within this turn.
222 /// If move has to be continued next turn the return value has the Error property Incomplete.
223 /// Operation breaks even if it could be continued within the turn if a new foreign unit or city is spotted,
224 /// in this case the result has the NewUnitOrCitySpotted property set.
225 /// Hostile terrain is considered to find a compromise between damage and reaching the target fast.
226 /// </summary>
227 /// <param name="target">location to move adjacent to</param>
228 /// <returns>result of operation</returns>
229 public PlayResult MoveToNeighborOf__Turn(Location target)
230 {
231 if (target.IsNeighborOf(Location))
232 return PlayResult.NoChange;
233 else
234 return MoveTo(target, true);
235 }
236
237 // internal
238 PlayResult MoveTo(Location target, bool approach)
239 {
240 if (!target.IsValid)
241 return new PlayResult(PlayError.InvalidLocation);
242 if (MustPauseForMountains)
243 return new PlayResult(PlayError.Incomplete);
244
245 // pathfinding necessary
246 TravelSprawl sprawl = null;
247 if (approach)
248 sprawl = new TravelSprawl(theEmpire, this, target);
249 else
250 sprawl = new TravelSprawl(theEmpire, this);
251 foreach (Location reachedLocation in sprawl)
252 {
253 if (reachedLocation == target)
254 break;
255 }
256 if (!sprawl.WasIterated(target))
257 return new PlayResult(PlayError.NoWay);
258
259 Location[] path = sprawl.Path(target);
260 foreach (Location step in path)
261 {
262 if (sprawl.Distance(step).NewTurn)
263 return new PlayResult(PlayError.Incomplete); // has to be continued next turn
264 if (!IsTerrainResistant && Location.OneTurnHostileDamage == 0 && step.OneTurnHostileDamage > 0)
265 { // recover before passing hostile terrain?
266 int damageToNextNonHostileLocation = sprawl.DamageToNextNonHostileLocation(Location, target);
267 if (damageToNextNonHostileLocation >= 100)
268 return new PlayResult(PlayError.NoWay);
269 else if (Location.OneTurnHostileDamage == 0 && Health <= damageToNextNonHostileLocation)
270 return new PlayResult(PlayError.RecoverFirst);
271 }
272
273 PlayResult result = Step__Turn(step);
274 if (!result.OK || result.UnitRemoved || result.NewUnitOrCitySpotted)
275 return result;
276 }
277 return PlayResult.Success;
278 }
279
280 /// <summary>
281 /// Move unit to neighbor location.
282 /// Causes loading to transport if:
283 /// (1) unit is ground unit and target location is water and has transport present
284 /// (2) unit is aircraft and target location has carrier present
285 /// </summary>
286 /// <param name="target">location to move to, must be neighbor of current location</param>
287 /// <returns>result of operation</returns>
288 public PlayResult Step__Turn(Location target)
289 {
290 if (!target.IsValid)
291 return new PlayResult(PlayError.InvalidLocation);
292 RC targetRC = target - Location;
293 if (targetRC.Distance > 3)
294 return new PlayResult(PlayError.RulesViolation);
295
296 OtherLocation[] newObservations = null;
297 if (model.HasExtendedObservationRange || target.ProvidesExtendedObservationRange)
298 newObservations = target.Distance5Area;
299 else
300 newObservations = target.Neighbors;
301 for (int i = 0; i < newObservations.Length; i++)
302 {
303 if (newObservations[i].Location.IsObserved)
304 newObservations[i] = new OtherLocation(new Location(theEmpire, -1), new RC(0, 0)); // not a new observation, make invalid
305 }
306 bool foreignCityChangePossible = false;
307 foreach (OtherLocation observation in newObservations)
308 {
309 if (observation.Location.IsValid)
310 foreignCityChangePossible |= observation.Location.HasForeignCity;
311 }
312
313 int moveCommand = Protocol.sMoveUnit + (((targetRC.a - targetRC.b) & 7) << 4) + (((targetRC.a + targetRC.b) & 7) << 7);
314 List<City> citiesChanged = null;
315 if (Load > 0)
316 {
317 if ((!target.HasForeignUnit && target.MayCauseUnrest != Location.MayCauseUnrest) || // crossing border changing unrest
318 theEmpire.TestPlay(moveCommand, indexInSharedMemory).UnitRemoved) // transport will die
319 { // reports of all home cities of transported units will become invalid
320 citiesChanged = new List<City>();
321 foreach (Unit unit in theEmpire.Units)
322 {
323 if (unit.Transport == this && unit.Home != null && !citiesChanged.Contains(unit.Home))
324 citiesChanged.Add(unit.Home);
325 }
326 }
327 }
328 bool targetHadForeignCityBefore = target.HasForeignCity;
329 bool causedUnrestBefore = CausesUnrest;
330 PlayResult result = theEmpire.Play(moveCommand, indexInSharedMemory);
331 if (result.Effective)
332 {
333 AEmpire.UpdateArea updateArea = AEmpire.UpdateArea.Basic;
334
335 foreach (OtherLocation observation in newObservations)
336 {
337 if (observation.Location.IsValid)
338 foreignCityChangePossible |= observation.Location.HasForeignCity;
339 }
340 if (foreignCityChangePossible)
341 updateArea |= AEmpire.UpdateArea.ForeignCities;
342
343 if (result.UnitRemoved)
344 updateArea |= AEmpire.UpdateArea.Units;
345 if (targetHadForeignCityBefore && !target.HasForeignCity)
346 {
347 updateArea |= AEmpire.UpdateArea.ForeignCities; // foreign city destroyed or captured
348 if (target.HasOwnCity)
349 updateArea |= AEmpire.UpdateArea.Cities; // captured, new own city
350 }
351 if (updateArea != AEmpire.UpdateArea.Basic)
352 theEmpire.UpdateLists(updateArea);
353 if (Home != null && (!Exists || CausesUnrest != causedUnrestBefore))
354 Home.InvalidateReport();
355 if (citiesChanged != null)
356 {
357 foreach (City city in citiesChanged)
358 city.InvalidateReport();
359 }
360
361 if (theEmpire.HadEvent__Turn((EmpireEvent)Protocol.phStealTech)) // capture with temple of zeus
362 theEmpire.StealAdvance();
363 }
364 return result;
365 }
366
367 /// <summary>
368 /// Attack a unit. Moves along shortest possible path considering all known information.
369 /// Only does the part of the move that is possible within this turn.
370 /// If move has to be continued next turn the return value has the Error property Incomplete.
371 /// Hostile terrain is considered to find a compromise between damage and reaching the target fast.
372 /// </summary>
373 /// <param name="target">unit to attack</param>
374 /// <returns>result of operation</returns>
375 public PlayResult Attack__Turn(Location target)
376 {
377 if (!target.IsValid)
378 return new PlayResult(PlayError.InvalidLocation);
379 PlayResult moved = MoveToNeighborOf__Turn(target);
380 if (!moved.OK || moved.UnitRemoved || moved.NewUnitOrCitySpotted)
381 return moved;
382 else
383 return Step__Turn(target);
384 }
385
386 /// <summary>
387 /// Attack a unit. Moves along shortest possible path considering all known information.
388 /// Only does the part of the move that is possible within this turn.
389 /// If move has to be continued next turn the return value has the Error property Incomplete.
390 /// Operation breaks even if it could be continued within the turn if a new foreign unit or city is spotted,
391 /// in this case the result has the NewUnitOrCitySpotted property set.
392 /// Hostile terrain is considered to find a compromise between damage and reaching the target fast.
393 /// </summary>
394 /// <param name="target">unit to attack</param>
395 /// <returns>result of operation</returns>
396 public PlayResult Attack__Turn(IUnitInfo unit)
397 {
398 return Attack__Turn(unit.Location);
399 }
400
401 /// <summary>
402 /// Attack a city. If city is defended, attack defender. If city is undefended, capture (Ground) or bombard (Sea, Air) it.
403 /// Moves along shortest possible path considering all known information.
404 /// Only does the part of the move that is possible within this turn.
405 /// If move has to be continued next turn the return value has the Error property Incomplete.
406 /// Operation breaks even if it could be continued within the turn if a new foreign unit or city is spotted,
407 /// in this case the result has the NewUnitOrCitySpotted property set.
408 /// Hostile terrain is considered to find a compromise between damage and reaching the target fast.
409 /// </summary>
410 /// <param name="target">city to attack</param>
411 /// <returns>result of operation</returns>
412 public PlayResult Attack__Turn(ICity city)
413 {
414 return Attack__Turn(city.Location);
415 }
416
417 public PlayResult DoSpyMission__Turn(SpyMission mission, Location target)
418 {
419 if (!target.IsValid)
420 return new PlayResult(PlayError.InvalidLocation);
421 PlayResult result = theEmpire.Play(Protocol.sSetSpyMission + ((int)mission << 4));
422 if (!result.OK)
423 return result;
424 else
425 {
426 result = MoveToNeighborOf__Turn(target);
427 if (!result.OK || result.UnitRemoved || result.NewUnitOrCitySpotted)
428 return result;
429 else
430 return Step__Turn(target);
431 }
432 }
433
434 public PlayResult DoSpyMission__Turn(SpyMission mission, ICity city)
435 {
436 return DoSpyMission__Turn(mission, city.Location);
437 }
438
439 //bool MoveForecast__Turn(ToLoc; var RemainingMovement: integer)
440 //{
441 // return true; // todo !!!
442 //}
443
444 //bool AttackForecast__Turn(ToLoc,AttackMovement; var RemainingHealth: integer)
445 //{
446 // return true; // todo !!!
447 //}
448
449 //bool DefenseForecast__Turn(euix,ToLoc: integer; var RemainingHealth: integer)
450 //{
451 // return true; // todo !!!
452 //}
453
454 /// <summary>
455 /// Disband unit. If located in city producing a unit, utilize material.
456 /// </summary>
457 /// <returns>result of operation</returns>
458 public PlayResult Disband__Turn()
459 {
460 City city = Location.OwnCity;
461
462 List<City> citiesChanged = null;
463 if (Load > 0)
464 {
465 citiesChanged = new List<City>();
466 foreach (Unit unit in theEmpire.Units)
467 {
468 if (unit.Transport == this && unit.Home != null && !citiesChanged.Contains(unit.Home))
469 citiesChanged.Add(unit.Home);
470 }
471 }
472
473 PlayResult result = theEmpire.Play(Protocol.sRemoveUnit, indexInSharedMemory);
474 if (result.OK)
475 {
476 theEmpire.UpdateLists(AEmpire.UpdateArea.Units);
477 if (Home != null)
478 Home.InvalidateReport();
479 if (city != null)
480 city.InvalidateReport(); // in case unit was utilized
481 if (citiesChanged != null)
482 {
483 foreach (City city1 in citiesChanged)
484 city1.InvalidateReport();
485 }
486 }
487 return result;
488 }
489
490 /// <summary>
491 /// start settler job
492 /// </summary>
493 /// <param name="job">the job to start</param>
494 /// <returns>result of operation</returns>
495 public PlayResult StartJob__Turn(Job job)
496 {
497 return theEmpire.Play(Protocol.sStartJob + ((int)job << 4), indexInSharedMemory);
498 }
499
500 /// <summary>
501 /// set home of unit in city it's located in
502 /// </summary>
503 /// <returns>result of operation</returns>
504 public PlayResult SetHomeHere__Turn()
505 {
506 City oldHome = Home;
507 PlayResult result = theEmpire.Play(Protocol.sSetUnitHome, indexInSharedMemory);
508 if (result.OK)
509 {
510 if (oldHome != null)
511 oldHome.InvalidateReport();
512 if (Home != null)
513 Home.InvalidateReport();
514 }
515 return result;
516 }
517
518 /// <summary>
519 /// load unit to transport at same location
520 /// </summary>
521 /// <returns>result of operation</returns>
522 public PlayResult LoadToTransport__Turn()
523 {
524 return theEmpire.Play(Protocol.sLoadUnit, indexInSharedMemory);
525 }
526
527 /// <summary>
528 /// unload unit from transport
529 /// </summary>
530 /// <returns>result of operation</returns>
531 public PlayResult UnloadFromTransport__Turn()
532 {
533 return theEmpire.Play(Protocol.sUnloadUnit, indexInSharedMemory);
534 }
535
536 /// <summary>
537 /// if this unit is a transport, select it as target for subsequent loading of units
538 /// </summary>
539 /// <returns></returns>
540 public PlayResult SelectAsTransport__Turn()
541 {
542 return theEmpire.Play(Protocol.sSelectTransport, indexInSharedMemory);
543 }
544
545 /// <summary>
546 /// add unit to the city it's located in
547 /// </summary>
548 /// <returns>result of operation</returns>
549 public PlayResult AddToCity__Turn()
550 {
551 City city = Location.OwnCity;
552 PlayResult result = theEmpire.Play(Protocol.sAddToCity, indexInSharedMemory);
553 if (result.OK)
554 {
555 if (Home != null)
556 Home.InvalidateReport();
557 if (city != null)
558 city.InvalidateReport();
559 }
560 return result;
561 }
562 #endregion
563
564 #region template internal stuff
565 int indexInSharedMemory = -1;
566 int* address;
567
568 /// <summary>
569 /// INTERNAL - only access from CevoAILib classes!
570 /// </summary>
571 public int IndexInSharedMemory
572 {
573 get { return indexInSharedMemory; }
574 set
575 {
576 if (value != indexInSharedMemory)
577 {
578 indexInSharedMemory = value;
579 if (indexInSharedMemory >= 0)
580 address = (int*)theEmpire.address[4] + ROReadPoint.SizeOfUn * indexInSharedMemory;
581 }
582 }
583 }
584 #endregion
585 }
586
587 unsafe struct MovingUnit : IUnitInfo
588 {
589 readonly AEmpire theEmpire;
590 int[] showMoveData;
591
592 public MovingUnit(AEmpire empire, int* data)
593 {
594 this.theEmpire = empire;
595 showMoveData = new int[13];
596 for (int i = 0; i < 13; i++)
597 showMoveData[i] = data[i];
598 }
599
600 public override string ToString()
601 {
602 return Model.ToString();
603 }
604
605 #region IUnitInfo members
606 public Nation Nation { get { return new Nation(theEmpire, showMoveData[0]); } }
607 public ModelBase Model { get { return theEmpire.ForeignModels[showMoveData[3]]; } }
608 public Location Location { get { return new Location(theEmpire, showMoveData[5]); } }
609 public bool AreOtherUnitsPresent { get { return (showMoveData[4] & Protocol.unMulti) != 0; } }
610 public bool IsLoaded { get { return false; } }
611 public int Speed { get { return AUnit.UnitSpeed(this); } }
612 public bool IsTerrainResistant { get { return Model.IsTerrainResistant || Nation.HasWonder(Building.HangingGardens); } }
613 public int Experience { get { return showMoveData[11]; } }
614 public int ExperienceLevel { get { return Experience / Cevo.ExperienceLevelCost; } }
615 public int Health { get { return showMoveData[1]; } }
616 public bool IsFortified { get { return (showMoveData[4] & Protocol.unFortified) != 0; } }
617 public int Load { get { return showMoveData[12]; } }
618 public int Fuel { get { return showMoveData[10]; } }
619 public Job Job { get { return Job.None; } }
620 #endregion
621 }
622
623 /// <summary>
624 /// foreign unit, abstract base class
625 /// </summary>
626 struct ForeignUnit : IUnitInfo
627 {
628 readonly AEmpire theEmpire;
629 readonly Location location;
630 readonly ModelBase model;
631 readonly int data2;
632 readonly int data3;
633
634 public ForeignUnit(AEmpire empire, Location location, ModelBase model, int data2, int data3)
635 {
636 this.theEmpire = empire;
637 this.location = location;
638 this.model = model;
639 this.data2 = data2;
640 this.data3 = data3;
641 }
642
643 public override string ToString()
644 {
645 return string.Format("{0}@{1}", model, location.ID);
646 }
647
648 #region IUnitInfo members
649 public Nation Nation { get { return new Nation(theEmpire, data2 & 0xFF); } }
650 public ModelBase Model { get { return model; } }
651 public Location Location { get { return location; } }
652
653 /// <summary>
654 /// whether other units are present at the same location
655 /// </summary>
656 public bool AreOtherUnitsPresent { get { return (data3 & (Protocol.unMulti << 16)) != 0; } }
657
658 /// <summary>
659 /// alwas false, loaded foreign units are not in list
660 /// </summary>
661 public bool IsLoaded { get { return false; } }
662
663 /// <summary>
664 /// movement points this unit has per turn, considering damage and wonders
665 /// </summary>
666 public int Speed { get { return AUnit.UnitSpeed(this); } }
667
668 /// <summary>
669 /// whether this unit passes hostile terrain without damage
670 /// </summary>
671 public bool IsTerrainResistant { get { return model.IsTerrainResistant || Nation.HasWonder(Building.HangingGardens); } }
672
673 /// <summary>
674 /// Experience points collected.
675 /// </summary>
676 public int Experience { get { return data3 & 0xFF; } }
677
678 /// <summary>
679 /// Experience level as applied in combat. 0 = Green ... 4 = Elite.
680 /// </summary>
681 public int ExperienceLevel { get { return Experience / Cevo.ExperienceLevelCost; } }
682
683 public int Health { get { return (sbyte)((data2 >> 8) & 0xFF); } }
684 public bool IsFortified { get { return (data3 & (Protocol.unFortified << 16)) != 0; } }
685
686 /// <summary>
687 /// total number of units loaded to this unit
688 /// </summary>
689 public int Load { get { return (sbyte)((data3 >> 8) & 0xFF); } }
690
691 /// <summary>
692 /// Fuel remaining, not necessarily the same as Model.Fuel.
693 /// </summary>
694 public int Fuel { get { return (sbyte)((data2 >> 16) & 0xFF); } }
695
696 /// <summary>
697 /// settler job this unit is currently doing
698 /// </summary>
699 public Job Job { get { return (Job)((data2 >> 24) & 0xFF); } }
700 #endregion
701 }
702
703 unsafe sealed class ForeignUnitList : IEnumerable<IUnitInfo>
704 {
705 readonly AEmpire theEmpire;
706 readonly int* address;
707
708 public ForeignUnitList(AEmpire empire)
709 {
710 theEmpire = empire;
711 address = (int*)empire.address[7];
712 }
713
714 IUnitInfo this[int index]
715 {
716 get
717 {
718 return new ForeignUnit(
719 theEmpire,
720 new Location(theEmpire, address[ROReadPoint.SizeOfUnitInfo * index]),
721 theEmpire.ForeignModels[(address[ROReadPoint.SizeOfUnitInfo * index + 1] >> 16) & 0xFFFF],
722 address[ROReadPoint.SizeOfUnitInfo * index + 2],
723 address[ROReadPoint.SizeOfUnitInfo * index + 3]);
724 }
725 }
726
727 /// <summary>
728 /// INTERNAL - only call from CevoAILib classes!
729 /// </summary>
730 public IUnitInfo UnitByLocation(Location location)
731 {
732 int index = -1;
733 bool inRange = true;
734 do
735 {
736 index++;
737 inRange = (index < theEmpire.address[ROReadPoint.TestFlags + 10]);
738 }
739 while (inRange && address[ROReadPoint.SizeOfUnitInfo * index] != location.ID);
740 if (inRange)
741 return this[index];
742 else
743 return null;
744 }
745
746 #region IEnumerable members
747 class Enumerator : IEnumerator<IUnitInfo>
748 {
749 ForeignUnitList list;
750 int index;
751
752 public Enumerator(ForeignUnitList list) { this.list = list; index = -1; }
753 public void Reset() { index = -1; }
754 public IUnitInfo Current { get { return list[index]; } }
755 object System.Collections.IEnumerator.Current { get { return list[index]; } }
756 public void Dispose() { }
757
758 public bool MoveNext()
759 {
760 bool inRange = true;
761 do
762 {
763 index++;
764 inRange = (index < list.theEmpire.address[ROReadPoint.TestFlags + 10]);
765 }
766 while (inRange && list.address[ROReadPoint.SizeOfUnitInfo * index] < 0); // LID < 0 indicates gap in list
767 return inRange;
768 }
769 }
770
771 public IEnumerator<IUnitInfo> GetEnumerator() { return new Enumerator(this); }
772 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return new Enumerator(this); }
773 #endregion
774 }
775}
Note: See TracBrowser for help on using the repository browser.