source: trunk/CityProcessing.pas@ 319

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