source: trunk/CityProcessing.pas

Last change on this file was 550, checked in by chronos, 12 hours ago
  • Modified: Code cleanup.
File size: 56.2 KB
Line 
1{$INCLUDE Switches.inc}
2unit CityProcessing;
3
4interface
5
6uses
7 Protocol, Database;
8
9// Reporting
10procedure GetCityAreaInfo(P, Loc: Integer; var CityAreaInfo: TCityAreaInfo);
11function CanCityGrow(P, cix: Integer): Boolean;
12function GetCityReport(P, cix: Integer; var CityReport: TCityReport): Integer;
13function GetCityReportNew(P, cix: Integer;
14 var CityReportNew: TCityReportNew): Integer;
15
16// Internal Tile Picking
17function AddBestCityTile(P, cix: Integer): Boolean;
18procedure CityGrowth(P, cix: Integer);
19procedure CityShrink(P, cix: Integer);
20procedure Pollute(P, cix: Integer);
21
22// Turn Processing
23procedure PayCityMaintenance(P, cix: Integer);
24procedure CollectCityResources(P, cix: Integer);
25function CityTurn(P, cix: Integer): Boolean;
26
27// Tile Access
28function SetCityTiles(P, cix, NewTiles: Integer;
29 TestOnly: Boolean = False): Integer;
30procedure GetCityTileAdvice(P, cix: Integer; var Advice: TCityTileAdviceData);
31
32// Start/End Game
33procedure InitGame;
34procedure ReleaseGame;
35
36
37implementation
38
39type
40 TTradeProcessing = record
41 TaxBonus: Integer;
42 LuxBonus: Integer;
43 ScienceBonus: Integer;
44 FutResBonus: Integer;
45 ScienceDoubling: Integer;
46 HappyBase: Integer;
47 RelCorr: Single;
48 FlexibleLuxury: Boolean;
49 end;
50
51 TProdProcessing = record
52 ProdBonus: Integer;
53 PollBonus: Integer;
54 FutProdBonus: Integer;
55 PollThreshold: Integer;
56 end;
57
58 PCityReportEx = ^TCityReportEx;
59
60 TCityReportEx = record
61 BaseHappiness: Integer;
62 BaseControl: Integer;
63 Material: Integer;
64 ProdProcessing: TProdProcessing;
65 TradeProcessing: TTradeProcessing;
66 end;
67
68var
69 MaxDist: Integer;
70
71{
72 Reporting
73 ____________________________________________________________________
74}
75procedure GetCityAreaInfo(P, Loc: Integer; var CityAreaInfo: TCityAreaInfo);
76var
77 V21, Loc1, p1: Integer;
78 Radius: TVicinity21Loc;
79begin
80{$IFOPT O-}Assert(1 shl P and InvalidTreatyMap = 0); {$ENDIF}
81 with CityAreaInfo do
82 begin
83 V21_to_Loc(Loc, Radius);
84 for V21 := 0 to 26 do
85 begin
86 Loc1 := Radius[V21];
87 if (Loc1 < 0) or (Loc1 >= MapSize) then
88 Available[V21] := faInvalid
89 else
90 begin
91 p1 := RealMap[Loc1] shr 27;
92 if (p1 < nPl) and (p1 <> P) and (RW[P].Treaty[p1] >= trPeace) then
93 Available[V21] := faTreaty
94 else if (ZoCMap[Loc1] > 0) and (Occupant[Loc1] <> P) and
95 (RW[P].Treaty[Occupant[Loc1]] < trAlliance) then
96 Available[V21] := faSiege
97 else if (UsedByCity[Loc1] <> -1) and (UsedByCity[Loc1] <> Loc) then
98 Available[V21] := faNotAvailable
99 else
100 Available[V21] := faAvailable;
101 end;
102 end;
103 end;
104end;
105
106function CanCityGrow(P, cix: Integer): Boolean;
107begin
108 with RW[P].City[cix] do
109 Result := (Size < MaxCitySize) and
110 ((Size < NeedAqueductSize) or (Built[imAqueduct] = 1) and
111 (Size < NeedSewerSize) or (Built[imSewer] = 1));
112end;
113
114procedure DetermineCityProdProcessing(P, cix: Integer;
115 var ProdProcessing: TProdProcessing);
116begin
117 with RW[P].City[cix], ProdProcessing do
118 begin
119 ProdBonus := 0;
120 PollBonus := 0;
121 if Built[imFactory] = 1 then
122 Inc(ProdBonus);
123 if Built[imMfgPlant] = 1 then
124 Inc(ProdBonus);
125 if (Built[imPower] = 1) or (Built[imHydro] = 1) or (Built[imNuclear] = 1) or
126 (GWonder[woHoover].EffectiveOwner = P) then
127 ProdBonus := ProdBonus * 2;
128 if Built[imFactory] = 1 then
129 Inc(PollBonus);
130 if Built[imMfgPlant] = 1 then
131 Inc(PollBonus);
132 if (Built[imFactory] + Built[imMfgPlant] > 0) then
133 if (Built[imHydro] > 0) or (GWonder[woHoover].EffectiveOwner = P) then
134 Dec(PollBonus)
135 else if (Built[imNuclear] = 0) and (Built[imPower] = 1) then
136 Inc(PollBonus);
137 if (RW[P].Government <= gDespotism) or (Built[imRecycling] = 1) then
138 PollBonus := -2; // no pollution
139 PollThreshold := Size;
140 FutProdBonus := 0;
141 if RW[P].Tech[futProductionTechnology] > 0 then
142 begin // future tech benefits
143 if Built[imFactory] = 1 then
144 Inc(FutProdBonus, FactoryFutureBonus * RW[P].Tech
145 [futProductionTechnology]);
146 if Built[imMfgPlant] = 1 then
147 Inc(FutProdBonus, MfgPlantFutureBonus * RW[P].Tech
148 [futProductionTechnology]);
149 end;
150 end;
151end;
152
153procedure BoostProd(BaseProd: Integer; ProdProcessing: TProdProcessing;
154 var Prod, Poll: Integer);
155begin
156 Poll := BaseProd * (2 + ProdProcessing.PollBonus) shr 1;
157 if Poll <= ProdProcessing.PollThreshold then
158 Poll := 0
159 else
160 Dec(Poll, ProdProcessing.PollThreshold);
161 if ProdProcessing.FutProdBonus > 0 then
162 Prod := BaseProd * (100 + ProdProcessing.ProdBonus * 50 +
163 ProdProcessing.FutProdBonus) div 100
164 else
165 Prod := BaseProd * (2 + ProdProcessing.ProdBonus) shr 1;
166end;
167
168procedure DetermineCityTradeProcessing(P, cix, HappinessBeforeLux: Integer;
169 var TradeProcessing: TTradeProcessing);
170var
171 I, Dist: Integer;
172begin
173 with RW[P].City[cix], TradeProcessing do
174 begin
175 TaxBonus := 0;
176 ScienceBonus := 0;
177 if Built[imMarket] = 1 then
178 Inc(TaxBonus, 2);
179 if Built[imBank] = 1 then
180 begin
181 Inc(TaxBonus, 3);
182 if RW[P].NatBuilt[imStockEx] = 1 then
183 Inc(TaxBonus, 3);
184 end;
185 LuxBonus := TaxBonus;
186 if Built[imLibrary] = 1 then
187 Inc(ScienceBonus, 2);
188 if Built[imUniversity] = 1 then
189 Inc(ScienceBonus, 3);
190 if Built[imResLab] = 1 then
191 Inc(ScienceBonus, 3);
192 ScienceDoubling := 0;
193 if Built[imNatObs] > 0 then
194 Inc(ScienceDoubling);
195 if RW[P].Government = gFundamentalism then
196 Dec(ScienceDoubling)
197 else if (GWonder[woNewton].EffectiveOwner = P) and
198 (RW[P].Government = gMonarchy) then
199 Inc(ScienceDoubling);
200 FlexibleLuxury := ((ServerVersion[P] >= $0100F1) and
201 (GWonder[woLiberty].EffectiveOwner = P) or (ServerVersion[P] < $0100F1)
202 and (GWonder[woMich].EffectiveOwner = P)) and
203 (RW[P].Government <> gAnarchy);
204 FutResBonus := 0;
205 if RW[P].Tech[futResearchTechnology] > 0 then
206 begin // future tech benefits
207 if Built[imUniversity] = 1 then
208 Inc(FutResBonus, UniversityFutureBonus * RW[P].Tech
209 [futResearchTechnology]);
210 if Built[imResLab] = 1 then
211 Inc(FutResBonus, ResLabFutureBonus * RW[P].Tech[futResearchTechnology]);
212 end;
213 if (RW[P].NatBuilt[imPalace] > 0) or (ServerVersion[P] < $010000) then
214 begin // calculate corruption
215 Dist := MaxDist;
216 for I := 0 to RW[P].nCity - 1 do
217 if (RW[P].City[I].Loc >= 0) and (RW[P].City[I].Built[imPalace] = 1) then
218 Dist := Distance(Loc, RW[P].City[I].Loc);
219 if (Dist = 0) or (CorrLevel[RW[P].Government] = 0) then
220 RelCorr := 0.0
221 else
222 begin
223 RelCorr := Dist / MaxDist;
224 if CorrLevel[RW[P].Government] > 1 then
225 RelCorr := Exp(Ln(RelCorr) / CorrLevel[RW[P].Government]);
226 if Built[imCourt] = 1 then
227 RelCorr := RelCorr / 2;
228 // !!! floating point calculation always deterministic???
229 end
230 end
231 else if Built[imCourt] = 1 then
232 RelCorr := 0.5
233 else
234 RelCorr := 1.0;
235 HappyBase := Size + HappinessBeforeLux;
236 end;
237end;
238
239procedure SplitTrade(Trade, TaxRate, LuxRate, Working: Integer;
240 TradeProcessing: TTradeProcessing; var Corruption, Tax, Lux,
241 Science: Integer);
242var
243 plus: Integer;
244begin
245 Corruption := Trunc(Trade * TradeProcessing.RelCorr);
246 Tax := (TaxRate * (Trade - Corruption) + 50) div 100;
247 if TradeProcessing.FlexibleLuxury then
248 begin
249 plus := Working * 2 - TradeProcessing.HappyBase;
250 // required additional luxury
251 if plus > 0 then
252 begin
253 Lux := (4 * plus + 3 + TradeProcessing.LuxBonus)
254 div (4 + TradeProcessing.LuxBonus);
255 if Lux > Trade - Corruption then
256 Lux := Trade - Corruption;
257 if Tax > Trade - Corruption - Lux then
258 Tax := Trade - Corruption - Lux;
259 end
260 else
261 Lux := 0;
262 end
263 else if (LuxRate = 0) or (TaxRate = 100) then
264 Lux := 0
265 else
266 Lux := (LuxRate * (Trade - Corruption) + 49) div 100;
267 Science := Trade - Corruption - Lux - Tax;
268 Tax := Tax * (4 + TradeProcessing.TaxBonus) shr 2;
269 Lux := Lux * (4 + TradeProcessing.LuxBonus) shr 2;
270 if TradeProcessing.FutResBonus > 0 then
271 Science := Science * (100 + TradeProcessing.ScienceBonus * 25 +
272 TradeProcessing.FutResBonus) div 100
273 else
274 Science := Science * (4 + TradeProcessing.ScienceBonus) shr 2;
275 Science := Science shl 2 shr (2 - TradeProcessing.ScienceDoubling);
276end;
277
278function GetProjectCost(P, cix: Integer): Integer;
279var
280 I: Integer;
281begin
282 with RW[P].City[cix] do
283 begin
284 if Project and cpImp = 0 then
285 begin
286 Result := RW[P].Model[Project and cpIndex].Cost; { unit project }
287 if Project and cpConscripts <> 0 then
288 begin
289 I := RW[P].Model[Project and cpIndex].MCost;
290 Result := Result - 3 * I;
291 if Result <= 0 then
292 Result := I;
293 end
294 else if RW[P].Model[Project and cpIndex].Cap[mcLine] > 0 then
295 if Project0 and (not cpAuto or cpRepeat) = Project and not cpAuto or cpRepeat
296 then
297 Result := Result shr 1
298 else
299 Result := Result * 2;
300 end
301 else
302 begin { improvement project }
303 Result := Imp[Project and cpIndex].Cost;
304 if (Project and cpIndex < nWonder) and (GWonder[woColossus].EffectiveOwner = P)
305 then
306 Result := Result * ColossusEffect div 100;
307 end;
308 Result := Result * BuildCostMod[Difficulty[P]] div 12;
309 end;
310end;
311
312function GetSmallCityReport(P, cix: Integer; var CityReport: TCityReport;
313 PCityReportEx: PCityReportEx = nil): Integer;
314var
315 I, uix, V21, Loc1, ForcedSupport, BaseHappiness, Control: Integer;
316 ProdProcessing: TProdProcessing;
317 TradeProcessing: TTradeProcessing;
318 Radius: TVicinity21Loc;
319 UnitReport: TUnitReport;
320 RareOK: array [0 .. 3] of Integer;
321 TileInfo: TTileInfo;
322begin
323 with RW[P].City[cix], CityReport do
324 begin
325 if HypoTiles <= 0 then
326 HypoTiles := Tiles;
327 if HypoTax < 0 then
328 HypoTax := RW[P].TaxRate;
329 if HypoLux < 0 then
330 HypoLux := RW[P].LuxRate;
331
332 if (Flags and chCaptured <> 0) or (RW[P].Government = gAnarchy) then
333 begin
334 Working := 0;
335 for V21 := 1 to 26 do
336 if HypoTiles and (1 shl V21) <> 0 then
337 Inc(Working); // for backward compatibility
338
339 if RW[P].Government = gFundamentalism then
340 begin
341 Happy := Size;
342 Control := Size;
343 end // !!! old bug, kept for compatibility
344 else
345 begin
346 Happy := 0;
347 Control := 0;
348 end;
349
350 BaseHappiness := BasicHappy * 2;
351 Support := 0;
352 Deployed := 0;
353 Eaten := Size * 2;
354 FoodRep := Size * 2;
355 ProdRep := 0;
356 Trade := 0;
357 PollRep := 0;
358 Corruption := 0;
359 Tax := 0;
360 Lux := 0;
361 Science := 0;
362
363 if PCityReportEx <> nil then
364 begin
365 PCityReportEx.Material := ProdRep;
366 PCityReportEx.BaseHappiness := BaseHappiness;
367 PCityReportEx.BaseControl := Control;
368 end;
369 end
370 else // not captured, no anarchy
371 begin
372 Control := 0;
373 BaseHappiness := BasicHappy * 2;
374 Happy := BasicHappy;
375 if (Built[imColosseum] > 0) then
376 begin
377 if (Happy < (Size + 1) shr 1) then
378 Happy := (Size + 1) shr 1;
379 if Size > 4 then
380 BaseHappiness := Size;
381 end;
382 for I := 0 to nWonder - 1 do
383 if Built[I] = 1 then
384 begin
385 Inc(Happy);
386 Inc(BaseHappiness, 2);
387 end;
388 if Built[imTemple] = 1 then
389 begin
390 Inc(Happy);
391 Inc(BaseHappiness, 2);
392 end;
393 if Built[imCathedral] = 1 then
394 begin
395 Inc(Happy, 2);
396 Inc(BaseHappiness, 4);
397 if GWonder[woBach].EffectiveOwner = P then
398 begin
399 Inc(Happy);
400 Inc(BaseHappiness, 2);
401 end;
402 end;
403 if Built[imTheater] > 0 then
404 begin
405 Inc(Happy, 2);
406 Inc(BaseHappiness, 4);
407 end;
408
409 // calculate unit support
410{$IFOPT O-}Assert(InvalidTreatyMap = 0); {$ENDIF}
411 Support := 0;
412 ForcedSupport := 0;
413 Eaten := Size * 2;
414 Deployed := 0;
415 for uix := 0 to RW[P].nUn - 1 do
416 with RW[P].Un[uix] do
417 if (Loc >= 0) and (Home = cix) then
418 begin
419 GetUnitReport(P, uix, UnitReport);
420 Inc(Eaten, UnitReport.FoodSupport);
421 if UnitReport.ReportFlags and urfAlwaysSupport <> 0 then
422 Inc(ForcedSupport, UnitReport.ProdSupport)
423 else
424 Inc(Support, UnitReport.ProdSupport);
425 if UnitReport.ReportFlags and urfDeployed <> 0 then
426 Inc(Deployed);
427 end;
428 if Deployed >= Happy then
429 Happy := 0
430 else
431 Dec(Happy, Deployed);
432 Dec(Support, Size * SupportFree[RW[P].Government] shr 1);
433 if Support < 0 then
434 Support := 0;
435 Inc(Support, ForcedSupport);
436
437 { control }
438 case RW[P].Government of
439 gDespotism:
440 for uix := 0 to RW[P].nUn - 1 do
441 if (RW[P].Un[uix].Loc = Loc) and
442 (RW[P].Model[RW[P].Un[uix].mix].Kind = mkSpecial_TownGuard) then
443 begin
444 Inc(Happy);
445 Inc(Control, 2);
446 end;
447 gFundamentalism:
448 begin
449 BaseHappiness := 0; // done by control
450 Happy := Size;
451 Control := Size;
452 end;
453 end;
454
455 // collect processing parameters
456 DetermineCityProdProcessing(P, cix, ProdProcessing);
457 DetermineCityTradeProcessing(P, cix, BaseHappiness + Control - 2 *
458 Deployed, TradeProcessing);
459
460 // collect resources
461 Working := 0;
462 FoodRep := 0;
463 ProdRep := 0;
464 Trade := 0;
465 FillChar(RareOK, SizeOf(RareOK), 0);
466 V21_to_Loc(Loc, Radius);
467 for V21 := 1 to 26 do
468 if HypoTiles and (1 shl V21) <> 0 then
469 begin { sum resources of exploited tiles }
470 Loc1 := Radius[V21];
471 if (Loc1 < 0) or (Loc1 >= MapSize) then
472 // HypoTiles go beyond map border!
473 begin
474 Result := eInvalid;
475 Exit;
476 end;
477 GetTileInfo(P, cix, Loc1, TileInfo);
478 Inc(FoodRep, TileInfo.Food);
479 Inc(ProdRep, TileInfo.Prod);
480 Inc(Trade, TileInfo.Trade);
481 if (RealMap[Loc1] and fModern <> 0) and
482 (RW[P].Tech[adMassProduction] >= tsApplicable) then
483 Inc(RareOK[RealMap[Loc1] shr 25 and 3]);
484 Inc(Working);
485 end;
486 if Built[imAlgae] = 1 then
487 Inc(FoodRep, 12);
488
489 if PCityReportEx <> nil then
490 begin
491 PCityReportEx.Material := ProdRep;
492 PCityReportEx.BaseHappiness := BaseHappiness;
493 PCityReportEx.BaseControl := Control;
494 PCityReportEx.ProdProcessing := ProdProcessing;
495 PCityReportEx.TradeProcessing := TradeProcessing;
496 end;
497
498 BoostProd(ProdRep, ProdProcessing, ProdRep, PollRep);
499 SplitTrade(Trade, HypoTax, HypoLux, Working, TradeProcessing, Corruption,
500 Tax, Lux, Science);
501 Happy := Happy + (Lux + Size and 1) shr 1;
502 // new style disorder requires 1 lux less for cities with odd size
503
504 // check if rare resource available
505 if (GTestFlags and tfNoRareNeed = 0) and (ProdRep > Support) and
506 (Project and cpImp <> 0) and ((Project and cpIndex = imShipComp) and
507 (RareOK[1] = 0) or (Project and cpIndex = imShipPow) and (RareOK[2] = 0)
508 or (Project and cpIndex = imShipHab) and (RareOK[3] = 0)) then
509 ProdRep := Support;
510 end;
511 end;
512 Result := eOk;
513end;
514
515function GetCityReport(P, cix: Integer; var CityReport: TCityReport): Integer;
516begin
517 Result := GetSmallCityReport(P, cix, CityReport);
518 CityReport.Storage := StorageSize[Difficulty[P]];
519 CityReport.ProdCost := GetProjectCost(P, cix);
520end;
521
522function GetCityReportNew(P, cix: Integer;
523 var CityReportNew: TCityReportNew): Integer;
524var
525 CityReport: TCityReport;
526 CityReportEx: TCityReportEx;
527begin
528 with CityReportNew do
529 begin
530 CityReport.HypoTiles := HypoTiles;
531 CityReport.HypoTax := HypoTaxRate;
532 CityReport.HypoLux := HypoLuxuryRate;
533 Result := GetSmallCityReport(P, cix, CityReport, @CityReportEx);
534 FoodSupport := CityReport.Eaten - 2 * RW[P].City[cix].Size;
535 MaterialSupport := CityReport.Support;
536 ProjectCost := GetProjectCost(P, cix);
537 Storage := StorageSize[Difficulty[P]];
538 Deployed := CityReport.Deployed;
539 Morale := CityReportEx.BaseHappiness;
540 CollectedControl := CityReportEx.BaseControl +
541 (RW[P].City[cix].Size - CityReport.Working) * 2;
542 CollectedFood := CityReport.FoodRep;
543 CollectedMaterial := CityReportEx.Material;
544 CollectedTrade := CityReport.Trade;
545 Working := CityReport.Working;
546 Production := CityReport.ProdRep - CityReport.Support;
547 AddPollution := CityReport.PollRep;
548 Corruption := CityReport.Corruption;
549 Tax := CityReport.Tax;
550 Science := CityReport.Science;
551 Luxury := CityReport.Lux;
552 FoodSurplus := CityReport.FoodRep - CityReport.Eaten;
553 HappinessBalance := Morale + Luxury + CollectedControl - RW[P].City[cix]
554 .Size - 2 * Deployed;
555 end;
556end;
557
558{
559 Internal Tile Picking
560 ____________________________________________________________________
561}
562procedure NextBest(P, cix: Integer; var SelectedLoc, SelectedV21: Integer);
563{ best tile unused but available by city cix }
564var
565 Resources, Most, Loc1, p1, V21: Integer;
566 TileInfo: TTileInfo;
567 Radius: TVicinity21Loc;
568begin
569{$IFOPT O-}Assert(1 shl P and InvalidTreatyMap = 0); {$ENDIF}
570 Most := 0;
571 SelectedLoc := -1;
572 SelectedV21 := -1;
573 with RW[P].City[cix] do
574 begin
575 V21_to_Loc(Loc, Radius);
576 for V21 := 1 to 26 do
577 begin
578 Loc1 := Radius[V21];
579 if (Loc1 >= 0) and (Loc1 < MapSize) and (UsedByCity[Loc1] = -1) then
580 begin
581 p1 := RealMap[Loc1] shr 27;
582 if ((p1 = nPl) or (p1 = P) or (RW[P].Treaty[p1] < trPeace)) and
583 ((ZoCMap[Loc1] = 0) or (Occupant[Loc1] = P) or
584 (RW[P].Treaty[Occupant[Loc1]] = trAlliance)) then
585 begin
586 GetTileInfo(P, cix, Loc1, TileInfo);
587 Resources := TileInfo.Food shl 16 + TileInfo.Prod shl 8 +
588 TileInfo.Trade;
589 { priority: 1.food - 2.prod - 3.trade }
590 if Resources > Most then
591 begin
592 SelectedLoc := Loc1;
593 SelectedV21 := V21;
594 Most := Resources;
595 end;
596 end;
597 end;
598 end;
599 end;
600end;
601
602procedure NextWorst(P, cix: Integer; var SelectedLoc, SelectedV21: Integer);
603{ worst tile used by city cix }
604var
605 Resources, Least, Loc1, V21: Integer;
606 Radius: TVicinity21Loc;
607 TileInfo: TTileInfo;
608begin
609 Least := MaxInt;
610 SelectedLoc := -1;
611 SelectedV21 := -1;
612 with RW[P].City[cix] do
613 begin
614 V21_to_Loc(Loc, Radius);
615 for V21 := 1 to 26 do
616 if V21 <> CityOwnTile then
617 begin
618 Loc1 := Radius[V21];
619 if (Loc1 >= 0) and (Loc1 < MapSize) and (1 shl V21 and Tiles <> 0) then
620 begin
621 GetTileInfo(P, cix, Loc1, TileInfo);
622 Resources := TileInfo.Food shl 16 + TileInfo.Prod shl 8 +
623 TileInfo.Trade;
624 { priority: 1.food - 2.prod - 3.trade }
625 if Resources < Least then
626 begin
627 SelectedLoc := Loc1;
628 SelectedV21 := V21;
629 Least := Resources;
630 end;
631 end;
632 end;
633 end;
634end;
635
636function NextPoll(P, cix: Integer): Integer;
637var
638 Resources, Best, dx, dy, Loc1, Dist, BestDist, V21, pTerr: Integer;
639 Radius: TVicinity21Loc;
640 TileInfo: TTileInfo;
641begin
642 BestDist := MaxInt;
643{$IFOPT O-}Assert(1 shl P and InvalidTreatyMap = 0); {$ENDIF}
644 Best := 0;
645 Result := -1;
646 with RW[P].City[cix] do
647 begin
648 V21_to_Loc(Loc, Radius);
649 for V21 := 1 to 26 do
650 if V21 <> CityOwnTile then
651 begin
652 Loc1 := Radius[V21];
653 if (Loc1 >= 0) and (Loc1 < MapSize) and
654 (RealMap[Loc1] and fTerrain >= fGrass) and
655 (RealMap[Loc1] and (fPoll or fDeadLands or fCity) = 0) then
656 begin
657 pTerr := RealMap[Loc1] shr 27;
658 if (pTerr = nPl) or (pTerr = P) or (RW[P].Treaty[pTerr] < trPeace)
659 then
660 begin
661 GetTileInfo(P, cix, Loc1, TileInfo);
662 Resources := TileInfo.Prod shl 16 + TileInfo.Trade shl 8 +
663 TileInfo.Food;
664 { priority: 1.prod - 2.trade - 3.food }
665 dy := V21 shr 2 - 3;
666 dx := V21 and 3 shl 1 - 3 + (dy + 3) and 1;
667 Dist := Abs(dx) + Abs(dy) + Abs(Abs(dx) - Abs(dy)) shr 1;
668 if (Resources > Best) or (Resources = Best) and (Dist < BestDist)
669 then
670 begin
671 Result := Loc1;
672 Best := Resources;
673 BestDist := Dist;
674 end;
675 end;
676 end;
677 end;
678 end;
679end;
680
681function AddBestCityTile(P, cix: Integer): Boolean;
682var
683 TileLoc, V21: Integer;
684begin
685 NextBest(P, cix, TileLoc, V21);
686 Result := TileLoc >= 0;
687 if Result then
688 with RW[P].City[cix] do
689 begin
690 Assert(1 shl V21 and Tiles = 0);
691 Tiles := Tiles or (1 shl V21);
692 UsedByCity[TileLoc] := Loc;
693 end;
694end;
695
696procedure CityGrowth(P, cix: Integer);
697var
698 TileLoc, V21: Integer;
699 AltCityReport: TCityReport;
700begin
701 with RW[P].City[cix] do
702 begin
703 Inc(Size);
704 NextBest(P, cix, TileLoc, V21);
705 if TileLoc >= 0 then
706 begin { test whether exploitation of tile would lead to disorder }
707 AltCityReport.HypoTiles := Tiles + 1 shl V21;
708 AltCityReport.HypoTax := -1;
709 AltCityReport.HypoLux := -1;
710 GetSmallCityReport(P, cix, AltCityReport);
711 if AltCityReport.Working - AltCityReport.Happy <= Size shr 1 then
712 // !!! change to new style disorder
713 begin { no disorder -- exploit tile }
714 Assert(1 shl V21 and Tiles = 0);
715 Tiles := Tiles or (1 shl V21);
716 UsedByCity[TileLoc] := Loc;
717 end;
718 end;
719 end;
720end;
721
722procedure CityShrink(P, cix: Integer);
723var
724 TileLoc, V21, Working: Integer;
725 AltCityReport: TCityReport;
726begin
727 with RW[P].City[cix] do
728 begin
729 Working := 0;
730 for V21 := 1 to 26 do
731 if Tiles and (1 shl V21) <> 0 then
732 Inc(Working);
733 Dec(Size);
734 if Food > StorageSize[Difficulty[P]] then
735 Food := StorageSize[Difficulty[P]];
736 NextWorst(P, cix, TileLoc, V21);
737 if Working > Size then
738 begin { all citizens were working -- worst tile no longer exploited }
739 Assert(1 shl V21 and Tiles <> 0);
740 Tiles := Tiles and not(1 shl V21);
741 UsedByCity[TileLoc] := -1;
742 end
743 else { test whether exploitation of tile would lead to disorder }
744 begin
745 AltCityReport.HypoTiles := -1;
746 AltCityReport.HypoTax := -1;
747 AltCityReport.HypoLux := -1;
748 GetSmallCityReport(P, cix, AltCityReport);
749 if AltCityReport.Working - AltCityReport.Happy > Size shr 1 then
750 // !!! change to new style disorder
751 begin { disorder -- don't exploit tile }
752 Assert(1 shl V21 and Tiles <> 0);
753 Tiles := Tiles and not(1 shl V21);
754 UsedByCity[TileLoc] := -1;
755 end;
756 end;
757 end;
758end;
759
760procedure Pollute(P, cix: Integer);
761var
762 PollutionLoc: Integer;
763begin
764 with RW[P].City[cix] do
765 begin
766 Pollution := Pollution - MaxPollution;
767 PollutionLoc := NextPoll(P, cix);
768 if PollutionLoc >= 0 then
769 begin
770 Inc(Flags, chPollution);
771 RealMap[PollutionLoc] := RealMap[PollutionLoc] or fPoll;
772 end;
773 end;
774end;
775
776{
777 Turn Processing
778 ____________________________________________________________________
779}
780procedure PayCityMaintenance(P, cix: Integer);
781var
782 I: Integer;
783begin
784 with RW[P], City[cix] do
785 for I := nWonder to nImp - 1 do
786 if (Built[I] > 0) and (Project0 and (cpImp or cpIndex) <> (cpImp or I))
787 then // don't pay maintenance when just completed
788 begin
789 Dec(Money, Imp[I].Maint);
790 if Money < 0 then
791 begin { out of money - sell improvement }
792 Inc(Money, Imp[I].Cost * BuildCostMod[Difficulty[P]] div 12);
793 Built[I] := 0;
794 if Imp[I].Kind <> ikCommon then
795 begin
796 Assert(I <> imSpacePort);
797 // never sell automatically! (solution: no maintenance)
798 NatBuilt[I] := 0;
799 if I = imGrWall then
800 GrWallContinent[P] := -1;
801 end;
802 Inc(Flags, chImprovementLost);
803 end;
804 end;
805end;
806
807procedure CollectCityResources(P, cix: Integer);
808var
809 CityStorage, CityProjectCost: Integer;
810 CityReport: TCityReportNew;
811 Disorder: Boolean;
812begin
813 with RW[P], City[cix], CityReport do
814 if Flags and chCaptured <> 0 then
815 begin
816 Flags := Flags and not chDisorder;
817 Dec(Flags, $10000);
818 if Flags and chCaptured = 0 then
819 Flags := Flags or chAfterCapture;
820 end
821 else if Government = gAnarchy then
822 Flags := Flags and not chDisorder
823 else
824 begin
825 HypoTiles := -1;
826 HypoTaxRate := -1;
827 HypoLuxuryRate := -1;
828 GetCityReportNew(P, cix, CityReport);
829 CityStorage := StorageSize[Difficulty[P]];
830 CityProjectCost := GetProjectCost(P, cix);
831
832 Disorder := (HappinessBalance < 0);
833 if Disorder and (Flags and chDisorder <> 0) then
834 CollectedMaterial := 0; // second turn disorder
835 if Disorder then
836 Flags := Flags or chDisorder
837 else
838 Flags := Flags and not chDisorder;
839
840 if not Disorder and ((Government = gFuture) or (Size >= NeedAqueductSize)
841 and (FoodSurplus < 2)) and (FoodSurplus > 0) then
842 Inc(Money, FoodSurplus)
843 else if not(Disorder and (FoodSurplus > 0)) then
844 begin { calculate new food storage }
845 Food := Food + FoodSurplus;
846 if ((GTestFlags and tfImmGrow <> 0) or (Food >= CityStorage) and
847 (Food - FoodSurplus < CityStorage)) // only warn once
848 and (Size < MaxCitySize) and
849 (Project and (cpImp + cpIndex) <> cpImp + imAqueduct) and
850 (Project and (cpImp + cpIndex) <> cpImp + imSewer) and
851 not CanCityGrow(P, cix) then
852 Inc(Flags, chNoGrowthWarning);
853 end;
854
855 if Prod > CityProjectCost then
856 begin
857 Inc(Money, Prod - CityProjectCost);
858 Prod := CityProjectCost;
859 end;
860 if Production < 0 then
861 Flags := Flags or chUnitLost
862 else if not Disorder and (Flags and chProductionSabotaged = 0) then
863 if Project and (cpImp + cpIndex) = cpImp + imTrGoods then
864 Inc(Money, Production)
865 else
866 Inc(Prod, Production);
867
868 if not Disorder then
869 begin
870 { sum research points and taxes }
871 Inc(Research, Science);
872 Inc(Money, Tax);
873 Pollution := Pollution + AddPollution;
874 end;
875 end;
876end;
877
878function CityTurn(P, cix: Integer): Boolean;
879// return value: whether city keeps existing
880var
881 I, uix, cix2, p1, SizeMod, CityStorage, CityProjectCost, NewImp, Det,
882 TestDet: Integer;
883 LackOfMaterial, CheckGrow, DoProd, IsActive: Boolean;
884begin
885 with RW[P], City[cix] do
886 begin
887 SizeMod := 0;
888 CityStorage := StorageSize[Difficulty[P]];
889 CityProjectCost := GetProjectCost(P, cix);
890
891 LackOfMaterial := Flags and chUnitLost <> 0;
892 Flags := Flags and not chUnitLost;
893
894 IsActive := (Government <> gAnarchy) and (Flags and chCaptured = 0);
895 CheckGrow := (Flags and chDisorder = 0) and IsActive and
896 (Government <> gFuture);
897 if CheckGrow and (GTestFlags and tfImmGrow <> 0) then { fast growth }
898 begin
899 if CanCityGrow(P, cix) then
900 Inc(SizeMod);
901 end
902 else if CheckGrow and (Food >= CityStorage) then { normal growth }
903 begin
904 if CanCityGrow(P, cix) then
905 begin
906 if Built[imGranary] = 1 then
907 Dec(Food, CityStorage shr 1)
908 else
909 Dec(Food, CityStorage);
910 Inc(SizeMod);
911 end;
912 end
913 else if Food < 0 then { famine }
914 begin
915 Food := 0;
916 // check if settlers or conscripts there to disband
917 uix := -1;
918 for I := 0 to nUn - 1 do
919 if (Un[I].Loc >= 0) and (Un[I].Home = cix) and
920 ((Model[Un[I].mix].Kind = mkSettler)
921 { and (GWonder[woFreeSettlers].EffectiveOwner<>p) }
922 or (Un[I].Flags and unConscripts <> 0)) and
923 ((uix = -1) or (Model[Un[I].mix].Cost < Model[Un[uix].mix].Cost) or
924 (Model[Un[I].mix].Cost = Model[Un[uix].mix].Cost) and
925 (Un[I].Exp < Un[uix].Exp)) then
926 uix := I;
927
928 if uix >= 0 then
929 begin
930 RemoveUnit_UpdateMap(P, uix);
931 Inc(Flags, chUnitLost);
932 end
933 else
934 begin
935 Dec(SizeMod);
936 Inc(Flags, chPopDecrease);
937 end
938 end;
939 if Food > CityStorage then
940 Food := CityStorage;
941
942 if LackOfMaterial then
943 begin
944 if Flags and chUnitLost = 0 then
945 begin { one unit lost }
946 uix := -1;
947 Det := MaxInt;
948 for I := 0 to nUn - 1 do
949 if (Un[I].Loc >= 0) and (Un[I].Home = cix) then
950 with Model[Un[I].mix] do
951 begin
952 if Kind = mkSpecial_TownGuard then
953 TestDet := Un[I].Health + Un[I].Exp shl 8
954 // disband townguards first
955 else
956 begin
957 TestDet := Un[I].Health + Un[I].Exp shl 8 + Cost shl 16;
958 // value of unit
959 if Flags and mdDoubleSupport <> 0 then
960 TestDet := TestDet shr 1;
961 // double support, tend to disband first
962 end;
963 if TestDet < Det then
964 begin
965 uix := I;
966 Det := TestDet;
967 end;
968 end;
969 if uix >= 0 then
970 begin
971 RemoveUnit_UpdateMap(P, uix);
972 Inc(Flags, chUnitLost);
973 end;
974 end;
975 end;
976
977 if GTestFlags and tfImmImprove <> 0 then
978 Prod := CityProjectCost;
979 DoProd := (Project and (cpImp + cpIndex) <> cpImp + imTrGoods) and
980 (Prod >= CityProjectCost);
981
982 // check if wonder already built
983 if (Project and cpImp <> 0) and (Project and cpIndex < nWonder) and
984 (GWonder[Project and cpIndex].CityID <> WonderNotBuiltYet) then
985 begin
986 Inc(Flags, chOldWonder);
987 DoProd := False;
988 end;
989
990 // check if producing settlers would disband city
991 if DoProd and (Project and (cpImp or cpDisbandCity) = 0) and
992 ((Size + SizeMod - 2 < 2) and
993 (Model[Project and cpIndex].Kind = mkSettler) or (Size + SizeMod - 1 < 2)
994 and ((Model[Project and cpIndex].Kind = mkSlaves) or
995 (Project and cpConscripts <> 0))) then
996 begin
997 Inc(Flags, chNoSettlerProd);
998 DoProd := False;
999 end;
1000
1001 if DoProd then
1002 begin { project complete }
1003 Dec(Prod, CityProjectCost);
1004 if Project and cpImp = 0 then { produce unit }
1005 begin
1006 if nUn < numax then
1007 begin
1008 CreateUnit(P, Project and cpIndex);
1009 Un[nUn - 1].Loc := Loc;
1010 with Un[nUn - 1] do
1011 begin
1012 Home := cix;
1013 if (Model[mix].Domain < dSea) and (Built[imElite] = 1) then
1014 Exp := ExpCost * (nExp - 1) { elite }
1015 else if (Model[mix].Domain < dSea) and (Built[imBarracks] = 1) or
1016 (Model[mix].Domain = dSea) and (Built[imDockyard] = 1) or
1017 (Model[mix].Domain = dAir) and (Built[imAirport] = 1) then
1018 Exp := ExpCost * 2; { vet }
1019 if Project and cpConscripts <> 0 then
1020 Flags := Flags or unConscripts;
1021 end;
1022 PlaceUnit(P, nUn - 1);
1023 UpdateUnitMap(Loc);
1024 if Model[Project and cpIndex].Kind = mkSettler then
1025 Dec(SizeMod, 2) { settler produced - city shrink }
1026 else if (Model[Project and cpIndex].Kind = mkSlaves) or
1027 (Project and cpConscripts <> 0) then
1028 Dec(SizeMod); { slaves/conscripts produced - city shrink }
1029 end;
1030 Project0 := Project or cpRepeat or cpCompleted;
1031 end
1032 else if Imp[Project and cpIndex].Kind = ikShipPart then
1033 begin { produce ship parts }
1034 Inc(GShip[P].Parts[Project and cpIndex - imShipComp]);
1035 Project0 := Project or cpCompleted;
1036 end
1037 else { produce improvement }
1038 begin
1039 NewImp := Project and cpIndex;
1040 Inc(Money, Prod); { change rest to money }
1041 Project0 := Project or cpCompleted;
1042 Project := cpImp + imTrGoods;
1043 Prod := 0;
1044
1045 if Imp[NewImp].Kind in [ikNatLocal, ikNatGlobal] then
1046 begin // nat. project
1047 for I := 0 to nCity - 1 do
1048 if (City[I].Loc >= 0) and (City[I].Built[NewImp] = 1) then
1049 begin { allowed only once }
1050 Inc(Money, Imp[NewImp].Cost * BuildCostMod[Difficulty[P]] div 12);
1051 City[I].Built[NewImp] := 0;
1052 end;
1053 NatBuilt[NewImp] := 1;
1054
1055 // immediate nat. project effects
1056 case NewImp of
1057 imGrWall:
1058 GrWallContinent[P] := Continent[Loc];
1059 end;
1060 end;
1061
1062 if NewImp < nWonder then
1063 begin // wonder
1064 GWonder[NewImp].CityID := ID;
1065 GWonder[NewImp].EffectiveOwner := P;
1066 CheckExpiration(NewImp);
1067
1068 // immediate wonder effects
1069 case NewImp of
1070 woEiffel:
1071 begin // reactivate wonders
1072 for I := 0 to nWonder - 1 do
1073 if Imp[I].Expiration >= 0 then
1074 for cix2 := 0 to nCity - 1 do
1075 if (City[cix2].Loc >= 0) and (City[cix2].Built[I] = 1)
1076 then
1077 GWonder[I].EffectiveOwner := P;
1078 end;
1079 woLighthouse:
1080 CheckSpecialModels(P, preLighthouse);
1081 woLeo:
1082 begin
1083 Inc(Research, TechBaseCost(nTech[P], Difficulty[P]) +
1084 TechBaseCost(nTech[P] + 2, Difficulty[P]));
1085 CheckSpecialModels(P, preLeo);
1086 end;
1087 woPyramids:
1088 CheckSpecialModels(P, preBuilder);
1089 woMir:
1090 begin
1091 for p1 := 0 to nPl - 1 do
1092 if (p1 <> P) and (1 shl p1 and GAlive <> 0) then
1093 begin
1094 if RW[P].Treaty[p1] = trNoContact then
1095 IntroduceEnemy(P, p1);
1096 GiveCivilReport(P, p1);
1097 GiveMilReport(P, p1);
1098 end;
1099 end;
1100 end;
1101 end;
1102
1103 for I := 0 to nImpReplacement - 1 do // sell obsolete buildings
1104 if (ImpReplacement[I].NewImp = NewImp) and
1105 (Built[ImpReplacement[I].OldImp] > 0) then
1106 begin
1107 Inc(RW[P].Money, Imp[ImpReplacement[I].OldImp].Cost * BuildCostMod
1108 [Difficulty[P]] div 12);
1109 Built[ImpReplacement[I].OldImp] := 0;
1110 end;
1111
1112 if NewImp in [imPower, imHydro, imNuclear] then
1113 for I := 0 to nImp - 1 do
1114 if (I <> NewImp) and (I in [imPower, imHydro, imNuclear]) and
1115 (Built[I] > 0) then
1116 begin // sell obsolete power plant
1117 Inc(RW[P].Money, Imp[I].Cost * BuildCostMod[Difficulty[P]
1118 ] div 12);
1119 Built[I] := 0;
1120 end;
1121
1122 Built[NewImp] := 1;
1123 end;
1124 Prod0 := Prod;
1125 Inc(Flags, chProduction);
1126 end
1127 else
1128 begin
1129 Project0 := Project0 and not cpCompleted;
1130 if Project0 and not cpAuto <> Project and not cpAuto then
1131 Project0 := Project;
1132 Prod0 := Prod;
1133 end;
1134
1135 if SizeMod > 0 then
1136 begin
1137 CityGrowth(P, cix);
1138 Inc(Flags, chPopIncrease);
1139 end;
1140 Result := Size + SizeMod >= 2;
1141 if Result then
1142 while SizeMod < 0 do
1143 begin
1144 CityShrink(P, cix);
1145 Inc(SizeMod);
1146 end;
1147 end;
1148end;
1149
1150{
1151 Tile Access
1152 ____________________________________________________________________
1153}
1154function SetCityTiles(P, cix, NewTiles: Integer;
1155 TestOnly: Boolean = False): Integer;
1156var
1157 V21, Working, ChangeTiles, AddTiles, Loc1: Integer;
1158 CityAreaInfo: TCityAreaInfo;
1159 Radius: TVicinity21Loc;
1160begin
1161 with RW[P].City[cix] do
1162 begin
1163 ChangeTiles := NewTiles xor Integer(Tiles);
1164 AddTiles := NewTiles and not Tiles;
1165 if Mode = moPlaying then
1166 begin // do all checks
1167 if NewTiles and not $67F7F76 <> 0 then
1168 begin
1169 Result := eInvalid;
1170 Exit
1171 end; // invalid tile index included
1172 if NewTiles and (1 shl 13) = 0 then
1173 begin
1174 Result := eViolation;
1175 Exit
1176 end; // city tile must be exploited
1177 if ChangeTiles = 0 then
1178 begin
1179 Result := eNotChanged;
1180 Exit
1181 end;
1182 if AddTiles <> 0 then
1183 begin
1184 // check if new tiles possible
1185 GetCityAreaInfo(P, Loc, CityAreaInfo);
1186 for V21 := 1 to 26 do
1187 if AddTiles and (1 shl V21) <> 0 then
1188 if CityAreaInfo.Available[V21] <> faAvailable then
1189 begin
1190 Result := eTileNotAvailable;
1191 Exit;
1192 end;
1193 // not more tiles than inhabitants
1194 Working := 0;
1195 for V21 := 1 to 26 do
1196 if NewTiles and (1 shl V21) <> 0 then
1197 Inc(Working);
1198 if Working > Size then
1199 begin
1200 Result := eNoWorkerAvailable;
1201 Exit;
1202 end;
1203 end;
1204 end;
1205 Result := eOk;
1206 if not TestOnly then
1207 begin
1208 V21_to_Loc(Loc, Radius);
1209 for V21 := 1 to 26 do
1210 if ChangeTiles and (1 shl V21) <> 0 then
1211 begin
1212 Loc1 := Radius[V21];
1213 Assert((Loc1 >= 0) and (Loc1 < MapSize));
1214 if NewTiles and (1 shl V21) <> 0 then
1215 UsedByCity[Loc1] := Loc // employ tile
1216 else if UsedByCity[Loc1] <> Loc then
1217 Assert(Mode < moPlaying)
1218 // should only happen during loading, because of wrong sSetCityTiles command order
1219 else
1220 UsedByCity[Loc1] := -1; // unemploy tile
1221 end;
1222 Tiles := NewTiles;
1223 end;
1224 end;
1225end;
1226
1227procedure GetCityTileAdvice(P, cix: Integer; var Advice: TCityTileAdviceData);
1228const
1229 oFood = 0;
1230 oProd = 1;
1231 oTax = 2;
1232 oScience = 3;
1233type
1234 TTileData = record
1235 Food: Integer;
1236 Prod: Integer;
1237 Trade: Integer;
1238 SubValue: Integer;
1239 V21: Integer;
1240 end;
1241var
1242 I, V21, Loc1, nHierarchy, iH, iT, iH_Switch, MinWorking, MaxWorking,
1243 WantedProd, MinFood, MinProd, count, Take, MaxTake, AreaSize, FormulaCode,
1244 NeedRare, RareTiles, cix1, dx, dy, BestTiles, ProdBeforeBoost, TestTiles,
1245 SubPlus, SuperPlus: Integer;
1246 SuperValue, BestSuperValue, SubValue, BestSubValue: Integer;
1247 Value, BestValue, ValuePlus: Extended;
1248 ValueFormula_Weight: array [oFood .. oScience] of Extended;
1249 ValueFormula_Multiply: array [oFood .. oScience] of Boolean;
1250 Output: array [oFood .. oScience] of Integer;
1251 TileInfo, BaseTileInfo: TTileInfo;
1252 Radius, Radius1: TVicinity21Loc;
1253 TestReport: TCityReport;
1254 CityReportEx: TCityReportEx;
1255 CityAreaInfo: TCityAreaInfo;
1256 Hierarchy: array [0 .. 20, 0 .. 31] of TTileData;
1257 nTile, nSelection: array [0 .. 20] of Integer;
1258 SubCriterion: array [0 .. 27] of Integer;
1259 FoodWasted, FoodToTax, ProdToTax, RareOK, NeedStep2, IsBest: Boolean;
1260begin
1261 if (RW[P].Government = gAnarchy) or (RW[P].City[cix].Flags and chCaptured <> 0)
1262 then
1263 begin
1264 FillChar(Advice.CityReport, SizeOf(Advice.CityReport), 0);
1265 Advice.Tiles := 1 shl CityOwnTile;
1266 Advice.CityReport.HypoTiles := 1 shl CityOwnTile;
1267 Exit;
1268 end;
1269
1270 for I := oFood to oScience do
1271 begin // decode evaluation formula from weights parameter
1272 FormulaCode := Advice.ResourceWeights shr (24 - 8 * I) and $FF;
1273 ValueFormula_Multiply[I] := FormulaCode and $80 <> 0;
1274 if FormulaCode and $40 <> 0 then
1275 ValueFormula_Weight[I] := (FormulaCode and $0F) *
1276 (1 shl (FormulaCode and $30 shr 4)) / 16
1277 else
1278 ValueFormula_Weight[I] := (FormulaCode and $0F) *
1279 (1 shl (FormulaCode and $30 shr 4));
1280 end;
1281
1282 TestReport.HypoTiles := 1 shl CityOwnTile;
1283 TestReport.HypoTax := -1;
1284 TestReport.HypoLux := -1;
1285 GetSmallCityReport(P, cix, TestReport, @CityReportEx);
1286 with RW[P].City[cix] do
1287 begin
1288 V21_to_Loc(Loc, Radius);
1289 FoodToTax := RW[P].Government = gFuture;
1290 ProdToTax := Project and (cpImp + cpIndex) = cpImp + imTrGoods;
1291 FoodWasted := not FoodToTax and (Food = StorageSize[Difficulty[P]]) and
1292 not CanCityGrow(P, cix);
1293
1294 // sub criteria
1295 for V21 := 1 to 26 do
1296 begin
1297 Loc1 := Radius[V21];
1298 if Loc1 >= 0 then
1299 SubCriterion[V21] := 3360 - (Distance(Loc, Loc1) - 1) * 32 -
1300 V21 xor $15;
1301 end;
1302 for cix1 := 0 to RW[P].nCity - 1 do
1303 if cix1 <> cix then
1304 begin
1305 Loc1 := RW[P].City[cix1].Loc;
1306 if Loc1 >= 0 then
1307 begin
1308 if Distance(Loc, Loc1) <= 10 then
1309 begin // cities overlap -- prefer tiles outside common range
1310 V21_to_Loc(Loc1, Radius1);
1311 for V21 := 1 to 26 do
1312 begin
1313 Loc1 := Radius1[V21];
1314 if (Loc1 >= 0) and (Loc1 < MapSize) and (Distance(Loc, Loc1) <= 5)
1315 then
1316 begin
1317 dxdy(Loc, Loc1, dx, dy);
1318 Dec(SubCriterion[(dy + 3) shl 2 + (dx + 3) shr 1], 160);
1319 end;
1320 end;
1321 end;
1322 end;
1323 end;
1324
1325 GetCityAreaInfo(P, Loc, CityAreaInfo);
1326 AreaSize := 0;
1327 for V21 := 1 to 26 do
1328 if CityAreaInfo.Available[V21] = faAvailable then
1329 Inc(AreaSize);
1330
1331 if RW[P].Government = gFundamentalism then
1332 begin
1333 MinWorking := Size;
1334 MaxWorking := Size;
1335 end
1336 else
1337 begin
1338 MinWorking := CityReportEx.TradeProcessing.HappyBase shr 1;
1339 if MinWorking > Size then
1340 MinWorking := Size;
1341 if (RW[P].LuxRate = 0) and not CityReportEx.TradeProcessing.FlexibleLuxury
1342 then
1343 MaxWorking := MinWorking
1344 else
1345 MaxWorking := Size;
1346 end;
1347 if MaxWorking > AreaSize then
1348 begin
1349 MaxWorking := AreaSize;
1350 if MinWorking > AreaSize then
1351 MinWorking := AreaSize;
1352 end;
1353 if TestReport.Support = 0 then
1354 WantedProd := 0
1355 else
1356 WantedProd := 1 + (TestReport.Support * 100 - 1)
1357 div (100 + CityReportEx.ProdProcessing.ProdBonus * 50 +
1358 CityReportEx.ProdProcessing.FutProdBonus);
1359
1360 // consider resources for ship parts
1361 NeedRare := 0;
1362 if (GTestFlags and tfNoRareNeed = 0) and (Project and cpImp <> 0) then
1363 case Project and cpIndex of
1364 imShipComp:
1365 NeedRare := fCobalt;
1366 imShipPow:
1367 NeedRare := fUranium;
1368 imShipHab:
1369 NeedRare := fMercury;
1370 end;
1371 if NeedRare > 0 then
1372 begin
1373 RareTiles := 0;
1374 for V21 := 1 to 26 do
1375 begin
1376 Loc1 := Radius[V21];
1377 if (Loc1 >= 0) and (Loc1 < MapSize) and
1378 (RealMap[Loc1] and fModern = Cardinal(NeedRare)) then
1379 RareTiles := RareTiles or (1 shl V21);
1380 end;
1381 end;
1382
1383 // step 1: sort tiles to hierarchies
1384 nHierarchy := 0;
1385 for V21 := 1 to 26 do // non-rare tiles
1386 if (CityAreaInfo.Available[V21] = faAvailable) and
1387 ((NeedRare = 0) or (1 shl V21 and RareTiles = 0)) then
1388 begin
1389 Loc1 := Radius[V21];
1390 Assert((Loc1 >= 0) and (Loc1 < MapSize));
1391 GetTileInfo(P, cix, Loc1, TileInfo);
1392 if V21 = CityOwnTile then
1393 BaseTileInfo := TileInfo
1394 else
1395 begin
1396 iH := 0;
1397 while iH < nHierarchy do
1398 begin
1399 iT := 0;
1400 while (iT < nTile[iH]) and (TileInfo.Food <= Hierarchy[iH, iT].Food)
1401 and (TileInfo.Prod <= Hierarchy[iH, iT].Prod) and
1402 (TileInfo.Trade <= Hierarchy[iH, iT].Trade) and
1403 not ((TileInfo.Food = Hierarchy[iH, iT].Food) and
1404 (TileInfo.Prod = Hierarchy[iH, iT].Prod) and
1405 (TileInfo.Trade = Hierarchy[iH, iT].Trade) and
1406 (SubCriterion[V21] >= SubCriterion[Hierarchy[iH, iT].V21])) do
1407 Inc(iT);
1408 if (iT = nTile[iH]) // new worst tile in this hierarchy
1409 or ((TileInfo.Food >= Hierarchy[iH, iT].Food)
1410 // new middle tile in this hierarchy
1411 and (TileInfo.Prod >= Hierarchy[iH, iT].Prod) and
1412 (TileInfo.Trade >= Hierarchy[iH, iT].Trade)) then
1413 Break; // insert position found!
1414 Inc(iH);
1415 end;
1416 if iH = nHierarchy then
1417 begin // need to start new hierarchy
1418 nTile[iH] := 0;
1419 Inc(nHierarchy);
1420 iT := 0;
1421 end;
1422 Move(Hierarchy[iH, iT], Hierarchy[iH, iT + 1],
1423 (nTile[iH] - iT) * SizeOf(TTileData));
1424 Inc(nTile[iH]);
1425 Hierarchy[iH, iT].V21 := V21;
1426 Hierarchy[iH, iT].Food := TileInfo.Food;
1427 Hierarchy[iH, iT].Prod := TileInfo.Prod;
1428 Hierarchy[iH, iT].Trade := TileInfo.Trade;
1429 Hierarchy[iH, iT].SubValue := SubCriterion[V21];
1430 end;
1431 end;
1432 if NeedRare <> 0 then
1433 begin // rare tiles need own hierarchy
1434 iH := nHierarchy;
1435 for V21 := 1 to 26 do
1436 if (CityAreaInfo.Available[V21] = faAvailable) and
1437 (1 shl V21 and RareTiles <> 0) then
1438 begin
1439 Loc1 := Radius[V21];
1440 Assert((V21 <> CityOwnTile) and (Loc1 >= 0) and (Loc1 < MapSize));
1441 GetTileInfo(P, cix, Loc1, TileInfo);
1442 if iH = nHierarchy then
1443 begin // need to start new hierarchy
1444 nTile[iH] := 0;
1445 Inc(nHierarchy);
1446 iT := 0;
1447 end
1448 else
1449 iT := nTile[iH];
1450 Inc(nTile[iH]);
1451 Hierarchy[iH, iT].V21 := V21;
1452 Hierarchy[iH, iT].Food := TileInfo.Food; // = 0
1453 Hierarchy[iH, iT].Prod := TileInfo.Prod; // = 1
1454 Hierarchy[iH, iT].Trade := TileInfo.Trade; // = 0
1455 Hierarchy[iH, iT].SubValue := SubCriterion[V21];
1456 end;
1457 end;
1458 if Built[imAlgae] > 0 then
1459 Inc(BaseTileInfo.Food, 12);
1460
1461 // step 2: summarize resources
1462 for iH := 0 to nHierarchy - 1 do
1463 begin
1464 Move(Hierarchy[iH, 0], Hierarchy[iH, 1], nTile[iH] * SizeOf(TTileData));
1465 Hierarchy[iH, 0].Food := 0;
1466 Hierarchy[iH, 0].Prod := 0;
1467 Hierarchy[iH, 0].Trade := 0;
1468 Hierarchy[iH, 0].SubValue := 0;
1469 Hierarchy[iH, 0].V21 := 0;
1470 for iT := 1 to nTile[iH] do
1471 begin
1472 Inc(Hierarchy[iH, iT].Food, Hierarchy[iH, iT - 1].Food);
1473 Inc(Hierarchy[iH, iT].Prod, Hierarchy[iH, iT - 1].Prod);
1474 Inc(Hierarchy[iH, iT].Trade, Hierarchy[iH, iT - 1].Trade);
1475 Inc(Hierarchy[iH, iT].SubValue, Hierarchy[iH, iT - 1].SubValue);
1476 Hierarchy[iH, iT].V21 := 1 shl Hierarchy[iH, iT].V21 +
1477 Hierarchy[iH, iT - 1].V21;
1478 end;
1479 end;
1480
1481 // step 3: try all combinations
1482 BestValue := 0.0;
1483 BestSuperValue := 0;
1484 BestSubValue := 0;
1485 BestTiles := 0;
1486 FillChar(nSelection, SizeOf(nSelection), 0);
1487 TestReport.FoodRep := BaseTileInfo.Food;
1488 ProdBeforeBoost := BaseTileInfo.Prod;
1489 TestReport.Trade := BaseTileInfo.Trade;
1490 TestReport.Working := 1;
1491 MinFood := 0;
1492 MinProd := 0;
1493 iH_Switch := nHierarchy;
1494 count := 0;
1495 repeat
1496 // ensure minima
1497 iH := 0;
1498 while (TestReport.Working < MaxWorking) and (iH < iH_Switch) and
1499 ((TestReport.Working < MinWorking) or
1500 (TestReport.FoodRep < TestReport.Eaten) or
1501 (ProdBeforeBoost < WantedProd)) do
1502 begin
1503 Assert(nSelection[iH] = 0);
1504 Take := MinWorking - TestReport.Working;
1505 if Take > nTile[iH] then
1506 Take := nTile[iH]
1507 else
1508 begin
1509 if Take < 0 then
1510 Take := 0;
1511 MaxTake := nTile[iH];
1512 if TestReport.Working + MaxTake > MaxWorking then
1513 MaxTake := MaxWorking - TestReport.Working;
1514 while (Take < MaxTake) and
1515 (TestReport.FoodRep + Hierarchy[iH, Take].Food < MinFood) do
1516 Inc(Take);
1517 while (Take < MaxTake) and
1518 (ProdBeforeBoost + Hierarchy[iH, Take].Prod < MinProd) do
1519 Inc(Take);
1520 end;
1521 nSelection[iH] := Take;
1522 Inc(TestReport.Working, Take);
1523 with Hierarchy[iH, Take] do
1524 begin
1525 Inc(TestReport.FoodRep, Food);
1526 Inc(ProdBeforeBoost, Prod);
1527 Inc(TestReport.Trade, Trade);
1528 end;
1529 Inc(iH);
1530 end;
1531
1532 Assert((TestReport.Working >= MinWorking) and
1533 (TestReport.Working <= MaxWorking));
1534 if (TestReport.FoodRep >= MinFood) and (ProdBeforeBoost >= MinProd) then
1535 begin
1536 SplitTrade(TestReport.Trade, RW[P].TaxRate, RW[P].LuxRate,
1537 TestReport.Working, CityReportEx.TradeProcessing,
1538 TestReport.Corruption, TestReport.Tax, TestReport.Lux,
1539 TestReport.Science);
1540
1541 if CityReportEx.BaseHappiness + CityReportEx.BaseControl +
1542 TestReport.Lux + 2 * (Size - TestReport.Working) - 2 *
1543 TestReport.Deployed >= Size then
1544 begin // city is not in disorder -- evaluate combination
1545 Inc(count);
1546 if (MinProd < WantedProd) and (ProdBeforeBoost > MinProd) then
1547 begin // no combination reached wanted prod yet
1548 MinProd := ProdBeforeBoost;
1549 if MinProd > WantedProd then
1550 MinProd := WantedProd
1551 end;
1552 if MinProd = WantedProd then
1553 // do not care for food before prod is ensured
1554 if (MinFood < TestReport.Eaten) and (TestReport.FoodRep > MinFood)
1555 then
1556 begin // no combination reached wanted food yet
1557 MinFood := TestReport.FoodRep;
1558 if MinFood > TestReport.Eaten then
1559 MinFood := TestReport.Eaten
1560 end;
1561 BoostProd(ProdBeforeBoost, CityReportEx.ProdProcessing,
1562 TestReport.ProdRep, TestReport.PollRep);
1563 SuperValue := 0;
1564
1565 // super-criterion A: unit support granted?
1566 if TestReport.ProdRep >= TestReport.Support then
1567 SuperValue := SuperValue or 1 shl 30;
1568
1569 // super-criterion B: food demand granted?
1570 if TestReport.FoodRep >= TestReport.Eaten then
1571 SuperValue := SuperValue or 63 shl 24
1572 else if TestReport.FoodRep > TestReport.Eaten - 63 then
1573 SuperValue := SuperValue or
1574 (63 - (TestReport.Eaten - TestReport.FoodRep)) shl 24;
1575
1576 SuperPlus := SuperValue - BestSuperValue;
1577 if SuperPlus >= 0 then
1578 begin
1579 Output[oTax] := TestReport.Tax;
1580 Output[oScience] := TestReport.Science;
1581
1582 if TestReport.FoodRep < TestReport.Eaten then
1583 Output[oFood] := TestReport.FoodRep
1584 // appreciate what we have, combination will have bad supervalue anyway
1585 else if FoodWasted then
1586 Output[oFood] := 0
1587 else
1588 begin
1589 Output[oFood] := TestReport.FoodRep - TestReport.Eaten;
1590 if FoodToTax or (Size >= NeedAqueductSize) and (Output[oFood] = 1)
1591 then
1592 begin
1593 Inc(Output[oTax], Output[oFood]);
1594 Output[oFood] := 0;
1595 end;
1596 end;
1597
1598 if TestReport.ProdRep < TestReport.Support then
1599 Output[oProd] := TestReport.ProdRep
1600 // appreciate what we have, combination will have bad supervalue anyway
1601 else
1602 begin
1603 if NeedRare > 0 then
1604 begin
1605 RareOK := False;
1606 for iH := 0 to nHierarchy - 1 do
1607 if Hierarchy[iH, nSelection[iH]].V21 and RareTiles <> 0 then begin
1608 RareOK := True;
1609 Break;
1610 end;
1611 if not RareOK then
1612 TestReport.ProdRep := TestReport.Support;
1613 end;
1614 Output[oProd] := TestReport.ProdRep - TestReport.Support;
1615 if ProdToTax then
1616 begin
1617 Inc(Output[oTax], Output[oProd]);
1618 Output[oProd] := 0;
1619 end;
1620 end;
1621
1622 NeedStep2 := False;
1623 Value := 0;
1624 for I := oFood to oScience do
1625 if ValueFormula_Multiply[I] then
1626 NeedStep2 := True
1627 else
1628 Value := Value + ValueFormula_Weight[I] * Output[I];
1629 if NeedStep2 then
1630 begin
1631 if Value > 0 then
1632 Value := Ln(Value) + 123;
1633 for I := oFood to oScience do
1634 if ValueFormula_Multiply[I] and (Output[I] > 0) then
1635 Value := Value + ValueFormula_Weight[I] *
1636 (Ln(Output[I]) + 123);
1637 end;
1638
1639 ValuePlus := Value - BestValue;
1640 if (SuperPlus > 0) or (ValuePlus >= 0.0) then
1641 begin
1642 SubValue := (TestReport.FoodRep + ProdBeforeBoost +
1643 TestReport.Trade) shl 18;
1644 TestTiles := 1 shl CityOwnTile;
1645 for iH := 0 to nHierarchy - 1 do
1646 begin
1647 Inc(TestTiles, Hierarchy[iH, nSelection[iH]].V21);
1648 Inc(SubValue, Hierarchy[iH, nSelection[iH]].SubValue);
1649 end;
1650 IsBest := True;
1651 if (SuperPlus = 0) and (ValuePlus = 0.0) then
1652 begin
1653 SubPlus := SubValue - BestSubValue;
1654 if SubPlus < 0 then
1655 IsBest := False
1656 else if SubPlus = 0 then
1657 begin
1658 Assert(TestTiles <> BestTiles);
1659 IsBest := TestTiles > BestTiles
1660 end
1661 end;
1662 if IsBest then
1663 begin
1664 BestSuperValue := SuperValue;
1665 BestValue := Value;
1666 BestSubValue := SubValue;
1667 BestTiles := TestTiles;
1668 TestReport.Happy :=
1669 (CityReportEx.TradeProcessing.HappyBase - Size) div 2 +
1670 TestReport.Lux shr 1;
1671 Advice.CityReport := TestReport;
1672 end;
1673 end; // if (SuperPlus>0) or (ValuePlus>=0.0)
1674 end; // if SuperPlus>=0
1675 end;
1676 end;
1677
1678 // calculate next combination
1679 iH_Switch := 0;
1680 repeat
1681 with Hierarchy[iH_Switch, nSelection[iH_Switch]] do
1682 begin
1683 Dec(TestReport.FoodRep, Food);
1684 Dec(ProdBeforeBoost, Prod);
1685 Dec(TestReport.Trade, Trade);
1686 end;
1687 Inc(nSelection[iH_Switch]);
1688 Inc(TestReport.Working);
1689 if (nSelection[iH_Switch] <= nTile[iH_Switch]) and
1690 (TestReport.Working <= MaxWorking) then
1691 begin
1692 with Hierarchy[iH_Switch, nSelection[iH_Switch]] do
1693 begin
1694 Inc(TestReport.FoodRep, Food);
1695 Inc(ProdBeforeBoost, Prod);
1696 Inc(TestReport.Trade, Trade);
1697 end;
1698 Break;
1699 end;
1700 Dec(TestReport.Working, nSelection[iH_Switch]);
1701 nSelection[iH_Switch] := 0;
1702 Inc(iH_Switch);
1703 until iH_Switch = nHierarchy;
1704 until iH_Switch = nHierarchy; // everything tested -- done
1705 end;
1706 Assert(BestSuperValue > 0); // advice should always be possible
1707 Advice.Tiles := BestTiles;
1708 Advice.CityReport.HypoTiles := BestTiles;
1709end;
1710
1711{
1712 Start/End Game
1713 ____________________________________________________________________
1714}
1715procedure InitGame;
1716var
1717 P, I, mixTownGuard: Integer;
1718begin
1719 MaxDist := Distance(0, MapSize - lx shr 1);
1720 for P := 0 to nPl - 1 do
1721 if (1 shl P and GAlive <> 0) then
1722 with RW[P] do
1723 begin // initialize capital
1724 mixTownGuard := 0;
1725 while Model[mixTownGuard].Kind <> mkSpecial_TownGuard do
1726 Inc(mixTownGuard);
1727 with City[0] do
1728 begin
1729 Built[imPalace] := 1;
1730 Size := 4;
1731 for I := 2 to Size do
1732 AddBestCityTile(P, 0);
1733 Project := mixTownGuard;
1734 end;
1735 NatBuilt[imPalace] := 1;
1736 end;
1737end;
1738
1739procedure ReleaseGame;
1740begin
1741end;
1742
1743end.
Note: See TracBrowser for help on using the repository browser.