source: branches/delphi/CityProcessing.pas

Last change on this file was 6, checked in by chronos, 7 years ago
  • Modified: Formated all project source files using Delphi formatter as original indentation and other formatting was really bad.
File size: 55.8 KB
Line 
1{$INCLUDE switches}
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{$IFOPT O-}assert(1 shl p and InvalidTreatyMap = 0); {$ENDIF}
633 Best := 0;
634 result := -1;
635 with RW[p].City[cix] do
636 begin
637 V21_to_Loc(Loc, Radius);
638 for V21 := 1 to 26 do
639 if V21 <> CityOwnTile then
640 begin
641 Loc1 := Radius[V21];
642 if (Loc1 >= 0) and (Loc1 < MapSize) and
643 (RealMap[Loc1] and fTerrain >= fGrass) and
644 (RealMap[Loc1] and (fPoll or fDeadLands or fCity) = 0) then
645 begin
646 pTerr := RealMap[Loc1] shr 27;
647 if (pTerr = nPl) or (pTerr = p) or (RW[p].Treaty[pTerr] < trPeace)
648 then
649 begin
650 GetTileInfo(p, cix, Loc1, TileInfo);
651 Resources := TileInfo.Prod shl 16 + TileInfo.Trade shl 8 +
652 TileInfo.Food;
653 { priority: 1.prod - 2.trade - 3.food }
654 dy := V21 shr 2 - 3;
655 dx := V21 and 3 shl 1 - 3 + (dy + 3) and 1;
656 Dist := abs(dx) + abs(dy) + abs(abs(dx) - abs(dy)) shr 1;
657 if (Resources > Best) or (Resources = Best) and (Dist < BestDist)
658 then
659 begin
660 result := Loc1;
661 Best := Resources;
662 BestDist := Dist
663 end
664 end
665 end
666 end;
667 end
668end;
669
670function AddBestCityTile(p, cix: integer): boolean;
671var
672 TileLoc, V21: integer;
673begin
674 NextBest(p, cix, TileLoc, V21);
675 result := TileLoc >= 0;
676 if result then
677 with RW[p].City[cix] do
678 begin
679 assert(1 shl V21 and Tiles = 0);
680 Tiles := Tiles or (1 shl V21);
681 UsedByCity[TileLoc] := Loc
682 end
683end;
684
685procedure CityGrowth(p, cix: integer);
686var
687 TileLoc, V21: integer;
688 AltCityReport: TCityReport;
689begin
690 with RW[p].City[cix] do
691 begin
692 inc(Size);
693 NextBest(p, cix, TileLoc, V21);
694 if TileLoc >= 0 then
695 begin { test whether exploitation of tile would lead to disorder }
696 AltCityReport.HypoTiles := Tiles + 1 shl V21;
697 AltCityReport.HypoTax := -1;
698 AltCityReport.HypoLux := -1;
699 GetSmallCityReport(p, cix, AltCityReport);
700 if AltCityReport.Working - AltCityReport.Happy <= Size shr 1 then
701 // !!! change to new style disorder
702 begin { no disorder -- exploit tile }
703 assert(1 shl V21 and Tiles = 0);
704 Tiles := Tiles or (1 shl V21);
705 UsedByCity[TileLoc] := Loc
706 end
707 end;
708 end
709end;
710
711procedure CityShrink(p, cix: integer);
712var
713 TileLoc, V21, Working: integer;
714 AltCityReport: TCityReport;
715begin
716 with RW[p].City[cix] do
717 begin
718 Working := 0;
719 for V21 := 1 to 26 do
720 if Tiles and (1 shl V21) <> 0 then
721 inc(Working);
722 dec(Size);
723 if Food > StorageSize[Difficulty[p]] then
724 Food := StorageSize[Difficulty[p]];
725 NextWorst(p, cix, TileLoc, V21);
726 if Working > Size then
727 begin { all citizens were working -- worst tile no longer exploited }
728 assert(1 shl V21 and Tiles <> 0);
729 Tiles := Tiles and not(1 shl V21);
730 UsedByCity[TileLoc] := -1
731 end
732 else { test whether exploitation of tile would lead to disorder }
733 begin
734 AltCityReport.HypoTiles := -1;
735 AltCityReport.HypoTax := -1;
736 AltCityReport.HypoLux := -1;
737 GetSmallCityReport(p, cix, AltCityReport);
738 if AltCityReport.Working - AltCityReport.Happy > Size shr 1 then
739 // !!! change to new style disorder
740 begin { disorder -- don't exploit tile }
741 assert(1 shl V21 and Tiles <> 0);
742 Tiles := Tiles and not(1 shl V21);
743 UsedByCity[TileLoc] := -1
744 end
745 end;
746 end
747end;
748
749procedure Pollute(p, cix: integer);
750var
751 PollutionLoc: integer;
752begin
753 with RW[p].City[cix] do
754 begin
755 Pollution := Pollution - MaxPollution;
756 PollutionLoc := NextPoll(p, cix);
757 if PollutionLoc >= 0 then
758 begin
759 inc(Flags, chPollution);
760 RealMap[PollutionLoc] := RealMap[PollutionLoc] or fPoll;
761 end
762 end;
763end;
764
765{
766 Turn Processing
767 ____________________________________________________________________
768}
769procedure PayCityMaintenance(p, cix: integer);
770var
771 i: integer;
772begin
773 with RW[p], City[cix] do
774 for i := 28 to nImp - 1 do
775 if (Built[i] > 0) and (Project0 and (cpImp or cpIndex) <> (cpImp or i))
776 then // don't pay maintenance when just completed
777 begin
778 dec(Money, Imp[i].Maint);
779 if Money < 0 then
780 begin { out of money - sell improvement }
781 inc(Money, Imp[i].Cost * BuildCostMod[Difficulty[p]] div 12);
782 Built[i] := 0;
783 if Imp[i].Kind <> ikCommon then
784 begin
785 assert(i <> imSpacePort);
786 // never sell automatically! (solution: no maintenance)
787 NatBuilt[i] := 0;
788 if i = imGrWall then
789 GrWallContinent[p] := -1;
790 end;
791 inc(Flags, chImprovementLost)
792 end
793 end;
794end;
795
796procedure CollectCityResources(p, cix: integer);
797var
798 CityStorage, CityProjectCost: integer;
799 CityReport: TCityReportNew;
800 Disorder: boolean;
801begin
802 with RW[p], City[cix], CityReport do
803 if Flags and chCaptured <> 0 then
804 begin
805 Flags := Flags and not chDisorder;
806 dec(Flags, $10000);
807 if Flags and chCaptured = 0 then
808 Flags := Flags or chAfterCapture;
809 end
810 else if Government = gAnarchy then
811 Flags := Flags and not chDisorder
812 else
813 begin
814 HypoTiles := -1;
815 HypoTaxRate := -1;
816 HypoLuxuryRate := -1;
817 GetCityReportNew(p, cix, CityReport);
818 CityStorage := StorageSize[Difficulty[p]];
819 CityProjectCost := GetProjectCost(p, cix);
820
821 Disorder := (HappinessBalance < 0);
822 if Disorder and (Flags and chDisorder <> 0) then
823 CollectedMaterial := 0; // second turn disorder
824 if Disorder then
825 Flags := Flags or chDisorder
826 else
827 Flags := Flags and not chDisorder;
828
829 if not Disorder and ((Government = gFuture) or (Size >= NeedAqueductSize)
830 and (FoodSurplus < 2)) and (FoodSurplus > 0) then
831 inc(Money, FoodSurplus)
832 else if not(Disorder and (FoodSurplus > 0)) then
833 begin { calculate new food storage }
834 Food := Food + FoodSurplus;
835 if ((GTestFlags and tfImmGrow <> 0) or (Food >= CityStorage) and
836 (Food - FoodSurplus < CityStorage)) // only warn once
837 and (Size < MaxCitySize) and
838 (Project and (cpImp + cpIndex) <> cpImp + imAqueduct) and
839 (Project and (cpImp + cpIndex) <> cpImp + imSewer) and
840 not CanCityGrow(p, cix) then
841 inc(Flags, chNoGrowthWarning);
842 end;
843
844 if Prod > CityProjectCost then
845 begin
846 inc(Money, Prod - CityProjectCost);
847 Prod := CityProjectCost
848 end;
849 if Production < 0 then
850 Flags := Flags or chUnitLost
851 else if not Disorder and (Flags and chProductionSabotaged = 0) then
852 if Project and (cpImp + cpIndex) = cpImp + imTrGoods then
853 inc(Money, Production)
854 else
855 inc(Prod, Production);
856
857 if not Disorder then
858 begin
859 { sum research points and taxes }
860 inc(Research, Science);
861 inc(Money, Tax);
862 Pollution := Pollution + AddPollution;
863 end;
864 end;
865end;
866
867function CityTurn(p, cix: integer): boolean;
868// return value: whether city keeps existing
869var
870 i, uix, cix2, p1, SizeMod, CityStorage, CityProjectCost, NewImp, Det,
871 TestDet: integer;
872 LackOfMaterial, CheckGrow, DoProd, IsActive: boolean;
873begin
874 with RW[p], City[cix] do
875 begin
876 SizeMod := 0;
877 CityStorage := StorageSize[Difficulty[p]];
878 CityProjectCost := GetProjectCost(p, cix);
879
880 LackOfMaterial := Flags and chUnitLost <> 0;
881 Flags := Flags and not chUnitLost;
882
883 IsActive := (Government <> gAnarchy) and (Flags and chCaptured = 0);
884 CheckGrow := (Flags and chDisorder = 0) and IsActive and
885 (Government <> gFuture);
886 if CheckGrow and (GTestFlags and tfImmGrow <> 0) then { fast growth }
887 begin
888 if CanCityGrow(p, cix) then
889 inc(SizeMod)
890 end
891 else if CheckGrow and (Food >= CityStorage) then { normal growth }
892 begin
893 if CanCityGrow(p, cix) then
894 begin
895 if Built[imGranary] = 1 then
896 dec(Food, CityStorage shr 1)
897 else
898 dec(Food, CityStorage);
899 inc(SizeMod)
900 end
901 end
902 else if Food < 0 then { famine }
903 begin
904 Food := 0;
905 // check if settlers or conscripts there to disband
906 uix := -1;
907 for i := 0 to nUn - 1 do
908 if (Un[i].Loc >= 0) and (Un[i].Home = cix) and
909 ((Model[Un[i].mix].Kind = mkSettler)
910 { and (GWonder[woFreeSettlers].EffectiveOwner<>p) }
911 or (Un[i].Flags and unConscripts <> 0)) and
912 ((uix = -1) or (Model[Un[i].mix].Cost < Model[Un[uix].mix].Cost) or
913 (Model[Un[i].mix].Cost = Model[Un[uix].mix].Cost) and
914 (Un[i].Exp < Un[uix].Exp)) then
915 uix := i;
916
917 if uix >= 0 then
918 begin
919 RemoveUnit_UpdateMap(p, uix);
920 inc(Flags, chUnitLost);
921 end
922 else
923 begin
924 dec(SizeMod);
925 inc(Flags, chPopDecrease)
926 end
927 end;
928 if Food > CityStorage then
929 Food := CityStorage;
930
931 if LackOfMaterial then
932 begin
933 if Flags and chUnitLost = 0 then
934 begin { one unit lost }
935 uix := -1;
936 Det := MaxInt;
937 for i := 0 to nUn - 1 do
938 if (Un[i].Loc >= 0) and (Un[i].Home = cix) then
939 with Model[Un[i].mix] do
940 begin
941 if Kind = mkSpecial_TownGuard then
942 TestDet := Un[i].Health + Un[i].Exp shl 8
943 // disband townguards first
944 else
945 begin
946 TestDet := Un[i].Health + Un[i].Exp shl 8 + Cost shl 16;
947 // value of unit
948 if Flags and mdDoubleSupport <> 0 then
949 TestDet := TestDet shr 1;
950 // double support, tend to disband first
951 end;
952 if TestDet < Det then
953 begin
954 uix := i;
955 Det := TestDet
956 end;
957 end;
958 if uix >= 0 then
959 begin
960 RemoveUnit_UpdateMap(p, uix);
961 inc(Flags, chUnitLost);
962 end
963 end
964 end;
965
966 if GTestFlags and tfImmImprove <> 0 then
967 Prod := CityProjectCost;
968 DoProd := (Project and (cpImp + cpIndex) <> cpImp + imTrGoods) and
969 (Prod >= CityProjectCost);
970
971 // check if wonder already built
972 if (Project and cpImp <> 0) and (Project and cpIndex < 28) and
973 (GWonder[Project and cpIndex].CityID <> -1) then
974 begin
975 inc(Flags, chOldWonder);
976 DoProd := false;
977 end;
978
979 // check if producing settlers would disband city
980 if DoProd and (Project and (cpImp or cpDisbandCity) = 0) and
981 ((Size + SizeMod - 2 < 2) and
982 (Model[Project and cpIndex].Kind = mkSettler) or (Size + SizeMod - 1 < 2)
983 and ((Model[Project and cpIndex].Kind = mkSlaves) or
984 (Project and cpConscripts <> 0))) then
985 begin
986 inc(Flags, chNoSettlerProd);
987 DoProd := false;
988 end;
989
990 if DoProd then
991 begin { project complete }
992 dec(Prod, CityProjectCost);
993 if Project and cpImp = 0 then { produce unit }
994 begin
995 if nUn < numax then
996 begin
997 CreateUnit(p, Project and cpIndex);
998 Un[nUn - 1].Loc := Loc;
999 with Un[nUn - 1] do
1000 begin
1001 Home := cix;
1002 if (Model[mix].Domain < dSea) and (Built[imElite] = 1) then
1003 Exp := ExpCost * (nExp - 1) { elite }
1004 else if (Model[mix].Domain < dSea) and (Built[imBarracks] = 1) or
1005 (Model[mix].Domain = dSea) and (Built[imDockyard] = 1) or
1006 (Model[mix].Domain = dAir) and (Built[imAirport] = 1) then
1007 Exp := ExpCost * 2; { vet }
1008 if Project and cpConscripts <> 0 then
1009 Flags := Flags or unConscripts
1010 end;
1011 PlaceUnit(p, nUn - 1);
1012 UpdateUnitMap(Loc);
1013 if Model[Project and cpIndex].Kind = mkSettler then
1014 dec(SizeMod, 2) { settler produced - city shrink }
1015 else if (Model[Project and cpIndex].Kind = mkSlaves) or
1016 (Project and cpConscripts <> 0) then
1017 dec(SizeMod); { slaves/conscripts produced - city shrink }
1018 end;
1019 Project0 := Project or cpRepeat or cpCompleted;
1020 end
1021 else if Imp[Project and cpIndex].Kind = ikShipPart then
1022 begin { produce ship parts }
1023 inc(GShip[p].Parts[Project and cpIndex - imShipComp]);
1024 Project0 := Project or cpCompleted;
1025 end
1026 else { produce improvement }
1027 begin
1028 NewImp := Project and cpIndex;
1029 inc(Money, Prod); { change rest to money }
1030 Project0 := Project or cpCompleted;
1031 Project := cpImp + imTrGoods;
1032 Prod := 0;
1033
1034 if Imp[NewImp].Kind in [ikNatLocal, ikNatGlobal] then
1035 begin // nat. project
1036 for i := 0 to nCity - 1 do
1037 if (City[i].Loc >= 0) and (City[i].Built[NewImp] = 1) then
1038 begin { allowed only once }
1039 inc(Money, Imp[NewImp].Cost * BuildCostMod[Difficulty[p]] div 12);
1040 City[i].Built[NewImp] := 0;
1041 end;
1042 NatBuilt[NewImp] := 1;
1043
1044 // immediate nat. project effects
1045 case NewImp of
1046 imGrWall:
1047 GrWallContinent[p] := Continent[Loc];
1048 end;
1049 end;
1050
1051 if NewImp < 28 then
1052 begin // wonder
1053 GWonder[NewImp].CityID := ID;
1054 GWonder[NewImp].EffectiveOwner := p;
1055 CheckExpiration(NewImp);
1056
1057 // immediate wonder effects
1058 case NewImp of
1059 woEiffel:
1060 begin // reactivate wonders
1061 for i := 0 to 27 do
1062 if Imp[i].Expiration >= 0 then
1063 for cix2 := 0 to nCity - 1 do
1064 if (City[cix2].Loc >= 0) and (City[cix2].Built[i] = 1)
1065 then
1066 GWonder[i].EffectiveOwner := p
1067 end;
1068 woLighthouse:
1069 CheckSpecialModels(p, preLighthouse);
1070 woLeo:
1071 begin
1072 inc(Research, TechBaseCost(nTech[p], Difficulty[p]) +
1073 TechBaseCost(nTech[p] + 2, Difficulty[p]));
1074 CheckSpecialModels(p, preLeo);
1075 end;
1076 woPyramids:
1077 CheckSpecialModels(p, preBuilder);
1078 woMir:
1079 begin
1080 for p1 := 0 to nPl - 1 do
1081 if (p1 <> p) and (1 shl p1 and GAlive <> 0) then
1082 begin
1083 if RW[p].Treaty[p1] = trNoContact then
1084 IntroduceEnemy(p, p1);
1085 GiveCivilReport(p, p1);
1086 GiveMilReport(p, p1)
1087 end;
1088 end
1089 end;
1090 end;
1091
1092 for i := 0 to nImpReplacement - 1 do // sell obsolete buildings
1093 if (ImpReplacement[i].NewImp = NewImp) and
1094 (Built[ImpReplacement[i].OldImp] > 0) then
1095 begin
1096 inc(RW[p].Money, Imp[ImpReplacement[i].OldImp].Cost * BuildCostMod
1097 [Difficulty[p]] div 12);
1098 Built[ImpReplacement[i].OldImp] := 0;
1099 end;
1100
1101 if NewImp in [imPower, imHydro, imNuclear] then
1102 for i := 0 to nImp - 1 do
1103 if (i <> NewImp) and (i in [imPower, imHydro, imNuclear]) and
1104 (Built[i] > 0) then
1105 begin // sell obsolete power plant
1106 inc(RW[p].Money, Imp[i].Cost * BuildCostMod[Difficulty[p]
1107 ] div 12);
1108 Built[i] := 0;
1109 end;
1110
1111 Built[NewImp] := 1;
1112 end;
1113 Prod0 := Prod;
1114 inc(Flags, chProduction)
1115 end
1116 else
1117 begin
1118 Project0 := Project0 and not cpCompleted;
1119 if Project0 and not cpAuto <> Project and not cpAuto then
1120 Project0 := Project;
1121 Prod0 := Prod;
1122 end;
1123
1124 if SizeMod > 0 then
1125 begin
1126 CityGrowth(p, cix);
1127 inc(Flags, chPopIncrease);
1128 end;
1129 result := Size + SizeMod >= 2;
1130 if result then
1131 while SizeMod < 0 do
1132 begin
1133 CityShrink(p, cix);
1134 inc(SizeMod)
1135 end;
1136 end
1137end; // CityTurn
1138
1139{
1140 Tile Access
1141 ____________________________________________________________________
1142}
1143function SetCityTiles(p, cix, NewTiles: integer;
1144 TestOnly: boolean = false): integer;
1145var
1146 V21, Working, ChangeTiles, AddTiles, Loc1: integer;
1147 CityAreaInfo: TCityAreaInfo;
1148 Radius: TVicinity21Loc;
1149begin
1150 with RW[p].City[cix] do
1151 begin
1152 ChangeTiles := NewTiles xor integer(Tiles);
1153 AddTiles := NewTiles and not Tiles;
1154 if Mode = moPlaying then
1155 begin // do all checks
1156 if NewTiles and not $67F7F76 <> 0 then
1157 begin
1158 result := eInvalid;
1159 exit
1160 end; // invalid tile index included
1161 if NewTiles and (1 shl 13) = 0 then
1162 begin
1163 result := eViolation;
1164 exit
1165 end; // city tile must be exploited
1166 if ChangeTiles = 0 then
1167 begin
1168 result := eNotChanged;
1169 exit
1170 end;
1171 if AddTiles <> 0 then
1172 begin
1173 // check if new tiles possible
1174 GetCityAreaInfo(p, Loc, CityAreaInfo);
1175 for V21 := 1 to 26 do
1176 if AddTiles and (1 shl V21) <> 0 then
1177 if CityAreaInfo.Available[V21] <> faAvailable then
1178 begin
1179 result := eTileNotAvailable;
1180 exit
1181 end;
1182 // not more tiles than inhabitants
1183 Working := 0;
1184 for V21 := 1 to 26 do
1185 if NewTiles and (1 shl V21) <> 0 then
1186 inc(Working);
1187 if Working > Size then
1188 begin
1189 result := eNoWorkerAvailable;
1190 exit
1191 end;
1192 end;
1193 end;
1194 result := eOk;
1195 if not TestOnly then
1196 begin
1197 V21_to_Loc(Loc, Radius);
1198 for V21 := 1 to 26 do
1199 if ChangeTiles and (1 shl V21) <> 0 then
1200 begin
1201 Loc1 := Radius[V21];
1202 assert((Loc1 >= 0) and (Loc1 < MapSize));
1203 if NewTiles and (1 shl V21) <> 0 then
1204 UsedByCity[Loc1] := Loc // employ tile
1205 else if UsedByCity[Loc1] <> Loc then
1206 assert(Mode < moPlaying)
1207 // should only happen during loading, because of wrong sSetCityTiles command order
1208 else
1209 UsedByCity[Loc1] := -1 // unemploy tile
1210 end;
1211 Tiles := NewTiles
1212 end
1213 end;
1214end;
1215
1216procedure GetCityTileAdvice(p, cix: integer; var Advice: TCityTileAdviceData);
1217const
1218 oFood = 0;
1219 oProd = 1;
1220 oTax = 2;
1221 oScience = 3;
1222type
1223 TTileData = record
1224 Food, Prod, Trade, SubValue, V21: integer;
1225 end;
1226var
1227 i, V21, Loc1, nHierarchy, iH, iT, iH_Switch, MinWorking, MaxWorking,
1228 WantedProd, MinFood, MinProd, count, Take, MaxTake, AreaSize, FormulaCode,
1229 NeedRare, RareTiles, cix1, dx, dy, BestTiles, ProdBeforeBoost, TestTiles,
1230 SubPlus, SuperPlus: integer;
1231 SuperValue, BestSuperValue, SubValue, BestSubValue: integer;
1232 Value, BestValue, ValuePlus: extended;
1233 ValueFormula_Weight: array [oFood .. oScience] of extended;
1234 ValueFormula_Multiply: array [oFood .. oScience] of boolean;
1235 Output: array [oFood .. oScience] of integer;
1236 TileInfo, BaseTileInfo: TTileInfo;
1237 Radius, Radius1: TVicinity21Loc;
1238 TestReport: TCityReport;
1239 CityReportEx: TCityReportEx;
1240 CityAreaInfo: TCityAreaInfo;
1241 Hierarchy: array [0 .. 20, 0 .. 31] of TTileData;
1242 nTile, nSelection: array [0 .. 20] of integer;
1243 SubCriterion: array [0 .. 27] of integer;
1244 FoodWasted, FoodToTax, ProdToTax, RareOK, NeedStep2, IsBest: boolean;
1245begin
1246 if (RW[p].Government = gAnarchy) or (RW[p].City[cix].Flags and chCaptured <> 0)
1247 then
1248 begin
1249 FillChar(Advice.CityReport, SizeOf(Advice.CityReport), 0);
1250 Advice.Tiles := 1 shl CityOwnTile;
1251 Advice.CityReport.HypoTiles := 1 shl CityOwnTile;
1252 exit;
1253 end;
1254
1255 for i := oFood to oScience do
1256 begin // decode evaluation formula from weights parameter
1257 FormulaCode := Advice.ResourceWeights shr (24 - 8 * i) and $FF;
1258 ValueFormula_Multiply[i] := FormulaCode and $80 <> 0;
1259 if FormulaCode and $40 <> 0 then
1260 ValueFormula_Weight[i] := (FormulaCode and $0F) *
1261 (1 shl (FormulaCode and $30 shr 4)) / 16
1262 else
1263 ValueFormula_Weight[i] := (FormulaCode and $0F) *
1264 (1 shl (FormulaCode and $30 shr 4));
1265 end;
1266
1267 TestReport.HypoTiles := 1 shl CityOwnTile;
1268 TestReport.HypoTax := -1;
1269 TestReport.HypoLux := -1;
1270 GetSmallCityReport(p, cix, TestReport, @CityReportEx);
1271 with RW[p].City[cix] do
1272 begin
1273 V21_to_Loc(Loc, Radius);
1274 FoodToTax := RW[p].Government = gFuture;
1275 ProdToTax := Project and (cpImp + cpIndex) = cpImp + imTrGoods;
1276 FoodWasted := not FoodToTax and (Food = StorageSize[Difficulty[p]]) and
1277 not CanCityGrow(p, cix);
1278
1279 // sub criteria
1280 for V21 := 1 to 26 do
1281 begin
1282 Loc1 := Radius[V21];
1283 if Loc1 >= 0 then
1284 SubCriterion[V21] := 3360 - (Distance(Loc, Loc1) - 1) * 32 -
1285 V21 xor $15;
1286 end;
1287 for cix1 := 0 to RW[p].nCity - 1 do
1288 if cix1 <> cix then
1289 begin
1290 Loc1 := RW[p].City[cix1].Loc;
1291 if Loc1 >= 0 then
1292 begin
1293 if Distance(Loc, Loc1) <= 10 then
1294 begin // cities overlap -- prefer tiles outside common range
1295 V21_to_Loc(Loc1, Radius1);
1296 for V21 := 1 to 26 do
1297 begin
1298 Loc1 := Radius1[V21];
1299 if (Loc1 >= 0) and (Loc1 < MapSize) and (Distance(Loc, Loc1) <= 5)
1300 then
1301 begin
1302 dxdy(Loc, Loc1, dx, dy);
1303 dec(SubCriterion[(dy + 3) shl 2 + (dx + 3) shr 1], 160);
1304 end
1305 end
1306 end
1307 end
1308 end;
1309
1310 GetCityAreaInfo(p, Loc, CityAreaInfo);
1311 AreaSize := 0;
1312 for V21 := 1 to 26 do
1313 if CityAreaInfo.Available[V21] = faAvailable then
1314 inc(AreaSize);
1315
1316 if RW[p].Government = gFundamentalism then
1317 begin
1318 MinWorking := Size;
1319 MaxWorking := Size;
1320 end
1321 else
1322 begin
1323 MinWorking := CityReportEx.TradeProcessing.HappyBase shr 1;
1324 if MinWorking > Size then
1325 MinWorking := Size;
1326 if (RW[p].LuxRate = 0) and not CityReportEx.TradeProcessing.FlexibleLuxury
1327 then
1328 MaxWorking := MinWorking
1329 else
1330 MaxWorking := Size;
1331 end;
1332 if MaxWorking > AreaSize then
1333 begin
1334 MaxWorking := AreaSize;
1335 if MinWorking > AreaSize then
1336 MinWorking := AreaSize;
1337 end;
1338 if TestReport.Support = 0 then
1339 WantedProd := 0
1340 else
1341 WantedProd := 1 + (TestReport.Support * 100 - 1)
1342 div (100 + CityReportEx.ProdProcessing.ProdBonus * 50 +
1343 CityReportEx.ProdProcessing.FutProdBonus);
1344
1345 // consider resources for ship parts
1346 NeedRare := 0;
1347 if (GTestFlags and tfNoRareNeed = 0) and (Project and cpImp <> 0) then
1348 case Project and cpIndex of
1349 imShipComp:
1350 NeedRare := fCobalt;
1351 imShipPow:
1352 NeedRare := fUranium;
1353 imShipHab:
1354 NeedRare := fMercury;
1355 end;
1356 if NeedRare > 0 then
1357 begin
1358 RareTiles := 0;
1359 for V21 := 1 to 26 do
1360 begin
1361 Loc1 := Radius[V21];
1362 if (Loc1 >= 0) and (Loc1 < MapSize) and
1363 (RealMap[Loc1] and fModern = cardinal(NeedRare)) then
1364 RareTiles := RareTiles or (1 shl V21);
1365 end
1366 end;
1367
1368 // step 1: sort tiles to hierarchies
1369 nHierarchy := 0;
1370 for V21 := 1 to 26 do // non-rare tiles
1371 if (CityAreaInfo.Available[V21] = faAvailable) and
1372 ((NeedRare = 0) or (1 shl V21 and RareTiles = 0)) then
1373 begin
1374 Loc1 := Radius[V21];
1375 assert((Loc1 >= 0) and (Loc1 < MapSize));
1376 GetTileInfo(p, cix, Loc1, TileInfo);
1377 if V21 = CityOwnTile then
1378 BaseTileInfo := TileInfo
1379 else
1380 begin
1381 iH := 0;
1382 while iH < nHierarchy do
1383 begin
1384 iT := 0;
1385 while (iT < nTile[iH]) and (TileInfo.Food <= Hierarchy[iH, iT].Food)
1386 and (TileInfo.Prod <= Hierarchy[iH, iT].Prod) and
1387 (TileInfo.Trade <= Hierarchy[iH, iT].Trade) and
1388 not((TileInfo.Food = Hierarchy[iH, iT].Food) and
1389 (TileInfo.Prod = Hierarchy[iH, iT].Prod) and
1390 (TileInfo.Trade = Hierarchy[iH, iT].Trade) and
1391 (SubCriterion[V21] >= SubCriterion[Hierarchy[iH, iT].V21])) do
1392 inc(iT);
1393 if (iT = nTile[iH]) // new worst tile in this hierarchy
1394 or ((TileInfo.Food >= Hierarchy[iH, iT].Food)
1395 // new middle tile in this hierarchy
1396 and (TileInfo.Prod >= Hierarchy[iH, iT].Prod) and
1397 (TileInfo.Trade >= Hierarchy[iH, iT].Trade)) then
1398 break; // insert position found!
1399 inc(iH);
1400 end;
1401 if iH = nHierarchy then
1402 begin // need to start new hierarchy
1403 nTile[iH] := 0;
1404 inc(nHierarchy);
1405 iT := 0;
1406 end;
1407 move(Hierarchy[iH, iT], Hierarchy[iH, iT + 1],
1408 (nTile[iH] - iT) * SizeOf(TTileData));
1409 inc(nTile[iH]);
1410 Hierarchy[iH, iT].V21 := V21;
1411 Hierarchy[iH, iT].Food := TileInfo.Food;
1412 Hierarchy[iH, iT].Prod := TileInfo.Prod;
1413 Hierarchy[iH, iT].Trade := TileInfo.Trade;
1414 Hierarchy[iH, iT].SubValue := SubCriterion[V21];
1415 end
1416 end;
1417 if NeedRare <> 0 then
1418 begin // rare tiles need own hierarchy
1419 iH := nHierarchy;
1420 for V21 := 1 to 26 do
1421 if (CityAreaInfo.Available[V21] = faAvailable) and
1422 (1 shl V21 and RareTiles <> 0) then
1423 begin
1424 Loc1 := Radius[V21];
1425 assert((V21 <> CityOwnTile) and (Loc1 >= 0) and (Loc1 < MapSize));
1426 GetTileInfo(p, cix, Loc1, TileInfo);
1427 if iH = nHierarchy then
1428 begin // need to start new hierarchy
1429 nTile[iH] := 0;
1430 inc(nHierarchy);
1431 iT := 0;
1432 end
1433 else
1434 iT := nTile[iH];
1435 inc(nTile[iH]);
1436 Hierarchy[iH, iT].V21 := V21;
1437 Hierarchy[iH, iT].Food := TileInfo.Food; // = 0
1438 Hierarchy[iH, iT].Prod := TileInfo.Prod; // = 1
1439 Hierarchy[iH, iT].Trade := TileInfo.Trade; // = 0
1440 Hierarchy[iH, iT].SubValue := SubCriterion[V21];
1441 end;
1442 end;
1443 if Built[imAlgae] > 0 then
1444 inc(BaseTileInfo.Food, 12);
1445
1446 // step 2: summarize resources
1447 for iH := 0 to nHierarchy - 1 do
1448 begin
1449 move(Hierarchy[iH, 0], Hierarchy[iH, 1], nTile[iH] * SizeOf(TTileData));
1450 Hierarchy[iH, 0].Food := 0;
1451 Hierarchy[iH, 0].Prod := 0;
1452 Hierarchy[iH, 0].Trade := 0;
1453 Hierarchy[iH, 0].SubValue := 0;
1454 Hierarchy[iH, 0].V21 := 0;
1455 for iT := 1 to nTile[iH] do
1456 begin
1457 inc(Hierarchy[iH, iT].Food, Hierarchy[iH, iT - 1].Food);
1458 inc(Hierarchy[iH, iT].Prod, Hierarchy[iH, iT - 1].Prod);
1459 inc(Hierarchy[iH, iT].Trade, Hierarchy[iH, iT - 1].Trade);
1460 inc(Hierarchy[iH, iT].SubValue, Hierarchy[iH, iT - 1].SubValue);
1461 Hierarchy[iH, iT].V21 := 1 shl Hierarchy[iH, iT].V21 +
1462 Hierarchy[iH, iT - 1].V21;
1463 end;
1464 end;
1465
1466 // step 3: try all combinations
1467 BestValue := 0.0;
1468 BestSuperValue := 0;
1469 BestSubValue := 0;
1470 BestTiles := 0;
1471 FillChar(nSelection, SizeOf(nSelection), 0);
1472 TestReport.FoodRep := BaseTileInfo.Food;
1473 ProdBeforeBoost := BaseTileInfo.Prod;
1474 TestReport.Trade := BaseTileInfo.Trade;
1475 TestReport.Working := 1;
1476 MinFood := 0;
1477 MinProd := 0;
1478 iH_Switch := nHierarchy;
1479 count := 0;
1480 repeat
1481 // ensure minima
1482 iH := 0;
1483 while (TestReport.Working < MaxWorking) and (iH < iH_Switch) and
1484 ((TestReport.Working < MinWorking) or
1485 (TestReport.FoodRep < TestReport.Eaten) or
1486 (ProdBeforeBoost < WantedProd)) do
1487 begin
1488 assert(nSelection[iH] = 0);
1489 Take := MinWorking - TestReport.Working;
1490 if Take > nTile[iH] then
1491 Take := nTile[iH]
1492 else
1493 begin
1494 if Take < 0 then
1495 Take := 0;
1496 MaxTake := nTile[iH];
1497 if TestReport.Working + MaxTake > MaxWorking then
1498 MaxTake := MaxWorking - TestReport.Working;
1499 while (Take < MaxTake) and
1500 (TestReport.FoodRep + Hierarchy[iH, Take].Food < MinFood) do
1501 inc(Take);
1502 while (Take < MaxTake) and
1503 (ProdBeforeBoost + Hierarchy[iH, Take].Prod < MinProd) do
1504 inc(Take);
1505 end;
1506 nSelection[iH] := Take;
1507 inc(TestReport.Working, Take);
1508 with Hierarchy[iH, Take] do
1509 begin
1510 inc(TestReport.FoodRep, Food);
1511 inc(ProdBeforeBoost, Prod);
1512 inc(TestReport.Trade, Trade);
1513 end;
1514 inc(iH);
1515 end;
1516
1517 assert((TestReport.Working >= MinWorking) and
1518 (TestReport.Working <= MaxWorking));
1519 if (TestReport.FoodRep >= MinFood) and (ProdBeforeBoost >= MinProd) then
1520 begin
1521 SplitTrade(TestReport.Trade, RW[p].TaxRate, RW[p].LuxRate,
1522 TestReport.Working, CityReportEx.TradeProcessing,
1523 TestReport.Corruption, TestReport.Tax, TestReport.Lux,
1524 TestReport.Science);
1525
1526 if CityReportEx.BaseHappiness + CityReportEx.BaseControl +
1527 TestReport.Lux + 2 * (Size - TestReport.Working) - 2 *
1528 TestReport.Deployed >= Size then
1529 begin // city is not in disorder -- evaluate combination
1530 inc(count);
1531 if (MinProd < WantedProd) and (ProdBeforeBoost > MinProd) then
1532 begin // no combination reached wanted prod yet
1533 MinProd := ProdBeforeBoost;
1534 if MinProd > WantedProd then
1535 MinProd := WantedProd
1536 end;
1537 if MinProd = WantedProd then
1538 // do not care for food before prod is ensured
1539 if (MinFood < TestReport.Eaten) and (TestReport.FoodRep > MinFood)
1540 then
1541 begin // no combination reached wanted food yet
1542 MinFood := TestReport.FoodRep;
1543 if MinFood > TestReport.Eaten then
1544 MinFood := TestReport.Eaten
1545 end;
1546 BoostProd(ProdBeforeBoost, CityReportEx.ProdProcessing,
1547 TestReport.ProdRep, TestReport.PollRep);
1548 SuperValue := 0;
1549
1550 // super-criterion A: unit support granted?
1551 if TestReport.ProdRep >= TestReport.Support then
1552 SuperValue := SuperValue or 1 shl 30;
1553
1554 // super-criterion B: food demand granted?
1555 if TestReport.FoodRep >= TestReport.Eaten then
1556 SuperValue := SuperValue or 63 shl 24
1557 else if TestReport.FoodRep > TestReport.Eaten - 63 then
1558 SuperValue := SuperValue or
1559 (63 - (TestReport.Eaten - TestReport.FoodRep)) shl 24;
1560
1561 SuperPlus := SuperValue - BestSuperValue;
1562 if SuperPlus >= 0 then
1563 begin
1564 Output[oTax] := TestReport.Tax;
1565 Output[oScience] := TestReport.Science;
1566
1567 if TestReport.FoodRep < TestReport.Eaten then
1568 Output[oFood] := TestReport.FoodRep
1569 // appreciate what we have, combination will have bad supervalue anyway
1570 else if FoodWasted then
1571 Output[oFood] := 0
1572 else
1573 begin
1574 Output[oFood] := TestReport.FoodRep - TestReport.Eaten;
1575 if FoodToTax or (Size >= NeedAqueductSize) and (Output[oFood] = 1)
1576 then
1577 begin
1578 inc(Output[oTax], Output[oFood]);
1579 Output[oFood] := 0;
1580 end;
1581 end;
1582
1583 if TestReport.ProdRep < TestReport.Support then
1584 Output[oProd] := TestReport.ProdRep
1585 // appreciate what we have, combination will have bad supervalue anyway
1586 else
1587 begin
1588 if NeedRare > 0 then
1589 begin
1590 RareOK := false;
1591 for iH := 0 to nHierarchy - 1 do
1592 if Hierarchy[iH, nSelection[iH]].V21 and RareTiles <> 0 then
1593 RareOK := true;
1594 if not RareOK then
1595 TestReport.ProdRep := TestReport.Support;
1596 end;
1597 Output[oProd] := TestReport.ProdRep - TestReport.Support;
1598 if ProdToTax then
1599 begin
1600 inc(Output[oTax], Output[oProd]);
1601 Output[oProd] := 0;
1602 end;
1603 end;
1604
1605 NeedStep2 := false;
1606 Value := 0;
1607 for i := oFood to oScience do
1608 if ValueFormula_Multiply[i] then
1609 NeedStep2 := true
1610 else
1611 Value := Value + ValueFormula_Weight[i] * Output[i];
1612 if NeedStep2 then
1613 begin
1614 if Value > 0 then
1615 Value := ln(Value) + 123;
1616 for i := oFood to oScience do
1617 if ValueFormula_Multiply[i] and (Output[i] > 0) then
1618 Value := Value + ValueFormula_Weight[i] *
1619 (ln(Output[i]) + 123);
1620 end;
1621
1622 ValuePlus := Value - BestValue;
1623 if (SuperPlus > 0) or (ValuePlus >= 0.0) then
1624 begin
1625 SubValue := (TestReport.FoodRep + ProdBeforeBoost +
1626 TestReport.Trade) shl 18;
1627 TestTiles := 1 shl CityOwnTile;
1628 for iH := 0 to nHierarchy - 1 do
1629 begin
1630 inc(TestTiles, Hierarchy[iH, nSelection[iH]].V21);
1631 inc(SubValue, Hierarchy[iH, nSelection[iH]].SubValue);
1632 end;
1633 IsBest := true;
1634 if (SuperPlus = 0) and (ValuePlus = 0.0) then
1635 begin
1636 SubPlus := SubValue - BestSubValue;
1637 if SubPlus < 0 then
1638 IsBest := false
1639 else if SubPlus = 0 then
1640 begin
1641 assert(TestTiles <> BestTiles);
1642 IsBest := TestTiles > BestTiles
1643 end
1644 end;
1645 if IsBest then
1646 begin
1647 BestSuperValue := SuperValue;
1648 BestValue := Value;
1649 BestSubValue := SubValue;
1650 BestTiles := TestTiles;
1651 TestReport.Happy :=
1652 (CityReportEx.TradeProcessing.HappyBase - Size) div 2 +
1653 TestReport.Lux shr 1;
1654 Advice.CityReport := TestReport;
1655 end
1656 end // if (SuperPlus>0) or (ValuePlus>=0.0)
1657 end // if SuperPlus>=0
1658 end
1659 end;
1660
1661 // calculate next combination
1662 iH_Switch := 0;
1663 repeat
1664 with Hierarchy[iH_Switch, nSelection[iH_Switch]] do
1665 begin
1666 dec(TestReport.FoodRep, Food);
1667 dec(ProdBeforeBoost, Prod);
1668 dec(TestReport.Trade, Trade);
1669 end;
1670 inc(nSelection[iH_Switch]);
1671 inc(TestReport.Working);
1672 if (nSelection[iH_Switch] <= nTile[iH_Switch]) and
1673 (TestReport.Working <= MaxWorking) then
1674 begin
1675 with Hierarchy[iH_Switch, nSelection[iH_Switch]] do
1676 begin
1677 inc(TestReport.FoodRep, Food);
1678 inc(ProdBeforeBoost, Prod);
1679 inc(TestReport.Trade, Trade);
1680 end;
1681 break;
1682 end;
1683 dec(TestReport.Working, nSelection[iH_Switch]);
1684 nSelection[iH_Switch] := 0;
1685 inc(iH_Switch);
1686 until iH_Switch = nHierarchy;
1687 until iH_Switch = nHierarchy; // everything tested -- done
1688 end;
1689 assert(BestSuperValue > 0); // advice should always be possible
1690 Advice.Tiles := BestTiles;
1691 Advice.CityReport.HypoTiles := BestTiles;
1692end; // GetCityTileAdvice
1693
1694{
1695 Start/End Game
1696 ____________________________________________________________________
1697}
1698procedure InitGame;
1699var
1700 p, i, mixTownGuard: integer;
1701begin
1702 MaxDist := Distance(0, MapSize - lx shr 1);
1703 for p := 0 to nPl - 1 do
1704 if (1 shl p and GAlive <> 0) then
1705 with RW[p] do
1706 begin // initialize capital
1707 mixTownGuard := 0;
1708 while Model[mixTownGuard].Kind <> mkSpecial_TownGuard do
1709 inc(mixTownGuard);
1710 with City[0] do
1711 begin
1712 Built[imPalace] := 1;
1713 Size := 4;
1714 for i := 2 to Size do
1715 AddBestCityTile(p, 0);
1716 Project := mixTownGuard;
1717 end;
1718 NatBuilt[imPalace] := 1;
1719 end;
1720end;
1721
1722procedure ReleaseGame;
1723begin
1724end;
1725
1726end.
Note: See TracBrowser for help on using the repository browser.