source: branches/AlphaChannel/AI Template/Project/Lib/Nation.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: 41.4 KB
Line 
1using System;
2using System.Collections.Generic;
3using Common;
4using CevoAILib.Diplomacy;
5using AI;
6
7namespace CevoAILib
8{
9 enum Phase { BeginOfTurn, Turn, EndOfTurn, ForeignTurn };
10
11 enum Relation { NoContact = 0, NoTreaty = 1, CeaseFire = 2, Peace = 3, FriendlyContact = 4, Alliance = 5, Identity = 6 }
12
13 enum Attitude { Hostile = 0, Icy = 1, Uncooperative = 2, Neutral = 3, Receptive = 4, Cordial = 5, Enthusiastic = 6 }
14
15 enum Government { Anarchy = 0, Despotism = 1, Monarchy = 2, Republic = 3, Fundamentalism = 4, Communism = 5, Democracy = 6, FutureSociety = 7 }
16
17 enum EmpireEvent { ResearchComplete = 0, AnarchyOver = 3, GliderLost = 8, AircraftLost = 9, PeaceViolation = 10, PeaceEvacuation = 11 }
18
19 enum Advance
20 {
21 None = -1, MilitaryResearch = 0x800,
22
23 AdvancedFlight = 0, AmphibiousWarfare = 1, Astronomy = 2, AtomicTheory = 3, Automobile = 4,
24 Ballistics = 5, Banking = 6, BridgeBuilding = 7, BronzeWorking = 8, CeremonialBurial = 9,
25 Chemistry = 10, Chivalry = 11, Composites = 12, CodeOfLaws = 13, CombinedArms = 14,
26 CombustionEngine = 15, Communism = 16, Computers = 17, Conscription = 18, Construction = 19,
27 TheCorporation = 20, SpaceFlight = 21, Currency = 22, Democracy = 23, Economics = 24,
28 Electricity = 25, Electronics = 26, Engineering = 27, Environmentalism = 28, TheWheel = 29,
29 Explosives = 30, Flight = 31, Espionage = 32, Gunpowder = 33, HorsebackRiding = 34,
30 ImpulseDrive = 35, Industrialization = 36, SmartWeapons = 37, Invention = 38, IronWorking = 39,
31 TheLaser = 40, NuclearPower = 41, Literature = 42, TheInternet = 43, Magnetism = 44,
32 MapMaking = 45, Masonry = 46, MassProduction = 47, Mathematics = 48, Medicine = 49,
33 Metallurgy = 50, Miniaturization = 51, MobileWarfare = 52, Monarchy = 53, Mysticism = 54,
34 Navigation = 55, NuclearFission = 56, Philosophy = 57, Physics = 58, Plastics = 59,
35 Poetry = 60, Pottery = 61, Radio = 62, Recycling = 63, Refrigeration = 64,
36 Monotheism = 65, TheRepublic = 66, Robotics = 67, Rocketry = 68, Railroad = 69,
37 Sanitation = 70, Science = 71, Writing = 72, Seafaring = 73, SelfContainedEnvironment = 74,
38 Stealth = 75, SteamEngine = 76, Steel = 77, SyntheticFood = 78, Tactics = 79,
39 Theology = 80, TheoryOfGravity = 81, Trade = 82, TransstellarColonization = 83, University = 84,
40 AdvancedRocketry = 85, WarriorCode = 86, Alphabet = 87, Polytheism = 88, Refining = 89,
41 ComputingTechnology = 90, NanoTechnology = 91, MaterialTechnology = 92, ArtificialIntelligence = 93,
42
43 FirstCommon = 0, LastCommon = 89, FirstFuture = 90, LastFuture = 93
44 }
45
46 struct Economy
47 {
48 public readonly int TaxRate;
49 public readonly int Research;
50 public readonly int Wealth;
51
52 public Economy(int taxRate, int wealth)
53 {
54 this.TaxRate = taxRate;
55 this.Wealth = wealth;
56 this.Research = 100 - taxRate - wealth;
57 }
58
59 public override string ToString()
60 {
61 return string.Format("T{0} R{1} W{2}", TaxRate, Research, Wealth);
62 }
63 }
64
65 struct ColonyShipParts
66 {
67 public readonly int ComponentCount;
68 public readonly int PowerCount;
69 public readonly int HabitationCount;
70 public int this[Building part]
71 {
72 get
73 {
74 switch (part)
75 {
76 case Building.ColonyShipComponent: return ComponentCount;
77 case Building.PowerModule: return PowerCount;
78 case Building.HabitationModule: return HabitationCount;
79 default: return 0;
80 }
81 }
82 }
83
84 public ColonyShipParts(int componentCount, int powerCount, int habitationCount)
85 {
86 this.ComponentCount = componentCount;
87 this.PowerCount = powerCount;
88 this.HabitationCount = habitationCount;
89 }
90
91 public override string ToString()
92 {
93 return string.Format("C{0} P{1} H{2}", ComponentCount, PowerCount, HabitationCount);
94 }
95 }
96
97 unsafe struct Nation
98 {
99 public static Nation None { get { return new Nation(null, -1); } }
100
101 readonly AEmpire theEmpire;
102 public readonly int ID;
103
104 public Nation(AEmpire empire, int id) // empire refers to the own empire, not the one of the nation
105 {
106 this.theEmpire = empire;
107 this.ID = id;
108 }
109
110 public override string ToString()
111 {
112 return string.Format("{0}", ID);
113 }
114
115 public static bool operator ==(Nation nation1, Nation nation2) { return nation1.ID == nation2.ID; }
116 public static bool operator !=(Nation nation1, Nation nation2) { return nation1.ID != nation2.ID; }
117 public override bool Equals(object obj) { return ID == ((Nation)obj).ID; }
118 public override int GetHashCode() { return ID; }
119
120 int* report { get { return (int*)theEmpire.address[10 + ID]; } }
121
122 /// <summary>
123 /// whether this nation is still in the game
124 /// </summary>
125 public bool Subsists
126 {
127 get
128 {
129 if (ID < 0)
130 return false;
131 else
132 return (theEmpire.address[ROReadPoint.TestFlags + 2] & (1 << ID)) != 0;
133 }
134 }
135
136 /// <summary>
137 /// whether this nation has a specific wonder in one of its cities AND this wonder's effect has not yet expired
138 /// </summary>
139 /// <param name="wonder">the wonder</param>
140 /// <returns>true if nation has wonder and wonder is effective, false if it has not or wonder is expired</returns>
141 public bool HasWonder(Building wonder)
142 {
143 if (ID < 0)
144 return false;
145 else
146 return theEmpire.address[ROReadPoint.Wonder + 2 * (int)wonder + 1] == ID;
147 }
148
149 /// <summary>
150 /// government form of this nation
151 /// </summary>
152 public Government Government
153 {
154 get
155 {
156 if (this == theEmpire.Us)
157 return (Government)theEmpire.address[ROReadPoint.TestFlags + 13];
158 else
159 return (Government)report[5 + Protocol.nPl];
160 }
161 }
162
163 /// <summary>
164 /// credibility of this nation
165 /// </summary>
166 public int Credibility
167 {
168 get
169 {
170 if (this == theEmpire.Us)
171 return theEmpire.address[ROReadPoint.TestFlags + 5];
172 else
173 return report[4];
174 }
175 }
176
177 /// <summary>
178 /// colony ship of this nation
179 /// </summary>
180 public ColonyShipParts ColonyShip
181 {
182 get
183 {
184 return new ColonyShipParts(theEmpire.address[ROReadPoint.Ship + 3 * ID],
185 theEmpire.address[ROReadPoint.Ship + 3 * ID + 1],
186 theEmpire.address[ROReadPoint.Ship + 3 * ID + 2]);
187 }
188 }
189 }
190
191 interface IDossier
192 {
193 int TurnOfReport { get; }
194 int Treasury { get; }
195 bool Has(Advance advance);
196 bool HasAlmost(Advance advance);
197 int FutureTechnology(Advance advance);
198 Advance Researching { get; }
199 int ResearchPile { get; }
200 Relation RelationTo(Nation nation);
201 }
202
203 /// <summary>
204 /// own empire, abstract base class
205 /// </summary>
206 unsafe abstract class AEmpire : IDossier
207 {
208 #region abstract
209 protected abstract void NewGame();
210 protected abstract void Resume();
211 protected abstract void OnTurn();
212 protected abstract void OnStealAdvance(Advance[] selection);
213 protected abstract void OnForeignMove(IUnitInfo unit, Location destination);
214 protected abstract void OnBeforeForeignCapture(Nation nation, ICity city);
215 protected abstract void OnAfterForeignCapture();
216 protected abstract void OnBeforeForeignAttack(IUnitInfo attacker, Location target, BattleOutcome outcome);
217 protected abstract void OnAfterForeignAttack();
218 protected abstract void OnChanceToNegotiate(Phase situation, Nation Opponent, ref bool wantNegotiation, ref bool cancelTreatyIfRejected);
219 protected abstract void OnNegotiate(Negotiation negotiation);
220 #endregion
221
222 public readonly Nation Us;
223 public readonly Map Map;
224 public readonly ToughSet<Unit> Units = new ToughSet<Unit>();
225 public readonly ForeignUnitList ForeignUnits;
226 public readonly List<Model> Models = new List<Model>();
227 public readonly List<ForeignModel> ForeignModels = new List<ForeignModel>();
228 public readonly ToughSet<City> Cities = new ToughSet<City>();
229 public readonly ToughSet<ForeignCity> ForeignCities = new ToughSet<ForeignCity>();
230
231 /// <summary>
232 /// Model blueprint for military research.
233 /// </summary>
234 public readonly Blueprint Blueprint;
235
236 /// <summary>
237 /// persistent memory
238 /// </summary>
239 public readonly Persistent Persistent;
240
241 public AEmpire(int nationID, IntPtr serverPtr, IntPtr dataPtr, bool isNewGame)
242 {
243 int* data = (int*)dataPtr;
244 serverCall = (ServerCall)System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer(serverPtr, typeof(ServerCall));
245 address = (int*)data[4 + Protocol.nPl + nationID];
246 DifficultyLevel = data[4 + nationID];
247 TurnWhenGameEnds = data[3];
248 this.isNewGame = isNewGame;
249 foreignTurnUpdateAreas = UpdateArea.All;
250
251 Us = new Nation(this, nationID);
252 Map = new Map(this, data[0], data[1], data[2]);
253 ForeignUnits = new ForeignUnitList(this);
254 Blueprint = new Blueprint((Empire)this);
255 Persistent = new Persistent((Empire)this, (IntPtr)address[0]);
256 debugMapAddress = (int*)address[ROReadPoint.OracleIncome + 1];
257
258 UpdateLists(UpdateArea.All);
259 }
260
261 // for convenience, map all members of Us to empire
262 public bool Subsists { get { return Us.Subsists; } }
263 public Government Government { get { return Us.Government; } }
264 public int Credibility { get { return Us.Credibility; } }
265 public bool HasWonder(Building wonder) { return Us.HasWonder(wonder); }
266 public ColonyShipParts ColonyShip { get { return Us.ColonyShip; } }
267
268 #region IDossier members
269 public int TurnOfReport { get { return Turn; } }
270 public int Treasury { get { return address[ROReadPoint.TestFlags + 14]; } }
271
272 /// <summary>
273 /// whether an advance has been completely researched
274 /// </summary>
275 /// <param name="advance">the advance</param>
276 /// <returns>true if researched, false if not</returns>
277 public bool Has(Advance advance) { return ((sbyte*)(address + ROReadPoint.Tech))[(int)advance] >= 0; }
278
279 /// <summary>
280 /// whether an advance was gained from a trade with another nation or from the temple of zeus wonder
281 /// </summary>
282 /// <param name="advance">the advance</param>
283 /// <returns>true if gained, false if not</returns>
284 public bool HasAlmost(Advance advance) { return ((sbyte*)(address + ROReadPoint.Tech))[(int)advance] == -1; }
285
286 /// <summary>
287 /// science points collected for current research
288 /// </summary>
289 public int ResearchPile { get { return address[ROReadPoint.TestFlags + 17]; } }
290
291 /// <summary>
292 /// advance currently researched
293 /// </summary>
294 public Advance Researching
295 {
296 get
297 {
298 int ad = address[ROReadPoint.TestFlags + 18];
299 if (ad < 0)
300 return Advance.None;
301 else
302 return (Advance)ad;
303 }
304 }
305
306 /// <summary>
307 /// relation to specific other nation
308 /// </summary>
309 /// <param name="thirdNation">the other nation</param>
310 /// <returns>the relation</returns>
311 public Relation RelationTo(Nation nation)
312 {
313 if (nation == Us)
314 return Relation.Identity;
315 else
316 return (Relation)(address[ROReadPoint.Attitude + Protocol.nPl + nation.ID] + 1);
317 }
318
319 /// <summary>
320 /// number of future technologies developed
321 /// </summary>
322 /// <param name="advance">the future technology</param>
323 /// <returns>number</returns>
324 public int FutureTechnology(Advance advance)
325 {
326 sbyte raw = ((sbyte*)(address + ROReadPoint.Tech))[(int)advance];
327 if (raw <= 0)
328 return 0;
329 else
330 return raw;
331 }
332 #endregion
333
334 public bool IsMyTurn { get { return phase != Phase.ForeignTurn; } }
335 public int Turn { get { return address[ROReadPoint.TestFlags + 1]; } }
336
337 /// <summary>
338 /// whether a specific nation level event occurred in this turn
339 /// </summary>
340 /// <param name="empireEvent">the event</param>
341 /// <returns>true if the event occurred, false if not</returns>
342 public bool HadEvent__Turn(EmpireEvent empireEvent) { return (address[ROReadPoint.TestFlags + 3] & (1 << (int)empireEvent)) != 0; }
343
344 public readonly int DifficultyLevel;
345 //bool IsManipulationActivated(Manipulation manipulation); todo !!!
346 public readonly int TurnWhenGameEnds;
347 public int TurnOfAnarchyBegin { get { return address[ROReadPoint.TestFlags + 4]; } }
348 public int MaximumCredibilityLeft { get { return address[ROReadPoint.TestFlags + 6]; } }
349
350 /// <summary>
351 /// current economy settings, call ChangeEconomy__Turn to change
352 /// </summary>
353 public Economy Economy { get { return new Economy(address[ROReadPoint.TestFlags + 15], address[ROReadPoint.TestFlags + 16]); } }
354
355 public int IncomeFromOracle { get { return address[ROReadPoint.OracleIncome]; } }
356 public bool CanSetResearch__Turn(Advance advance) { return TestPlay(Protocol.sSetResearch, (int)advance).OK; }
357 public RelationDetails RelationDetailsTo(Nation nation) { return new RelationDetails(this, nation); }
358 public BattleHistory BattleHistory { get { return new BattleHistory(this); } }
359
360 /// <summary>
361 /// number of nations that are still in the game
362 /// </summary>
363 public int NumberOfSubsistingNations
364 {
365 get
366 {
367 int aliveArray = address[ROReadPoint.TestFlags + 2];
368 int count = 0;
369 for (int nationID = 0; nationID < Protocol.nPl; nationID++)
370 {
371 if ((aliveArray & (1 << nationID)) != 0)
372 count++;
373 }
374 return count;
375 }
376 }
377
378 /// <summary>
379 /// set of nations that are still in the game
380 /// </summary>
381 public Nation[] SubsistingNations
382 {
383 get
384 {
385 Nation[] subsistingNations = new Nation[NumberOfSubsistingNations];
386 int aliveArray = address[ROReadPoint.TestFlags + 2];
387 int count = 0;
388 for (int nationID = 0; nationID < Protocol.nPl; nationID++)
389 {
390 if ((aliveArray & (1 << nationID)) != 0)
391 {
392 subsistingNations[count] = new Nation(this, nationID);
393 count++;
394 }
395 }
396 return subsistingNations;
397 }
398 }
399
400 /// <summary>
401 /// science points to collect before the current research is complete
402 /// </summary>
403 public int CurrentResearchCost
404 {
405 get
406 {
407 fixed (int* researchCost = new int[1])
408 {
409 Play(Protocol.sGetTechCost, 0, researchCost);
410 return researchCost[0];
411 }
412 }
413 }
414
415 /// <summary>
416 /// Whether a specific building is built in one of the own cities.
417 /// Applicable to wonders and state improvements.
418 /// In case of wonders true is only returned if this wonder's effect has not yet expired.
419 /// </summary>
420 /// <param name="wonder">the wonder</param>
421 /// <returns>true if built and effective, false if not</returns>
422 public bool Has(Building building)
423 {
424 if (building < Building.WonderRange)
425 return Us.HasWonder(building);
426 else
427 return ((sbyte*)(address + ROReadPoint.NatBuilt))[(int)building - (int)Building.WonderRange] > 0;
428 }
429
430 public bool Wonder_WasBuilt(Building wonder) { return address[ROReadPoint.Wonder + 2 * (int)wonder] != -1; }
431 public bool Wonder_WasDestroyed(Building wonder) { return address[ROReadPoint.Wonder + 2 * (int)wonder] == -2; }
432 public bool Wonder_IsInCity(Building wonder, ICity city) { return address[ROReadPoint.Wonder + 2 * (int)wonder] == city.ID; }
433
434 public Dossier LastDossier(Nation nation) { return new Dossier(this, nation); }
435 public MilitaryReport LastMilitaryReport(Nation nation) { return new MilitaryReport(this, nation); }
436
437 #region effective methods
438 /// <summary>
439 /// Start revolution.
440 /// </summary>
441 /// <returns>result of operation</returns>
442 public PlayResult Revolution__Turn()
443 {
444 PlayResult result = Play(Protocol.sRevolution);
445 if (result.OK)
446 InvalidateAllCityReports();
447 return result;
448 }
449
450 /// <summary>
451 /// Set government form. Requires AnarchyOver event to have occurred this turn.
452 /// </summary>
453 /// <param name="newGovernment">new government form</param>
454 /// <returns>result of operation</returns>
455 public PlayResult SetGovernment__Turn(Government newGovernment)
456 {
457 PlayResult result = Play(Protocol.sSetGovernment, (int)newGovernment);
458 if (result.Effective)
459 InvalidateAllCityReports();
460 return result;
461 }
462
463 /// <summary>
464 /// Change economy settings.
465 /// </summary>
466 /// <param name="economy">new economy settings</param>
467 /// <returns>result of operation</returns>
468 public PlayResult ChangeEconomy__Turn(Economy economy)
469 {
470 PlayResult result = Play(Protocol.sSetRates, (economy.TaxRate / 10 & 0xf) + ((economy.Wealth / 10 & 0xf) << 4));
471 if (result.Effective)
472 InvalidateAllCityReports();
473 return result;
474 }
475
476 /// <summary>
477 /// Set new advance to research. Requires ResearchComplete event to have occurred this turn.
478 /// If advance is MilitaryResearch Blueprint must have been designed as desired already.
479 /// </summary>
480 /// <param name="advance">advance to research</param>
481 /// <returns>result of operation</returns>
482 public PlayResult SetResearch__Turn(Advance advance) { return Play(Protocol.sSetResearch, (int)advance); }
483
484 /// <summary>
485 /// Steal advance as offered by the temple of zeus wonder.
486 /// Call from OnStealAdvance handler only.
487 /// </summary>
488 /// <param name="advance">the advance to steal</param>
489 /// <returns>result of operation</returns>
490 public PlayResult StealAdvance__Turn(Advance advance) { return Play(Protocol.sStealTech, (int)advance); }
491
492 /// <summary>
493 /// change attitude to other nation
494 /// </summary>
495 /// <param name="nation">the nation</param>
496 /// <param name="attitude">the attitude</param>
497 /// <returns>result of operation</returns>
498 public PlayResult ChangeAttitudeTo(Nation nation, Attitude attitude) { return Play(Protocol.sSetAttitude + (nation.ID << 4), (int)attitude); }
499 #endregion
500
501 #region template internal stuff
502 [System.Runtime.InteropServices.UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.StdCall)]
503 delegate int ServerCall(int command, int nation, int subject, void* data);
504
505 readonly ServerCall serverCall;
506 readonly bool isNewGame;
507
508 /// <summary>
509 /// INTERNAL - only access from CevoAILib classes!
510 /// </summary>
511 public readonly int* address;
512
513 /// <summary>
514 /// INTERNAL - only access from CevoAILib classes!
515 /// </summary>
516 public readonly int* debugMapAddress;
517
518 /// <summary>
519 /// INTERNAL - only access from CevoAILib classes!
520 /// </summary>
521 public readonly Unit[] UnitLookup = new Unit[Cevo.MaxUnitsPerNation];
522
523 /// <summary>
524 /// INTERNAL - only access from CevoAILib classes!
525 /// </summary>
526 public readonly City[] CityLookup = new City[Cevo.MaxCitiesPerNation];
527
528 readonly byte[] foreignCityLookup = new byte[Cevo.MaxCitiesPerNation * Cevo.MaxNumberOfNations];
529 bool called = false;
530 Phase phase = Phase.ForeignTurn;
531 bool foreignMoveSkipped;
532 bool foreignMoveIsCapture;
533 UpdateArea foreignTurnUpdateAreas;
534
535 // diplomacy
536 Nation possibleNegotiationWith = Nation.None;
537 bool cancelTreatyIfRejected = false;
538 List<Nation> nationsContacted = new List<Nation>();
539 List<Nation> nationsNotToContact = new List<Nation>();
540 Negotiation currentNegotiation = null;
541
542 /// <summary>
543 /// INTERNAL - only call from CevoAILib classes!
544 /// </summary>
545 public void StealAdvance()
546 {
547 List<Advance> stealable = new List<Advance>();
548 for (Advance testAdvance = Advance.FirstCommon; testAdvance <= Advance.LastCommon; testAdvance++)
549 {
550 if (TestPlay(Protocol.sStealTech, (int)testAdvance).OK)
551 stealable.Add(testAdvance);
552 }
553 if (stealable.Count > 0)
554 OnStealAdvance(stealable.ToArray());
555 }
556
557 /// <summary>
558 /// INTERNAL - only call from CevoAILib classes!
559 /// </summary>
560 public PlayResult Play(int command, int subject, void* data)
561 {
562 return new PlayResult(serverCall(command, Us.ID, subject, data));
563 }
564
565 /// <summary>
566 /// INTERNAL - only call from CevoAILib classes!
567 /// </summary>
568 public PlayResult Play(int command, int subject, int data)
569 {
570 fixed (int* dataPtr = new int[1])
571 {
572 dataPtr[0] = data;
573 return new PlayResult(serverCall(command, Us.ID, subject, dataPtr));
574 }
575 }
576
577 /// <summary>
578 /// INTERNAL - only call from CevoAILib classes!
579 /// </summary>
580 public PlayResult Play(int command, int subject)
581 {
582 return new PlayResult(serverCall(command, Us.ID, subject, null));
583 }
584
585 /// <summary>
586 /// INTERNAL - only call from CevoAILib classes!
587 /// </summary>
588 public PlayResult Play(int command)
589 {
590 return new PlayResult(serverCall(command, Us.ID, 0, null));
591 }
592
593 /// <summary>
594 /// INTERNAL - only call from CevoAILib classes!
595 /// </summary>
596 public PlayResult TestPlay(int command, int subject, void* data)
597 {
598 return new PlayResult(serverCall(command - Protocol.sExecute, Us.ID, subject, data));
599 }
600
601 /// <summary>
602 /// INTERNAL - only call from CevoAILib classes!
603 /// </summary>
604 public PlayResult TestPlay(int command, int subject, int data)
605 {
606 fixed (int* dataPtr = new int[1])
607 {
608 dataPtr[0] = data;
609 return new PlayResult(serverCall(command - Protocol.sExecute, Us.ID, subject, dataPtr));
610 }
611 }
612
613 /// <summary>
614 /// INTERNAL - only call from CevoAILib classes!
615 /// </summary>
616 public PlayResult TestPlay(int command, int subject)
617 {
618 return new PlayResult(serverCall(command - Protocol.sExecute, Us.ID, subject, null));
619 }
620
621 /// <summary>
622 /// INTERNAL - only call from CevoAILib classes!
623 /// </summary>
624 public PlayResult TestPlay(int command)
625 {
626 return new PlayResult(serverCall(command - Protocol.sExecute, Us.ID, 0, null));
627 }
628
629 /// <summary>
630 /// INTERNAL - only call from CevoAILib classes!
631 /// </summary>
632 public void InvalidateAllCityReports() { foreach (City city in Cities) city.InvalidateReport(); }
633
634 [Flags]
635 public enum UpdateArea { Basic = 0x00, Units = 0x01, Cities = 0x02, ForeignCities = 0x08, All = 0xFF }
636
637 /// <summary>
638 /// INTERNAL - only call from CevoAILib classes!
639 /// update the lists Models and ForeignModels
640 /// and to correct the lists Units, Cities and ForeignCities
641 /// this includes address correction of objects, adding objects for new items,
642 /// removing objects of destroyed items and marking these objects
643 /// </summary>
644 public void UpdateLists(UpdateArea areas)
645 {
646 // models
647 int* sharedMemoryList = (int*)address[6];
648 int sharedMemoryCount = address[ROReadPoint.TestFlags + 9];
649 for (int indexInSharedMemory = Models.Count; indexInSharedMemory < sharedMemoryCount; indexInSharedMemory++)
650 Models.Add(new Model((Empire)this, indexInSharedMemory));
651
652 // foreign models
653 sharedMemoryList = (int*)address[9];
654 sharedMemoryCount = address[ROReadPoint.TestFlags + 12];
655 for (int indexInSharedMemory = ForeignModels.Count; indexInSharedMemory < sharedMemoryCount; indexInSharedMemory++)
656 ForeignModels.Add(new ForeignModel((Empire)this, indexInSharedMemory));
657
658 // cities
659#if DEBUG
660 bool doCities = true;
661#else
662 bool doCities = (areas & UpdateArea.Cities) != 0;
663#endif
664 if (doCities)
665 {
666 sharedMemoryList = (int*)address[5];
667 sharedMemoryCount = address[ROReadPoint.TestFlags + 8];
668 Array.Clear(CityLookup, 0, sharedMemoryCount);
669 foreach (City city in Cities)
670 {
671 int indexInSharedMemory = city.IndexInSharedMemory; // items do only move backward in shared memory
672 int originalIndex = indexInSharedMemory;
673 while (indexInSharedMemory >= 0 &&
674 (indexInSharedMemory >= sharedMemoryCount || // index beyond bounds
675 (sharedMemoryList[ROReadPoint.SizeOfCity * indexInSharedMemory + 3] & 0xFFFF) != city.ID || // not the right city
676 sharedMemoryList[ROReadPoint.SizeOfCity * indexInSharedMemory] < 0)) // LID < 0 indicates gap in list
677 indexInSharedMemory--;
678 if (indexInSharedMemory < 0)
679 { // city was captured or destroyd, remove
680 if ((areas & UpdateArea.Cities) == 0) // debug check fails
681 throw new Exception("UpdateLists: City removal is not updated!");
682 city.IndexInSharedMemory = -1;
683 Cities.RemoveCurrent();
684 }
685 else
686 { // city still exists, correct shared memory index
687 if (indexInSharedMemory != originalIndex)
688 {
689 if ((areas & UpdateArea.Cities) == 0) // debug check fails
690 throw new Exception("UpdateLists: City index change is not updated!");
691 city.IndexInSharedMemory = indexInSharedMemory;
692 }
693 CityLookup[indexInSharedMemory] = city;
694 }
695 }
696 for (int indexInSharedMemory = 0; indexInSharedMemory < sharedMemoryCount; indexInSharedMemory++)
697 {
698 if (CityLookup[indexInSharedMemory] == null &&
699 sharedMemoryList[ROReadPoint.SizeOfCity * indexInSharedMemory] >= 0) // LID < 0 indicates gap in list
700 { // shared memory object not in list
701 if ((areas & UpdateArea.Cities) == 0) // debug check fails
702 throw new Exception("UpdateLists: City creation is not updated!");
703 City city = new City((Empire)this, indexInSharedMemory);
704 Cities.Add(city);
705 CityLookup[indexInSharedMemory] = city;
706 }
707 }
708 }
709
710 // foreign cities
711#if DEBUG
712 bool doForeignCities = true;
713#else
714 bool doForeignCities = (areas & UpdateArea.ForeignCities) != 0;
715#endif
716 if (doForeignCities)
717 {
718 sharedMemoryList = (int*)address[8];
719 sharedMemoryCount = address[ROReadPoint.TestFlags + 11];
720 Array.Clear(foreignCityLookup, 0, sharedMemoryCount);
721 foreach (ForeignCity city in ForeignCities)
722 {
723 int indexInSharedMemory = city.IndexInSharedMemory; // items do only move backward in shared memory
724 int originalIndex = indexInSharedMemory;
725 while (indexInSharedMemory >= 0 &&
726 (indexInSharedMemory >= sharedMemoryCount || // index beyond bounds
727 ((sharedMemoryList[ROReadPoint.SizeOfCityInfo * indexInSharedMemory + 3] >> 16) & 0xFFFF) != city.ID || // not the right city
728 sharedMemoryList[ROReadPoint.SizeOfCityInfo * indexInSharedMemory] < 0)) // LID < 0 indicates gap in list
729 indexInSharedMemory--;
730 if (indexInSharedMemory < 0)
731 { // city was captured or destroyd, remove
732 if ((areas & UpdateArea.ForeignCities) == 0) // debug check fails
733 throw new Exception("UpdateLists: Foreign city removal is not updated!");
734 city.IndexInSharedMemory = -1;
735 ForeignCities.RemoveCurrent();
736 }
737 else
738 { // city still exists, correct shared memory index
739 if (indexInSharedMemory != originalIndex)
740 {
741 if ((areas & UpdateArea.ForeignCities) == 0) // debug check fails
742 throw new Exception("UpdateLists: Foreign city index change is not updated!");
743 city.IndexInSharedMemory = indexInSharedMemory;
744 }
745 foreignCityLookup[indexInSharedMemory] = 1;
746 }
747 }
748 for (int indexInSharedMemory = 0; indexInSharedMemory < sharedMemoryCount; indexInSharedMemory++)
749 {
750 if (foreignCityLookup[indexInSharedMemory] == 0 &&
751 sharedMemoryList[ROReadPoint.SizeOfCityInfo * indexInSharedMemory] >= 0) // LID < 0 indicates gap in list
752 {
753 if ((areas & UpdateArea.ForeignCities) == 0) // debug check fails
754 throw new Exception("UpdateLists: Foreign city creation is not updated!");
755 ForeignCities.Add(new ForeignCity((Empire)this, indexInSharedMemory));
756 }
757 }
758 }
759
760 // units
761#if DEBUG
762 bool doUnits = true;
763#else
764 bool doUnits = (areas & UpdateArea.Units) != 0;
765#endif
766 if (doUnits)
767 {
768 sharedMemoryList = (int*)address[4];
769 sharedMemoryCount = address[ROReadPoint.TestFlags + 7];
770 Array.Clear(UnitLookup, 0, sharedMemoryCount);
771 foreach (Unit unit in Units)
772 {
773 int indexInSharedMemory = unit.IndexInSharedMemory; // items do only move backward in shared memory
774 int originalIndex = indexInSharedMemory;
775 while (indexInSharedMemory >= 0 &&
776 (indexInSharedMemory >= sharedMemoryCount || // index beyond bounds
777 (sharedMemoryList[ROReadPoint.SizeOfUn * indexInSharedMemory + 3] & 0xFFFF) != unit.ID || // not the right unit
778 sharedMemoryList[ROReadPoint.SizeOfUn * indexInSharedMemory] < 0)) // LID < 0 indicates gap in list
779 indexInSharedMemory--;
780 if (indexInSharedMemory < 0)
781 { // unit was destroyd, remove
782 if ((areas & UpdateArea.Units) == 0) // debug check fails
783 throw new Exception("UpdateLists: Unit removal is not updated!");
784 unit.IndexInSharedMemory = -1;
785 Units.RemoveCurrent();
786 }
787 else
788 { // unit still exists, correct shared memory index
789 if (indexInSharedMemory != originalIndex)
790 {
791 if ((areas & UpdateArea.Units) == 0) // debug check fails
792 throw new Exception("UpdateLists: Unit index change is not updated!");
793 unit.IndexInSharedMemory = indexInSharedMemory;
794 }
795 UnitLookup[indexInSharedMemory] = unit;
796 }
797 }
798 for (int indexInSharedMemory = 0; indexInSharedMemory < sharedMemoryCount; indexInSharedMemory++)
799 {
800 if (UnitLookup[indexInSharedMemory] == null &&
801 sharedMemoryList[ROReadPoint.SizeOfUn * indexInSharedMemory] >= 0) // LID < 0 indicates gap in list
802 { // shared memory object not in list
803 if ((areas & UpdateArea.Units) == 0) // debug check fails
804 throw new Exception("UpdateLists: Unit creation is not updated!");
805 Unit unit = new Unit((Empire)this, indexInSharedMemory);
806 Units.Add(unit);
807 UnitLookup[indexInSharedMemory] = unit;
808 }
809 }
810 }
811 }
812
813 // internal
814 void ForeignTurnUpdate()
815 {
816 UpdateLists(foreignTurnUpdateAreas);
817 foreignTurnUpdateAreas = UpdateArea.Basic;
818 }
819
820 /// <summary>
821 /// INTERNAL - only call from Plugin class!
822 /// </summary>
823 public void Process(int command, IntPtr dataPtr)
824 {
825 if (!called)
826 {
827 UpdateLists(UpdateArea.All);
828 if (isNewGame)
829 NewGame();
830 else
831 Resume();
832 }
833 called = true;
834
835 int* data = (int*)dataPtr;
836 switch (command)
837 {
838 case Protocol.cTurn:
839 case Protocol.cContinue:
840 {
841 if (!Subsists)
842 {
843 Play(Protocol.sTurn);
844 return;
845 }
846
847 if (command == Protocol.cTurn)
848 {
849 phase = Phase.BeginOfTurn;
850 nationsContacted.Clear();
851 if (Researching != Advance.MilitaryResearch)
852 Play(Protocol.sCreateDevModel, (int)ModelDomain.Ground); // keep blueprint current
853 UpdateLists(UpdateArea.All);
854 }
855 if (command == Protocol.cContinue && possibleNegotiationWith != Nation.None)
856 { // that means a negotiation attempt was made but rejected
857 if (cancelTreatyIfRejected && RelationTo(possibleNegotiationWith) >= Relation.Peace)
858 Play(Protocol.sCancelTreaty);
859 }
860 else
861 nationsNotToContact.Clear();
862 currentNegotiation = null;
863 possibleNegotiationWith = Nation.None;
864 cancelTreatyIfRejected = false;
865
866 InvalidateAllCityReports(); // turn begin and after negotiation
867
868 while (true)
869 {
870 if (Government != Government.Anarchy)
871 {
872 foreach (Nation nation in SubsistingNations)
873 {
874 if (nation != Us &&
875 RelationTo(nation) != Relation.NoContact &&
876 nation.Government != Government.Anarchy &&
877 !(nationsContacted.Contains(nation)) &&
878 !(nationsNotToContact.Contains(nation)) &&
879 TestPlay(Protocol.scContact + (nation.ID << 4)).OK)
880 {
881 bool wantNegotiation = false;
882 cancelTreatyIfRejected = false;
883 OnChanceToNegotiate(phase, nation, ref wantNegotiation, ref cancelTreatyIfRejected);
884 if (wantNegotiation)
885 {
886 nationsContacted.Add(nation);
887 possibleNegotiationWith = nation;
888 Play(Protocol.scContact + (nation.ID << 4));
889 return;
890 }
891 else
892 nationsNotToContact.Add(nation);
893 }
894 }
895 }
896 if (phase == Phase.BeginOfTurn)
897 {
898 phase = Phase.Turn;
899 OnTurn();
900 phase = Phase.EndOfTurn;
901 nationsContacted.Clear();
902 nationsNotToContact.Clear();
903 }
904 else
905 break;
906 }
907
908#if DEBUG
909 UpdateLists(UpdateArea.Basic); // check for list update problems
910#endif
911
912 foreignTurnUpdateAreas = UpdateArea.Units; // units might be disbanded in after-turn processing
913 phase = Phase.ForeignTurn;
914 Play(Protocol.sTurn);
915 break;
916 }
917
918 case Protocol.scContact:
919 {
920 if (phase != Phase.ForeignTurn)
921 throw new Exception("Error in logic: scDipStart should not be called in own turn!");
922 ForeignTurnUpdate();
923 possibleNegotiationWith = new Nation(this, data[0]);
924 bool wantNegotiation = false;
925 bool dummy = false;
926 OnChanceToNegotiate(phase, possibleNegotiationWith, ref wantNegotiation, ref dummy);
927 if (wantNegotiation)
928 Play(Protocol.scDipStart);
929 else
930 Play(Protocol.scReject);
931 break;
932 }
933
934 case Protocol.scDipStart:
935 case Protocol.scDipNotice:
936 case Protocol.scDipAccept:
937 case Protocol.scDipCancelTreaty:
938 case Protocol.scDipOffer:
939 case Protocol.scDipBreak:
940 {
941 if (currentNegotiation == null)
942 currentNegotiation = new Negotiation(this, phase, possibleNegotiationWith);
943 possibleNegotiationWith = Nation.None;
944 cancelTreatyIfRejected = false;
945
946 if (command == Protocol.scDipStart) // no statements yet in this negotiation
947 {
948 if (phase == Phase.ForeignTurn)
949 throw new Exception("Error in logic: scDipStart should only be called in own turn!");
950 currentNegotiation.SetOurNextStatement(new SuggestEnd());
951 }
952 else
953 {
954 bool afterTrade = (command == Protocol.scDipAccept || // opponent accepted our suggested trade
955 (currentNegotiation.History.Count > 0 && currentNegotiation.OurNextStatement is AcceptTrade)); // we accepted opponent suggested trade
956 if (phase == Phase.ForeignTurn)
957 {
958 if (afterTrade)
959 foreignTurnUpdateAreas |= UpdateArea.ForeignCities; // in case map was traded
960 ForeignTurnUpdate();
961 }
962 else
963 {
964 if (afterTrade)
965 UpdateLists(UpdateArea.ForeignCities); // in case map was traded
966 }
967
968 IStatement oppenentStatement = StatementFactory.OpponentStatementFromCommand(this, currentNegotiation.Opponent, command, data);
969 if (currentNegotiation.History.Count == 0 && phase == Phase.ForeignTurn)
970 currentNegotiation.History.Insert(0, new ExchangeOfStatements(new SuggestEnd() /*imaginary, has not happened*/, oppenentStatement));
971 else
972 currentNegotiation.History.Insert(0, new ExchangeOfStatements(currentNegotiation.OurNextStatement, oppenentStatement));
973 if (oppenentStatement is CancelTreaty || oppenentStatement is Break)
974 currentNegotiation.SetOurNextStatement(new Notice()); // initialize with standard response
975 else
976 currentNegotiation.SetOurNextStatement(new SuggestEnd()); // initialize with standard response
977 }
978
979 OnNegotiate(currentNegotiation);
980
981 if (currentNegotiation.OurNextStatement is SuggestTrade)
982 {
983 fixed (int* tradeData = new int[14])
984 {
985 ((SuggestTrade)currentNegotiation.OurNextStatement).FillRawStream(tradeData);
986 Play(currentNegotiation.OurNextStatement.Command, 0, tradeData);
987 }
988 }
989 else
990 Play(currentNegotiation.OurNextStatement.Command);
991 break;
992 }
993
994 case Protocol.cShowEndContact: { currentNegotiation = null; break; }
995
996 case Protocol.cShowMoving:
997 case Protocol.cShowCapturing:
998 {
999 if (phase != Phase.ForeignTurn)
1000 throw new Exception("Error in logic: cShowMoving should not be called in own turn!");
1001 foreignMoveIsCapture = (command == Protocol.cShowCapturing);
1002 Relation relationToMovingNation = (Relation)(address[ROReadPoint.Attitude + Protocol.nPl + data[0]] + 1);
1003 foreignMoveSkipped = !foreignMoveIsCapture && relationToMovingNation == Relation.Alliance;
1004 // allied movement: low relevance, high frequency, so skip
1005 if (foreignMoveSkipped)
1006 foreignTurnUpdateAreas |= UpdateArea.ForeignCities; // allies movement might gain new city information
1007 else
1008 {
1009 ForeignTurnUpdate();
1010 MovingUnit unit = new MovingUnit(this, data);
1011 RC test = new RC((data[6] + data[7]) >> 1, (data[7] - data[6]) >> 1);
1012 Location target = unit.Location + new RC((data[6] + data[7]) >> 1, (data[7] - data[6]) >> 1);
1013 OnForeignMove(unit, target);
1014 if (foreignMoveIsCapture)
1015 OnBeforeForeignCapture(unit.Nation, target.City);
1016 }
1017 break;
1018 }
1019
1020 case Protocol.cShowAttacking:
1021 {
1022 if (phase != Phase.ForeignTurn)
1023 throw new Exception("Error in logic: cShowAttacking should not be called in own turn!");
1024 ForeignTurnUpdate();
1025 MovingUnit unit = new MovingUnit(this, data);
1026 Location target = unit.Location + new RC((data[6] + data[7]) >> 1, (data[7] - data[6]) >> 1);
1027 OnBeforeForeignAttack(unit, target, new BattleOutcome(data[8], data[9]));
1028 break;
1029 }
1030
1031 case Protocol.cShowAfterMove:
1032 {
1033 if (phase != Phase.ForeignTurn)
1034 throw new Exception("Error in logic: cShowAfterMove should not be called in own turn!");
1035 if (foreignMoveIsCapture && !foreignMoveSkipped)
1036 {
1037 ForeignTurnUpdate();
1038 OnAfterForeignCapture(); // cShowCityChanged was already called here
1039 }
1040 break;
1041 }
1042
1043 case Protocol.cShowAfterAttack:
1044 {
1045 if (phase != Phase.ForeignTurn)
1046 throw new Exception("Error in logic: cShowAfterAttack should not be called in own turn!");
1047 foreignTurnUpdateAreas |= UpdateArea.Units; // if city was destroyed by attack, cShowCityChanged was already called here
1048 ForeignTurnUpdate();
1049 OnAfterForeignAttack();
1050 break;
1051 }
1052
1053 case Protocol.cShowCityChanged:
1054 {
1055 if (phase != Phase.ForeignTurn)
1056 throw new Exception("Error in logic: cShowCityChanged should not be called in own turn!");
1057 foreignTurnUpdateAreas |= UpdateArea.All; // not called very often, so full update doesn't hurt
1058 break;
1059 }
1060 }
1061 }
1062 #endregion
1063 }
1064
1065 unsafe struct RelationDetails
1066 {
1067 readonly AEmpire theEmpire;
1068 readonly Nation nation;
1069
1070 public RelationDetails(AEmpire empire, Nation nation)
1071 {
1072 this.theEmpire = empire;
1073 this.nation = nation;
1074 }
1075
1076 int* report { get { return (int*)theEmpire.address[10 + nation.ID]; } }
1077
1078 public Attitude OurAttitudeToThem { get { return (Attitude)theEmpire.address[ROReadPoint.Attitude + nation.ID]; } }
1079 public Attitude TheirAttitudeToUs { get { return (Attitude)report[4]; } }
1080 public int TurnOfLastNegotiation { get { return report[0]; } }
1081 public int TurnOfLastCancellingTreaty { get { return theEmpire.address[ROReadPoint.nBattleHistory + 3 + nation.ID]; } }
1082 public int TurnOfPeaceEvacuationBegin { get { return theEmpire.address[ROReadPoint.Attitude + 2 * Protocol.nPl + nation.ID]; } }
1083 }
1084
1085 unsafe struct Dossier : IDossier
1086 {
1087 readonly AEmpire theEmpire;
1088 readonly Nation nation;
1089
1090 public Dossier(AEmpire empire, Nation nation)
1091 {
1092 this.theEmpire = empire;
1093 this.nation = nation;
1094 }
1095
1096 public override string ToString()
1097 {
1098 if (TurnOfReport >= 0)
1099 return string.Format("{0}", TurnOfReport);
1100 else
1101 return "NA";
1102 }
1103
1104 public bool IsAvailable { get { return TurnOfReport >= 0; } }
1105
1106 int* report { get { return (int*)theEmpire.address[10 + nation.ID]; } }
1107
1108 public int TurnOfReport { get { return report[1]; } }
1109 public int Treasury { get { return report[6 + Protocol.nPl]; } }
1110
1111 /// <summary>
1112 /// whether an advance has been completely researched
1113 /// </summary>
1114 /// <param name="advance">the advance</param>
1115 /// <returns>true if researched, false if not</returns>
1116 public bool Has(Advance advance) { return ((sbyte*)(report + 9 + Protocol.nPl))[(int)advance] >= 0; }
1117
1118 /// <summary>
1119 /// whether an advance was gained from a trade with another nation or from the temple of zeus wonder
1120 /// </summary>
1121 /// <param name="advance">the advance</param>
1122 /// <returns>true if gained, false if not</returns>
1123 public bool HasAlmost(Advance advance) { return ((sbyte*)(report + 9 + Protocol.nPl))[(int)advance] == -1; }
1124
1125 /// <summary>
1126 /// science points collected for current research
1127 /// </summary>
1128 public int ResearchPile { get { return report[8 + Protocol.nPl]; } }
1129
1130 /// <summary>
1131 /// advance currently researched
1132 /// </summary>
1133 public Advance Researching
1134 {
1135 get
1136 {
1137 int ad = report[7 + Protocol.nPl];
1138 if (ad < 0)
1139 return Advance.None;
1140 else
1141 return (Advance)ad;
1142 }
1143 }
1144
1145 /// <summary>
1146 /// relation to specific other nation
1147 /// </summary>
1148 /// <param name="thirdNation">the other nation</param>
1149 /// <returns>the relation</returns>
1150 public Relation RelationTo(Nation thirdNation)
1151 {
1152 if (thirdNation == nation)
1153 return Relation.Identity;
1154 else
1155 return (Relation)(report[5 + thirdNation.ID] + 1);
1156 }
1157
1158 /// <summary>
1159 /// number of future technologies developed
1160 /// </summary>
1161 /// <param name="advance">the future technology</param>
1162 /// <returns>number</returns>
1163 public int FutureTechnology(Advance advance)
1164 {
1165 sbyte raw = ((sbyte*)(report + 9 + Protocol.nPl))[(int)advance];
1166 if (raw <= 0)
1167 return 0;
1168 else
1169 return raw;
1170 }
1171 }
1172
1173 unsafe struct MilitaryReport
1174 {
1175 readonly AEmpire theEmpire;
1176 readonly Nation nation;
1177
1178 public MilitaryReport(AEmpire empire, Nation nation)
1179 {
1180 this.theEmpire = empire;
1181 this.nation = nation;
1182 }
1183
1184 public override string ToString()
1185 {
1186 if (TurnOfReport >= 0)
1187 return string.Format("{0}", TurnOfReport);
1188 else
1189 return "NA";
1190 }
1191
1192 public bool IsAvailable { get { return TurnOfReport >= 0; } }
1193
1194 int* report { get { return (int*)theEmpire.address[10 + nation.ID]; } }
1195
1196 public int TurnOfReport { get { return report[2]; } }
1197
1198 // todo !!!
1199 }
1200
1201 unsafe struct BattleHistory
1202 {
1203 readonly AEmpire theEmpire;
1204
1205 public BattleHistory(AEmpire empire)
1206 {
1207 theEmpire = empire;
1208 }
1209
1210 // todo !!!
1211 }
1212}
Note: See TracBrowser for help on using the repository browser.