source: trunk/LocalPlayer/ClientTools.pas@ 328

Last change on this file since 328 was 328, checked in by chronos, 9 months ago
  • Modified: Use nWonder constant as number of wonders instead of numeric value.
  • Modified: Code cleanup.
File size: 21.9 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
181 Result := True;
182end;
183
184procedure ItsMeAgain(p: integer);
185begin
186 if G.RO[p] <> nil then
187 MyRO := pointer(G.RO[p])
188 else if G.SuperVisorRO[p] <> nil then
189 MyRO := pointer(G.SuperVisorRO[p])
190 else
191 exit;
192 me := p;
193 MyMap := pointer(MyRO.Map);
194 MyUn := pointer(MyRO.Un);
195 MyCity := pointer(MyRO.City);
196 MyModel := pointer(MyRO.Model);
197end;
198
199function GetAge(p: integer): integer;
200var
201 i: integer;
202begin
203 if p = me then
204 begin
205 Result := 0;
206 for i := 1 to 3 do
207 if MyRO.Tech[AgePreq[i]] >= tsApplicable then
208 Result := i;
209 end
210 else
211 begin
212 Result := 0;
213 for i := 1 to 3 do
214 if MyRO.EnemyReport[p].Tech[AgePreq[i]] >= tsApplicable then
215 Result := i;
216 end;
217end;
218
219function IsCivilReportNew(Enemy: integer): boolean;
220var
221 i: integer;
222begin
223 assert(Enemy <> me);
224 i := MyRO.EnemyReport[Enemy].TurnOfCivilReport;
225 Result := (i = MyRO.Turn) or (i = MyRO.Turn - 1) and (Enemy > me);
226end;
227
228function IsMilReportNew(Enemy: integer): boolean;
229var
230 i: integer;
231begin
232 assert(Enemy <> me);
233 i := MyRO.EnemyReport[Enemy].TurnOfMilReport;
234 Result := (i = MyRO.Turn) or (i = MyRO.Turn - 1) and (Enemy > me);
235end;
236
237function CutCityFoodSurplus(FoodSurplus: integer; IsCityAlive: boolean;
238 gov, size: integer): integer;
239begin
240 Result := FoodSurplus;
241 if not IsCityAlive or (Result > 0) and ((gov = gFuture) or
242 (size >= NeedAqueductSize) and (Result < 2)) then
243 Result := 0; { no growth }
244end;
245
246function CityTaxBalance(cix: integer; const CityReport: TCityReportNew): integer;
247var
248 i: integer;
249begin
250 Result := 0;
251 if (CityReport.HappinessBalance >= 0) { no disorder } and
252 (MyCity[cix].Flags and chCaptured = 0) then // not captured
253 begin
254 Inc(Result, CityReport.Tax);
255 if (MyCity[cix].Project and (cpImp + cpIndex) = cpImp + imTrGoods) and
256 (CityReport.Production > 0) then
257 Inc(Result, CityReport.Production);
258 if ((MyRO.Government = gFuture) or (MyCity[cix].size >=
259 NeedAqueductSize) and (CityReport.FoodSurplus < 2)) and
260 (CityReport.FoodSurplus > 0) then
261 Inc(Result, CityReport.FoodSurplus);
262 end;
263 for i := nWonder to nImp - 1 do
264 if MyCity[cix].Built[i] > 0 then
265 Dec(Result, Imp[i].Maint);
266end;
267
268procedure SumCities(var TaxSum, ScienceSum: integer);
269var
270 cix: integer;
271 CityReport: TCityReportNew;
272begin
273 TaxSum := MyRO.OracleIncome;
274 ScienceSum := 0;
275 if MyRO.Government = gAnarchy then
276 exit;
277 for cix := 0 to MyRO.nCity - 1 do
278 if MyCity[cix].Loc >= 0 then
279 begin
280 CityReport.HypoTiles := -1;
281 CityReport.HypoTaxRate := -1;
282 CityReport.HypoLuxuryRate := -1;
283 Server(sGetCityReportNew, me, cix, CityReport);
284 if (CityReport.HappinessBalance >= 0) { no disorder } and
285 (MyCity[cix].Flags and chCaptured = 0) then // not captured
286 ScienceSum := ScienceSum + CityReport.Science;
287 TaxSum := TaxSum + CityTaxBalance(cix, CityReport);
288 end;
289end;
290
291function JobTest(uix, Job: integer; IgnoreResults: JobResultSet): boolean;
292var
293 Test: integer;
294begin
295 Test := Server(sStartJob + Job shl 4 - sExecute, me, uix, nil^);
296 Result := (Test >= rExecuted) or (Test in IgnoreResults);
297end;
298
299procedure GetUnitInfo(Loc: integer; var uix: integer; var UnitInfo: TUnitInfo);
300var
301 i, Cnt: integer;
302begin
303 if MyMap[Loc] and fOwned <> 0 then
304 begin
305 Server(sGetDefender, me, Loc, uix);
306 Cnt := 0;
307 for i := 0 to MyRO.nUn - 1 do
308 if MyUn[i].Loc = Loc then
309 Inc(Cnt);
310 MakeUnitInfo(me, MyUn[uix], UnitInfo);
311 if Cnt > 1 then
312 UnitInfo.Flags := UnitInfo.Flags or unMulti;
313 end
314 else
315 begin
316 uix := MyRO.nEnemyUn - 1;
317 while (uix >= 0) and (MyRO.EnemyUn[uix].Loc <> Loc) do
318 Dec(uix);
319 UnitInfo := MyRO.EnemyUn[uix];
320 end;
321end; { GetUnitInfo }
322
323procedure GetCityInfo(Loc: integer; var cix: integer; var CityInfo: TCityInfo);
324begin
325 if MyMap[Loc] and fOwned <> 0 then
326 begin
327 CityInfo.Loc := Loc;
328 cix := MyRO.nCity - 1;
329 while (cix >= 0) and (MyCity[cix].Loc <> Loc) do
330 Dec(cix);
331 with CityInfo do
332 begin
333 Owner := me;
334 ID := MyCity[cix].ID;
335 size := MyCity[cix].size;
336 Flags := 0;
337 if MyCity[cix].Built[imPalace] > 0 then
338 Inc(Flags, ciCapital);
339 if (MyCity[cix].Built[imWalls] > 0) or
340 (MyMap[MyCity[cix].Loc] and fGrWall <> 0) then
341 Inc(Flags, ciWalled);
342 if MyCity[cix].Built[imCoastalFort] > 0 then
343 Inc(Flags, ciCoastalFort);
344 if MyCity[cix].Built[imMissileBat] > 0 then
345 Inc(Flags, ciMissileBat);
346 if MyCity[cix].Built[imBunker] > 0 then
347 Inc(Flags, ciBunker);
348 if MyCity[cix].Built[imSpacePort] > 0 then
349 Inc(Flags, ciSpacePort);
350 end;
351 end
352 else
353 begin
354 cix := MyRO.nEnemyCity - 1;
355 while (cix >= 0) and (MyRO.EnemyCity[cix].Loc <> Loc) do
356 Dec(cix);
357 CityInfo := MyRO.EnemyCity[cix];
358 end;
359end;
360
361function UnitExhausted(uix: integer): boolean;
362 // check if another move of this unit is still possible
363var
364 dx, dy: integer;
365begin
366 Result := True;
367 if (MyUn[uix].Movement > 0) or
368 (MyRO.Wonder[woShinkansen].EffectiveOwner = me) then
369 if (MyUn[uix].Movement >= 100) or
370 ((MyModel[MyUn[uix].mix].Kind = mkCaravan) and
371 (MyMap[MyUn[uix].Loc] and fCity <> 0)) then
372 Result := False
373 else
374 for dx := -2 to 2 do
375 for dy := -2 to 2 do
376 if abs(dx) + abs(dy) = 2 then
377 if Server(sMoveUnit - sExecute + dx and 7 shl 4 + dy and
378 7 shl 7, me, uix, nil^) >= rExecuted then
379 Result := False;
380end;
381
382function ModelHash(const ModelInfo: TModelInfo): integer;
383var
384 i, FeatureCode, Hash1, Hash2, Hash2r, d: cardinal;
385begin
386 with ModelInfo do
387 if Kind > mkEnemyDeveloped then
388 Result := integer($C0000000 + Speed div 50 + Kind shl 8)
389 else
390 begin
391 FeatureCode := 0;
392 for i := mcFirstNonCap to nFeature - 1 do
393 if 1 shl Domain and Feature[i].Domains <> 0 then
394 begin
395 FeatureCode := FeatureCode * 2;
396 if 1 shl (i - mcFirstNonCap) <> 0 then
397 Inc(FeatureCode);
398 end;
399 case Domain of
400 dGround:
401 begin
402 assert(FeatureCode < 1 shl 8);
403 assert(Attack < 5113);
404 assert(Defense < 2273);
405 assert(Cost < 1611);
406 Hash1 := (Attack * 2273 + Defense) * 9 + (Speed - 150) div 50;
407 Hash2 := FeatureCode * 1611 + Cost;
408 end;
409 dSea:
410 begin
411 assert(FeatureCode < 1 shl 9);
412 assert(Attack < 12193);
413 assert(Defense < 6097);
414 assert(Cost < 4381);
415 Hash1 := ((Attack * 6097 + Defense) * 5 +
416 (Speed - 350) div 100) * 2;
417 if Weight >= 6 then
418 Inc(Hash1);
419 Hash2 := ((TTrans * 17 + ATrans_Fuel) shl 9 + FeatureCode) *
420 4381 + Cost;
421 end;
422 dAir:
423 begin
424 assert(FeatureCode < 1 shl 5);
425 assert(Attack < 2407);
426 assert(Defense < 1605);
427 assert(Bombs < 4813);
428 assert(Cost < 2089);
429 Hash1 := (Attack * 1605 + Defense) shl 5 + FeatureCode;
430 Hash2 := ((Bombs * 7 + ATrans_Fuel) * 4 + TTrans) * 2089 + Cost;
431 end;
432 end;
433 Hash2r := 0;
434 for i := 0 to 7 do
435 begin
436 Hash2r := Hash2r * 13;
437 d := Hash2 div 13;
438 Inc(Hash2r, Hash2 - d * 13);
439 Hash2 := d;
440 end;
441 Result := integer(Domain shl 30 + Hash1 xor Hash2r);
442 end;
443end;
444
445function ProcessEnhancement(uix: integer; const Jobs: TEnhancementJobs): integer;
446 { return values:
447 eJobDone - all applicable jobs done
448 eOK - enhancement not complete
449 eDied - job done and died (thurst) }
450var
451 stage, NextJob, Tile: integer;
452 Done: set of jNone .. jPoll;
453begin
454 Done := [];
455 Tile := MyMap[MyUn[uix].Loc];
456 if Tile and fRoad <> 0 then
457 include(Done, jRoad);
458 if Tile and fRR <> 0 then
459 include(Done, jRR);
460 if (Tile and fTerImp = tiIrrigation) or (Tile and fTerImp = tiFarm) then
461 include(Done, jIrr);
462 if Tile and fTerImp = tiFarm then
463 include(Done, jFarm);
464 if Tile and fTerImp = tiMine then
465 include(Done, jMine);
466 if Tile and fPoll = 0 then
467 include(Done, jPoll);
468
469 if MyUn[uix].Job = jNone then
470 Result := eJobDone
471 else
472 Result := eOK;
473 while (Result <> eOK) and (Result <> eDied) do
474 begin
475 stage := -1;
476 repeat
477 if stage = -1 then
478 NextJob := jPoll
479 else
480 NextJob := Jobs[Tile and fTerrain, stage];
481 if (NextJob = jNone) or not (NextJob in Done) then
482 Break;
483 Inc(stage);
484 until stage = 5;
485 if (stage = 5) or (NextJob = jNone) then
486 begin
487 Result := eJobDone;
488 Break;
489 end; // tile enhancement complete
490 Result := Server(sStartJob + NextJob shl 4, me, uix, nil^);
491 include(Done, NextJob);
492 end;
493end;
494
495function AutoBuild(cix: integer; const ImpOrder: TImpOrder): boolean;
496var
497 i, NewProject: integer;
498begin
499 Result := False;
500 if (MyCity[cix].Project and (cpImp + cpIndex) = cpImp + imTrGoods) or
501 (MyCity[cix].Flags and chProduction <> 0) then
502 begin
503 i := 0;
504 repeat
505 while (ImpOrder[i] >= 0) and (MyCity[cix].Built[ImpOrder[i]] > 0) do
506 Inc(i);
507 if ImpOrder[i] < 0 then
508 Break;
509 assert(i < nImp);
510 NewProject := cpImp + ImpOrder[i];
511 if Server(sSetCityProject, me, cix, NewProject) >= rExecuted then
512 begin
513 Result := True;
514 CityOptimizer_CityChange(cix);
515 Break;
516 end;
517 Inc(i);
518 until False;
519 end;
520end;
521
522procedure CalculateAdvValues;
523var
524 i, j: integer;
525 known: array [0 .. nAdv - 1] of integer;
526
527 procedure MarkPreqs(i: integer);
528 begin
529 if known[i] = 0 then
530 begin
531 known[i] := 1;
532 if (i <> adScience) and (i <> adMassProduction) then
533 begin
534 if (AdvPreq[i, 0] >= 0) then
535 MarkPreqs(AdvPreq[i, 0]);
536 if (AdvPreq[i, 1] >= 0) then
537 MarkPreqs(AdvPreq[i, 1]);
538 end;
539 end;
540 end;
541
542begin
543 FillChar(AdvValue, SizeOf(AdvValue), 0);
544 for i := 0 to nAdv - 1 do
545 begin
546 FillChar(known, SizeOf(known), 0);
547 MarkPreqs(i);
548 for j := 0 to nAdv - 1 do
549 if known[j] > 0 then
550 Inc(AdvValue[i]);
551 if i in FutureTech then
552 Inc(AdvValue[i], 3000)
553 else if known[adMassProduction] > 0 then
554 Inc(AdvValue[i], 2000)
555 else if known[adScience] > 0 then
556 Inc(AdvValue[i], 1000);
557 end;
558end;
559
560procedure DebugMessage(Level: integer; Text: string);
561begin
562 Server(sMessage, me, Level, PChar(Text)^);
563end;
564
565function MarkCitiesAround(Loc, cixExcept: integer): boolean;
566 // return whether a city was marked
567var
568 cix: integer;
569begin
570 Result := False;
571 for cix := 0 to MyRO.nCity - 1 do
572 if (cix <> cixExcept) and (MyCity[cix].Loc >= 0) and
573 (MyCity[cix].Flags and chCaptured = 0) and
574 (Distance(MyCity[cix].Loc, Loc) <= 5) then
575 begin
576 CityNeedsOptimize[cix] := True;
577 Result := True;
578 end;
579end;
580
581procedure OptimizeCities(CheckOnly: boolean);
582var
583 cix, fix, dx, dy, Loc1, OptiType: integer;
584 Done: boolean;
585 Advice: TCityTileAdviceData;
586begin
587 repeat
588 Done := True;
589 for cix := 0 to MyRO.nCity - 1 do
590 if CityNeedsOptimize[cix] then
591 begin
592 OptiType := (MyCity[cix].Status shr 4) and $0F;
593 if OptiType <> 0 then
594 begin
595 Advice.ResourceWeights := OfferedResourceWeights[OptiType];
596 Server(sGetCityTileAdvice, me, cix, Advice);
597 if Advice.Tiles <> MyCity[cix].Tiles then
598 if CheckOnly then
599 begin
600 // TODO: What is this assert for?
601 // Need to optimize city tiles but CheckOnly true?
602 //assert(false)
603 end
604 else
605 begin
606 for fix := 1 to 26 do
607 if MyCity[cix].Tiles and not Advice.Tiles and
608 (1 shl fix) <> 0 then
609 begin // tile no longer used by this city -- check using it by another
610 dy := fix shr 2 - 3;
611 dx := fix and 3 shl 1 - 3 + (dy + 3) and 1;
612 Loc1 := dLoc(MyCity[cix].Loc, dx, dy);
613 if MarkCitiesAround(Loc1, cix) then
614 Done := False;
615 end;
616 Server(sSetCityTiles, me, cix, Advice.Tiles);
617 end;
618 end;
619 CityNeedsOptimize[cix] := False;
620 end;
621 until Done;
622end;
623
624procedure CityOptimizer_BeginOfTurn;
625var
626 cix: integer;
627begin
628 FillChar(CityNeedsOptimize, MyRO.nCity - 1, 0); // false
629 if MyRO.Government <> gAnarchy then
630 begin
631 for cix := 0 to MyRO.nCity - 1 do
632 if (MyCity[cix].Loc >= 0) and (MyCity[cix].Flags and chCaptured = 0)
633 then
634 CityNeedsOptimize[cix] := True;
635 OptimizeCities(False); // optimize all cities
636 end;
637end;
638
639procedure CityOptimizer_CityChange(cix: integer);
640begin
641 if (MyRO.Government <> gAnarchy) and (MyCity[cix].Flags and
642 chCaptured = 0) then
643 begin
644 CityNeedsOptimize[cix] := True;
645 OptimizeCities(False);
646 end;
647end;
648
649procedure CityOptimizer_TileBecomesAvailable(Loc: integer);
650begin
651 if (MyRO.Government <> gAnarchy) and MarkCitiesAround(Loc, -1) then
652 OptimizeCities(False);
653end;
654
655procedure CityOptimizer_ReleaseCityTiles(cix, ReleasedTiles: integer);
656var
657 fix, dx, dy, Loc1: integer;
658 Done: boolean;
659begin
660 if (MyRO.Government <> gAnarchy) and (ReleasedTiles <> 0) then
661 begin
662 Done := True;
663 for fix := 1 to 26 do
664 if ReleasedTiles and (1 shl fix) <> 0 then
665 begin
666 dy := fix shr 2 - 3;
667 dx := fix and 3 shl 1 - 3 + (dy + 3) and 1;
668 Loc1 := dLoc(MyCity[cix].Loc, dx, dy);
669 if MarkCitiesAround(Loc1, cix) then
670 Done := False;
671 end;
672 if not Done then
673 OptimizeCities(False);
674 end;
675end;
676
677procedure CityOptimizer_BeforeRemoveUnit(uix: integer);
678var
679 uix1: integer;
680begin
681 if MyRO.Government <> gAnarchy then
682 begin
683 if MyUn[uix].Home >= 0 then
684 CityNeedsOptimize[MyUn[uix].Home] := True;
685
686 // transported units are also removed
687 for uix1 := 0 to MyRO.nUn - 1 do
688 if (MyUn[uix1].Loc >= 0) and (MyUn[uix1].Master = uix) and
689 (MyUn[uix1].Home >= 0) then
690 CityNeedsOptimize[MyUn[uix1].Home] := True;
691 end;
692end;
693
694procedure CityOptimizer_AfterRemoveUnit;
695begin
696 if MyRO.Government <> gAnarchy then
697 OptimizeCities(False);
698end;
699
700procedure CityOptimizer_EndOfTurn;
701// all cities should already be optimized here -- only check this
702var
703 cix: integer;
704begin
705{$IFOPT O-}
706 if MyRO.Government <> gAnarchy then
707 begin
708 FillChar(CityNeedsOptimize, MyRO.nCity - 1, 0); // false
709 for cix := 0 to MyRO.nCity - 1 do
710 if (MyCity[cix].Loc >= 0) and (MyCity[cix].Flags and chCaptured = 0)
711 then
712 CityNeedsOptimize[cix] := True;
713 OptimizeCities(True); // check all cities
714 end;
715{$ENDIF}
716end;
717
718function GetMyCityByLoc(Loc: Integer): PCity;
719var
720 I: Integer;
721begin
722 I := MyRO.nCity - 1;
723 while (I >= 0) and (MyCity[I].Loc <> Loc) do Dec(I);
724 if I >= 0 then Result := @MyCity[I]
725 else Result := nil;
726end;
727
728function GetEnemyCityByLoc(Loc: Integer): PCityInfo;
729var
730 I: Integer;
731begin
732 I := MyRO.nEnemyCity - 1;
733 while (I >= 0) and (MyRo.EnemyCity[I].Loc <> Loc) do Dec(I);
734 if I >= 0 then Result := @MyRo.EnemyCity[I]
735 else Result := nil;
736end;
737
738function GetMyUnitByLoc(Loc: Integer): PUn;
739var
740 I: Integer;
741begin
742 I := MyRO.nUn - 1;
743 while (I >= 0) and (MyUn[I].Loc <> Loc) do Dec(I);
744 if I >= 0 then Result := @MyUn[I]
745 else Result := nil;
746end;
747
748function GetEnemyUnitByLoc(Loc: Integer): PUnitInfo;
749var
750 I: Integer;
751begin
752 I := MyRO.nEnemyUn - 1;
753 while (I >= 0) and (MyRO.EnemyUn[I].Loc <> Loc) do Dec(I);
754 if I >= 0 then Result := @MyRO.EnemyUn[I]
755 else Result := nil;
756end;
757
758
759initialization
760
761 Assert(nImp < 128);
762 CalculateAdvValues;
763
764end.
Note: See TracBrowser for help on using the repository browser.