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

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