source: tags/1.3.5/LocalPlayer/ClientTools.pas

Last change on this file was 592, checked in by chronos, 4 months ago
  • Fixed: Avoided more GTK2 chrashes.
  • Fixed: Build StdAI with O1 optimization level to avoid crash.
  • Modified: Code cleanup.
File size: 22.1 KB
Line 
1{$INCLUDE Switches.inc}
2unit ClientTools;
3
4interface
5
6uses
7 Protocol;
8
9const
10 nOfferedResourceWeights = 6;
11 OfferedResourceWeights: array [0 .. nOfferedResourceWeights - 1] of Cardinal =
12 (rwOff, rwMaxScience, rwForceScience, rwMaxGrowth, rwForceProd, rwMaxProd);
13
14type
15 TImpOrder = array [0 .. (nImp + 4) div 4 * 4 - 1] of ShortInt;
16 TEnhancementJobs = array [0 .. 11, 0 .. 7] of Byte;
17 JobResultSet = set of 0 .. 39;
18
19 TMapOption = (
20 // options switched by buttons
21 moPolitical = 0, moCityNames = 1, moGreatWall = 4, moGrid = 5, moBareTerrain = 6,
22 // other options
23 moEditMode = 16, moLocCodes = 17
24 );
25 TMapOptions = set of TMapOption;
26
27 TSaveOption = (soAlEffectiveMovesOnly = 0, soEnMoves = 1, soEnAttacks = 2,
28 soEnNoMoves = 3, soWaitTurn = 4, soEffectiveMovesOnly = 5, soEnFastMoves = 6,
29 soSlowMoves = 7, soFastMoves = 8, soVeryFastMoves = 9, soNames = 10,
30 soRepList = 11, soRepScreens = 12, soSoundOff = 13, soSoundOn = 14,
31 soSoundOnAlt = 15, soScrollSlow = 16, soScrollFast = 17, soScrollOff = 18,
32 soAlSlowMoves = 19, soAlFastMoves = 20, somAlNoMoves = 21, soTellAI = 30);
33 TSaveOptions = set of TSaveOption;
34
35var
36 Server: TServerCall;
37 G: TNewGameData;
38 Me: Integer;
39 MyRO: ^TPlayerContext;
40 MyMap: ^TTileList;
41 MyUn: ^TUnList;
42 MyCity: ^TCityList;
43 MyModel: ^TModelList;
44
45 AdvValue: array [0 .. nAdv - 1] of Integer;
46
47function dLoc(Loc, dx, dy: Integer): Integer;
48function Distance(Loc0, Loc1: Integer): Integer;
49function UnrestAtLoc(uix, Loc: Integer): Boolean;
50function GetMoveAdvice(uix, ToLoc: Integer;
51 var MoveAdviceData: TMoveAdviceData): Integer;
52function ColorOfHealth(Health: Integer): Integer;
53function IsMultiPlayerGame: Boolean;
54procedure ItsMeAgain(P: Integer);
55function GetAge(P: Integer): Integer;
56function IsCivilReportNew(Enemy: Integer): Boolean;
57function IsMilReportNew(Enemy: Integer): Boolean;
58function CutCityFoodSurplus(FoodSurplus: Integer; IsCityAlive: Boolean;
59 gov, Size: Integer): Integer;
60function CityTaxBalance(cix: Integer; const CityReport: TCityReportNew): Integer;
61procedure SumCities(var TaxSum, ScienceSum: Integer);
62function JobTest(uix, Job: Integer; IgnoreResults: JobResultSet = []): Boolean;
63procedure GetUnitInfo(Loc: Integer; var uix: Integer; var UnitInfo: TUnitInfo);
64procedure GetCityInfo(Loc: Integer; var cix: Integer; var CityInfo: TCityInfo);
65function UnitExhausted(uix: Integer): Boolean;
66function ModelHash(const ModelInfo: TModelInfo): Integer;
67function ProcessEnhancement(uix: Integer; const Jobs: TEnhancementJobs): Integer;
68function AutoBuild(cix: Integer; const ImpOrder: TImpOrder): Boolean;
69procedure DebugMessage(Level: Integer; Text: string);
70procedure CityOptimizer_BeginOfTurn;
71procedure CityOptimizer_CityChange(cix: Integer);
72procedure CityOptimizer_TileBecomesAvailable(Loc: Integer);
73procedure CityOptimizer_ReleaseCityTiles(cix, ReleasedTiles: Integer);
74procedure CityOptimizer_BeforeRemoveUnit(uix: Integer);
75procedure CityOptimizer_AfterRemoveUnit;
76procedure CityOptimizer_EndOfTurn;
77function GetMyCityByLoc(Loc: Integer): PCity;
78function GetEnemyCityByLoc(Loc: Integer): PCityInfo;
79function GetMyUnitByLoc(Loc: Integer): PUn;
80function GetEnemyUnitByLoc(Loc: Integer): PUnitInfo;
81
82
83implementation
84
85var
86 CityNeedsOptimize: array [0 .. ncmax - 1] of Boolean;
87
88function dLoc(Loc, dx, dy: Integer): Integer;
89var
90 y0: Integer;
91begin
92 y0 := (Loc + G.lx * 1024) div G.lx - 1024;
93 Result := (Loc + (dx + y0 and 1 + G.lx * 1024) shr 1) mod G.lx + G.lx * (y0 + dy);
94end;
95
96function Distance(Loc0, Loc1: Integer): Integer;
97var
98 dx, dy: Integer;
99begin
100 Inc(Loc0, G.lx * 1024);
101 Inc(Loc1, G.lx * 1024);
102 dx := Abs(((Loc1 mod G.lx * 2 + Loc1 div G.lx and 1) -
103 (Loc0 mod G.lx * 2 + Loc0 div G.lx and 1) + 3 * G.lx) mod (2 * G.lx) - G.lx);
104 dy := Abs(Loc1 div G.lx - Loc0 div G.lx);
105 Result := dx + dy + Abs(dx - dy) shr 1;
106end;
107
108function UnrestAtLoc(uix, Loc: Integer): Boolean;
109var
110 uix1: Integer;
111begin
112 Result := False;
113 if MyModel[MyUn[uix].mix].Flags and mdCivil = 0 then
114 case MyRO.Government of
115 gRepublic, gFuture:
116 Result := (MyRO.Territory[Loc] >= 0) and (MyRO.Territory[Loc] <> Me) and
117 (MyRO.Treaty[MyRO.Territory[Loc]] < trAlliance);
118 gDemocracy:
119 Result := (MyRO.Territory[Loc] < 0) or (MyRO.Territory[Loc] <> Me) and
120 (MyRO.Treaty[MyRO.Territory[Loc]] < trAlliance);
121 end;
122 with MyModel[MyUn[uix].mix] do
123 if Cap[mcSeaTrans] + Cap[mcAirTrans] + Cap[mcCarrier] > 0 then
124 for uix1 := 0 to MyRO.nUn - 1 do // check transported units too
125 if (MyUn[uix1].Loc >= 0) and (MyUn[uix1].Master = uix) then
126 Result := Result or UnrestAtLoc(uix1, Loc);
127end;
128
129function GetMoveAdvice(uix, ToLoc: Integer;
130 var MoveAdviceData: TMoveAdviceData): Integer;
131var
132 MinEndHealth: Integer;
133begin
134 if MyModel[MyUn[uix].mix].Domain = dGround then
135 MinEndHealth := 100
136 else
137 MinEndHealth := 1; // resistent to hostile terrain -- don't consider
138 repeat
139 if MyUn[uix].Health >= MinEndHealth then
140 begin
141 MoveAdviceData.ToLoc := ToLoc;
142 MoveAdviceData.MoreTurns := 999;
143 MoveAdviceData.MaxHostile_MovementLeft := MyUn[uix].Health - MinEndHealth;
144 Result := Server(sGetMoveAdvice, Me, uix, MoveAdviceData);
145 if (MinEndHealth <= 1) or (Result <> eNoWay) then
146 Exit;
147 end;
148 case MinEndHealth of
149 100:
150 MinEndHealth := 50;
151 50:
152 MinEndHealth := 25;
153 25:
154 MinEndHealth := 12;
155 else
156 MinEndHealth := 1
157 end;
158 until False;
159end;
160
161function ColorOfHealth(Health: Integer): Integer;
162var
163 Red, Green: Integer;
164begin
165 Green := 400 * Health div 100;
166 if Green > 200 then
167 Green := 200;
168 Red := 510 * (100 - Health) div 100;
169 if Red > 255 then
170 Red := 255;
171 Result := Green shl 8 + Red;
172end;
173
174function IsMultiPlayerGame: Boolean;
175var
176 p1: Integer;
177begin
178 Result := False;
179 for p1 := 1 to nPl - 1 do
180 if G.RO[p1] <> nil then begin
181 Result := True;
182 Break;
183 end;
184end;
185
186procedure ItsMeAgain(P: Integer);
187begin
188 if G.RO[P] <> nil then
189 MyRO := Pointer(G.RO[P])
190 else if G.SuperVisorRO[P] <> nil then
191 MyRO := Pointer(G.SuperVisorRO[P])
192 else
193 Exit;
194 Me := P;
195 MyMap := Pointer(MyRO.Map);
196 MyUn := Pointer(MyRO.Un);
197 MyCity := Pointer(MyRO.City);
198 MyModel := Pointer(MyRO.Model);
199end;
200
201function GetAge(P: Integer): Integer;
202var
203 I: Integer;
204begin
205 if P = Me then begin
206 Result := 0;
207 for I := 1 to 3 do
208 if MyRO.Tech[AgePreq[I]] >= tsApplicable then
209 Result := I;
210 end else begin
211 Result := 0;
212 for I := 1 to 3 do
213 if MyRO.EnemyReport[P].Tech[AgePreq[I]] >= tsApplicable then
214 Result := I;
215 end;
216end;
217
218function IsCivilReportNew(Enemy: Integer): Boolean;
219var
220 I: Integer;
221begin
222 Assert(Enemy <> Me);
223 I := MyRO.EnemyReport[Enemy].TurnOfCivilReport;
224 Result := (I = MyRO.Turn) or (I = MyRO.Turn - 1) and (Enemy > Me);
225end;
226
227function IsMilReportNew(Enemy: Integer): Boolean;
228var
229 I: Integer;
230begin
231 Assert(Enemy <> Me);
232 I := MyRO.EnemyReport[Enemy].TurnOfMilReport;
233 Result := (I = MyRO.Turn) or (I = MyRO.Turn - 1) and (Enemy > Me);
234end;
235
236function CutCityFoodSurplus(FoodSurplus: Integer; IsCityAlive: Boolean;
237 gov, Size: Integer): Integer;
238begin
239 Result := FoodSurplus;
240 if not IsCityAlive or (Result > 0) and ((gov = gFuture) or
241 (Size >= NeedAqueductSize) and (Result < 2)) then
242 Result := 0; { no growth }
243end;
244
245function CityTaxBalance(cix: Integer; const CityReport: TCityReportNew): Integer;
246var
247 I: Integer;
248begin
249 Result := 0;
250 if (CityReport.HappinessBalance >= 0) { no disorder } and
251 (MyCity[cix].Flags and chCaptured = 0) then // not captured
252 begin
253 Inc(Result, CityReport.Tax);
254 if (MyCity[cix].Project and (cpImp + cpIndex) = cpImp + imTrGoods) and
255 (CityReport.Production > 0) then
256 Inc(Result, CityReport.Production);
257 if ((MyRO.Government = gFuture) or (MyCity[cix].Size >=
258 NeedAqueductSize) and (CityReport.FoodSurplus < 2)) and
259 (CityReport.FoodSurplus > 0) then
260 Inc(Result, CityReport.FoodSurplus);
261 end;
262 for I := nWonder to nImp - 1 do
263 if MyCity[cix].Built[I] > 0 then
264 Dec(Result, Imp[I].Maint);
265end;
266
267procedure SumCities(var TaxSum, ScienceSum: Integer);
268var
269 cix: Integer;
270 CityReport: TCityReportNew;
271begin
272 TaxSum := MyRO.OracleIncome;
273 ScienceSum := 0;
274 if MyRO.Government = gAnarchy then
275 Exit;
276 for cix := 0 to MyRO.nCity - 1 do
277 if MyCity[cix].Loc >= 0 then
278 begin
279 CityReport.HypoTiles := -1;
280 CityReport.HypoTaxRate := -1;
281 CityReport.HypoLuxuryRate := -1;
282 Server(sGetCityReportNew, Me, cix, CityReport);
283 if (CityReport.HappinessBalance >= 0) { no disorder } and
284 (MyCity[cix].Flags and chCaptured = 0) then // not captured
285 ScienceSum := ScienceSum + CityReport.Science;
286 TaxSum := TaxSum + CityTaxBalance(cix, CityReport);
287 end;
288end;
289
290function JobTest(uix, Job: Integer; IgnoreResults: JobResultSet): Boolean;
291var
292 Test: Integer;
293begin
294 Test := Server(sStartJob + Job shl 4 - sExecute, Me, uix, nil^);
295 Result := (Test >= rExecuted) or (Test in IgnoreResults);
296end;
297
298procedure GetUnitInfo(Loc: Integer; var uix: Integer; var UnitInfo: TUnitInfo);
299var
300 I, Cnt: Integer;
301begin
302 if MyMap[Loc] and fOwned <> 0 then
303 begin
304 Server(sGetDefender, Me, Loc, uix);
305 Cnt := 0;
306 for I := 0 to MyRO.nUn - 1 do
307 if MyUn[I].Loc = Loc then
308 Inc(Cnt);
309 MakeUnitInfo(Me, MyUn[uix], UnitInfo);
310 if Cnt > 1 then
311 UnitInfo.Flags := UnitInfo.Flags or unMulti;
312 end
313 else
314 begin
315 uix := MyRO.nEnemyUn - 1;
316 while (uix >= 0) and (MyRO.EnemyUn[uix].Loc <> Loc) do
317 Dec(uix);
318 UnitInfo := MyRO.EnemyUn[uix];
319 end;
320end;
321
322procedure GetCityInfo(Loc: Integer; var cix: Integer; var CityInfo: TCityInfo);
323begin
324 if MyMap[Loc] and fOwned <> 0 then
325 begin
326 CityInfo.Loc := Loc;
327 cix := MyRO.nCity - 1;
328 while (cix >= 0) and (MyCity[cix].Loc <> Loc) do
329 Dec(cix);
330 with CityInfo do
331 begin
332 Owner := Me;
333 ID := MyCity[cix].ID;
334 Size := MyCity[cix].Size;
335 Flags := 0;
336 if MyCity[cix].Built[imPalace] > 0 then
337 Inc(Flags, ciCapital);
338 if (MyCity[cix].Built[imWalls] > 0) or
339 (MyMap[MyCity[cix].Loc] and fGrWall <> 0) then
340 Inc(Flags, ciWalled);
341 if MyCity[cix].Built[imCoastalFort] > 0 then
342 Inc(Flags, ciCoastalFort);
343 if MyCity[cix].Built[imMissileBat] > 0 then
344 Inc(Flags, ciMissileBat);
345 if MyCity[cix].Built[imBunker] > 0 then
346 Inc(Flags, ciBunker);
347 if MyCity[cix].Built[imSpacePort] > 0 then
348 Inc(Flags, ciSpacePort);
349 end;
350 end
351 else
352 begin
353 cix := MyRO.nEnemyCity - 1;
354 while (cix >= 0) and (MyRO.EnemyCity[cix].Loc <> Loc) do
355 Dec(cix);
356 CityInfo := MyRO.EnemyCity[cix];
357 end;
358end;
359
360function UnitExhausted(uix: Integer): Boolean;
361 // check if another move of this unit is still possible
362var
363 dx, dy: Integer;
364begin
365 Result := True;
366 if (MyUn[uix].Movement > 0) or
367 (MyRO.Wonder[woShinkansen].EffectiveOwner = Me) then
368 if (MyUn[uix].Movement >= 100) or
369 ((MyModel[MyUn[uix].mix].Kind = mkCaravan) and
370 (MyMap[MyUn[uix].Loc] and fCity <> 0)) then
371 Result := False
372 else
373 for dx := -2 to 2 do
374 for dy := -2 to 2 do
375 if Abs(dx) + Abs(dy) = 2 then
376 if Server(sMoveUnit - sExecute + dx and 7 shl 4 + dy and
377 7 shl 7, Me, uix, nil^) >= rExecuted then begin
378 Result := False;
379 Exit;
380 end;
381end;
382
383function ModelHash(const ModelInfo: TModelInfo): Integer;
384var
385 I, FeatureCode, Hash1, Hash2, Hash2r, D: Cardinal;
386begin
387 with ModelInfo do
388 if Kind > mkEnemyDeveloped then
389 Result := Integer($C0000000 + Speed div 50 + Kind shl 8)
390 else
391 begin
392 FeatureCode := 0;
393 for I := mcFirstNonCap to nFeature - 1 do
394 if 1 shl Domain and Feature[I].Domains <> 0 then
395 begin
396 FeatureCode := FeatureCode * 2;
397 if 1 shl (I - mcFirstNonCap) <> 0 then
398 Inc(FeatureCode);
399 end;
400 case Domain of
401 dGround:
402 begin
403 Assert(FeatureCode < 1 shl 8);
404 Assert(Attack < 5113);
405 Assert(Defense < 2273);
406 Assert(Cost < 1611);
407 Hash1 := (Attack * 2273 + Defense) * 9 + (Speed - 150) div 50;
408 Hash2 := FeatureCode * 1611 + Cost;
409 end;
410 dSea:
411 begin
412 Assert(FeatureCode < 1 shl 9);
413 Assert(Attack < 12193);
414 Assert(Defense < 6097);
415 Assert(Cost < 4381);
416 Hash1 := ((Attack * 6097 + Defense) * 5 +
417 (Speed - 350) div 100) * 2;
418 if Weight >= 6 then
419 Inc(Hash1);
420 Hash2 := ((TTrans * 17 + ATrans_Fuel) shl 9 + FeatureCode) *
421 4381 + Cost;
422 end;
423 dAir:
424 begin
425 Assert(FeatureCode < 1 shl 5);
426 Assert(Attack < 2407);
427 Assert(Defense < 1605);
428 Assert(Bombs < 4813);
429 Assert(Cost < 2089);
430 Hash1 := (Attack * 1605 + Defense) shl 5 + FeatureCode;
431 Hash2 := ((Bombs * 7 + ATrans_Fuel) * 4 + TTrans) * 2089 + Cost;
432 end;
433 end;
434 Hash2r := 0;
435 for I := 0 to 7 do
436 begin
437 Hash2r := Hash2r * 13;
438 D := Hash2 div 13;
439 Inc(Hash2r, Hash2 - D * 13);
440 Hash2 := D;
441 end;
442 Result := Integer(Domain shl 30 + Hash1 xor Hash2r);
443 end;
444end;
445
446function ProcessEnhancement(uix: Integer; const Jobs: TEnhancementJobs): Integer;
447 { return values:
448 eJobDone - all applicable jobs done
449 eOK - enhancement not complete
450 eDied - job done and died (thurst) }
451var
452 Stage, NextJob, Tile: Integer;
453 Done: set of jNone..jPoll;
454begin
455 Done := [];
456 Tile := MyMap[MyUn[uix].Loc];
457 if Tile and fRoad <> 0 then
458 Include(Done, jRoad);
459 if Tile and fRR <> 0 then
460 Include(Done, jRR);
461 if (Tile and fTerImp = tiIrrigation) or (Tile and fTerImp = tiFarm) then
462 Include(Done, jIrr);
463 if Tile and fTerImp = tiFarm then
464 Include(Done, jFarm);
465 if Tile and fTerImp = tiMine then
466 Include(Done, jMine);
467 if Tile and fPoll = 0 then
468 Include(Done, jPoll);
469
470 if MyUn[uix].Job = jNone then
471 Result := eJobDone
472 else
473 Result := eOK;
474 while (Result <> eOK) and (Result <> eDied) do
475 begin
476 Stage := -1;
477 repeat
478 if Stage = -1 then
479 NextJob := jPoll
480 else
481 NextJob := Jobs[Tile and fTerrain, Stage];
482 if (NextJob = jNone) or not (NextJob in Done) then
483 Break;
484 Inc(Stage);
485 until Stage = 5;
486 if (Stage = 5) or (NextJob = jNone) then
487 begin
488 Result := eJobDone;
489 Break;
490 end; // tile enhancement complete
491 Result := Server(sStartJob + NextJob shl 4, Me, uix, nil^);
492 Include(Done, NextJob);
493 end;
494end;
495
496function AutoBuild(cix: Integer; const ImpOrder: TImpOrder): Boolean;
497var
498 I, NewProject: Integer;
499begin
500 Result := False;
501 if (MyCity[cix].Project and (cpImp + cpIndex) = cpImp + imTrGoods) or
502 (MyCity[cix].Flags and chProduction <> 0) then
503 begin
504 I := 0;
505 repeat
506 while (ImpOrder[I] >= 0) and (MyCity[cix].Built[ImpOrder[I]] > 0) do
507 Inc(I);
508 if ImpOrder[I] < 0 then
509 Break;
510 Assert(I < nImp);
511 NewProject := cpImp + ImpOrder[I];
512 if Server(sSetCityProject, Me, cix, NewProject) >= rExecuted then
513 begin
514 Result := True;
515 CityOptimizer_CityChange(cix);
516 Break;
517 end;
518 Inc(I);
519 until False;
520 end;
521end;
522
523procedure CalculateAdvValues;
524var
525 I, J: Integer;
526 known: array [0 .. nAdv - 1] of Integer;
527
528 procedure MarkPreqs(I: Integer);
529 begin
530 if known[I] = 0 then
531 begin
532 known[I] := 1;
533 if (I <> adScience) and (I <> adMassProduction) then
534 begin
535 if (AdvPreq[I, 0] >= 0) then
536 MarkPreqs(AdvPreq[I, 0]);
537 if (AdvPreq[I, 1] >= 0) then
538 MarkPreqs(AdvPreq[I, 1]);
539 end;
540 end;
541 end;
542
543begin
544 FillChar(AdvValue, SizeOf(AdvValue), 0);
545 for I := 0 to nAdv - 1 do
546 begin
547 FillChar(known, SizeOf(known), 0);
548 MarkPreqs(I);
549 for J := 0 to nAdv - 1 do
550 if known[J] > 0 then
551 Inc(AdvValue[I]);
552 if I in FutureTech then
553 Inc(AdvValue[I], 3000)
554 else if known[adMassProduction] > 0 then
555 Inc(AdvValue[I], 2000)
556 else if known[adScience] > 0 then
557 Inc(AdvValue[I], 1000);
558 end;
559end;
560
561procedure DebugMessage(Level: Integer; Text: string);
562begin
563 Server(sMessage, Me, Level, PChar(Text)^);
564end;
565
566function MarkCitiesAround(Loc, cixExcept: Integer): Boolean;
567 // return whether a city was marked
568var
569 cix: Integer;
570begin
571 Result := False;
572 for cix := 0 to MyRO.nCity - 1 do
573 if (cix <> cixExcept) and (MyCity[cix].Loc >= 0) and
574 (MyCity[cix].Flags and chCaptured = 0) and
575 (Distance(MyCity[cix].Loc, Loc) <= 5) then
576 begin
577 CityNeedsOptimize[cix] := True;
578 Result := True;
579 end;
580end;
581
582procedure OptimizeCities(CheckOnly: Boolean);
583var
584 cix, fix, dx, dy, Loc1, OptiType: Integer;
585 Done: Boolean;
586 Advice: TCityTileAdviceData;
587begin
588 repeat
589 Done := True;
590 for cix := 0 to MyRO.nCity - 1 do
591 if CityNeedsOptimize[cix] then
592 begin
593 OptiType := (MyCity[cix].Status shr 4) and $0F;
594 if OptiType <> 0 then
595 begin
596 Advice.ResourceWeights := OfferedResourceWeights[OptiType];
597 Server(sGetCityTileAdvice, Me, cix, Advice);
598 if Advice.Tiles <> MyCity[cix].Tiles then
599 if CheckOnly then
600 begin
601 // TODO: What is this assert for?
602 // Need to optimize city tiles but CheckOnly true?
603 //Assert(false)
604 end
605 else
606 begin
607 for fix := 1 to 26 do
608 if MyCity[cix].Tiles and not Advice.Tiles and
609 (1 shl fix) <> 0 then
610 begin // tile no longer used by this city -- check using it by another
611 dy := fix shr 2 - 3;
612 dx := fix and 3 shl 1 - 3 + (dy + 3) and 1;
613 Loc1 := dLoc(MyCity[cix].Loc, dx, dy);
614 if MarkCitiesAround(Loc1, cix) then begin
615 Done := False;
616 Break;
617 end;
618 end;
619 Server(sSetCityTiles, Me, cix, Advice.Tiles);
620 end;
621 end;
622 CityNeedsOptimize[cix] := False;
623 end;
624 until Done;
625end;
626
627procedure CityOptimizer_BeginOfTurn;
628var
629 cix: Integer;
630begin
631 FillChar(CityNeedsOptimize, MyRO.nCity - 1, 0); // false
632 if MyRO.Government <> gAnarchy then
633 begin
634 for cix := 0 to MyRO.nCity - 1 do
635 if (MyCity[cix].Loc >= 0) and (MyCity[cix].Flags and chCaptured = 0)
636 then
637 CityNeedsOptimize[cix] := True;
638 OptimizeCities(False); // optimize all cities
639 end;
640end;
641
642procedure CityOptimizer_CityChange(cix: Integer);
643begin
644 if (MyRO.Government <> gAnarchy) and (cix <> -1) and (MyCity[cix].Flags and
645 chCaptured = 0) then
646 begin
647 CityNeedsOptimize[cix] := True;
648 OptimizeCities(False);
649 end;
650end;
651
652procedure CityOptimizer_TileBecomesAvailable(Loc: Integer);
653begin
654 if (MyRO.Government <> gAnarchy) and MarkCitiesAround(Loc, -1) then
655 OptimizeCities(False);
656end;
657
658procedure CityOptimizer_ReleaseCityTiles(cix, ReleasedTiles: Integer);
659var
660 fix, dx, dy, Loc1: Integer;
661 Done: Boolean;
662begin
663 if (MyRO.Government <> gAnarchy) and (ReleasedTiles <> 0) then
664 begin
665 Done := True;
666 for fix := 1 to 26 do
667 if ReleasedTiles and (1 shl fix) <> 0 then
668 begin
669 dy := fix shr 2 - 3;
670 dx := fix and 3 shl 1 - 3 + (dy + 3) and 1;
671 Loc1 := dLoc(MyCity[cix].Loc, dx, dy);
672 if MarkCitiesAround(Loc1, cix) then begin
673 Done := False;
674 Break;
675 end;
676 end;
677 if not Done then
678 OptimizeCities(False);
679 end;
680end;
681
682procedure CityOptimizer_BeforeRemoveUnit(uix: Integer);
683var
684 uix1: Integer;
685begin
686 if MyRO.Government <> gAnarchy then
687 begin
688 if MyUn[uix].Home >= 0 then
689 CityNeedsOptimize[MyUn[uix].Home] := True;
690
691 // transported units are also removed
692 for uix1 := 0 to MyRO.nUn - 1 do
693 if (MyUn[uix1].Loc >= 0) and (MyUn[uix1].Master = uix) and
694 (MyUn[uix1].Home >= 0) then
695 CityNeedsOptimize[MyUn[uix1].Home] := True;
696 end;
697end;
698
699procedure CityOptimizer_AfterRemoveUnit;
700begin
701 if MyRO.Government <> gAnarchy then
702 OptimizeCities(False);
703end;
704
705procedure CityOptimizer_EndOfTurn;
706// all cities should already be optimized here -- only check this
707var
708 cix: Integer;
709begin
710{$IFOPT O-}
711 if MyRO.Government <> gAnarchy then
712 begin
713 FillChar(CityNeedsOptimize, MyRO.nCity - 1, 0); // false
714 for cix := 0 to MyRO.nCity - 1 do
715 if (MyCity[cix].Loc >= 0) and (MyCity[cix].Flags and chCaptured = 0)
716 then
717 CityNeedsOptimize[cix] := True;
718 OptimizeCities(True); // check all cities
719 end;
720{$ENDIF}
721end;
722
723function GetMyCityByLoc(Loc: Integer): PCity;
724var
725 I: Integer;
726begin
727 I := MyRO.nCity - 1;
728 while (I >= 0) and (MyCity[I].Loc <> Loc) do Dec(I);
729 if I >= 0 then Result := @MyCity[I]
730 else Result := nil;
731end;
732
733function GetEnemyCityByLoc(Loc: Integer): PCityInfo;
734var
735 I: Integer;
736begin
737 I := MyRO.nEnemyCity - 1;
738 while (I >= 0) and (MyRo.EnemyCity[I].Loc <> Loc) do Dec(I);
739 if I >= 0 then Result := @MyRo.EnemyCity[I]
740 else Result := nil;
741end;
742
743function GetMyUnitByLoc(Loc: Integer): PUn;
744var
745 I: Integer;
746begin
747 I := MyRO.nUn - 1;
748 while (I >= 0) and (MyUn[I].Loc <> Loc) do Dec(I);
749 if I >= 0 then Result := @MyUn[I]
750 else Result := nil;
751end;
752
753function GetEnemyUnitByLoc(Loc: Integer): PUnitInfo;
754var
755 I: Integer;
756begin
757 I := MyRO.nEnemyUn - 1;
758 while (I >= 0) and (MyRO.EnemyUn[I].Loc <> Loc) do Dec(I);
759 if I >= 0 then Result := @MyRO.EnemyUn[I]
760 else Result := nil;
761end;
762
763
764initialization
765
766Assert(nImp < 128);
767CalculateAdvValues;
768
769end.
Note: See TracBrowser for help on using the repository browser.