source: tags/1.3.1/AI/StdAI/CustomAI.pas

Last change on this file was 289, checked in by chronos, 3 years ago
  • Modified: Code formatted using JEDI code formatter.
  • Modified: Protocol copied from main game.
  • Modified: Added Barbarina into project tree.
File size: 32.7 KB
Line 
1{$INCLUDE Switches.inc}
2unit CustomAI;
3
4interface
5
6uses
7{$IFDEF DEBUG}SysUtils,{$ENDIF} // necessary for debug exceptions
8 Protocol;
9
10type
11 TNegoTime = (BeginOfTurn, EndOfTurn, EnemyCalled);
12
13 TCustomAI = class
14 public
15 procedure Process(Command: integer; var Data);
16
17 // overridables
18 constructor Create(Nation: integer); virtual;
19 destructor Destroy; override;
20 procedure SetDataDefaults; virtual;
21 procedure SetDataRandom; virtual;
22 procedure OnBeforeEnemyAttack(UnitInfo: TUnitInfo;
23 ToLoc, EndHealth, EndHealthDef: integer); virtual;
24 procedure OnBeforeEnemyCapture(UnitInfo: TUnitInfo; ToLoc: integer); virtual;
25 procedure OnAfterEnemyAttack; virtual;
26 procedure OnAfterEnemyCapture; virtual;
27
28 protected
29 me: integer; // index of the controlled nation
30 RO: ^TPlayerContext;
31 Map: ^TTileList;
32 MyUnit: ^TUnList;
33 MyCity: ^TCityList;
34 MyModel: ^TModelList;
35
36 cixStateImp: array[imPalace..imSpacePort] of integer;
37
38 // negotiation
39 Opponent: integer; // nation i'm in negotiation with, -1 indicates no-negotiation mode
40 MyAction, MyLastAction, OppoAction: integer;
41 MyOffer, MyLastOffer, OppoOffer: TOffer;
42
43 // overridables
44 procedure DoTurn; virtual;
45 procedure DoNegotiation; virtual;
46 function ChooseResearchAdvance: integer; virtual;
47 function ChooseStealAdvance: integer; virtual;
48 function ChooseGovernment: integer; virtual;
49 function WantNegotiation(Nation: integer; NegoTime: TNegoTime): boolean; virtual;
50 function OnNegoRejected_CancelTreaty: boolean; virtual;
51
52 // general functions
53 function IsResearched(Advance: integer): boolean;
54 function ResearchCost: integer;
55 function ChangeAttitude(Nation, Attitude: integer): integer;
56 function Revolution: integer;
57 function ChangeRates(Tax, Lux: integer): integer;
58 function PrepareNewModel(Domain: integer): integer;
59 function SetNewModelFeature(F, Count: integer): integer;
60 function AdvanceResearchable(Advance: integer): boolean;
61 function AdvanceStealable(Advance: integer): boolean;
62 function GetJobProgress(Loc: integer; var JobProgress: TJobProgressData): boolean;
63 function DebugMessage(Level: integer; Text: string): boolean;
64 function SetDebugMap(var DebugMap): boolean;
65
66 // unit functions
67 procedure Unit_FindMyDefender(Loc: integer; var uix: integer);
68 procedure Unit_FindEnemyDefender(Loc: integer; var euix: integer);
69 function Unit_Move(uix, ToLoc: integer): integer;
70 function Unit_Step(uix, ToLoc: integer): integer;
71 function Unit_Attack(uix, ToLoc: integer): integer;
72 function Unit_DoMission(uix, MissionType, ToLoc: integer): integer;
73 function Unit_MoveForecast(uix, ToLoc: integer;
74 var RemainingMovement: integer): boolean;
75 function Unit_AttackForecast(uix, ToLoc, AttackMovement: integer;
76 var RemainingHealth: integer): boolean;
77 function Unit_DefenseForecast(euix, ToLoc: integer;
78 var RemainingHealth: integer): boolean;
79 function Unit_Disband(uix: integer): integer;
80 function Unit_StartJob(uix, NewJob: integer): integer;
81 function Unit_SetHomeHere(uix: integer): integer;
82 function Unit_Load(uix: integer): integer;
83 function Unit_Unload(uix: integer): integer;
84 function Unit_SelectTransport(uix: integer): integer;
85 function Unit_AddToCity(uix: integer): integer;
86
87 // city functions
88 procedure City_FindMyCity(Loc: integer; var cix: integer);
89 procedure City_FindEnemyCity(Loc: integer; var ecix: integer);
90 function City_HasProject(cix: integer): boolean;
91 function City_CurrentImprovementProject(cix: integer): integer;
92 function City_CurrentUnitProject(cix: integer): integer;
93 function City_GetTileInfo(cix, TileLoc: integer; var TileInfo: TTileInfo): integer;
94 function City_GetReport(cix: integer; var Report: TCityReport): integer;
95 function City_GetHypoReport(cix, HypoTiles, HypoTax, HypoLux: integer;
96 var Report: TCityReport): integer;
97 function City_GetReportNew(cix: integer; var Report: TCityReportNew): integer;
98 function City_GetHypoReportNew(cix, HypoTiles, HypoTaxRate, HypoLuxuryRate: integer;
99 var Report: TCityReportNew): integer;
100 function City_GetAreaInfo(cix: integer; var AreaInfo: TCityAreaInfo): integer;
101 function City_StartUnitProduction(cix, mix: integer): integer;
102 function City_StartEmigration(cix, mix: integer;
103 AllowDisbandCity, AsConscripts: boolean): integer;
104 function City_StartImprovement(cix, iix: integer): integer;
105 function City_Improvable(cix, iix: integer): boolean;
106 function City_StopProduction(cix: integer): integer;
107 function City_BuyProject(cix: integer): integer;
108 function City_SellImprovement(cix, iix: integer): integer;
109 function City_RebuildImprovement(cix, iix: integer): integer;
110 function City_SetTiles(cix, NewTiles: integer): integer;
111 procedure City_OptimizeTiles(cix: integer; ResourceWeights: cardinal = rwMaxGrowth);
112
113 // negotiation
114 function Nego_CheckMyAction: integer;
115
116 private
117 HaveTurned: boolean;
118 UnwantedNego: set of 0..nPl - 1;
119 Contacted: set of 0..nPl - 1;
120 procedure StealAdvance;
121 end;
122
123
124var
125 Server: TServerCall;
126 G: TNewGameData;
127 RWDataSize, MapSize: integer;
128 decompose24: cardinal;
129 nodata: pointer;
130
131const
132 CityOwnTile = 13; // = ab_to_V21(0,0)
133
134 // additional return codes
135 rLocationReached = $00010000;
136 // Unit_Move: move was not interrupted, location reached
137 rMoreTurns = $00020000;
138// Unit_Move: move was not interrupted, location not reached yet
139
140type
141 TVicinity8Loc = array[0..7] of integer;
142 TVicinity21Loc = array[0..27] of integer;
143
144
145procedure Init(NewGameData: TNewGameData);
146
147procedure ab_to_Loc(Loc0, a, b: integer; var Loc: integer);
148procedure Loc_to_ab(Loc0, Loc: integer; var a, b: integer);
149procedure ab_to_V8(a, b: integer; var V8: integer);
150procedure V8_to_ab(V8: integer; var a, b: integer);
151procedure ab_to_V21(a, b: integer; var V21: integer);
152procedure V21_to_ab(V21: integer; var a, b: integer);
153procedure V8_to_Loc(Loc0: integer; var VicinityLoc: TVicinity8Loc);
154procedure V21_to_Loc(Loc0: integer; var VicinityLoc: TVicinity21Loc);
155function Distance(Loc0, Loc1: integer): integer;
156
157
158implementation
159
160const
161 ab_v8: array[-4..4] of integer = (5, 6, 7, 4, -1, 0, 3, 2, 1);
162 v8_a: array[0..7] of integer = (1, 1, 0, -1, -1, -1, 0, 1);
163 v8_b: array[0..7] of integer = (0, 1, 1, 1, 0, -1, -1, -1);
164
165
166procedure ab_to_Loc(Loc0, a, b: integer; var Loc: integer);
167{relative location from Loc0}
168var
169 y0: integer;
170begin
171 assert((Loc0 >= 0) and (Loc0 < MapSize) and (a - b + G.lx >= 0));
172 y0 := cardinal(Loc0) * decompose24 shr 24;
173 Loc := (Loc0 + (a - b + y0 and 1 + G.lx + G.lx) shr 1) mod G.lx + G.lx * (y0 + a + b);
174 if Loc >= MapSize then
175 Loc := -$1000;
176end;
177
178procedure Loc_to_ab(Loc0, Loc: integer; var a, b: integer);
179{$IFDEF FPC}// freepascal
180var
181 dx, dy: integer;
182begin
183 dx := ((Loc mod G.lx * 2 + Loc div G.lx and 1) - (Loc0 mod G.lx * 2 + Loc0 div
184 G.lx and 1) + 3 * G.lx) mod (2 * G.lx) - G.lx;
185 dy := Loc div G.lx - Loc0 div G.lx;
186 a := (dx + dy) div 2;
187 b := (dy - dx) div 2;
188end;
189
190{$ELSE}// delphi
191register;
192asm
193push ebx
194
195// calculate
196push ecx
197div byte ptr [G]
198xor ebx,ebx
199mov bl,ah // ebx:=Loc0 mod G.lx
200mov ecx,eax
201and ecx,$000000FF // ecx:=Loc0 div G.lx
202mov eax,edx
203div byte ptr [G]
204xor edx,edx
205mov dl,ah // edx:=Loc mod G.lx
206and eax,$000000FF // eax:=Loc div G.lx
207sub edx,ebx // edx:=Loc mod G.lx-Loc0 mod G.lx
208mov ebx,eax
209sub ebx,ecx // ebx:=dy
210and eax,1
211and ecx,1
212add edx,edx
213add eax,edx
214sub eax,ecx // eax:=dx, not normalized
215pop ecx
216
217// normalize
218mov edx,dword ptr [G]
219cmp eax,edx
220jl @a
221 sub eax,edx
222 sub eax,edx
223 jmp @ok
224@a:
225neg edx
226cmp eax,edx
227jnl @ok
228 sub eax,edx
229 sub eax,edx
230
231// return results
232@ok:
233mov edx,ebx
234sub edx,eax
235add eax,ebx
236sar edx,1 // edx:=b
237mov ebx,[b]
238mov [ebx],edx
239sar eax,1 // eax:=a
240mov [a],eax
241
242pop ebx
243end;
244{$ENDIF}
245
246procedure ab_to_V8(a, b: integer; var V8: integer);
247begin
248 assert((abs(a) <= 1) and (abs(b) <= 1) and ((a <> 0) or (b <> 0)));
249 V8 := ab_v8[2 * b + b + a];
250end;
251
252procedure V8_to_ab(V8: integer; var a, b: integer);
253begin
254 a := v8_a[V8];
255 b := V8_b[V8];
256end;
257
258procedure ab_to_V21(a, b: integer; var V21: integer);
259begin
260 V21 := (a + b + 3) shl 2 + (a - b + 3) shr 1;
261end;
262
263procedure V21_to_ab(V21: integer; var a, b: integer);
264var
265 dx, dy: integer;
266begin
267 dy := V21 shr 2 - 3;
268 dx := V21 and 3 shl 1 - 3 + (dy + 3) and 1;
269 a := (dx + dy) div 2;
270 b := (dy - dx) div 2;
271end;
272
273procedure V8_to_Loc(Loc0: integer; var VicinityLoc: TVicinity8Loc);
274var
275 x0, y0, lx: integer;
276begin
277 lx := G.lx;
278 y0 := cardinal(Loc0) * decompose24 shr 24;
279 x0 := Loc0 - y0 * lx; // Loc0 mod lx;
280 VicinityLoc[1] := Loc0 + lx * 2;
281 VicinityLoc[3] := Loc0 - 1;
282 VicinityLoc[5] := Loc0 - lx * 2;
283 VicinityLoc[7] := Loc0 + 1;
284 Inc(Loc0, y0 and 1);
285 VicinityLoc[0] := Loc0 + lx;
286 VicinityLoc[2] := Loc0 + lx - 1;
287 VicinityLoc[4] := Loc0 - lx - 1;
288 VicinityLoc[6] := Loc0 - lx;
289
290 // world is round!
291 if x0 < lx - 1 then
292 begin
293 if x0 = 0 then
294 begin
295 Inc(VicinityLoc[3], lx);
296 if y0 and 1 = 0 then
297 begin
298 Inc(VicinityLoc[2], lx);
299 Inc(VicinityLoc[4], lx);
300 end;
301 end;
302 end
303 else
304 begin
305 Dec(VicinityLoc[7], lx);
306 if y0 and 1 = 1 then
307 begin
308 Dec(VicinityLoc[0], lx);
309 Dec(VicinityLoc[6], lx);
310 end;
311 end;
312
313 // check south pole
314 case G.ly - y0 of
315 1:
316 begin
317 VicinityLoc[0] := -$1000;
318 VicinityLoc[1] := -$1000;
319 VicinityLoc[2] := -$1000;
320 end;
321 2: VicinityLoc[1] := -$1000;
322 end;
323end;
324
325procedure V21_to_Loc(Loc0: integer; var VicinityLoc: TVicinity21Loc);
326var
327 dx, dy, bit, y0, xComp, yComp, xComp0, xCompSwitch: integer;
328 dst: ^integer;
329begin
330 y0 := cardinal(Loc0) * decompose24 shr 24;
331 xComp0 := Loc0 - y0 * G.lx - 1; // Loc0 mod G.lx -1
332 xCompSwitch := xComp0 - 1 + y0 and 1;
333 if xComp0 < 0 then
334 Inc(xComp0, G.lx);
335 if xCompSwitch < 0 then
336 Inc(xCompSwitch, G.lx);
337 xCompSwitch := xCompSwitch xor xComp0;
338 yComp := G.lx * (y0 - 3);
339 dst := @VicinityLoc;
340 bit := 1;
341 for dy := 0 to 6 do
342 if yComp < MapSize then
343 begin
344 xComp0 := xComp0 xor xCompSwitch;
345 xComp := xComp0;
346 for dx := 0 to 3 do
347 begin
348 if bit and $67F7F76 <> 0 then
349 dst^ := xComp + yComp
350 else
351 dst^ := -1;
352 Inc(xComp);
353 if xComp >= G.lx then
354 Dec(xComp, G.lx);
355 Inc(dst);
356 bit := bit shl 1;
357 end;
358 Inc(yComp, G.lx);
359 end
360 else
361 begin
362 for dx := 0 to 3 do
363 begin
364 dst^ := -$1000;
365 Inc(dst);
366 end;
367 end;
368end;
369
370function Distance(Loc0, Loc1: integer): integer;
371var
372 a, b, dx, dy: integer;
373begin
374 Loc_to_ab(Loc0, Loc1, a, b);
375 dx := abs(a - b);
376 dy := abs(a + b);
377 Result := dx + dy + abs(dx - dy) shr 1;
378end;
379
380
381procedure Init(NewGameData: TNewGameData);
382{$IFDEF DEBUG}var
383 Loc: integer;
384{$ENDIF}
385begin
386 G := NewGameData;
387 MapSize := G.lx * G.ly;
388 decompose24 := (1 shl 24 - 1) div G.lx + 1;
389{$IFDEF DEBUG}
390 for Loc := 0 to MapSize - 1 do
391 assert(cardinal(Loc) * decompose24 shr 24 = cardinal(Loc div G.lx));
392{$ENDIF}
393end;
394
395
396constructor TCustomAI.Create(Nation: integer);
397begin
398 inherited Create;
399 me := Nation;
400 RO := pointer(G.RO[Nation]);
401 Map := pointer(RO.Map);
402 MyUnit := pointer(RO.Un);
403 MyCity := pointer(RO.City);
404 MyModel := pointer(RO.Model);
405 Opponent := -1;
406end;
407
408destructor TCustomAI.Destroy;
409begin
410 Server(sSetDebugMap, me, 0, nodata^);
411end;
412
413
414procedure TCustomAI.Process(Command: integer; var Data);
415var
416 Nation, NewResearch, NewGov, Count, ad, cix, iix: integer;
417 NegoTime: TNegoTime;
418begin
419 case Command of
420 cTurn, cContinue:
421 begin
422 if RO.Alive and (1 shl me) = 0 then
423 begin // I'm dead, huhu
424 Server(sTurn, me, 0, nodata^);
425 exit;
426 end;
427 if Command = cTurn then
428 begin
429 fillchar(cixStateImp, sizeof(cixStateImp), $FF);
430 for cix := 0 to RO.nCity - 1 do
431 if MyCity[cix].Loc >= 0 then
432 for iix := imPalace to imSpacePort do
433 if MyCity[cix].Built[iix] > 0 then
434 cixStateImp[iix] := cix;
435 if RO.Happened and phChangeGov <> 0 then
436 begin
437 NewGov := ChooseGovernment;
438 if NewGov > gAnarchy then
439 Server(sSetGovernment, me, NewGov, nodata^);
440 end;
441 HaveTurned := False;
442 Contacted := [];
443 end;
444 if (Command = cContinue) and (MyAction = scContact) then
445 begin
446 if OnNegoRejected_CancelTreaty then
447 if RO.Treaty[Opponent] >= trPeace then
448 if Server(sCancelTreaty, me, 0, nodata^) < rExecuted then
449 assert(False);
450 end
451 else
452 UnwantedNego := [];
453 Opponent := -1;
454 repeat
455 if HaveTurned then
456 NegoTime := EndOfTurn
457 else
458 NegoTime := BeginOfTurn;
459 if RO.Government <> gAnarchy then
460 for Nation := 0 to nPl - 1 do
461 if (Nation <> me) and (1 shl Nation and RO.Alive <> 0) and
462 (RO.Treaty[Nation] >= trNone) and not (Nation in Contacted) and not
463 (Nation in UnwantedNego) and
464 (Server(scContact - sExecute + Nation shl 4, me, 0, nodata^) >= rExecuted) then
465 if WantNegotiation(Nation, NegoTime) then
466 begin
467 if Server(scContact + Nation shl 4, me, 0, nodata^) >= rExecuted then
468 begin
469 include(Contacted, Nation);
470 Opponent := Nation;
471 MyAction := scContact;
472 exit;
473 end;
474 end
475 else
476 include(UnwantedNego, Nation);
477 if NegoTime = BeginOfTurn then
478 begin
479 DoTurn;
480 HaveTurned := True;
481 Contacted := [];
482 UnwantedNego := [];
483 end
484 else
485 break;
486 until False;
487 if RO.Happened and phTech <> 0 then
488 begin
489 NewResearch := ChooseResearchAdvance;
490 if NewResearch < 0 then
491 begin // choose random research
492 Count := 0;
493 for ad := 0 to nAdv - 1 do
494 if AdvanceResearchable(ad) then
495 begin
496 Inc(Count);
497 if random(Count) = 0 then
498 NewResearch := ad;
499 end;
500 end;
501 Server(sSetResearch, me, NewResearch, nodata^);
502 end;
503 if Server(sTurn, me, 0, nodata^) < rExecuted then
504 assert(False);
505 end;
506 scContact:
507 if WantNegotiation(integer(Data), EnemyCalled) then
508 begin
509 if Server(scDipStart, me, 0, nodata^) < rExecuted then
510 assert(False);
511 Opponent := integer(Data);
512 MyAction := scDipStart;
513 end
514 else
515 begin
516 if Server(scReject, me, 0, nodata^) < rExecuted then
517 assert(False);
518 end;
519 scDipStart, scDipNotice, scDipAccept, scDipCancelTreaty, scDipOffer, scDipBreak:
520 begin
521 OppoAction := Command;
522 if Command = scDipOffer then
523 OppoOffer := TOffer(Data);
524 if Command = scDipStart then
525 MyLastAction := scContact
526 else
527 begin
528 MyLastAction := MyAction;
529 MyLastOffer := MyOffer;
530 end;
531 if (OppoAction = scDipCancelTreaty) or (OppoAction = scDipBreak) then
532 MyAction := scDipNotice
533 else
534 begin
535 MyAction := scDipOffer;
536 MyOffer.nDeliver := 0;
537 MyOffer.nCost := 0;
538 end;
539 DoNegotiation;
540 assert((MyAction = scDipNotice) or (MyAction = scDipAccept) or
541 (MyAction = scDipCancelTreaty) or (MyAction = scDipOffer) or (MyAction = scDipBreak));
542 if MyAction = scDipOffer then
543 Server(MyAction, me, 0, MyOffer)
544 else
545 Server(MyAction, me, 0, nodata^);
546 end;
547 cShowEndContact:
548 Opponent := -1;
549 end;
550end;
551
552{$HINTS OFF}
553procedure TCustomAI.SetDataDefaults;
554begin
555end;
556
557procedure TCustomAI.SetDataRandom;
558begin
559end;
560
561procedure TCustomAI.DoTurn;
562begin
563end;
564
565procedure TCustomAI.DoNegotiation;
566begin
567end;
568
569procedure TCustomAI.OnBeforeEnemyAttack(UnitInfo: TUnitInfo;
570 ToLoc, EndHealth, EndHealthDef: integer);
571begin
572end;
573
574procedure TCustomAI.OnBeforeEnemyCapture(UnitInfo: TUnitInfo; ToLoc: integer);
575begin
576end;
577
578procedure TCustomAI.OnAfterEnemyAttack;
579begin
580end;
581
582procedure TCustomAI.OnAfterEnemyCapture;
583begin
584end;
585
586function TCustomAI.ChooseResearchAdvance: integer;
587begin
588 Result := -1;
589end;
590
591function TCustomAI.ChooseStealAdvance: integer;
592begin
593 Result := -1;
594end;
595
596function TCustomAI.ChooseGovernment: integer;
597begin
598 Result := gDespotism;
599end;
600
601function TCustomAI.WantNegotiation(Nation: integer; NegoTime: TNegoTime): boolean;
602begin
603 Result := False;
604end;
605
606function TCustomAI.OnNegoRejected_CancelTreaty: boolean;
607begin
608 Result := False;
609end;
610
611{$HINTS ON}
612
613procedure TCustomAI.StealAdvance;
614var
615 Steal, ad, Count: integer;
616begin
617 Steal := ChooseStealAdvance;
618 if Steal < 0 then
619 begin // choose random advance
620 Count := 0;
621 for ad := 0 to nAdv - 1 do
622 if AdvanceStealable(ad) then
623 begin
624 Inc(Count);
625 if random(Count) = 0 then
626 Steal := ad;
627 end;
628 end;
629 if Steal >= 0 then
630 Server(sStealTech, me, Steal, nodata^);
631 RO.Happened := RO.Happened and not phStealTech;
632end;
633
634function TCustomAI.IsResearched(Advance: integer): boolean;
635begin
636 Result := (Advance = preNone) or (Advance <> preNA) and (RO.Tech[Advance] >= tsApplicable);
637end;
638
639function TCustomAI.ResearchCost: integer;
640begin
641 Server(sGetTechCost, me, 0, Result);
642end;
643
644function TCustomAI.ChangeAttitude(Nation, Attitude: integer): integer;
645begin
646 Result := Server(sSetAttitude + Nation shl 4, me, Attitude, nodata^);
647end;
648
649function TCustomAI.Revolution: integer;
650begin
651 Result := Server(sRevolution, me, 0, nodata^);
652end;
653
654function TCustomAI.ChangeRates(Tax, Lux: integer): integer;
655begin
656 Result := Server(sSetRates, me, Tax div 10 and $f + Lux div 10 and $f shl 4, nodata^);
657end;
658
659function TCustomAI.PrepareNewModel(Domain: integer): integer;
660begin
661 Result := Server(sCreateDevModel, me, Domain, nodata^);
662end;
663
664function TCustomAI.SetNewModelFeature(F, Count: integer): integer;
665begin
666 Result := Server(sSetDevModelCap + Count shl 4, me, F, nodata^);
667end;
668
669function TCustomAI.AdvanceResearchable(Advance: integer): boolean;
670begin
671 Result := Server(sSetResearch - sExecute, me, Advance, nodata^) >= rExecuted;
672end;
673
674function TCustomAI.AdvanceStealable(Advance: integer): boolean;
675begin
676 Result := Server(sStealTech - sExecute, me, Advance, nodata^) >= rExecuted;
677end;
678
679function TCustomAI.GetJobProgress(Loc: integer;
680 var JobProgress: TJobProgressData): boolean;
681begin
682 Result := Server(sGetJobProgress, me, Loc, JobProgress) >= rExecuted;
683end;
684
685function TCustomAI.DebugMessage(Level: integer; Text: string): boolean;
686begin
687 Text := copy('P' + char(48 + me) + ' ' + Text, 1, 254);
688 Server(sMessage, me, Level, PChar(Text)^);
689
690 Result := True;
691 // always returns true so that it can be used like
692 // "assert(DebugMessage(...));" -> not compiled in release build
693end;
694
695function TCustomAI.SetDebugMap(var DebugMap): boolean;
696begin
697 Server(sSetDebugMap, me, 0, DebugMap);
698
699 Result := True;
700 // always returns true so that it can be used like
701 // "assert(SetDebugMap(...));" -> not compiled in release build
702end;
703
704procedure TCustomAI.Unit_FindMyDefender(Loc: integer; var uix: integer);
705begin
706 if Server(sGetDefender, me, Loc, uix) < rExecuted then
707 uix := -1;
708end;
709
710procedure TCustomAI.Unit_FindEnemyDefender(Loc: integer; var euix: integer);
711begin
712 euix := RO.nEnemyUn - 1;
713 while (euix >= 0) and (RO.EnemyUn[euix].Loc <> Loc) do
714 Dec(euix);
715end;
716
717function TCustomAI.Unit_Move(uix, ToLoc: integer): integer;
718var
719 Step: integer;
720 DestinationReached: boolean;
721 Advice: TMoveAdviceData;
722begin
723 assert((uix >= 0) and (uix < RO.nUn) and (MyUnit[uix].Loc >= 0)); // is a unit
724{Loc_to_ab(MyUnit[uix].Loc,ToLoc,a,b);
725assert((a<>0) or (b<>0));
726if (a>=-1) and (a<=1) and (b>=-1) and (b<=1) then
727 begin // move to adjacent tile
728 !!!problem: if move is invalid, return codes are not consistent with other branch (eNoWay)
729 Advice.nStep:=1;
730 Advice.dx[0]:=a-b;
731 Advice.dy[0]:=a+b;
732 Advice.MoreTurns:=0;
733 Advice.MaxHostile_MovementLeft:=MyUnit[uix].Movement;
734 result:=eOK;
735 end
736else}
737 begin // move to non-adjacent tile, find shortest path
738 Advice.ToLoc := ToLoc;
739 Advice.MoreTurns := 9999;
740 Advice.MaxHostile_MovementLeft := 100;
741 Result := Server(sGetMoveAdvice, me, uix, Advice);
742 end;
743 if Result = eOk then
744 begin
745 DestinationReached := False;
746 Step := 0;
747 repeat
748 if Result and (rExecuted or rUnitRemoved) = rExecuted then
749 // check if destination reached
750 if (ToLoc >= 0) and (Advice.MoreTurns = 0) and (Step = Advice.nStep - 1) and
751 ((Map[ToLoc] and (fUnit or fOwned) = fUnit) // attack
752 or (Map[ToLoc] and (fCity or fOwned) = fCity) and
753 ((MyModel[MyUnit[uix].mix].Domain <> dGround) // bombardment
754 or (MyModel[MyUnit[uix].mix].Flags and mdCivil <> 0))) then // can't capture
755 begin
756 DestinationReached := True;
757 break;
758 end // stop next to destination
759 else if Step = Advice.nStep then
760 DestinationReached := True; // normal move -- stop at destination
761
762 if (Step = Advice.nStep) or (Result <> eOK) and (Result <> eLoaded) then
763 break;
764
765 Result := Server(sMoveUnit + (Advice.dx[Step] and 7) shl 4 +
766 (Advice.dy[Step] and 7) shl 7, me, uix, nodata^);
767 Inc(Step);
768 if RO.Happened and phStealTech <> 0 then
769 StealAdvance;
770 until False;
771 if DestinationReached then
772 if Advice.nStep = 25 then
773 Result := Unit_Move(uix, ToLoc) // Shinkansen
774 else if Advice.MoreTurns = 0 then
775 Result := Result or rLocationReached
776 else
777 Result := Result or rMoreTurns;
778 end;
779end;
780
781function TCustomAI.Unit_Step(uix, ToLoc: integer): integer;
782var
783 a, b: integer;
784begin
785 Loc_to_ab(MyUnit[uix].Loc, ToLoc, a, b);
786 assert(((a <> 0) or (b <> 0)) and (a >= -1) and (a <= 1) and (b >= -1) and (b <= 1));
787 Result := Server(sMoveUnit + ((a - b) and 7) shl 4 + ((a + b) and 7) shl 7, me, uix, nodata^);
788 if RO.Happened and phStealTech <> 0 then
789 StealAdvance;
790end;
791
792function TCustomAI.Unit_Attack(uix, ToLoc: integer): integer;
793var
794 a, b: integer;
795begin
796 assert((uix >= 0) and (uix < RO.nUn) and (MyUnit[uix].Loc >= 0) // is a unit
797 and ((Map[ToLoc] and (fUnit or fOwned) = fUnit) // is an attack
798 or (Map[ToLoc] and (fCity or fOwned) = fCity) and
799 (MyModel[MyUnit[uix].mix].Domain <> dGround))); // is a bombardment
800 Loc_to_ab(MyUnit[uix].Loc, ToLoc, a, b);
801 assert(((a <> 0) or (b <> 0)) and (a >= -1) and (a <= 1) and (b >= -1) and (b <= 1));
802 // attack to adjacent tile
803 Result := Server(sMoveUnit + (a - b) and 7 shl 4 + (a + b) and 7 shl 7, me, uix, nodata^);
804end;
805
806function TCustomAI.Unit_DoMission(uix, MissionType, ToLoc: integer): integer;
807var
808 a, b: integer;
809begin
810 Result := Server(sSetSpyMission + MissionType shl 4, me, 0, nodata^);
811 if Result >= rExecuted then
812 begin
813 assert((uix >= 0) and (uix < RO.nUn) and (MyUnit[uix].Loc >= 0) // is a unit
814 and (MyModel[MyUnit[uix].mix].Kind = mkDiplomat)); // is a commando
815 Loc_to_ab(MyUnit[uix].Loc, ToLoc, a, b);
816 assert(((a <> 0) or (b <> 0)) and (a >= -1) and (a <= 1) and (b >= -1) and (b <= 1));
817 // city must be adjacent
818 Result := Server(sMoveUnit - sExecute + (a - b) and 7 shl 4 + (a + b) and 7 shl 7, me, uix, nodata^);
819 if Result = eMissionDone then
820 Result := Server(sMoveUnit + (a - b) and 7 shl 4 + (a + b) and 7 shl 7, me, uix, nodata^)
821 else if (Result <> eNoTime_Move) and (Result <> eTreaty) and (Result <> eNoTurn) then
822 Result := eInvalid; // not a special commando mission!
823 end;
824end;
825
826function TCustomAI.Unit_MoveForecast(uix, ToLoc: integer;
827 var RemainingMovement: integer): boolean;
828var
829 Advice: TMoveAdviceData;
830begin
831 assert((uix >= 0) and (uix < RO.nUn) and (MyUnit[uix].Loc >= 0)); // is a unit
832 Advice.ToLoc := ToLoc;
833 Advice.MoreTurns := 0;
834 Advice.MaxHostile_MovementLeft := 100;
835 if Server(sGetMoveAdvice, me, uix, Advice) = eOk then
836 begin
837 RemainingMovement := Advice.MaxHostile_MovementLeft;
838 Result := True;
839 end
840 else
841 begin
842 RemainingMovement := -1;
843 Result := False;
844 end;
845end;
846
847// negative RemainingHealth is remaining helth of defender if lost
848function TCustomAI.Unit_AttackForecast(uix, ToLoc, AttackMovement: integer;
849 var RemainingHealth: integer): boolean;
850var
851 BattleForecast: TBattleForecast;
852begin
853 assert((uix >= 0) and (uix < RO.nUn) and (MyUnit[uix].Loc >= 0) // is a unit
854 and (Map[ToLoc] and (fUnit or fOwned) = fUnit)); // is an attack
855 RemainingHealth := -$100;
856 Result := False;
857 if AttackMovement >= 0 then
858 with MyUnit[uix] do
859 begin
860 BattleForecast.pAtt := me;
861 BattleForecast.mixAtt := mix;
862 BattleForecast.HealthAtt := Health;
863 BattleForecast.ExpAtt := Exp;
864 BattleForecast.FlagsAtt := Flags;
865 BattleForecast.Movement := AttackMovement;
866 if Server(sGetBattleForecast, me, ToLoc, BattleForecast) >= rExecuted then
867 begin
868 if BattleForecast.EndHealthAtt > 0 then
869 RemainingHealth := BattleForecast.EndHealthAtt
870 else
871 RemainingHealth := -BattleForecast.EndHealthDef;
872 Result := True;
873 end;
874 end;
875end;
876
877function TCustomAI.Unit_DefenseForecast(euix, ToLoc: integer;
878 var RemainingHealth: integer): boolean;
879var
880 BattleForecast: TBattleForecast;
881begin
882 assert((euix >= 0) and (euix < RO.nEnemyUn) and (RO.EnemyUn[euix].Loc >= 0) // is an enemy unit
883 and (Map[ToLoc] and (fUnit or fOwned) = (fUnit or fOwned))); // is an attack
884 RemainingHealth := $100;
885 Result := False;
886 with RO.EnemyUn[euix] do
887 begin
888 BattleForecast.pAtt := Owner;
889 BattleForecast.mixAtt := mix;
890 BattleForecast.HealthAtt := Health;
891 BattleForecast.ExpAtt := Exp;
892 BattleForecast.FlagsAtt := Flags;
893 BattleForecast.Movement := 100;
894 if Server(sGetBattleForecast, me, ToLoc, BattleForecast) >= rExecuted then
895 begin
896 if BattleForecast.EndHealthDef > 0 then
897 RemainingHealth := BattleForecast.EndHealthDef
898 else
899 RemainingHealth := -BattleForecast.EndHealthAtt;
900 Result := True;
901 end;
902 end;
903end;
904
905function TCustomAI.Unit_Disband(uix: integer): integer;
906begin
907 Result := Server(sRemoveUnit, me, uix, nodata^);
908end;
909
910function TCustomAI.Unit_StartJob(uix, NewJob: integer): integer;
911begin
912 Result := Server(sStartJob + NewJob shl 4, me, uix, nodata^);
913end;
914
915function TCustomAI.Unit_SetHomeHere(uix: integer): integer;
916begin
917 Result := Server(sSetUnitHome, me, uix, nodata^);
918end;
919
920function TCustomAI.Unit_Load(uix: integer): integer;
921begin
922 Result := Server(sLoadUnit, me, uix, nodata^);
923end;
924
925function TCustomAI.Unit_Unload(uix: integer): integer;
926begin
927 Result := Server(sUnloadUnit, me, uix, nodata^);
928end;
929
930function TCustomAI.Unit_AddToCity(uix: integer): integer;
931begin
932 Result := Server(sAddToCity, me, uix, nodata^);
933end;
934
935function TCustomAI.Unit_SelectTransport(uix: integer): integer;
936begin
937 Result := Server(sSelectTransport, me, uix, nodata^);
938end;
939
940
941procedure TCustomAI.City_FindMyCity(Loc: integer; var cix: integer);
942begin
943 if Map[Loc] and (fCity or fOwned) <> fCity or fOwned then
944 cix := -1
945 else
946 begin
947 cix := RO.nCity - 1;
948 while (cix >= 0) and (MyCity[cix].Loc <> Loc) do
949 Dec(cix);
950 end;
951end;
952
953procedure TCustomAI.City_FindEnemyCity(Loc: integer; var ecix: integer);
954begin
955 if Map[Loc] and (fCity or fOwned) <> fCity then
956 ecix := -1
957 else
958 begin
959 ecix := RO.nEnemyCity - 1;
960 while (ecix >= 0) and (RO.EnemyCity[ecix].Loc <> Loc) do
961 Dec(ecix);
962 end;
963end;
964
965function TCustomAI.City_HasProject(cix: integer): boolean;
966begin
967 Result := MyCity[cix].Project and (cpImp + cpIndex) <> cpImp + imTrGoods;
968end;
969
970function TCustomAI.City_CurrentImprovementProject(cix: integer): integer;
971begin
972 if MyCity[cix].Project and cpImp = 0 then
973 Result := -1
974 else
975 begin
976 Result := MyCity[cix].Project and cpIndex;
977 if Result = imTrGoods then
978 Result := -1;
979 end;
980end;
981
982function TCustomAI.City_CurrentUnitProject(cix: integer): integer;
983begin
984 if MyCity[cix].Project and cpImp <> 0 then
985 Result := -1
986 else
987 Result := MyCity[cix].Project and cpIndex;
988end;
989
990function TCustomAI.City_GetTileInfo(cix, TileLoc: integer;
991 var TileInfo: TTileInfo): integer;
992begin
993 TileInfo.ExplCity := cix;
994 Result := Server(sGetHypoCityTileInfo, me, TileLoc, TileInfo);
995end;
996
997function TCustomAI.City_GetReport(cix: integer; var Report: TCityReport): integer;
998begin
999 Report.HypoTiles := -1;
1000 Report.HypoTax := -1;
1001 Report.HypoLux := -1;
1002 Result := Server(sGetCityReport, me, cix, Report);
1003end;
1004
1005function TCustomAI.City_GetHypoReport(cix, HypoTiles, HypoTax, HypoLux: integer;
1006 var Report: TCityReport): integer;
1007begin
1008 Report.HypoTiles := HypoTiles;
1009 Report.HypoTax := HypoTax;
1010 Report.HypoLux := HypoLux;
1011 Result := Server(sGetCityReport, me, cix, Report);
1012end;
1013
1014function TCustomAI.City_GetReportNew(cix: integer; var Report: TCityReportNew): integer;
1015begin
1016 Report.HypoTiles := -1;
1017 Report.HypoTaxRate := -1;
1018 Report.HypoLuxuryRate := -1;
1019 Result := Server(sGetCityReportNew, me, cix, Report);
1020end;
1021
1022function TCustomAI.City_GetHypoReportNew(cix, HypoTiles, HypoTaxRate,
1023 HypoLuxuryRate: integer; var Report: TCityReportNew): integer;
1024begin
1025 Report.HypoTiles := HypoTiles;
1026 Report.HypoTaxRate := HypoTaxRate;
1027 Report.HypoLuxuryRate := HypoLuxuryRate;
1028 Result := Server(sGetCityReportNew, me, cix, Report);
1029end;
1030
1031function TCustomAI.City_GetAreaInfo(cix: integer; var AreaInfo: TCityAreaInfo): integer;
1032begin
1033 Result := Server(sGetCityAreaInfo, me, cix, AreaInfo);
1034end;
1035
1036function TCustomAI.City_StartUnitProduction(cix, mix: integer): integer;
1037begin
1038 if (MyCity[cix].Project and (cpImp + cpIndex) <> mix) then
1039 // not already producing that
1040 Result := Server(sSetCityProject, me, cix, mix);
1041end;
1042
1043function TCustomAI.City_StartEmigration(cix, mix: integer;
1044 AllowDisbandCity, AsConscripts: boolean): integer;
1045var
1046 NewProject: integer;
1047begin
1048 NewProject := mix;
1049 if AllowDisbandCity then
1050 NewProject := NewProject or cpDisbandCity;
1051 if AsConscripts then
1052 NewProject := NewProject or cpConscripts;
1053 Result := Server(sSetCityProject, me, cix, NewProject);
1054end;
1055
1056function TCustomAI.City_StartImprovement(cix, iix: integer): integer;
1057var
1058 NewProject: integer;
1059begin
1060 NewProject := iix + cpImp;
1061 if (MyCity[cix].Project and (cpImp + cpIndex) <> NewProject) then
1062 // not already producing that
1063 Result := Server(sSetCityProject, me, cix, NewProject);
1064end;
1065
1066function TCustomAI.City_Improvable(cix, iix: integer): boolean;
1067var
1068 NewProject: integer;
1069begin
1070 NewProject := iix + cpImp;
1071 Result := Server(sSetCityProject - sExecute, me, cix, NewProject) >= rExecuted;
1072end;
1073
1074function TCustomAI.City_StopProduction(cix: integer): integer;
1075var
1076 NewProject: integer;
1077begin
1078 NewProject := imTrGoods + cpImp;
1079 Result := Server(sSetCityProject, me, cix, NewProject);
1080end;
1081
1082function TCustomAI.City_BuyProject(cix: integer): integer;
1083begin
1084 Result := Server(sBuyCityProject, me, cix, nodata^);
1085end;
1086
1087function TCustomAI.City_SellImprovement(cix, iix: integer): integer;
1088begin
1089 Result := Server(sSellCityImprovement, me, cix, iix);
1090end;
1091
1092function TCustomAI.City_RebuildImprovement(cix, iix: integer): integer;
1093begin
1094 Result := Server(sRebuildCityImprovement, me, cix, iix);
1095end;
1096
1097function TCustomAI.City_SetTiles(cix, NewTiles: integer): integer;
1098begin
1099 Result := Server(sSetCityTiles, me, cix, NewTiles);
1100end;
1101
1102procedure TCustomAI.City_OptimizeTiles(cix: integer; ResourceWeights: cardinal);
1103var
1104 Advice: TCityTileAdviceData;
1105begin
1106 Advice.ResourceWeights := ResourceWeights;
1107 Server(sGetCityTileAdvice, me, cix, Advice);
1108 City_SetTiles(cix, Advice.Tiles);
1109end;
1110
1111
1112// negotiation
1113function TCustomAI.Nego_CheckMyAction: integer;
1114begin
1115 assert(Opponent >= 0); // only allowed in negotiation mode
1116 assert((MyAction = scDipNotice) or (MyAction = scDipAccept) or
1117 (MyAction = scDipCancelTreaty) or (MyAction = scDipOffer) or (MyAction = scDipBreak));
1118 if MyAction = scDipOffer then
1119 Result := Server(MyAction - sExecute, me, 0, MyOffer)
1120 else
1121 Result := Server(MyAction - sExecute, me, 0, nodata^);
1122end;
1123
1124
1125initialization
1126 nodata := pointer(0);
1127 RWDataSize := 0;
1128
1129end.
Note: See TracBrowser for help on using the repository browser.