source: tags/1.3.1/CityProcessing.pas

Last change on this file was 425, checked in by chronos, 2 years ago
  • Modified: Code cleanup.
File size: 56.1 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
1608 RareOK := true;
1609 if not RareOK then
1610 TestReport.ProdRep := TestReport.Support;
1611 end;
1612 Output[oProd] := TestReport.ProdRep - TestReport.Support;
1613 if ProdToTax then
1614 begin
1615 inc(Output[oTax], Output[oProd]);
1616 Output[oProd] := 0;
1617 end;
1618 end;
1619
1620 NeedStep2 := false;
1621 Value := 0;
1622 for i := oFood to oScience do
1623 if ValueFormula_Multiply[i] then
1624 NeedStep2 := true
1625 else
1626 Value := Value + ValueFormula_Weight[i] * Output[i];
1627 if NeedStep2 then
1628 begin
1629 if Value > 0 then
1630 Value := ln(Value) + 123;
1631 for i := oFood to oScience do
1632 if ValueFormula_Multiply[i] and (Output[i] > 0) then
1633 Value := Value + ValueFormula_Weight[i] *
1634 (ln(Output[i]) + 123);
1635 end;
1636
1637 ValuePlus := Value - BestValue;
1638 if (SuperPlus > 0) or (ValuePlus >= 0.0) then
1639 begin
1640 SubValue := (TestReport.FoodRep + ProdBeforeBoost +
1641 TestReport.Trade) shl 18;
1642 TestTiles := 1 shl CityOwnTile;
1643 for iH := 0 to nHierarchy - 1 do
1644 begin
1645 inc(TestTiles, Hierarchy[iH, nSelection[iH]].V21);
1646 inc(SubValue, Hierarchy[iH, nSelection[iH]].SubValue);
1647 end;
1648 IsBest := true;
1649 if (SuperPlus = 0) and (ValuePlus = 0.0) then
1650 begin
1651 SubPlus := SubValue - BestSubValue;
1652 if SubPlus < 0 then
1653 IsBest := false
1654 else if SubPlus = 0 then
1655 begin
1656 assert(TestTiles <> BestTiles);
1657 IsBest := TestTiles > BestTiles
1658 end
1659 end;
1660 if IsBest then
1661 begin
1662 BestSuperValue := SuperValue;
1663 BestValue := Value;
1664 BestSubValue := SubValue;
1665 BestTiles := TestTiles;
1666 TestReport.Happy :=
1667 (CityReportEx.TradeProcessing.HappyBase - Size) div 2 +
1668 TestReport.Lux shr 1;
1669 Advice.CityReport := TestReport;
1670 end;
1671 end; // if (SuperPlus>0) or (ValuePlus>=0.0)
1672 end; // if SuperPlus>=0
1673 end;
1674 end;
1675
1676 // calculate next combination
1677 iH_Switch := 0;
1678 repeat
1679 with Hierarchy[iH_Switch, nSelection[iH_Switch]] do
1680 begin
1681 dec(TestReport.FoodRep, Food);
1682 dec(ProdBeforeBoost, Prod);
1683 dec(TestReport.Trade, Trade);
1684 end;
1685 inc(nSelection[iH_Switch]);
1686 inc(TestReport.Working);
1687 if (nSelection[iH_Switch] <= nTile[iH_Switch]) and
1688 (TestReport.Working <= MaxWorking) then
1689 begin
1690 with Hierarchy[iH_Switch, nSelection[iH_Switch]] do
1691 begin
1692 inc(TestReport.FoodRep, Food);
1693 inc(ProdBeforeBoost, Prod);
1694 inc(TestReport.Trade, Trade);
1695 end;
1696 break;
1697 end;
1698 dec(TestReport.Working, nSelection[iH_Switch]);
1699 nSelection[iH_Switch] := 0;
1700 inc(iH_Switch);
1701 until iH_Switch = nHierarchy;
1702 until iH_Switch = nHierarchy; // everything tested -- done
1703 end;
1704 assert(BestSuperValue > 0); // advice should always be possible
1705 Advice.Tiles := BestTiles;
1706 Advice.CityReport.HypoTiles := BestTiles;
1707end;
1708
1709{
1710 Start/End Game
1711 ____________________________________________________________________
1712}
1713procedure InitGame;
1714var
1715 p, i, mixTownGuard: integer;
1716begin
1717 MaxDist := Distance(0, MapSize - lx shr 1);
1718 for p := 0 to nPl - 1 do
1719 if (1 shl p and GAlive <> 0) then
1720 with RW[p] do
1721 begin // initialize capital
1722 mixTownGuard := 0;
1723 while Model[mixTownGuard].Kind <> mkSpecial_TownGuard do
1724 inc(mixTownGuard);
1725 with City[0] do
1726 begin
1727 Built[imPalace] := 1;
1728 Size := 4;
1729 for i := 2 to Size do
1730 AddBestCityTile(p, 0);
1731 Project := mixTownGuard;
1732 end;
1733 NatBuilt[imPalace] := 1;
1734 end;
1735end;
1736
1737procedure ReleaseGame;
1738begin
1739end;
1740
1741end.
Note: See TracBrowser for help on using the repository browser.