source: branches/AlphaChannel/AI Template/Project/Lib/CityBase.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: 24.4 KB
Line 
1using System;
2using System.Collections.Generic;
3using AI;
4
5namespace CevoAILib
6{
7 enum Building
8 {
9 None = 28,
10
11 Pyramids = 0, TempleOfZeus = 1, HangingGardens = 2, Colossus = 3, Lighthouse = 4,
12 GreatLibrary = 5, Oracle = 6, SunTsusWarAcademy = 7, LeonardosWorkshop = 8, MagellansExpedition = 9,
13 MichelangelosChapel = 10, NewtonsCollege = 12, BachsCathedral = 13,
14 StatueOfLiberty = 15, EiffelTower = 16, HooverDam = 17, ShinkansenExpress = 18, ManhattanProject = 19,
15 MIRSpaceStation = 20,
16 WonderRange = 28, // for logic only, < WonderRange means wonder (better use Cevo.Pedia(Building).Kind)
17
18 Barracks = 29, Granary = 30, Temple = 31, Marketplace = 32, Library = 33, Courthouse = 34,
19 CityWalls = 35, Aqueduct = 36, Bank = 37, Cathedral = 38, University = 39,
20 Harbor = 40, Theater = 41, Factory = 42, MfgPlant = 43, RecyclingCenter = 44,
21 PowerStation = 45, HydroelectricDam = 46, NuclearPlant = 47, OffshorePlatform = 48, TownHall = 49,
22 SewerSystem = 50, Supermarket = 51, Superhighways = 52, ResearchLab = 53, SAM = 54,
23 CoastalFortress = 55, Airport = 56, Dockyard = 57,
24
25 Palace = 58, GreatWall = 59, Colosseum = 60, Observatory = 61, MilitaryAcademy = 62,
26 CommandBunker = 63, AlgaePlant = 64, StockExchange = 65, SpacePort = 66,
27
28 ColonyShipComponent = 67, PowerModule = 68, HabitationModule = 69,
29 }
30
31 enum CityEvent
32 {
33 CivilDisorder = 0, ProductionComplete = 1, PopulationGrowth = 2, PopulationDecrease = 3, UnitDisbanded = 4,
34 ImprovementSold = 5, ProductionSabotaged = 6, MaximumSizeReached = 7, Pollution = 8, CityUnderSiege = 9,
35 WonderAlreadyExists = 10, EmigrationDelayed = 11, CityFounded = 12, TakeoverComplete = 13
36 }
37
38 //enum ExploitableLocationStatus { Available = 0, ExploitedByOtherCity = 1, Siege = 2, DisallowedByTreaty = 4 }
39
40 [Flags]
41 enum UnitProductionOptions { None = 0x00, AllowDisbandCity = 0x01, AsConscripts = 0x02 }
42
43 /// <summary>
44 /// Input parameter for City.OptimizeExploitedLocations__Turn method
45 /// </summary>
46 class ResourceWeights
47 {
48 public enum Op {Add = 0, Multiply = 1}
49
50 /// <summary>
51 /// predefined value: max growth
52 /// </summary>
53 public static readonly ResourceWeights MaxGrowth = new ResourceWeights(120, Op.Add, 0.125, Op.Add, 0.0625, Op.Add, 0.0625, Op.Add);
54
55 /// <summary>
56 /// predefined value: max production
57 /// </summary>
58 public static readonly ResourceWeights MaxProduction = new ResourceWeights(0.0625, Op.Add, 120, Op.Add, 30, Op.Add, 1, Op.Add);
59
60 /// <summary>
61 /// predefined value: max research
62 /// </summary>
63 public static readonly ResourceWeights MaxResearch = new ResourceWeights(0.0625, Op.Add, 4, Op.Add, 4, Op.Add, 8, Op.Add);
64
65 /// <summary>
66 /// predefined value: hurry production
67 /// </summary>
68 public static readonly ResourceWeights HurryProduction = new ResourceWeights(0.5, Op.Multiply, 8, Op.Add, 2, Op.Add, 1, Op.Add);
69
70 /// <summary>
71 /// predefined value: hurry research
72 /// </summary>
73 public static readonly ResourceWeights HurryResearch = new ResourceWeights(0.5, Op.Multiply, 1, Op.Add, 1, Op.Add, 1, Op.Add);
74
75 /// <summary>
76 /// INTERNAL - only access from CevoAILib classes!
77 /// </summary>
78 public readonly uint Code;
79
80 /// <summary>
81 /// Weights resources using a formula of the shape Pow(A1,wA1) * Pow(A2,wA2) * ... * (B1*wB1 + B2*wB2 + ...).
82 /// </summary>
83 /// <param name="foodWeight">weight of food</param>
84 /// <param name="foodOp">operation for food weight, Multiply = A part, Add = B part of formula</param>
85 /// <param name="productionWeight">weight of production</param>
86 /// <param name="productionOp">operation for production weight, Multiply = A part, Add = B part of formula</param>
87 /// <param name="taxWeight">weight of tax</param>
88 /// <param name="taxOp">operation for tax weight, Multiply = A part, Add = B part of formula</param>
89 /// <param name="scienceWeight">weight of science</param>
90 /// <param name="scienceOp">operation for science weight, Multiply = A part, Add = B part of formula</param>
91 public ResourceWeights(double foodWeight, Op foodOp, double productionWeight, Op productionOp, double taxWeight, Op taxOp, double scienceWeight, Op scienceOp)
92 {
93 Code = (ItemCode(foodWeight, foodOp) << 24)
94 + (ItemCode(productionWeight, productionOp) << 16)
95 + (ItemCode(taxWeight, taxOp) << 8)
96 + ItemCode(scienceWeight, scienceOp);
97 }
98
99 uint ItemCode(double weight, Op op)
100 {
101 int exp = (int)(Math.Log(weight, 2.0) + Math.Log(32.0 / 31.0, 2.0) + 990) - 993;
102 if (exp >= 4)
103 return 0x3F | ((uint)op << 7); // above maximum
104
105 if (exp < -4)
106 exp = -4;
107 uint mant = (uint)(weight * (1 << (4 - exp)) / 16.0 + 0.5);
108 if (mant > 15)
109 mant = 15;
110 if (exp < 0)
111 return mant | ((uint)(exp + 4) << 4) | ((uint)op << 7) | 0x40;
112 else
113 return mant | ((uint)exp << 4) | ((uint)op << 7);
114 }
115 }
116
117 /// <summary>
118 /// set of the 3 basic resources (food, material, trade)
119 /// </summary>
120 struct BaseResourceSet
121 {
122 public int Food;
123 public int Material;
124 public int Trade;
125
126 public BaseResourceSet(int food, int material, int trade)
127 {
128 this.Food = food;
129 this.Material = material;
130 this.Trade = trade;
131 }
132
133 public override string ToString()
134 {
135 return string.Format("F{0} M{1} T{2}", Food, Material, Trade);
136 }
137 }
138
139 //struct ExploitableLocation
140 //{
141 // public readonly Location Location;
142 // public readonly RC RC;
143 // public readonly ExploitableLocationStatus Status;
144 // public readonly BaseResourceSet PotentialResources;
145
146 // public ExploitableLocation(Location location, RC RC, ExploitableLocationStatus status, BaseResourceSet potentialResources)
147 // {
148 // this.Location = location;
149 // this.RC = RC;
150 // this.Status = status;
151 // this.PotentialResources = potentialResources;
152 // }
153 //}
154
155 /// <summary>
156 /// basic city information as available for both own and foreign cities
157 /// </summary>
158 interface ICity
159 {
160 bool Exists { get; }
161 int ID { get; }
162 Location Location { get; }
163 Nation Nation { get; }
164 Nation Founder { get; }
165 int SerialNo { get; }
166 int Size { get; }
167 bool Has(Building building);
168 }
169
170 /// <summary>
171 /// own city, abstract base class
172 /// </summary>
173 unsafe abstract class ACity : ICity
174 {
175 protected readonly Empire theEmpire;
176 protected readonly int id;
177
178 public ACity(Empire empire, int indexInSharedMemory)
179 {
180 this.theEmpire = empire;
181 IndexInSharedMemory = indexInSharedMemory;
182 id = address[3] & 0xFFFF; // save to be able to find city back
183 }
184
185 public override string ToString()
186 {
187 return string.Format("{0}.{1}@{2}", (id >> 12) & 0xF, id & 0xFFF, address[0]);
188 }
189
190 #region ICity Members
191 /// <summary>
192 /// true - city still exists, false - city has been destroyed
193 /// </summary>
194 public bool Exists { get { return indexInSharedMemory >= 0; } }
195
196 /// <summary>
197 /// unique city ID
198 /// </summary>
199 public int ID { get { return id; } }
200
201 public Location Location { get { return new Location(theEmpire, address[0]); } }
202 public Nation Nation { get { return theEmpire.Us; } }
203 public Nation Founder { get { return new Nation(theEmpire, (id >> 12) & 0xF); } }
204
205 /// <summary>
206 /// number of cities the founding nation founded before this one
207 /// </summary>
208 public int SerialNo { get { return id & 0xFFF; } }
209
210 public int Size { get { return (address[3] >> 16) & 0xFFFF; } }
211
212 /// <summary>
213 /// Whether the city has a specific building or wonder.
214 /// </summary>
215 /// <param name="building">the building</param>
216 /// <returns>whether building exists in this city</returns>
217 public bool Has(Building building) { return ((byte*)(address + 10))[(int)building] > 0; }
218 #endregion
219
220 /// <summary>
221 /// City area, i.e. the locations of all tiles that might potentially be exploited by the city, including the city location.
222 /// Usually the array has 21 elements, but it's less if the city is close to the upper or lower end of the map.
223 /// </summary>
224 OtherLocation[] Area { get { return Location.Distance5Area; } }
225
226 /// <summary>
227 /// Whether a location is in the area of the city, i.e. might potentially be exploited by it.
228 /// </summary>
229 /// <param name="otherLocation">the location</param>
230 /// <returns>true if in area, false if not</returns>
231 public bool AreaSpans(Location otherLocation) { return otherLocation.IsValid && (otherLocation - Location).Distance <= 5; }
232
233 /// <summary>
234 /// whether the city had a specific event in this turn
235 /// </summary>
236 /// <param name="cityEvent">the event</param>
237 /// <returns>true if event occurred, false if not</returns>
238 public bool HadEvent__Turn(CityEvent cityEvent) { return (address[7] & (1 << (int)cityEvent)) != 0; }
239
240 /// <summary>
241 /// If city was captured, turns until the takeover is complete and the city can be managed. Always 0 for cities that were not captured.
242 /// </summary>
243 public int TurnsTillTakeoverComplete { get { return (address[7] >> 16) & 0xF; } }
244
245 /// <summary>
246 /// food collected by the city
247 /// </summary>
248 public int FoodPile { get { return address[5] & 0xFFFF; } }
249
250 /// <summary>
251 /// material collected by the city
252 /// </summary>
253 public int MaterialPile { get { return address[6] & 0xFFFF; } }
254
255 /// <summary>
256 /// pollution accumulated in the city
257 /// </summary>
258 public int PollutionPile { get { return (address[5] >> 16) & 0xFFFF; } }
259
260 /// <summary>
261 /// size of food storage
262 /// </summary>
263 public int StorageSize { get { return Cevo.StorageSize[theEmpire.DifficultyLevel]; } }
264
265 /// <summary>
266 /// number of units that might have their home in this city without requiring material support
267 /// </summary>
268 public int FreeSupport { get { return Size * Cevo.Pedia(theEmpire.Government).FreeSupport / 2; } }
269
270 #region report
271 public int Morale { get { UpdateReport(); return report[3]; } }
272 public int Control { get { UpdateReport(); return report[9]; } }
273 public int Wealth { get { UpdateReport(); return report[20]; } }
274 public int Unrest { get { UpdateReport(); return 2 * report[8]; } }
275 public int HappinessBalance { get { UpdateReport(); return report[21]; } }
276 public int FoodSupport { get { UpdateReport(); return report[4]; } }
277 public int MaterialSupport { get { UpdateReport(); return report[5]; } }
278 public int ProductionCost { get { UpdateReport(); return report[6]; } }
279 public BaseResourceSet TotalResourcesFromArea { get { UpdateReport(); return new BaseResourceSet(report[10], report[11], report[12]); } }
280 public int FoodSurplus { get { UpdateReport(); return report[14]; } }
281 public int MaterialSurplus { get { UpdateReport(); return report[15]; } }
282 public int PollutionPlus { get { UpdateReport(); return report[16]; } }
283 public int Corruption { get { UpdateReport(); return report[17]; } }
284 public int TaxOutput { get { UpdateReport(); return report[18]; } }
285 public int ScienceOutput { get { UpdateReport(); return report[19]; } }
286 #endregion
287
288 public int NumberOfExploitedLocations
289 {
290 get
291 {
292 int array = address[8];
293 int count = 0;
294 for (int V21 = 1; V21 < 27; V21++)
295 {
296 if ((array & (1 << V21)) != 0)
297 count++;
298 }
299 return count;
300 }
301 }
302
303 public Location[] ExploitedLocations
304 {
305 get
306 {
307 int[] distance5IDs = new int[28];
308 theEmpire.Map.GetDistance5IDs(Location.ID, distance5IDs);
309 Location[] exploitedLocations = new Location[NumberOfExploitedLocations];
310 int array = address[8];
311 int count = 0;
312 for (int V21 = 1; V21 < 27; V21++)
313 {
314 if ((array & (1 << V21)) != 0)
315 {
316 exploitedLocations[count] = new Location(theEmpire, distance5IDs[V21]);
317 count++;
318 }
319 }
320 return exploitedLocations;
321 }
322 }
323
324 //public XC GetExploitableLocations__Turn(ref ExploitableLocation[] exploitableLocations)
325 //{
326 // XC result;
327 // int[] distance5IDs = new int[28];
328 // theEmpire.Map.GetDistance5IDs(Location.ID, distance5IDs);
329
330 // fixed (int* cityAreaInfo = new int[28], tileInfo = new int[4])
331 // {
332 // result = theEmpire.Play(Protocol.sGetCityAreaInfo, Location.ID, cityAreaInfo);
333 // if (result.OK)
334 // {
335 // int count = 0;
336 // for (int V21 = 1; V21 < 27; V21++)
337 // {
338 // if (distance5IDs[V21] >= 0)
339 // count++;
340 // }
341 // exploitableLocations = new ExploitableLocation[count];
342 // count = 0;
343 // for (int V21 = 1; V21 < 27; V21++)
344 // {
345 // if (distance5IDs[V21] >= 0)
346 // {
347 // int dy = (V21 >> 2) - 3;
348 // int dx = ((V21 & 3) << 1) - 3 + ((dy + 3) & 1);
349 // tileInfo[3] = indexInSharedMemory;
350 // theEmpire.Play(Protocol.sGetHypoCityTileInfo, distance5IDs[V21], tileInfo);
351 // exploitableLocations[count] = new ExploitableLocation(
352 // new Location(theEmpire, distance5IDs[V21]),
353 // new RC((dx + dy) >> 1, (dy - dx) >> 1),
354 // (ExploitableLocationStatus)cityAreaInfo[V21],
355 // new BaseResourceSet(tileInfo[0], tileInfo[1], tileInfo[2]));
356 // count++;
357 // }
358 // }
359 // }
360 // }
361 // return result;
362 //}
363
364 /// <summary>
365 /// model of unit currently in production, null if production project is not a unit
366 /// </summary>
367 public Model UnitInProduction
368 {
369 get
370 {
371 int project = address[4] & 0xFFFF;
372 if ((project & Protocol.cpImp) != 0)
373 return null;
374 else
375 return theEmpire.Models[project & Protocol.cpIndex];
376 }
377 }
378
379 /// <summary>
380 /// building currently in production, Building.None if production project is not a building
381 /// </summary>
382 public Building BuildingInProduction
383 {
384 get
385 {
386 int project = address[4] & 0xFFFF;
387 if ((project & Protocol.cpImp) == 0)
388 return Building.None;
389 else
390 return (Building)(project & Protocol.cpIndex);
391 }
392 }
393
394 public bool CanSetBuildingInProduction__Turn(Building building)
395 {
396 return theEmpire.TestPlay(Protocol.sSetCityProject, indexInSharedMemory, ((int)building & Protocol.cpIndex) | Protocol.cpImp).OK;
397 }
398
399 /// <summary>
400 /// persistent custom value
401 /// </summary>
402 public int Status
403 {
404 get { return address[1]; }
405 set { address[1] = value; }
406 }
407
408 #region effective methods
409 //public XC SetExploitedLocations__Turn(Location[] locations)
410 //{
411 // int[] distance5IDs = new int[28];
412 // theEmpire.Map.GetDistance5IDs(Location.ID, distance5IDs);
413 // int array = 0;
414 // foreach (Location location in locations)
415 // {
416 // int V21 = Array.IndexOf<int>(distance5IDs, location.ID);
417 // if (V21 < 0)
418 // return new XC(ServerReturnCode.TileNotAvailable);
419 // array += 1 << V21;
420 // }
421 // XC result = theEmpire.Play(Protocol.sSetCityTiles, indexInSharedMemory, array);
422 // if (result.Effective)
423 // InvalidateReport();
424 // return result;
425 //}
426
427 /// <summary>
428 /// Change selection of tiles to exploit by the city.
429 /// Does not touch the tile selection of other cities.
430 /// </summary>
431 /// <param name="resourceWeights">selection strategy: how to weight the different resource types</param>
432 /// <returns>result of operation</returns>
433 public PlayResult OptimizeExploitedLocations__Turn(ResourceWeights resourceWeights)
434 {
435 PlayResult result;
436 fixed (uint* cityTileAdvice = new uint[20])
437 {
438 cityTileAdvice[0] = resourceWeights.Code;
439 result = theEmpire.Play(Protocol.sGetCityTileAdvice, indexInSharedMemory, cityTileAdvice);
440 if (result.OK)
441 result = theEmpire.Play(Protocol.sSetCityTiles, indexInSharedMemory, (int)cityTileAdvice[1]);
442 }
443 if (result.Effective)
444 InvalidateReport();
445 return result;
446 }
447
448 /// <summary>
449 /// Do no longer exploit any tile except the tile of the city itself. Combined with OptimizeExploitedLocations, this can
450 /// be used to set priorities for tile exploitation between cities with overlapping area. Typical sequence:
451 /// (1) LowPriorityCity.StopExploitation
452 /// (2) HighPriorityCity.OptimizeExploitedLocations
453 /// (3) LowPriorityCity.OptimizeExploitedLocations
454 /// Usually calling this should be followed by an OptimizeExploitedLocations for the same city within the same turn. Otherwise
455 /// the city will remain in the non-exploiting state and start to decline.
456 /// </summary>
457 /// <returns>result of operation</returns>
458 public PlayResult StopExploitation__Turn()
459 {
460 PlayResult result = theEmpire.Play(Protocol.sSetCityTiles, indexInSharedMemory, 1<<13);
461 if (result.Effective)
462 InvalidateReport();
463 return result;
464 }
465
466 /// <summary>
467 /// Change production project to a unit.
468 /// </summary>
469 /// <param name="model">model of the unit to produce</param>
470 /// <param name="options">options</param>
471 /// <returns>result of operation</returns>
472 public PlayResult SetUnitInProduction__Turn(Model model, UnitProductionOptions options)
473 {
474 int optionArray = 0;
475 if ((options & UnitProductionOptions.AsConscripts) != 0)
476 optionArray += Protocol.cpConscripts;
477 if ((options & UnitProductionOptions.AllowDisbandCity) != 0)
478 optionArray += Protocol.cpDisbandCity;
479 PlayResult result = theEmpire.Play(Protocol.sSetCityProject, indexInSharedMemory, model.IndexInSharedMemory | optionArray);
480 if (result.Effective)
481 InvalidateReport();
482 return result;
483 }
484
485 /// <summary>
486 /// Change production project to a unit.
487 /// </summary>
488 /// <param name="model">model of the unit to produce</param>
489 /// <returns>result of operation</returns>
490 public PlayResult SetUnitInProduction__Turn(Model model)
491 {
492 return SetUnitInProduction__Turn(model, UnitProductionOptions.None);
493 }
494
495 /// <summary>
496 /// Change production project to a buiding or wonder.
497 /// </summary>
498 /// <param name="building">the building to produce</param>
499 /// <returns>result of operation</returns>
500 public PlayResult SetBuildingInProduction__Turn(Building building)
501 {
502 PlayResult result = theEmpire.Play(Protocol.sSetCityProject, indexInSharedMemory, ((int)building & Protocol.cpIndex) | Protocol.cpImp);
503 if (result.Effective)
504 InvalidateReport();
505 return result;
506 }
507
508 /// <summary>
509 /// stop production and set production to trade goods
510 /// </summary>
511 /// <returns>result of operation</returns>
512 public PlayResult StopProduction__Turn()
513 {
514 return SetBuildingInProduction__Turn(Building.None);
515 }
516
517 /// <summary>
518 /// buy material to complete the production in the next turn
519 /// </summary>
520 /// <returns>result of operation</returns>
521 public PlayResult BuyMaterial__Turn()
522 {
523 return theEmpire.Play(Protocol.sBuyCityProject, indexInSharedMemory);
524 }
525
526 /// <summary>
527 /// sell an existing building
528 /// </summary>
529 /// <param name="building">the building to sell</param>
530 /// <returns>result of operation</returns>
531 public PlayResult SellBuilding__Turn(Building building)
532 {
533 PlayResult result = theEmpire.Play(Protocol.sSellCityImprovement, indexInSharedMemory, (int)building);
534 if (result.Effective)
535 {
536 if (building == Building.Palace || building == Building.StockExchange || building == Building.SpacePort)
537 theEmpire.InvalidateAllCityReports();
538 else
539 InvalidateReport();
540 }
541 return result;
542 }
543
544 /// <summary>
545 /// rebuild an existing building
546 /// </summary>
547 /// <param name="building">the building to rebuild</param>
548 /// <returns>result of operation</returns>
549 public PlayResult RebuildBuilding__Turn(Building building)
550 {
551 PlayResult result = theEmpire.Play(Protocol.sRebuildCityImprovement, indexInSharedMemory, (int)building);
552 if (result.Effective)
553 {
554 if (building == Building.Palace || building == Building.StockExchange || building == Building.SpacePort)
555 theEmpire.InvalidateAllCityReports();
556 else
557 InvalidateReport();
558 }
559 return result;
560 }
561 #endregion
562
563 #region template internal stuff
564 int indexInSharedMemory = -1;
565 int* address;
566 int[] report = new int[22];
567 bool isReportValid = false;
568
569 void UpdateReport()
570 {
571 if (!isReportValid)
572 {
573 report[0] = -1;
574 report[1] = -1;
575 report[2] = -1;
576 fixed (int* data = report)
577 {
578 theEmpire.Play(Protocol.sGetCityReportNew, indexInSharedMemory, data);
579 }
580 isReportValid = true;
581 }
582 }
583
584 /// <summary>
585 /// INTERNAL - only access from CevoAILib classes!
586 /// </summary>
587 public int IndexInSharedMemory
588 {
589 get { return indexInSharedMemory; }
590 set
591 {
592 if (value != indexInSharedMemory)
593 {
594 indexInSharedMemory = value;
595 if (indexInSharedMemory >= 0)
596 address = (int*)theEmpire.address[5] + ROReadPoint.SizeOfCity * indexInSharedMemory;
597 }
598 }
599 }
600
601 /// <summary>
602 /// INTERNAL - only call from CevoAILib classes!
603 /// </summary>
604 public void InvalidateReport() { isReportValid = false; }
605 #endregion
606 }
607
608 /// <summary>
609 /// foreign city, abstract base class
610 /// </summary>
611 unsafe abstract class AForeignCity : ICity
612 {
613 protected readonly Empire theEmpire;
614 protected readonly int id;
615
616 public AForeignCity(Empire empire, int indexInSharedMemory)
617 {
618 this.theEmpire = empire;
619 IndexInSharedMemory = indexInSharedMemory;
620 id = (address[3] >> 16) & 0xFFFF; // save to be able to find city back
621 }
622
623 public override string ToString()
624 {
625 return string.Format("{0}.{1}@{2}", (id >> 12) & 0xF, id & 0xFFF, address[0]);
626 }
627
628 #region ICity Members
629 /// <summary>
630 /// true - city still exists, false - city has been destroyed
631 /// </summary>
632 public bool Exists { get { return indexInSharedMemory >= 0; } }
633
634 /// <summary>
635 /// unique city ID
636 /// </summary>
637 public int ID { get { return id; } }
638
639 public Location Location { get { return new Location(theEmpire, address[0]); } }
640 public Nation Nation { get { return new Nation(theEmpire, address[3] & 0xFFFF); } }
641 public Nation Founder { get { return new Nation(theEmpire, (id >> 12) & 0xF); } }
642
643 /// <summary>
644 /// number of cities the founding nation founded before this one
645 /// </summary>
646 public int SerialNo { get { return id & 0xFFF; } }
647
648 public int Size { get { return address[4] & 0xFFFF; } }
649
650 /// <summary>
651 /// Whether the city has a specific building or wonder.
652 /// Only works for buildings which are known if built in a foreign city.
653 /// These are: wonders, palace, space port and all defense facilities.
654 /// For all others, the return value is always false.
655 /// </summary>
656 /// <param name="building">the building</param>
657 /// <returns>whether building exists in this city</returns>
658 public bool Has(Building building)
659 {
660 switch (building)
661 {
662 case Building.Palace:
663 return (address[4] & (Protocol.ciCapital << 16)) != 0;
664 case Building.SpacePort:
665 return (address[4] & (Protocol.ciSpacePort << 16)) != 0;
666 case Building.CommandBunker:
667 return (address[4] & (Protocol.ciBunker << 16)) != 0;
668 case Building.CityWalls:
669 return (address[4] & (Protocol.ciWalled << 16)) != 0;
670 case Building.CoastalFortress:
671 return (address[4] & (Protocol.ciCoastalFort << 16)) != 0;
672 case Building.SAM:
673 return (address[4] & (Protocol.ciMissileBat << 16)) != 0;
674 default:
675 if (building < Building.WonderRange)
676 return theEmpire.Wonder_IsInCity(building, this);
677 else
678 return false; // unknown
679 }
680 }
681 #endregion
682
683 /// <summary>
684 /// city size and building information dates back to this turn
685 /// </summary>
686 public int TurnOfInformation { get { return Location.TurnObservedLast; } }
687
688 /// <summary>
689 /// persistent custom value
690 /// </summary>
691 public int Status
692 {
693 get { return address[1]; }
694 set { address[1] = value; }
695 }
696
697 #region template internal stuff
698 int indexInSharedMemory = -1;
699 int* address;
700
701 /// <summary>
702 /// INTERNAL - only access from CevoAILib classes!
703 /// </summary>
704 public int IndexInSharedMemory
705 {
706 get { return indexInSharedMemory; }
707 set
708 {
709 if (value != indexInSharedMemory)
710 {
711 indexInSharedMemory = value;
712 if (indexInSharedMemory >= 0)
713 address = (int*)theEmpire.address[8] + ROReadPoint.SizeOfCityInfo * indexInSharedMemory;
714 }
715 }
716 }
717 #endregion
718 }
719}
Note: See TracBrowser for help on using the repository browser.