1  {$INCLUDE Switches.inc}


2  unit CityProcessing;


3 


4  interface


5 


6  uses


7  Protocol, Database;


8 


9  // 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;


14  var CityReportNew: TCityReportNew): integer;


15 


16  // Internal Tile Picking


17  function AddBestCityTile(p, cix: integer): boolean;


18  procedure CityGrowth(p, cix: integer);


19  procedure CityShrink(p, cix: integer);


20  procedure Pollute(p, cix: integer);


21 


22  // Turn Processing


23  procedure PayCityMaintenance(p, cix: integer);


24  procedure CollectCityResources(p, cix: integer);


25  function CityTurn(p, cix: integer): boolean;


26 


27  // Tile Access


28  function SetCityTiles(p, cix, NewTiles: integer;


29  TestOnly: boolean = false): integer;


30  procedure GetCityTileAdvice(p, cix: integer; var Advice: TCityTileAdviceData);


31 


32  // Start/End Game


33  procedure InitGame;


34  procedure ReleaseGame;


35 


36  implementation


37 


38  type


39  TTradeProcessing = record


40  TaxBonus, LuxBonus, ScienceBonus, FutResBonus, ScienceDoubling,


41  HappyBase: integer;


42  RelCorr: single;


43  FlexibleLuxury: boolean;


44  end;


45 


46  TProdProcessing = record


47  ProdBonus, PollBonus, FutProdBonus, PollThreshold: integer;


48  end;


49 


50  PCityReportEx = ^TCityReportEx;


51 


52  TCityReportEx = record


53  BaseHappiness, BaseControl, Material: integer;


54  ProdProcessing: TProdProcessing;


55  TradeProcessing: TTradeProcessing;


56  end;


57 


58  var


59  MaxDist: integer;


60 


61  {


62  Reporting


63  ____________________________________________________________________


64  }


65  procedure GetCityAreaInfo(p, Loc: integer; var CityAreaInfo: TCityAreaInfo);


66  var


67  V21, Loc1, p1: integer;


68  Radius: TVicinity21Loc;


69  begin


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


94  end;


95 


96  function CanCityGrow(p, cix: integer): boolean;


97  begin


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));


102  end;


103 


104  procedure DetermineCityProdProcessing(p, cix: integer;


105  var ProdProcessing: TProdProcessing);


106  begin


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;


141  end;


142 


143  procedure BoostProd(BaseProd: integer; ProdProcessing: TProdProcessing;


144  var Prod, Poll: integer);


145  begin


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;


156  end;


157 


158  procedure DetermineCityTradeProcessing(p, cix, HappinessBeforeLux: integer;


159  var TradeProcessing: TTradeProcessing);


160  var


161  i, Dist: integer;


162  begin


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


227  end;


228 


229  procedure SplitTrade(Trade, TaxRate, LuxRate, Working: integer;


230  TradeProcessing: TTradeProcessing; var Corruption, Tax, Lux,


231  Science: integer);


232  var


233  plus: integer;


234  begin


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);


266  end;


267 


268  function GetProjectCost(p, cix: integer): integer;


269  var


270  i: integer;


271  begin


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


300  end;


301 


302  function GetSmallCityReport(p, cix: integer; var CityReport: TCityReport;


303  PCityReportEx: PCityReportEx = nil): integer;


304  var


305  i, uix, V21, Loc1, ForcedSupport, BaseHappiness, Control: integer;


306  ProdProcessing: TProdProcessing;


307  TradeProcessing: TTradeProcessing;


308  Radius: TVicinity21Loc;


309  UnitReport: TUnitReport;


310  RareOK: array [0 .. 3] of integer;


311  TileInfo: TTileInfo;


312  begin


313  with RW[p].City[cix], CityReport do


314  begin


315  if HypoTiles <= 0 then


316  HypoTiles := Tiles;


317  if HypoTax < 0 then


318  HypoTax := RW[p].TaxRate;


319  if HypoLux < 0 then


320  HypoLux := RW[p].LuxRate;


321 


322  if (Flags and chCaptured <> 0) or (RW[p].Government = gAnarchy) then


323  begin


324  Working := 0;


325  for V21 := 1 to 26 do


326  if HypoTiles and (1 shl V21) <> 0 then


327  inc(Working); // for backward compatibility


328 


329  if RW[p].Government = gFundamentalism then


330  begin


331  Happy := Size;


332  Control := Size


333  end // !!! old bug, kept for compatibility


334  else


335  begin


336  Happy := 0;


337  Control := 0


338  end;


339 


340  BaseHappiness := BasicHappy * 2;


341  Support := 0;


342  Deployed := 0;


343  Eaten := Size * 2;


344  FoodRep := Size * 2;


345  ProdRep := 0;


346  Trade := 0;


347  PollRep := 0;


348  Corruption := 0;


349  Tax := 0;


350  Lux := 0;


351  Science := 0;


352 


353  if PCityReportEx <> nil then


354  begin


355  PCityReportEx.Material := ProdRep;


356  PCityReportEx.BaseHappiness := BaseHappiness;


357  PCityReportEx.BaseControl := Control;


358  end;


359  end


360  else // not captured, no anarchy


361  begin


362  Control := 0;


363  BaseHappiness := BasicHappy * 2;


364  Happy := BasicHappy;


365  if (Built[imColosseum] > 0) then


366  begin


367  if (Happy < (Size + 1) shr 1) then


368  Happy := (Size + 1) shr 1;


369  if Size > 4 then


370  BaseHappiness := Size;


371  end;


372  for i := 0 to 27 do


373  if Built[i] = 1 then


374  begin


375  inc(Happy);


376  inc(BaseHappiness, 2)


377  end;


378  if Built[imTemple] = 1 then


379  begin


380  inc(Happy);


381  inc(BaseHappiness, 2)


382  end;


383  if Built[imCathedral] = 1 then


384  begin


385  inc(Happy, 2);


386  inc(BaseHappiness, 4);


387  if GWonder[woBach].EffectiveOwner = p then


388  begin


389  inc(Happy);


390  inc(BaseHappiness, 2)


391  end;


392  end;


393  if Built[imTheater] > 0 then


394  begin


395  inc(Happy, 2);


396  inc(BaseHappiness, 4)


397  end;


398 


399  // calculate unit support


400  {$IFOPT O}assert(InvalidTreatyMap = 0); {$ENDIF}


401  Support := 0;


402  ForcedSupport := 0;


403  Eaten := Size * 2;


404  Deployed := 0;


405  for uix := 0 to RW[p].nUn  1 do


406  with RW[p].Un[uix] do


407  if (Loc >= 0) and (Home = cix) then


408  begin


409  GetUnitReport(p, uix, UnitReport);


410  inc(Eaten, UnitReport.FoodSupport);


411  if UnitReport.ReportFlags and urfAlwaysSupport <> 0 then


412  inc(ForcedSupport, UnitReport.ProdSupport)


413  else


414  inc(Support, UnitReport.ProdSupport);


415  if UnitReport.ReportFlags and urfDeployed <> 0 then


416  inc(Deployed);


417  end;


418  if Deployed >= Happy then


419  Happy := 0


420  else


421  dec(Happy, Deployed);


422  dec(Support, Size * SupportFree[RW[p].Government] shr 1);


423  if Support < 0 then


424  Support := 0;


425  inc(Support, ForcedSupport);


426 


427  { control }


428  case RW[p].Government of


429  gDespotism:


430  for uix := 0 to RW[p].nUn  1 do


431  if (RW[p].Un[uix].Loc = Loc) and


432  (RW[p].Model[RW[p].Un[uix].mix].Kind = mkSpecial_TownGuard) then


433  begin


434  inc(Happy);


435  inc(Control, 2)


436  end;


437  gFundamentalism:


438  begin


439  BaseHappiness := 0; // done by control


440  Happy := Size;


441  Control := Size


442  end;


443  end;


444 


445  // collect processing parameters


446  DetermineCityProdProcessing(p, cix, ProdProcessing);


447  DetermineCityTradeProcessing(p, cix, BaseHappiness + Control  2 *


448  Deployed, TradeProcessing);


449 


450  // collect resources


451  Working := 0;


452  FoodRep := 0;


453  ProdRep := 0;


454  Trade := 0;


455  FillChar(RareOK, SizeOf(RareOK), 0);


456  V21_to_Loc(Loc, Radius);


457  for V21 := 1 to 26 do


458  if HypoTiles and (1 shl V21) <> 0 then


459  begin { sum resources of exploited tiles }


460  Loc1 := Radius[V21];


461  if (Loc1 < 0) or (Loc1 >= MapSize) then


462  // HypoTiles go beyond map border!


463  begin


464  result := eInvalid;


465  exit


466  end;


467  GetTileInfo(p, cix, Loc1, TileInfo);


468  inc(FoodRep, TileInfo.Food);


469  inc(ProdRep, TileInfo.Prod);


470  inc(Trade, TileInfo.Trade);


471  if (RealMap[Loc1] and fModern <> 0) and


472  (RW[p].Tech[adMassProduction] >= tsApplicable) then


473  inc(RareOK[RealMap[Loc1] shr 25 and 3]);


474  inc(Working)


475  end;


476  if Built[imAlgae] = 1 then


477  inc(FoodRep, 12);


478 


479  if PCityReportEx <> nil then


480  begin


481  PCityReportEx.Material := ProdRep;


482  PCityReportEx.BaseHappiness := BaseHappiness;


483  PCityReportEx.BaseControl := Control;


484  PCityReportEx.ProdProcessing := ProdProcessing;


485  PCityReportEx.TradeProcessing := TradeProcessing;


486  end;


487 


488  BoostProd(ProdRep, ProdProcessing, ProdRep, PollRep);


489  SplitTrade(Trade, HypoTax, HypoLux, Working, TradeProcessing, Corruption,


490  Tax, Lux, Science);


491  Happy := Happy + (Lux + Size and 1) shr 1;


492  // new style disorder requires 1 lux less for cities with odd size


493 


494  // check if rare resource available


495  if (GTestFlags and tfNoRareNeed = 0) and (ProdRep > Support) and


496  (Project and cpImp <> 0) and ((Project and cpIndex = imShipComp) and


497  (RareOK[1] = 0) or (Project and cpIndex = imShipPow) and (RareOK[2] = 0)


498  or (Project and cpIndex = imShipHab) and (RareOK[3] = 0)) then


499  ProdRep := Support;


500  end;


501  end;


502  result := eOk;


503  end; { GetSmallCityReport }


504 


505  function GetCityReport(p, cix: integer; var CityReport: TCityReport): integer;


506  begin


507  result := GetSmallCityReport(p, cix, CityReport);


508  CityReport.Storage := StorageSize[Difficulty[p]];


509  CityReport.ProdCost := GetProjectCost(p, cix);


510  end;


511 


512  function GetCityReportNew(p, cix: integer;


513  var CityReportNew: TCityReportNew): integer;


514  var


515  CityReport: TCityReport;


516  CityReportEx: TCityReportEx;


517  begin


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;


546  end;


547 


548  {


549  Internal Tile Picking


550  ____________________________________________________________________


551  }


552  procedure NextBest(p, cix: integer; var SelectedLoc, SelectedV21: integer);


553  { best tile unused but available by city cix }


554  var


555  Resources, Most, Loc1, p1, V21: integer;


556  TileInfo: TTileInfo;


557  Radius: TVicinity21Loc;


558  begin


559  {$IFOPT O}assert(1 shl p and InvalidTreatyMap = 0); {$ENDIF}


560  Most := 0;


561  SelectedLoc := 1;


562  SelectedV21 := 1;


563  with RW[p].City[cix] do


564  begin


565  V21_to_Loc(Loc, Radius);


566  for V21 := 1 to 26 do


567  begin


568  Loc1 := Radius[V21];


569  if (Loc1 >= 0) and (Loc1 < MapSize) and (UsedByCity[Loc1] = 1) then


570  begin


571  p1 := RealMap[Loc1] shr 27;


572  if ((p1 = nPl) or (p1 = p) or (RW[p].Treaty[p1] < trPeace)) and


573  ((ZoCMap[Loc1] = 0) or (Occupant[Loc1] = p) or


574  (RW[p].Treaty[Occupant[Loc1]] = trAlliance)) then


575  begin


576  GetTileInfo(p, cix, Loc1, TileInfo);


577  Resources := TileInfo.Food shl 16 + TileInfo.Prod shl 8 +


578  TileInfo.Trade;


579  { priority: 1.food  2.prod  3.trade }


580  if Resources > Most then


581  begin


582  SelectedLoc := Loc1;


583  SelectedV21 := V21;


584  Most := Resources


585  end


586  end


587  end


588  end;


589  end;


590  end;


591 


592  procedure NextWorst(p, cix: integer; var SelectedLoc, SelectedV21: integer);


593  { worst tile used by city cix }


594  var


595  Resources, Least, Loc1, V21: integer;


596  Radius: TVicinity21Loc;


597  TileInfo: TTileInfo;


598  begin


599  Least := MaxInt;


600  SelectedLoc := 1;


601  SelectedV21 := 1;


602  with RW[p].City[cix] do


603  begin


604  V21_to_Loc(Loc, Radius);


605  for V21 := 1 to 26 do


606  if V21 <> CityOwnTile then


607  begin


608  Loc1 := Radius[V21];


609  if (Loc1 >= 0) and (Loc1 < MapSize) and (1 shl V21 and Tiles <> 0) then


610  begin


611  GetTileInfo(p, cix, Loc1, TileInfo);


612  Resources := TileInfo.Food shl 16 + TileInfo.Prod shl 8 +


613  TileInfo.Trade;


614  { priority: 1.food  2.prod  3.trade }


615  if Resources < Least then


616  begin


617  SelectedLoc := Loc1;


618  SelectedV21 := V21;


619  Least := Resources


620  end


621  end;


622  end


623  end


624  end;


625 


626  function NextPoll(p, cix: integer): integer;


627  var


628  Resources, Best, dx, dy, Loc1, Dist, BestDist, V21, pTerr: integer;


629  Radius: TVicinity21Loc;


630  TileInfo: TTileInfo;


631  begin


632  BestDist := MaxInt;


633  {$IFOPT O}assert(1 shl p and InvalidTreatyMap = 0); {$ENDIF}


634  Best := 0;


635  result := 1;


636  with RW[p].City[cix] do


637  begin


638  V21_to_Loc(Loc, Radius);


639  for V21 := 1 to 26 do


640  if V21 <> CityOwnTile then


641  begin


642  Loc1 := Radius[V21];


643  if (Loc1 >= 0) and (Loc1 < MapSize) and


644  (RealMap[Loc1] and fTerrain >= fGrass) and


645  (RealMap[Loc1] and (fPoll or fDeadLands or fCity) = 0) then


646  begin


647  pTerr := RealMap[Loc1] shr 27;


648  if (pTerr = nPl) or (pTerr = p) or (RW[p].Treaty[pTerr] < trPeace)


649  then


650  begin


651  GetTileInfo(p, cix, Loc1, TileInfo);


652  Resources := TileInfo.Prod shl 16 + TileInfo.Trade shl 8 +


653  TileInfo.Food;


654  { priority: 1.prod  2.trade  3.food }


655  dy := V21 shr 2  3;


656  dx := V21 and 3 shl 1  3 + (dy + 3) and 1;


657  Dist := abs(dx) + abs(dy) + abs(abs(dx)  abs(dy)) shr 1;


658  if (Resources > Best) or (Resources = Best) and (Dist < BestDist)


659  then


660  begin


661  result := Loc1;


662  Best := Resources;


663  BestDist := Dist;


664  end;


665  end;


666  end;


667  end;


668  end;


669  end;


670 


671  function AddBestCityTile(p, cix: integer): boolean;


672  var


673  TileLoc, V21: integer;


674  begin


675  NextBest(p, cix, TileLoc, V21);


676  result := TileLoc >= 0;


677  if result then


678  with RW[p].City[cix] do


679  begin


680  assert(1 shl V21 and Tiles = 0);


681  Tiles := Tiles or (1 shl V21);


682  UsedByCity[TileLoc] := Loc


683  end


684  end;


685 


686  procedure CityGrowth(p, cix: integer);


687  var


688  TileLoc, V21: integer;


689  AltCityReport: TCityReport;


690  begin


691  with RW[p].City[cix] do


692  begin


693  inc(Size);


694  NextBest(p, cix, TileLoc, V21);


695  if TileLoc >= 0 then


696  begin { test whether exploitation of tile would lead to disorder }


697  AltCityReport.HypoTiles := Tiles + 1 shl V21;


698  AltCityReport.HypoTax := 1;


699  AltCityReport.HypoLux := 1;


700  GetSmallCityReport(p, cix, AltCityReport);


701  if AltCityReport.Working  AltCityReport.Happy <= Size shr 1 then


702  // !!! change to new style disorder


703  begin { no disorder  exploit tile }


704  assert(1 shl V21 and Tiles = 0);


705  Tiles := Tiles or (1 shl V21);


706  UsedByCity[TileLoc] := Loc


707  end


708  end;


709  end


710  end;


711 


712  procedure CityShrink(p, cix: integer);


713  var


714  TileLoc, V21, Working: integer;


715  AltCityReport: TCityReport;


716  begin


717  with RW[p].City[cix] do


718  begin


719  Working := 0;


720  for V21 := 1 to 26 do


721  if Tiles and (1 shl V21) <> 0 then


722  inc(Working);


723  dec(Size);


724  if Food > StorageSize[Difficulty[p]] then


725  Food := StorageSize[Difficulty[p]];


726  NextWorst(p, cix, TileLoc, V21);


727  if Working > Size then


728  begin { all citizens were working  worst tile no longer exploited }


729  assert(1 shl V21 and Tiles <> 0);


730  Tiles := Tiles and not(1 shl V21);


731  UsedByCity[TileLoc] := 1


732  end


733  else { test whether exploitation of tile would lead to disorder }


734  begin


735  AltCityReport.HypoTiles := 1;


736  AltCityReport.HypoTax := 1;


737  AltCityReport.HypoLux := 1;


738  GetSmallCityReport(p, cix, AltCityReport);


739  if AltCityReport.Working  AltCityReport.Happy > Size shr 1 then


740  // !!! change to new style disorder


741  begin { disorder  don't exploit tile }


742  assert(1 shl V21 and Tiles <> 0);


743  Tiles := Tiles and not(1 shl V21);


744  UsedByCity[TileLoc] := 1


745  end


746  end;


747  end


748  end;


749 


750  procedure Pollute(p, cix: integer);


751  var


752  PollutionLoc: integer;


753  begin


754  with RW[p].City[cix] do


755  begin


756  Pollution := Pollution  MaxPollution;


757  PollutionLoc := NextPoll(p, cix);


758  if PollutionLoc >= 0 then


759  begin


760  inc(Flags, chPollution);


761  RealMap[PollutionLoc] := RealMap[PollutionLoc] or fPoll;


762  end


763  end;


764  end;


765 


766  {


767  Turn Processing


768  ____________________________________________________________________


769  }


770  procedure PayCityMaintenance(p, cix: integer);


771  var


772  i: integer;


773  begin


774  with RW[p], City[cix] do


775  for i := 28 to nImp  1 do


776  if (Built[i] > 0) and (Project0 and (cpImp or cpIndex) <> (cpImp or i))


777  then // don't pay maintenance when just completed


778  begin


779  dec(Money, Imp[i].Maint);


780  if Money < 0 then


781  begin { out of money  sell improvement }


782  inc(Money, Imp[i].Cost * BuildCostMod[Difficulty[p]] div 12);


783  Built[i] := 0;


784  if Imp[i].Kind <> ikCommon then


785  begin


786  assert(i <> imSpacePort);


787  // never sell automatically! (solution: no maintenance)


788  NatBuilt[i] := 0;


789  if i = imGrWall then


790  GrWallContinent[p] := 1;


791  end;


792  inc(Flags, chImprovementLost)


793  end


794  end;


795  end;


796 


797  procedure CollectCityResources(p, cix: integer);


798  var


799  CityStorage, CityProjectCost: integer;


800  CityReport: TCityReportNew;


801  Disorder: boolean;


802  begin


803  with RW[p], City[cix], CityReport do


804  if Flags and chCaptured <> 0 then


805  begin


806  Flags := Flags and not chDisorder;


807  dec(Flags, $10000);


808  if Flags and chCaptured = 0 then


809  Flags := Flags or chAfterCapture;


810  end


811  else if Government = gAnarchy then


812  Flags := Flags and not chDisorder


813  else


814  begin


815  HypoTiles := 1;


816  HypoTaxRate := 1;


817  HypoLuxuryRate := 1;


818  GetCityReportNew(p, cix, CityReport);


819  CityStorage := StorageSize[Difficulty[p]];


820  CityProjectCost := GetProjectCost(p, cix);


821 


822  Disorder := (HappinessBalance < 0);


823  if Disorder and (Flags and chDisorder <> 0) then


824  CollectedMaterial := 0; // second turn disorder


825  if Disorder then


826  Flags := Flags or chDisorder


827  else


828  Flags := Flags and not chDisorder;


829 


830  if not Disorder and ((Government = gFuture) or (Size >= NeedAqueductSize)


831  and (FoodSurplus < 2)) and (FoodSurplus > 0) then


832  inc(Money, FoodSurplus)


833  else if not(Disorder and (FoodSurplus > 0)) then


834  begin { calculate new food storage }


835  Food := Food + FoodSurplus;


836  if ((GTestFlags and tfImmGrow <> 0) or (Food >= CityStorage) and


837  (Food  FoodSurplus < CityStorage)) // only warn once


838  and (Size < MaxCitySize) and


839  (Project and (cpImp + cpIndex) <> cpImp + imAqueduct) and


840  (Project and (cpImp + cpIndex) <> cpImp + imSewer) and


841  not CanCityGrow(p, cix) then


842  inc(Flags, chNoGrowthWarning);


843  end;


844 


845  if Prod > CityProjectCost then


846  begin


847  inc(Money, Prod  CityProjectCost);


848  Prod := CityProjectCost


849  end;


850  if Production < 0 then


851  Flags := Flags or chUnitLost


852  else if not Disorder and (Flags and chProductionSabotaged = 0) then


853  if Project and (cpImp + cpIndex) = cpImp + imTrGoods then


854  inc(Money, Production)


855  else


856  inc(Prod, Production);


857 


858  if not Disorder then


859  begin


860  { sum research points and taxes }


861  inc(Research, Science);


862  inc(Money, Tax);


863  Pollution := Pollution + AddPollution;


864  end;


865  end;


866  end;


867 


868  function CityTurn(p, cix: integer): boolean;


869  // return value: whether city keeps existing


870  var


871  i, uix, cix2, p1, SizeMod, CityStorage, CityProjectCost, NewImp, Det,


872  TestDet: integer;


873  LackOfMaterial, CheckGrow, DoProd, IsActive: boolean;


874  begin


875  with RW[p], City[cix] do


876  begin


877  SizeMod := 0;


878  CityStorage := StorageSize[Difficulty[p]];


879  CityProjectCost := GetProjectCost(p, cix);


880 


881  LackOfMaterial := Flags and chUnitLost <> 0;


882  Flags := Flags and not chUnitLost;


883 


884  IsActive := (Government <> gAnarchy) and (Flags and chCaptured = 0);


885  CheckGrow := (Flags and chDisorder = 0) and IsActive and


886  (Government <> gFuture);


887  if CheckGrow and (GTestFlags and tfImmGrow <> 0) then { fast growth }


888  begin


889  if CanCityGrow(p, cix) then


890  inc(SizeMod)


891  end


892  else if CheckGrow and (Food >= CityStorage) then { normal growth }


893  begin


894  if CanCityGrow(p, cix) then


895  begin


896  if Built[imGranary] = 1 then


897  dec(Food, CityStorage shr 1)


898  else


899  dec(Food, CityStorage);


900  inc(SizeMod)


901  end


902  end


903  else if Food < 0 then { famine }


904  begin


905  Food := 0;


906  // check if settlers or conscripts there to disband


907  uix := 1;


908  for i := 0 to nUn  1 do


909  if (Un[i].Loc >= 0) and (Un[i].Home = cix) and


910  ((Model[Un[i].mix].Kind = mkSettler)


911  { and (GWonder[woFreeSettlers].EffectiveOwner<>p) }


912  or (Un[i].Flags and unConscripts <> 0)) and


913  ((uix = 1) or (Model[Un[i].mix].Cost < Model[Un[uix].mix].Cost) or


914  (Model[Un[i].mix].Cost = Model[Un[uix].mix].Cost) and


915  (Un[i].Exp < Un[uix].Exp)) then


916  uix := i;


917 


918  if uix >= 0 then


919  begin


920  RemoveUnit_UpdateMap(p, uix);


921  inc(Flags, chUnitLost);


922  end


923  else


924  begin


925  dec(SizeMod);


926  inc(Flags, chPopDecrease)


927  end


928  end;


929  if Food > CityStorage then


930  Food := CityStorage;


931 


932  if LackOfMaterial then


933  begin


934  if Flags and chUnitLost = 0 then


935  begin { one unit lost }


936  uix := 1;


937  Det := MaxInt;


938  for i := 0 to nUn  1 do


939  if (Un[i].Loc >= 0) and (Un[i].Home = cix) then


940  with Model[Un[i].mix] do


941  begin


942  if Kind = mkSpecial_TownGuard then


943  TestDet := Un[i].Health + Un[i].Exp shl 8


944  // disband townguards first


945  else


946  begin


947  TestDet := Un[i].Health + Un[i].Exp shl 8 + Cost shl 16;


948  // value of unit


949  if Flags and mdDoubleSupport <> 0 then


950  TestDet := TestDet shr 1;


951  // double support, tend to disband first


952  end;


953  if TestDet < Det then


954  begin


955  uix := i;


956  Det := TestDet


957  end;


958  end;


959  if uix >= 0 then


960  begin


961  RemoveUnit_UpdateMap(p, uix);


962  inc(Flags, chUnitLost);


963  end


964  end


965  end;


966 


967  if GTestFlags and tfImmImprove <> 0 then


968  Prod := CityProjectCost;


969  DoProd := (Project and (cpImp + cpIndex) <> cpImp + imTrGoods) and


970  (Prod >= CityProjectCost);


971 


972  // check if wonder already built


973  if (Project and cpImp <> 0) and (Project and cpIndex < 28) and


974  (GWonder[Project and cpIndex].CityID <> WonderNotBuiltYet) then


975  begin


976  inc(Flags, chOldWonder);


977  DoProd := false;


978  end;


979 


980  // check if producing settlers would disband city


981  if DoProd and (Project and (cpImp or cpDisbandCity) = 0) and


982  ((Size + SizeMod  2 < 2) and


983  (Model[Project and cpIndex].Kind = mkSettler) or (Size + SizeMod  1 < 2)


984  and ((Model[Project and cpIndex].Kind = mkSlaves) or


985  (Project and cpConscripts <> 0))) then


986  begin


987  inc(Flags, chNoSettlerProd);


988  DoProd := false;


989  end;


990 


991  if DoProd then


992  begin { project complete }


993  dec(Prod, CityProjectCost);


994  if Project and cpImp = 0 then { produce unit }


995  begin


996  if nUn < numax then


997  begin


998  CreateUnit(p, Project and cpIndex);


999  Un[nUn  1].Loc := Loc;


1000  with Un[nUn  1] do


1001  begin


1002  Home := cix;


1003  if (Model[mix].Domain < dSea) and (Built[imElite] = 1) then


1004  Exp := ExpCost * (nExp  1) { elite }


1005  else if (Model[mix].Domain < dSea) and (Built[imBarracks] = 1) or


1006  (Model[mix].Domain = dSea) and (Built[imDockyard] = 1) or


1007  (Model[mix].Domain = dAir) and (Built[imAirport] = 1) then


1008  Exp := ExpCost * 2; { vet }


1009  if Project and cpConscripts <> 0 then


1010  Flags := Flags or unConscripts


1011  end;


1012  PlaceUnit(p, nUn  1);


1013  UpdateUnitMap(Loc);


1014  if Model[Project and cpIndex].Kind = mkSettler then


1015  dec(SizeMod, 2) { settler produced  city shrink }


1016  else if (Model[Project and cpIndex].Kind = mkSlaves) or


1017  (Project and cpConscripts <> 0) then


1018  dec(SizeMod); { slaves/conscripts produced  city shrink }


1019  end;


1020  Project0 := Project or cpRepeat or cpCompleted;


1021  end


1022  else if Imp[Project and cpIndex].Kind = ikShipPart then


1023  begin { produce ship parts }


1024  inc(GShip[p].Parts[Project and cpIndex  imShipComp]);


1025  Project0 := Project or cpCompleted;


1026  end


1027  else { produce improvement }


1028  begin


1029  NewImp := Project and cpIndex;


1030  inc(Money, Prod); { change rest to money }


1031  Project0 := Project or cpCompleted;


1032  Project := cpImp + imTrGoods;


1033  Prod := 0;


1034 


1035  if Imp[NewImp].Kind in [ikNatLocal, ikNatGlobal] then


1036  begin // nat. project


1037  for i := 0 to nCity  1 do


1038  if (City[i].Loc >= 0) and (City[i].Built[NewImp] = 1) then


1039  begin { allowed only once }


1040  inc(Money, Imp[NewImp].Cost * BuildCostMod[Difficulty[p]] div 12);


1041  City[i].Built[NewImp] := 0;


1042  end;


1043  NatBuilt[NewImp] := 1;


1044 


1045  // immediate nat. project effects


1046  case NewImp of


1047  imGrWall:


1048  GrWallContinent[p] := Continent[Loc];


1049  end;


1050  end;


1051 


1052  if NewImp < 28 then


1053  begin // wonder


1054  GWonder[NewImp].CityID := ID;


1055  GWonder[NewImp].EffectiveOwner := p;


1056  CheckExpiration(NewImp);


1057 


1058  // immediate wonder effects


1059  case NewImp of


1060  woEiffel:


1061  begin // reactivate wonders


1062  for i := 0 to 27 do


1063  if Imp[i].Expiration >= 0 then


1064  for cix2 := 0 to nCity  1 do


1065  if (City[cix2].Loc >= 0) and (City[cix2].Built[i] = 1)


1066  then


1067  GWonder[i].EffectiveOwner := p


1068  end;


1069  woLighthouse:


1070  CheckSpecialModels(p, preLighthouse);


1071  woLeo:


1072  begin


1073  inc(Research, TechBaseCost(nTech[p], Difficulty[p]) +


1074  TechBaseCost(nTech[p] + 2, Difficulty[p]));


1075  CheckSpecialModels(p, preLeo);


1076  end;


1077  woPyramids:


1078  CheckSpecialModels(p, preBuilder);


1079  woMir:


1080  begin


1081  for p1 := 0 to nPl  1 do


1082  if (p1 <> p) and (1 shl p1 and GAlive <> 0) then


1083  begin


1084  if RW[p].Treaty[p1] = trNoContact then


1085  IntroduceEnemy(p, p1);


1086  GiveCivilReport(p, p1);


1087  GiveMilReport(p, p1)


1088  end;


1089  end


1090  end;


1091  end;


1092 


1093  for i := 0 to nImpReplacement  1 do // sell obsolete buildings


1094  if (ImpReplacement[i].NewImp = NewImp) and


1095  (Built[ImpReplacement[i].OldImp] > 0) then


1096  begin


1097  inc(RW[p].Money, Imp[ImpReplacement[i].OldImp].Cost * BuildCostMod


1098  [Difficulty[p]] div 12);


1099  Built[ImpReplacement[i].OldImp] := 0;


1100  end;


1101 


1102  if NewImp in [imPower, imHydro, imNuclear] then


1103  for i := 0 to nImp  1 do


1104  if (i <> NewImp) and (i in [imPower, imHydro, imNuclear]) and


1105  (Built[i] > 0) then


1106  begin // sell obsolete power plant


1107  inc(RW[p].Money, Imp[i].Cost * BuildCostMod[Difficulty[p]


1108  ] div 12);


1109  Built[i] := 0;


1110  end;


1111 


1112  Built[NewImp] := 1;


1113  end;


1114  Prod0 := Prod;


1115  inc(Flags, chProduction)


1116  end


1117  else


1118  begin


1119  Project0 := Project0 and not cpCompleted;


1120  if Project0 and not cpAuto <> Project and not cpAuto then


1121  Project0 := Project;


1122  Prod0 := Prod;


1123  end;


1124 


1125  if SizeMod > 0 then


1126  begin


1127  CityGrowth(p, cix);


1128  inc(Flags, chPopIncrease);


1129  end;


1130  result := Size + SizeMod >= 2;


1131  if result then


1132  while SizeMod < 0 do


1133  begin


1134  CityShrink(p, cix);


1135  inc(SizeMod)


1136  end;


1137  end


1138  end; // CityTurn


1139 


1140  {


1141  Tile Access


1142  ____________________________________________________________________


1143  }


1144  function SetCityTiles(p, cix, NewTiles: integer;


1145  TestOnly: boolean = false): integer;


1146  var


1147  V21, Working, ChangeTiles, AddTiles, Loc1: integer;


1148  CityAreaInfo: TCityAreaInfo;


1149  Radius: TVicinity21Loc;


1150  begin


1151  with RW[p].City[cix] do


1152  begin


1153  ChangeTiles := NewTiles xor integer(Tiles);


1154  AddTiles := NewTiles and not Tiles;


1155  if Mode = moPlaying then


1156  begin // do all checks


1157  if NewTiles and not $67F7F76 <> 0 then


1158  begin


1159  result := eInvalid;


1160  exit


1161  end; // invalid tile index included


1162  if NewTiles and (1 shl 13) = 0 then


1163  begin


1164  result := eViolation;


1165  exit


1166  end; // city tile must be exploited


1167  if ChangeTiles = 0 then


1168  begin


1169  result := eNotChanged;


1170  exit


1171  end;


1172  if AddTiles <> 0 then


1173  begin


1174  // check if new tiles possible


1175  GetCityAreaInfo(p, Loc, CityAreaInfo);


1176  for V21 := 1 to 26 do


1177  if AddTiles and (1 shl V21) <> 0 then


1178  if CityAreaInfo.Available[V21] <> faAvailable then


1179  begin


1180  result := eTileNotAvailable;


1181  exit


1182  end;


1183  // not more tiles than inhabitants


1184  Working := 0;


1185  for V21 := 1 to 26 do


1186  if NewTiles and (1 shl V21) <> 0 then


1187  inc(Working);


1188  if Working > Size then


1189  begin


1190  result := eNoWorkerAvailable;


1191  exit


1192  end;


1193  end;


1194  end;


1195  result := eOk;


1196  if not TestOnly then


1197  begin


1198  V21_to_Loc(Loc, Radius);


1199  for V21 := 1 to 26 do


1200  if ChangeTiles and (1 shl V21) <> 0 then


1201  begin


1202  Loc1 := Radius[V21];


1203  assert((Loc1 >= 0) and (Loc1 < MapSize));


1204  if NewTiles and (1 shl V21) <> 0 then


1205  UsedByCity[Loc1] := Loc // employ tile


1206  else if UsedByCity[Loc1] <> Loc then


1207  assert(Mode < moPlaying)


1208  // should only happen during loading, because of wrong sSetCityTiles command order


1209  else


1210  UsedByCity[Loc1] := 1 // unemploy tile


1211  end;


1212  Tiles := NewTiles


1213  end


1214  end;


1215  end;


1216 


1217  procedure GetCityTileAdvice(p, cix: integer; var Advice: TCityTileAdviceData);


1218  const


1219  oFood = 0;


1220  oProd = 1;


1221  oTax = 2;


1222  oScience = 3;


1223  type


1224  TTileData = record


1225  Food, Prod, Trade, SubValue, V21: integer;


1226  end;


1227  var


1228  i, V21, Loc1, nHierarchy, iH, iT, iH_Switch, MinWorking, MaxWorking,


1229  WantedProd, MinFood, MinProd, count, Take, MaxTake, AreaSize, FormulaCode,


1230  NeedRare, RareTiles, cix1, dx, dy, BestTiles, ProdBeforeBoost, TestTiles,


1231  SubPlus, SuperPlus: integer;


1232  SuperValue, BestSuperValue, SubValue, BestSubValue: integer;


1233  Value, BestValue, ValuePlus: extended;


1234  ValueFormula_Weight: array [oFood .. oScience] of extended;


1235  ValueFormula_Multiply: array [oFood .. oScience] of boolean;


1236  Output: array [oFood .. oScience] of integer;


1237  TileInfo, BaseTileInfo: TTileInfo;


1238  Radius, Radius1: TVicinity21Loc;


1239  TestReport: TCityReport;


1240  CityReportEx: TCityReportEx;


1241  CityAreaInfo: TCityAreaInfo;


1242  Hierarchy: array [0 .. 20, 0 .. 31] of TTileData;


1243  nTile, nSelection: array [0 .. 20] of integer;


1244  SubCriterion: array [0 .. 27] of integer;


1245  FoodWasted, FoodToTax, ProdToTax, RareOK, NeedStep2, IsBest: boolean;


1246  begin


1247  if (RW[p].Government = gAnarchy) or (RW[p].City[cix].Flags and chCaptured <> 0)


1248  then


1249  begin


1250  FillChar(Advice.CityReport, SizeOf(Advice.CityReport), 0);


1251  Advice.Tiles := 1 shl CityOwnTile;


1252  Advice.CityReport.HypoTiles := 1 shl CityOwnTile;


1253  exit;


1254  end;


1255 


1256  for i := oFood to oScience do


1257  begin // decode evaluation formula from weights parameter


1258  FormulaCode := Advice.ResourceWeights shr (24  8 * i) and $FF;


1259  ValueFormula_Multiply[i] := FormulaCode and $80 <> 0;


1260  if FormulaCode and $40 <> 0 then


1261  ValueFormula_Weight[i] := (FormulaCode and $0F) *


1262  (1 shl (FormulaCode and $30 shr 4)) / 16


1263  else


1264  ValueFormula_Weight[i] := (FormulaCode and $0F) *


1265  (1 shl (FormulaCode and $30 shr 4));


1266  end;


1267 


1268  TestReport.HypoTiles := 1 shl CityOwnTile;


1269  TestReport.HypoTax := 1;


1270  TestReport.HypoLux := 1;


1271  GetSmallCityReport(p, cix, TestReport, @CityReportEx);


1272  with RW[p].City[cix] do


1273  begin


1274  V21_to_Loc(Loc, Radius);


1275  FoodToTax := RW[p].Government = gFuture;


1276  ProdToTax := Project and (cpImp + cpIndex) = cpImp + imTrGoods;


1277  FoodWasted := not FoodToTax and (Food = StorageSize[Difficulty[p]]) and


1278  not CanCityGrow(p, cix);


1279 


1280  // sub criteria


1281  for V21 := 1 to 26 do


1282  begin


1283  Loc1 := Radius[V21];


1284  if Loc1 >= 0 then


1285  SubCriterion[V21] := 3360  (Distance(Loc, Loc1)  1) * 32 


1286  V21 xor $15;


1287  end;


1288  for cix1 := 0 to RW[p].nCity  1 do


1289  if cix1 <> cix then


1290  begin


1291  Loc1 := RW[p].City[cix1].Loc;


1292  if Loc1 >= 0 then


1293  begin


1294  if Distance(Loc, Loc1) <= 10 then


1295  begin // cities overlap  prefer tiles outside common range


1296  V21_to_Loc(Loc1, Radius1);


1297  for V21 := 1 to 26 do


1298  begin


1299  Loc1 := Radius1[V21];


1300  if (Loc1 >= 0) and (Loc1 < MapSize) and (Distance(Loc, Loc1) <= 5)


1301  then


1302  begin


1303  dxdy(Loc, Loc1, dx, dy);


1304  dec(SubCriterion[(dy + 3) shl 2 + (dx + 3) shr 1], 160);


1305  end


1306  end


1307  end


1308  end


1309  end;


1310 


1311  GetCityAreaInfo(p, Loc, CityAreaInfo);


1312  AreaSize := 0;


1313  for V21 := 1 to 26 do


1314  if CityAreaInfo.Available[V21] = faAvailable then


1315  inc(AreaSize);


1316 


1317  if RW[p].Government = gFundamentalism then


1318  begin


1319  MinWorking := Size;


1320  MaxWorking := Size;


1321  end


1322  else


1323  begin


1324  MinWorking := CityReportEx.TradeProcessing.HappyBase shr 1;


1325  if MinWorking > Size then


1326  MinWorking := Size;


1327  if (RW[p].LuxRate = 0) and not CityReportEx.TradeProcessing.FlexibleLuxury


1328  then


1329  MaxWorking := MinWorking


1330  else


1331  MaxWorking := Size;


1332  end;


1333  if MaxWorking > AreaSize then


1334  begin


1335  MaxWorking := AreaSize;


1336  if MinWorking > AreaSize then


1337  MinWorking := AreaSize;


1338  end;


1339  if TestReport.Support = 0 then


1340  WantedProd := 0


1341  else


1342  WantedProd := 1 + (TestReport.Support * 100  1)


1343  div (100 + CityReportEx.ProdProcessing.ProdBonus * 50 +


1344  CityReportEx.ProdProcessing.FutProdBonus);


1345 


1346  // consider resources for ship parts


1347  NeedRare := 0;


1348  if (GTestFlags and tfNoRareNeed = 0) and (Project and cpImp <> 0) then


1349  case Project and cpIndex of


1350  imShipComp:


1351  NeedRare := fCobalt;


1352  imShipPow:


1353  NeedRare := fUranium;


1354  imShipHab:


1355  NeedRare := fMercury;


1356  end;


1357  if NeedRare > 0 then


1358  begin


1359  RareTiles := 0;


1360  for V21 := 1 to 26 do


1361  begin


1362  Loc1 := Radius[V21];


1363  if (Loc1 >= 0) and (Loc1 < MapSize) and


1364  (RealMap[Loc1] and fModern = cardinal(NeedRare)) then


1365  RareTiles := RareTiles or (1 shl V21);


1366  end


1367  end;


1368 


1369  // step 1: sort tiles to hierarchies


1370  nHierarchy := 0;


1371  for V21 := 1 to 26 do // nonrare tiles


1372  if (CityAreaInfo.Available[V21] = faAvailable) and


1373  ((NeedRare = 0) or (1 shl V21 and RareTiles = 0)) then


1374  begin


1375  Loc1 := Radius[V21];


1376  assert((Loc1 >= 0) and (Loc1 < MapSize));


1377  GetTileInfo(p, cix, Loc1, TileInfo);


1378  if V21 = CityOwnTile then


1379  BaseTileInfo := TileInfo


1380  else


1381  begin


1382  iH := 0;


1383  while iH < nHierarchy do


1384  begin


1385  iT := 0;


1386  while (iT < nTile[iH]) and (TileInfo.Food <= Hierarchy[iH, iT].Food)


1387  and (TileInfo.Prod <= Hierarchy[iH, iT].Prod) and


1388  (TileInfo.Trade <= Hierarchy[iH, iT].Trade) and


1389  not((TileInfo.Food = Hierarchy[iH, iT].Food) and


1390  (TileInfo.Prod = Hierarchy[iH, iT].Prod) and


1391  (TileInfo.Trade = Hierarchy[iH, iT].Trade) and


1392  (SubCriterion[V21] >= SubCriterion[Hierarchy[iH, iT].V21])) do


1393  inc(iT);


1394  if (iT = nTile[iH]) // new worst tile in this hierarchy


1395  or ((TileInfo.Food >= Hierarchy[iH, iT].Food)


1396  // new middle tile in this hierarchy


1397  and (TileInfo.Prod >= Hierarchy[iH, iT].Prod) and


1398  (TileInfo.Trade >= Hierarchy[iH, iT].Trade)) then


1399  break; // insert position found!


1400  inc(iH);


1401  end;


1402  if iH = nHierarchy then


1403  begin // need to start new hierarchy


1404  nTile[iH] := 0;


1405  inc(nHierarchy);


1406  iT := 0;


1407  end;


1408  move(Hierarchy[iH, iT], Hierarchy[iH, iT + 1],


1409  (nTile[iH]  iT) * SizeOf(TTileData));


1410  inc(nTile[iH]);


1411  Hierarchy[iH, iT].V21 := V21;


1412  Hierarchy[iH, iT].Food := TileInfo.Food;


1413  Hierarchy[iH, iT].Prod := TileInfo.Prod;


1414  Hierarchy[iH, iT].Trade := TileInfo.Trade;


1415  Hierarchy[iH, iT].SubValue := SubCriterion[V21];


1416  end


1417  end;


1418  if NeedRare <> 0 then


1419  begin // rare tiles need own hierarchy


1420  iH := nHierarchy;


1421  for V21 := 1 to 26 do


1422  if (CityAreaInfo.Available[V21] = faAvailable) and


1423  (1 shl V21 and RareTiles <> 0) then


1424  begin


1425  Loc1 := Radius[V21];


1426  assert((V21 <> CityOwnTile) and (Loc1 >= 0) and (Loc1 < MapSize));


1427  GetTileInfo(p, cix, Loc1, TileInfo);


1428  if iH = nHierarchy then


1429  begin // need to start new hierarchy


1430  nTile[iH] := 0;


1431  inc(nHierarchy);


1432  iT := 0;


1433  end


1434  else


1435  iT := nTile[iH];


1436  inc(nTile[iH]);


1437  Hierarchy[iH, iT].V21 := V21;


1438  Hierarchy[iH, iT].Food := TileInfo.Food; // = 0


1439  Hierarchy[iH, iT].Prod := TileInfo.Prod; // = 1


1440  Hierarchy[iH, iT].Trade := TileInfo.Trade; // = 0


1441  Hierarchy[iH, iT].SubValue := SubCriterion[V21];


1442  end;


1443  end;


1444  if Built[imAlgae] > 0 then


1445  inc(BaseTileInfo.Food, 12);


1446 


1447  // step 2: summarize resources


1448  for iH := 0 to nHierarchy  1 do


1449  begin


1450  move(Hierarchy[iH, 0], Hierarchy[iH, 1], nTile[iH] * SizeOf(TTileData));


1451  Hierarchy[iH, 0].Food := 0;


1452  Hierarchy[iH, 0].Prod := 0;


1453  Hierarchy[iH, 0].Trade := 0;


1454  Hierarchy[iH, 0].SubValue := 0;


1455  Hierarchy[iH, 0].V21 := 0;


1456  for iT := 1 to nTile[iH] do


1457  begin


1458  inc(Hierarchy[iH, iT].Food, Hierarchy[iH, iT  1].Food);


1459  inc(Hierarchy[iH, iT].Prod, Hierarchy[iH, iT  1].Prod);


1460  inc(Hierarchy[iH, iT].Trade, Hierarchy[iH, iT  1].Trade);


1461  inc(Hierarchy[iH, iT].SubValue, Hierarchy[iH, iT  1].SubValue);


1462  Hierarchy[iH, iT].V21 := 1 shl Hierarchy[iH, iT].V21 +


1463  Hierarchy[iH, iT  1].V21;


1464  end;


1465  end;


1466 


1467  // step 3: try all combinations


1468  BestValue := 0.0;


1469  BestSuperValue := 0;


1470  BestSubValue := 0;


1471  BestTiles := 0;


1472  FillChar(nSelection, SizeOf(nSelection), 0);


1473  TestReport.FoodRep := BaseTileInfo.Food;


1474  ProdBeforeBoost := BaseTileInfo.Prod;


1475  TestReport.Trade := BaseTileInfo.Trade;


1476  TestReport.Working := 1;


1477  MinFood := 0;


1478  MinProd := 0;


1479  iH_Switch := nHierarchy;


1480  count := 0;


1481  repeat


1482  // ensure minima


1483  iH := 0;


1484  while (TestReport.Working < MaxWorking) and (iH < iH_Switch) and


1485  ((TestReport.Working < MinWorking) or


1486  (TestReport.FoodRep < TestReport.Eaten) or


1487  (ProdBeforeBoost < WantedProd)) do


1488  begin


1489  assert(nSelection[iH] = 0);


1490  Take := MinWorking  TestReport.Working;


1491  if Take > nTile[iH] then


1492  Take := nTile[iH]


1493  else


1494  begin


1495  if Take < 0 then


1496  Take := 0;


1497  MaxTake := nTile[iH];


1498  if TestReport.Working + MaxTake > MaxWorking then


1499  MaxTake := MaxWorking  TestReport.Working;


1500  while (Take < MaxTake) and


1501  (TestReport.FoodRep + Hierarchy[iH, Take].Food < MinFood) do


1502  inc(Take);


1503  while (Take < MaxTake) and


1504  (ProdBeforeBoost + Hierarchy[iH, Take].Prod < MinProd) do


1505  inc(Take);


1506  end;


1507  nSelection[iH] := Take;


1508  inc(TestReport.Working, Take);


1509  with Hierarchy[iH, Take] do


1510  begin


1511  inc(TestReport.FoodRep, Food);


1512  inc(ProdBeforeBoost, Prod);


1513  inc(TestReport.Trade, Trade);


1514  end;


1515  inc(iH);


1516  end;


1517 


1518  assert((TestReport.Working >= MinWorking) and


1519  (TestReport.Working <= MaxWorking));


1520  if (TestReport.FoodRep >= MinFood) and (ProdBeforeBoost >= MinProd) then


1521  begin


1522  SplitTrade(TestReport.Trade, RW[p].TaxRate, RW[p].LuxRate,


1523  TestReport.Working, CityReportEx.TradeProcessing,


1524  TestReport.Corruption, TestReport.Tax, TestReport.Lux,


1525  TestReport.Science);


1526 


1527  if CityReportEx.BaseHappiness + CityReportEx.BaseControl +


1528  TestReport.Lux + 2 * (Size  TestReport.Working)  2 *


1529  TestReport.Deployed >= Size then


1530  begin // city is not in disorder  evaluate combination


1531  inc(count);


1532  if (MinProd < WantedProd) and (ProdBeforeBoost > MinProd) then


1533  begin // no combination reached wanted prod yet


1534  MinProd := ProdBeforeBoost;


1535  if MinProd > WantedProd then


1536  MinProd := WantedProd


1537  end;


1538  if MinProd = WantedProd then


1539  // do not care for food before prod is ensured


1540  if (MinFood < TestReport.Eaten) and (TestReport.FoodRep > MinFood)


1541  then


1542  begin // no combination reached wanted food yet


1543  MinFood := TestReport.FoodRep;


1544  if MinFood > TestReport.Eaten then


1545  MinFood := TestReport.Eaten


1546  end;


1547  BoostProd(ProdBeforeBoost, CityReportEx.ProdProcessing,


1548  TestReport.ProdRep, TestReport.PollRep);


1549  SuperValue := 0;


1550 


1551  // supercriterion A: unit support granted?


1552  if TestReport.ProdRep >= TestReport.Support then


1553  SuperValue := SuperValue or 1 shl 30;


1554 


1555  // supercriterion B: food demand granted?


1556  if TestReport.FoodRep >= TestReport.Eaten then


1557  SuperValue := SuperValue or 63 shl 24


1558  else if TestReport.FoodRep > TestReport.Eaten  63 then


1559  SuperValue := SuperValue or


1560  (63  (TestReport.Eaten  TestReport.FoodRep)) shl 24;


1561 


1562  SuperPlus := SuperValue  BestSuperValue;


1563  if SuperPlus >= 0 then


1564  begin


1565  Output[oTax] := TestReport.Tax;


1566  Output[oScience] := TestReport.Science;


1567 


1568  if TestReport.FoodRep < TestReport.Eaten then


1569  Output[oFood] := TestReport.FoodRep


1570  // appreciate what we have, combination will have bad supervalue anyway


1571  else if FoodWasted then


1572  Output[oFood] := 0


1573  else


1574  begin


1575  Output[oFood] := TestReport.FoodRep  TestReport.Eaten;


1576  if FoodToTax or (Size >= NeedAqueductSize) and (Output[oFood] = 1)


1577  then


1578  begin


1579  inc(Output[oTax], Output[oFood]);


1580  Output[oFood] := 0;


1581  end;


1582  end;


1583 


1584  if TestReport.ProdRep < TestReport.Support then


1585  Output[oProd] := TestReport.ProdRep


1586  // appreciate what we have, combination will have bad supervalue anyway


1587  else


1588  begin


1589  if NeedRare > 0 then


1590  begin


1591  RareOK := false;


1592  for iH := 0 to nHierarchy  1 do


1593  if Hierarchy[iH, nSelection[iH]].V21 and RareTiles <> 0 then


1594  RareOK := true;


1595  if not RareOK then


1596  TestReport.ProdRep := TestReport.Support;


1597  end;


1598  Output[oProd] := TestReport.ProdRep  TestReport.Support;


1599  if ProdToTax then


1600  begin


1601  inc(Output[oTax], Output[oProd]);


1602  Output[oProd] := 0;


1603  end;


1604  end;


1605 


1606  NeedStep2 := false;


1607  Value := 0;


1608  for i := oFood to oScience do


1609  if ValueFormula_Multiply[i] then


1610  NeedStep2 := true


1611  else


1612  Value := Value + ValueFormula_Weight[i] * Output[i];


1613  if NeedStep2 then


1614  begin


1615  if Value > 0 then


1616  Value := ln(Value) + 123;


1617  for i := oFood to oScience do


1618  if ValueFormula_Multiply[i] and (Output[i] > 0) then


1619  Value := Value + ValueFormula_Weight[i] *


1620  (ln(Output[i]) + 123);


1621  end;


1622 


1623  ValuePlus := Value  BestValue;


1624  if (SuperPlus > 0) or (ValuePlus >= 0.0) then


1625  begin


1626  SubValue := (TestReport.FoodRep + ProdBeforeBoost +


1627  TestReport.Trade) shl 18;


1628  TestTiles := 1 shl CityOwnTile;


1629  for iH := 0 to nHierarchy  1 do


1630  begin


1631  inc(TestTiles, Hierarchy[iH, nSelection[iH]].V21);


1632  inc(SubValue, Hierarchy[iH, nSelection[iH]].SubValue);


1633  end;


1634  IsBest := true;


1635  if (SuperPlus = 0) and (ValuePlus = 0.0) then


1636  begin


1637  SubPlus := SubValue  BestSubValue;


1638  if SubPlus < 0 then


1639  IsBest := false


1640  else if SubPlus = 0 then


1641  begin


1642  assert(TestTiles <> BestTiles);


1643  IsBest := TestTiles > BestTiles


1644  end


1645  end;


1646  if IsBest then


1647  begin


1648  BestSuperValue := SuperValue;


1649  BestValue := Value;


1650  BestSubValue := SubValue;


1651  BestTiles := TestTiles;


1652  TestReport.Happy :=


1653  (CityReportEx.TradeProcessing.HappyBase  Size) div 2 +


1654  TestReport.Lux shr 1;


1655  Advice.CityReport := TestReport;


1656  end


1657  end // if (SuperPlus>0) or (ValuePlus>=0.0)


1658  end // if SuperPlus>=0


1659  end


1660  end;


1661 


1662  // calculate next combination


1663  iH_Switch := 0;


1664  repeat


1665  with Hierarchy[iH_Switch, nSelection[iH_Switch]] do


1666  begin


1667  dec(TestReport.FoodRep, Food);


1668  dec(ProdBeforeBoost, Prod);


1669  dec(TestReport.Trade, Trade);


1670  end;


1671  inc(nSelection[iH_Switch]);


1672  inc(TestReport.Working);


1673  if (nSelection[iH_Switch] <= nTile[iH_Switch]) and


1674  (TestReport.Working <= MaxWorking) then


1675  begin


1676  with Hierarchy[iH_Switch, nSelection[iH_Switch]] do


1677  begin


1678  inc(TestReport.FoodRep, Food);


1679  inc(ProdBeforeBoost, Prod);


1680  inc(TestReport.Trade, Trade);


1681  end;


1682  break;


1683  end;


1684  dec(TestReport.Working, nSelection[iH_Switch]);


1685  nSelection[iH_Switch] := 0;


1686  inc(iH_Switch);


1687  until iH_Switch = nHierarchy;


1688  until iH_Switch = nHierarchy; // everything tested  done


1689  end;


1690  assert(BestSuperValue > 0); // advice should always be possible


1691  Advice.Tiles := BestTiles;


1692  Advice.CityReport.HypoTiles := BestTiles;


1693  end; // GetCityTileAdvice


1694 


1695  {


1696  Start/End Game


1697  ____________________________________________________________________


1698  }


1699  procedure InitGame;


1700  var


1701  p, i, mixTownGuard: integer;


1702  begin


1703  MaxDist := Distance(0, MapSize  lx shr 1);


1704  for p := 0 to nPl  1 do


1705  if (1 shl p and GAlive <> 0) then


1706  with RW[p] do


1707  begin // initialize capital


1708  mixTownGuard := 0;


1709  while Model[mixTownGuard].Kind <> mkSpecial_TownGuard do


1710  inc(mixTownGuard);


1711  with City[0] do


1712  begin


1713  Built[imPalace] := 1;


1714  Size := 4;


1715  for i := 2 to Size do


1716  AddBestCityTile(p, 0);


1717  Project := mixTownGuard;


1718  end;


1719  NatBuilt[imPalace] := 1;


1720  end;


1721  end;


1722 


1723  procedure ReleaseGame;


1724  begin


1725  end;


1726 


1727  end.

