Changeset 6 for trunk/CityProcessing.pas


Ignore:
Timestamp:
Jan 7, 2017, 11:32:14 AM (7 years ago)
Author:
chronos
Message:
  • Modified: Formated all project source files using Delphi formatter as original indentation and other formatting was really bad.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/CityProcessing.pas

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