Ignore:
Timestamp:
Mar 5, 2021, 1:49:27 PM (4 years ago)
Author:
chronos
Message:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/LocalPlayer/ClientTools.pas

    r205 r294  
    1313
    1414type
    15   TImpOrder = array [0 .. (nImp + 4) div 4 * 4 - 1] of ShortInt;
    16   TEnhancementJobs = array [0 .. 11, 0 .. 7] of Byte;
     15  TImpOrder = array [0 .. (nImp + 4) div 4 * 4 - 1] of shortint;
     16  TEnhancementJobs = array [0 .. 11, 0 .. 7] of byte;
    1717  JobResultSet = set of 0 .. 39;
    1818
     
    4242function CutCityFoodSurplus(FoodSurplus: integer; IsCityAlive: boolean;
    4343  gov, size: integer): integer;
    44 function CityTaxBalance(cix: integer; const CityReport: TCityReportNew)
    45   : integer;
     44function CityTaxBalance(cix: integer; const CityReport: TCityReportNew): integer;
    4645procedure SumCities(var TaxSum, ScienceSum: integer);
    4746function JobTest(uix, Job: integer; IgnoreResults: JobResultSet = []): boolean;
     
    5049function UnitExhausted(uix: integer): boolean;
    5150function ModelHash(const ModelInfo: TModelInfo): integer;
    52 function ProcessEnhancement(uix: integer; const Jobs: TEnhancementJobs)
    53   : integer;
     51function ProcessEnhancement(uix: integer; const Jobs: TEnhancementJobs): integer;
    5452function AutoBuild(cix: integer; const ImpOrder: TImpOrder): boolean;
    5553procedure DebugMessage(Level: integer; Text: string);
     
    6260procedure CityOptimizer_EndOfTurn;
    6361
     62
    6463implementation
    6564
     
    7271begin
    7372  y0 := (Loc + G.lx * 1024) div G.lx - 1024;
    74   result := (Loc + (dx + y0 and 1 + G.lx * 1024) shr 1) mod G.lx + G.lx
    75     * (y0 + dy)
     73  Result := (Loc + (dx + y0 and 1 + G.lx * 1024) shr 1) mod G.lx + G.lx * (y0 + dy);
    7674end;
    7775
     
    8078  dx, dy: integer;
    8179begin
    82   inc(Loc0, G.lx * 1024);
    83   inc(Loc1, G.lx * 1024);
    84   dx := abs(((Loc1 mod G.lx * 2 + Loc1 div G.lx and 1) - (Loc0 mod G.lx * 2 +
    85     Loc0 div G.lx and 1) + 3 * G.lx) mod (2 * G.lx) - G.lx);
     80  Inc(Loc0, G.lx * 1024);
     81  Inc(Loc1, G.lx * 1024);
     82  dx := abs(((Loc1 mod G.lx * 2 + Loc1 div G.lx and 1) -
     83    (Loc0 mod G.lx * 2 + Loc0 div G.lx and 1) + 3 * G.lx) mod (2 * G.lx) - G.lx);
    8684  dy := abs(Loc1 div G.lx - Loc0 div G.lx);
    87   result := dx + dy + abs(dx - dy) shr 1;
     85  Result := dx + dy + abs(dx - dy) shr 1;
    8886end;
    8987
     
    9290  uix1: integer;
    9391begin
    94   result := false;
     92  Result := False;
    9593  if MyModel[MyUn[uix].mix].Flags and mdCivil = 0 then
    9694    case MyRO.Government of
    9795      gRepublic, gFuture:
    98         result := (MyRO.Territory[Loc] >= 0) and (MyRO.Territory[Loc] <> me) and
     96        Result := (MyRO.Territory[Loc] >= 0) and (MyRO.Territory[Loc] <> me) and
    9997          (MyRO.Treaty[MyRO.Territory[Loc]] < trAlliance);
    10098      gDemocracy:
    101         result := (MyRO.Territory[Loc] < 0) or (MyRO.Territory[Loc] <> me) and
     99        Result := (MyRO.Territory[Loc] < 0) or (MyRO.Territory[Loc] <> me) and
    102100          (MyRO.Treaty[MyRO.Territory[Loc]] < trAlliance);
    103101    end;
     
    106104      for uix1 := 0 to MyRO.nUn - 1 do // check transported units too
    107105        if (MyUn[uix1].Loc >= 0) and (MyUn[uix1].Master = uix) then
    108           result := result or UnrestAtLoc(uix1, Loc);
     106          Result := Result or UnrestAtLoc(uix1, Loc);
    109107end;
    110108
     
    124122      MoveAdviceData.MoreTurns := 999;
    125123      MoveAdviceData.MaxHostile_MovementLeft := MyUn[uix].Health - MinEndHealth;
    126       result := Server(sGetMoveAdvice, me, uix, MoveAdviceData);
    127       if (MinEndHealth <= 1) or (result <> eNoWay) then
     124      Result := Server(sGetMoveAdvice, me, uix, MoveAdviceData);
     125      if (MinEndHealth <= 1) or (Result <> eNoWay) then
    128126        exit;
    129127    end;
     
    135133      25:
    136134        MinEndHealth := 12;
     135      else
     136        MinEndHealth := 1
     137    end;
     138  until False;
     139end;
     140
     141function ColorOfHealth(Health: integer): integer;
     142var
     143  red, green: integer;
     144begin
     145  green := 400 * Health div 100;
     146  if green > 200 then
     147    green := 200;
     148  red := 510 * (100 - Health) div 100;
     149  if red > 255 then
     150    red := 255;
     151  Result := green shl 8 + red;
     152end;
     153
     154function IsMultiPlayerGame: boolean;
     155var
     156  p1: integer;
     157begin
     158  Result := False;
     159  for p1 := 1 to nPl - 1 do
     160    if G.RO[p1] <> nil then
     161      Result := True;
     162end;
     163
     164procedure ItsMeAgain(p: integer);
     165begin
     166  if G.RO[p] <> nil then
     167    MyRO := pointer(G.RO[p])
     168  else if G.SuperVisorRO[p] <> nil then
     169    MyRO := pointer(G.SuperVisorRO[p])
     170  else
     171    exit;
     172  me := p;
     173  MyMap := pointer(MyRO.Map);
     174  MyUn := pointer(MyRO.Un);
     175  MyCity := pointer(MyRO.City);
     176  MyModel := pointer(MyRO.Model);
     177end;
     178
     179function GetAge(p: integer): integer;
     180var
     181  i: integer;
     182begin
     183  if p = me then
     184  begin
     185    Result := 0;
     186    for i := 1 to 3 do
     187      if MyRO.Tech[AgePreq[i]] >= tsApplicable then
     188        Result := i;
     189  end
     190  else
     191  begin
     192    Result := 0;
     193    for i := 1 to 3 do
     194      if MyRO.EnemyReport[p].Tech[AgePreq[i]] >= tsApplicable then
     195        Result := i;
     196  end;
     197end;
     198
     199function IsCivilReportNew(Enemy: integer): boolean;
     200var
     201  i: integer;
     202begin
     203  assert(Enemy <> me);
     204  i := MyRO.EnemyReport[Enemy].TurnOfCivilReport;
     205  Result := (i = MyRO.Turn) or (i = MyRO.Turn - 1) and (Enemy > me);
     206end;
     207
     208function IsMilReportNew(Enemy: integer): boolean;
     209var
     210  i: integer;
     211begin
     212  assert(Enemy <> me);
     213  i := MyRO.EnemyReport[Enemy].TurnOfMilReport;
     214  Result := (i = MyRO.Turn) or (i = MyRO.Turn - 1) and (Enemy > me);
     215end;
     216
     217function CutCityFoodSurplus(FoodSurplus: integer; IsCityAlive: boolean;
     218  gov, size: integer): integer;
     219begin
     220  Result := FoodSurplus;
     221  if not IsCityAlive or (Result > 0) and ((gov = gFuture) or
     222    (size >= NeedAqueductSize) and (Result < 2)) then
     223    Result := 0; { no growth }
     224end;
     225
     226function CityTaxBalance(cix: integer; const CityReport: TCityReportNew): integer;
     227var
     228  i: integer;
     229begin
     230  Result := 0;
     231  if (CityReport.HappinessBalance >= 0) { no disorder } and
     232    (MyCity[cix].Flags and chCaptured = 0) then // not captured
     233  begin
     234    Inc(Result, CityReport.Tax);
     235    if (MyCity[cix].Project and (cpImp + cpIndex) = cpImp + imTrGoods) and
     236      (CityReport.Production > 0) then
     237      Inc(Result, CityReport.Production);
     238    if ((MyRO.Government = gFuture) or (MyCity[cix].size >=
     239      NeedAqueductSize) and (CityReport.FoodSurplus < 2)) and
     240      (CityReport.FoodSurplus > 0) then
     241      Inc(Result, CityReport.FoodSurplus);
     242  end;
     243  for i := 28 to nImp - 1 do
     244    if MyCity[cix].Built[i] > 0 then
     245      Dec(Result, Imp[i].Maint);
     246end;
     247
     248procedure SumCities(var TaxSum, ScienceSum: integer);
     249var
     250  cix: integer;
     251  CityReport: TCityReportNew;
     252begin
     253  TaxSum := MyRO.OracleIncome;
     254  ScienceSum := 0;
     255  if MyRO.Government = gAnarchy then
     256    exit;
     257  for cix := 0 to MyRO.nCity - 1 do
     258    if MyCity[cix].Loc >= 0 then
     259    begin
     260      CityReport.HypoTiles := -1;
     261      CityReport.HypoTaxRate := -1;
     262      CityReport.HypoLuxuryRate := -1;
     263      Server(sGetCityReportNew, me, cix, CityReport);
     264      if (CityReport.HappinessBalance >= 0) { no disorder } and
     265        (MyCity[cix].Flags and chCaptured = 0) then // not captured
     266        ScienceSum := ScienceSum + CityReport.Science;
     267      TaxSum := TaxSum + CityTaxBalance(cix, CityReport);
     268    end;
     269end;
     270
     271function JobTest(uix, Job: integer; IgnoreResults: JobResultSet): boolean;
     272var
     273  Test: integer;
     274begin
     275  Test := Server(sStartJob + Job shl 4 - sExecute, me, uix, nil^);
     276  Result := (Test >= rExecuted) or (Test in IgnoreResults);
     277end;
     278
     279procedure GetUnitInfo(Loc: integer; var uix: integer; var UnitInfo: TUnitInfo);
     280var
     281  i, Cnt: integer;
     282begin
     283  if MyMap[Loc] and fOwned <> 0 then
     284  begin
     285    Server(sGetDefender, me, Loc, uix);
     286    Cnt := 0;
     287    for i := 0 to MyRO.nUn - 1 do
     288      if MyUn[i].Loc = Loc then
     289        Inc(Cnt);
     290    MakeUnitInfo(me, MyUn[uix], UnitInfo);
     291    if Cnt > 1 then
     292      UnitInfo.Flags := UnitInfo.Flags or unMulti;
     293  end
     294  else
     295  begin
     296    uix := MyRO.nEnemyUn - 1;
     297    while (uix >= 0) and (MyRO.EnemyUn[uix].Loc <> Loc) do
     298      Dec(uix);
     299    UnitInfo := MyRO.EnemyUn[uix];
     300  end;
     301end; { GetUnitInfo }
     302
     303procedure GetCityInfo(Loc: integer; var cix: integer; var CityInfo: TCityInfo);
     304begin
     305  if MyMap[Loc] and fOwned <> 0 then
     306  begin
     307    CityInfo.Loc := Loc;
     308    cix := MyRO.nCity - 1;
     309    while (cix >= 0) and (MyCity[cix].Loc <> Loc) do
     310      Dec(cix);
     311    with CityInfo do
     312    begin
     313      Owner := me;
     314      ID := MyCity[cix].ID;
     315      size := MyCity[cix].size;
     316      Flags := 0;
     317      if MyCity[cix].Built[imPalace] > 0 then
     318        Inc(Flags, ciCapital);
     319      if (MyCity[cix].Built[imWalls] > 0) or
     320        (MyMap[MyCity[cix].Loc] and fGrWall <> 0) then
     321        Inc(Flags, ciWalled);
     322      if MyCity[cix].Built[imCoastalFort] > 0 then
     323        Inc(Flags, ciCoastalFort);
     324      if MyCity[cix].Built[imMissileBat] > 0 then
     325        Inc(Flags, ciMissileBat);
     326      if MyCity[cix].Built[imBunker] > 0 then
     327        Inc(Flags, ciBunker);
     328      if MyCity[cix].Built[imSpacePort] > 0 then
     329        Inc(Flags, ciSpacePort);
     330    end;
     331  end
     332  else
     333  begin
     334    cix := MyRO.nEnemyCity - 1;
     335    while (cix >= 0) and (MyRO.EnemyCity[cix].Loc <> Loc) do
     336      Dec(cix);
     337    CityInfo := MyRO.EnemyCity[cix];
     338  end;
     339end;
     340
     341function UnitExhausted(uix: integer): boolean;
     342  // check if another move of this unit is still possible
     343var
     344  dx, dy: integer;
     345begin
     346  Result := True;
     347  if (MyUn[uix].Movement > 0) or
     348    (MyRO.Wonder[woShinkansen].EffectiveOwner = me) then
     349    if (MyUn[uix].Movement >= 100) or
     350      ((MyModel[MyUn[uix].mix].Kind = mkCaravan) and
     351      (MyMap[MyUn[uix].Loc] and fCity <> 0)) then
     352      Result := False
    137353    else
    138       MinEndHealth := 1
    139     end;
    140   until false end;
    141 
    142   function ColorOfHealth(Health: integer): integer;
    143   var
    144     red, green: integer;
    145   begin
    146     green := 400 * Health div 100;
    147     if green > 200 then
    148       green := 200;
    149     red := 510 * (100 - Health) div 100;
    150     if red > 255 then
    151       red := 255;
    152     result := green shl 8 + red
    153   end;
    154 
    155   function IsMultiPlayerGame: boolean;
    156   var
    157     p1: integer;
    158   begin
    159     result := false;
    160     for p1 := 1 to nPl - 1 do
    161       if G.RO[p1] <> nil then
    162         result := true;
    163   end;
    164 
    165   procedure ItsMeAgain(p: integer);
    166   begin
    167     if G.RO[p] <> nil then
    168       MyRO := pointer(G.RO[p])
    169     else if G.SuperVisorRO[p] <> nil then
    170       MyRO := pointer(G.SuperVisorRO[p])
    171     else
    172       exit;
    173     me := p;
    174     MyMap := pointer(MyRO.Map);
    175     MyUn := pointer(MyRO.Un);
    176     MyCity := pointer(MyRO.City);
    177     MyModel := pointer(MyRO.Model);
    178   end;
    179 
    180   function GetAge(p: integer): integer;
    181   var
    182     i: integer;
    183   begin
    184     if p = me then
    185     begin
    186       result := 0;
    187       for i := 1 to 3 do
    188         if MyRO.Tech[AgePreq[i]] >= tsApplicable then
    189           result := i;
    190     end
     354      for dx := -2 to 2 do
     355        for dy := -2 to 2 do
     356          if abs(dx) + abs(dy) = 2 then
     357            if Server(sMoveUnit - sExecute + dx and 7 shl 4 + dy and
     358              7 shl 7, me, uix, nil^) >= rExecuted then
     359              Result := False;
     360end;
     361
     362function ModelHash(const ModelInfo: TModelInfo): integer;
     363var
     364  i, FeatureCode, Hash1, Hash2, Hash2r, d: cardinal;
     365begin
     366  with ModelInfo do
     367    if Kind > mkEnemyDeveloped then
     368      Result := integer($C0000000 + Speed div 50 + Kind shl 8)
    191369    else
    192370    begin
    193       result := 0;
    194       for i := 1 to 3 do
    195         if MyRO.EnemyReport[p].Tech[AgePreq[i]] >= tsApplicable then
    196           result := i;
    197     end
    198   end;
    199 
    200   function IsCivilReportNew(Enemy: integer): boolean;
    201   var
    202     i: integer;
    203   begin
    204     assert(Enemy <> me);
    205     i := MyRO.EnemyReport[Enemy].TurnOfCivilReport;
    206     result := (i = MyRO.Turn) or (i = MyRO.Turn - 1) and (Enemy > me);
    207   end;
    208 
    209   function IsMilReportNew(Enemy: integer): boolean;
    210   var
    211     i: integer;
    212   begin
    213     assert(Enemy <> me);
    214     i := MyRO.EnemyReport[Enemy].TurnOfMilReport;
    215     result := (i = MyRO.Turn) or (i = MyRO.Turn - 1) and (Enemy > me);
    216   end;
    217 
    218   function CutCityFoodSurplus(FoodSurplus: integer; IsCityAlive: boolean;
    219     gov, size: integer): integer;
    220   begin
    221     result := FoodSurplus;
    222     if not IsCityAlive or (result > 0) and
    223       ((gov = gFuture) or (size >= NeedAqueductSize) and (result < 2)) then
    224       result := 0; { no growth }
    225   end;
    226 
    227   function CityTaxBalance(cix: integer;
    228     const CityReport: TCityReportNew): integer;
    229   var
    230     i: integer;
    231   begin
    232     result := 0;
    233     if (CityReport.HappinessBalance >= 0) { no disorder }
    234       and (MyCity[cix].Flags and chCaptured = 0) then // not captured
    235     begin
    236       inc(result, CityReport.Tax);
    237       if (MyCity[cix].Project and (cpImp + cpIndex) = cpImp + imTrGoods) and
    238         (CityReport.Production > 0) then
    239         inc(result, CityReport.Production);
    240       if ((MyRO.Government = gFuture) or (MyCity[cix].size >= NeedAqueductSize)
    241         and (CityReport.FoodSurplus < 2)) and (CityReport.FoodSurplus > 0) then
    242         inc(result, CityReport.FoodSurplus);
     371      FeatureCode := 0;
     372      for i := mcFirstNonCap to nFeature - 1 do
     373        if 1 shl Domain and Feature[i].Domains <> 0 then
     374        begin
     375          FeatureCode := FeatureCode * 2;
     376          if 1 shl (i - mcFirstNonCap) <> 0 then
     377            Inc(FeatureCode);
     378        end;
     379      case Domain of
     380        dGround:
     381        begin
     382          assert(FeatureCode < 1 shl 8);
     383          assert(Attack < 5113);
     384          assert(Defense < 2273);
     385          assert(Cost < 1611);
     386          Hash1 := (Attack * 2273 + Defense) * 9 + (Speed - 150) div 50;
     387          Hash2 := FeatureCode * 1611 + Cost;
     388        end;
     389        dSea:
     390        begin
     391          assert(FeatureCode < 1 shl 9);
     392          assert(Attack < 12193);
     393          assert(Defense < 6097);
     394          assert(Cost < 4381);
     395          Hash1 := ((Attack * 6097 + Defense) * 5 +
     396            (Speed - 350) div 100) * 2;
     397          if Weight >= 6 then
     398            Inc(Hash1);
     399          Hash2 := ((TTrans * 17 + ATrans_Fuel) shl 9 + FeatureCode) *
     400            4381 + Cost;
     401        end;
     402        dAir:
     403        begin
     404          assert(FeatureCode < 1 shl 5);
     405          assert(Attack < 2407);
     406          assert(Defense < 1605);
     407          assert(Bombs < 4813);
     408          assert(Cost < 2089);
     409          Hash1 := (Attack * 1605 + Defense) shl 5 + FeatureCode;
     410          Hash2 := ((Bombs * 7 + ATrans_Fuel) * 4 + TTrans) * 2089 + Cost;
     411        end;
     412      end;
     413      Hash2r := 0;
     414      for i := 0 to 7 do
     415      begin
     416        Hash2r := Hash2r * 13;
     417        d := Hash2 div 13;
     418        Inc(Hash2r, Hash2 - d * 13);
     419        Hash2 := d;
     420      end;
     421      Result := integer(Domain shl 30 + Hash1 xor Hash2r);
    243422    end;
    244     for i := 28 to nImp - 1 do
    245       if MyCity[cix].Built[i] > 0 then
    246         dec(result, Imp[i].Maint);
    247   end;
    248 
    249   procedure SumCities(var TaxSum, ScienceSum: integer);
    250   var
    251     cix: integer;
    252     CityReport: TCityReportNew;
    253   begin
    254     TaxSum := MyRO.OracleIncome;
    255     ScienceSum := 0;
    256     if MyRO.Government = gAnarchy then
    257       exit;
    258     for cix := 0 to MyRO.nCity - 1 do
    259       if MyCity[cix].Loc >= 0 then
    260       begin
    261         CityReport.HypoTiles := -1;
    262         CityReport.HypoTaxRate := -1;
    263         CityReport.HypoLuxuryRate := -1;
    264         Server(sGetCityReportNew, me, cix, CityReport);
    265         if (CityReport.HappinessBalance >= 0) { no disorder }
    266           and (MyCity[cix].Flags and chCaptured = 0) then // not captured
    267           ScienceSum := ScienceSum + CityReport.Science;
    268         TaxSum := TaxSum + CityTaxBalance(cix, CityReport);
    269       end;
    270   end;
    271 
    272   function JobTest(uix, Job: integer; IgnoreResults: JobResultSet): boolean;
    273   var
    274     Test: integer;
    275   begin
    276     Test := Server(sStartJob + Job shl 4 - sExecute, me, uix, nil^);
    277     result := (Test >= rExecuted) or (Test in IgnoreResults);
    278   end;
    279 
    280   procedure GetUnitInfo(Loc: integer; var uix: integer;
    281     var UnitInfo: TUnitInfo);
    282   var
    283     i, Cnt: integer;
    284   begin
    285     if MyMap[Loc] and fOwned <> 0 then
    286     begin
    287       Server(sGetDefender, me, Loc, uix);
    288       Cnt := 0;
    289       for i := 0 to MyRO.nUn - 1 do
    290         if MyUn[i].Loc = Loc then
    291           inc(Cnt);
    292       MakeUnitInfo(me, MyUn[uix], UnitInfo);
    293       if Cnt > 1 then
    294         UnitInfo.Flags := UnitInfo.Flags or unMulti;
    295     end
    296     else
    297     begin
    298       uix := MyRO.nEnemyUn - 1;
    299       while (uix >= 0) and (MyRO.EnemyUn[uix].Loc <> Loc) do
    300         dec(uix);
    301       UnitInfo := MyRO.EnemyUn[uix];
    302     end
    303   end; { GetUnitInfo }
    304 
    305   procedure GetCityInfo(Loc: integer; var cix: integer;
    306     var CityInfo: TCityInfo);
    307   begin
    308     if MyMap[Loc] and fOwned <> 0 then
    309     begin
    310       CityInfo.Loc := Loc;
    311       cix := MyRO.nCity - 1;
    312       while (cix >= 0) and (MyCity[cix].Loc <> Loc) do
    313         dec(cix);
    314       with CityInfo do
    315       begin
    316         Owner := me;
    317         ID := MyCity[cix].ID;
    318         size := MyCity[cix].size;
    319         Flags := 0;
    320         if MyCity[cix].Built[imPalace] > 0 then
    321           inc(Flags, ciCapital);
    322         if (MyCity[cix].Built[imWalls] > 0) or
    323           (MyMap[MyCity[cix].Loc] and fGrWall <> 0) then
    324           inc(Flags, ciWalled);
    325         if MyCity[cix].Built[imCoastalFort] > 0 then
    326           inc(Flags, ciCoastalFort);
    327         if MyCity[cix].Built[imMissileBat] > 0 then
    328           inc(Flags, ciMissileBat);
    329         if MyCity[cix].Built[imBunker] > 0 then
    330           inc(Flags, ciBunker);
    331         if MyCity[cix].Built[imSpacePort] > 0 then
    332           inc(Flags, ciSpacePort);
    333       end
    334     end
    335     else
    336     begin
    337       cix := MyRO.nEnemyCity - 1;
    338       while (cix >= 0) and (MyRO.EnemyCity[cix].Loc <> Loc) do
    339         dec(cix);
    340       CityInfo := MyRO.EnemyCity[cix];
    341     end
    342   end;
    343 
    344   function UnitExhausted(uix: integer): boolean;
    345   // check if another move of this unit is still possible
    346   var
    347     dx, dy: integer;
    348   begin
    349     result := true;
    350     if (MyUn[uix].Movement > 0) or
    351       (MyRO.Wonder[woShinkansen].EffectiveOwner = me) then
    352       if (MyUn[uix].Movement >= 100) or
    353         ((MyModel[MyUn[uix].mix].Kind = mkCaravan) and
    354         (MyMap[MyUn[uix].Loc] and fCity <> 0)) then
    355         result := false
    356       else
    357         for dx := -2 to 2 do
    358           for dy := -2 to 2 do
    359             if abs(dx) + abs(dy) = 2 then
    360               if Server(sMoveUnit - sExecute + dx and 7 shl 4 + dy and 7 shl 7,
    361                 me, uix, nil^) >= rExecuted then
    362                 result := false;
    363   end;
    364 
    365   function ModelHash(const ModelInfo: TModelInfo): integer;
    366   var
    367     i, FeatureCode, Hash1, Hash2, Hash2r, d: cardinal;
    368   begin
    369     with ModelInfo do
    370       if Kind > mkEnemyDeveloped then
    371         result := integer($C0000000 + Speed div 50 + Kind shl 8)
    372       else
    373       begin
    374         FeatureCode := 0;
    375         for i := mcFirstNonCap to nFeature - 1 do
    376           if 1 shl Domain and Feature[i].Domains <> 0 then
    377           begin
    378             FeatureCode := FeatureCode * 2;
    379             if 1 shl (i - mcFirstNonCap) <> 0 then
    380               inc(FeatureCode);
    381           end;
    382         case Domain of
    383           dGround:
    384             begin
    385               assert(FeatureCode < 1 shl 8);
    386               assert(Attack < 5113);
    387               assert(Defense < 2273);
    388               assert(Cost < 1611);
    389               Hash1 := (Attack * 2273 + Defense) * 9 + (Speed - 150) div 50;
    390               Hash2 := FeatureCode * 1611 + Cost;
    391             end;
    392           dSea:
    393             begin
    394               assert(FeatureCode < 1 shl 9);
    395               assert(Attack < 12193);
    396               assert(Defense < 6097);
    397               assert(Cost < 4381);
    398               Hash1 := ((Attack * 6097 + Defense) * 5 + (Speed - 350)
    399                 div 100) * 2;
    400               if Weight >= 6 then
    401                 inc(Hash1);
    402               Hash2 := ((TTrans * 17 + ATrans_Fuel) shl 9 + FeatureCode) *
    403                 4381 + Cost;
    404             end;
    405           dAir:
    406             begin
    407               assert(FeatureCode < 1 shl 5);
    408               assert(Attack < 2407);
    409               assert(Defense < 1605);
    410               assert(Bombs < 4813);
    411               assert(Cost < 2089);
    412               Hash1 := (Attack * 1605 + Defense) shl 5 + FeatureCode;
    413               Hash2 := ((Bombs * 7 + ATrans_Fuel) * 4 + TTrans) * 2089 + Cost;
    414             end;
    415         end;
    416         Hash2r := 0;
    417         for i := 0 to 7 do
    418         begin
    419           Hash2r := Hash2r * 13;
    420           d := Hash2 div 13;
    421           inc(Hash2r, Hash2 - d * 13);
    422           Hash2 := d
    423         end;
    424         result := integer(Domain shl 30 + Hash1 xor Hash2r)
    425       end
    426   end;
    427 
    428   function ProcessEnhancement(uix: integer;
    429     const Jobs: TEnhancementJobs): integer;
     423end;
     424
     425function ProcessEnhancement(uix: integer; const Jobs: TEnhancementJobs): integer;
    430426  { return values:
    431427    eJobDone - all applicable jobs done
    432428    eOK - enhancement not complete
    433429    eDied - job done and died (thurst) }
    434   var
    435     stage, NextJob, Tile: integer;
    436     Done: Set of jNone .. jPoll;
    437   begin
    438     Done := [];
    439     Tile := MyMap[MyUn[uix].Loc];
    440     if Tile and fRoad <> 0 then
    441       include(Done, jRoad);
    442     if Tile and fRR <> 0 then
    443       include(Done, jRR);
    444     if (Tile and fTerImp = tiIrrigation) or (Tile and fTerImp = tiFarm) then
    445       include(Done, jIrr);
    446     if Tile and fTerImp = tiFarm then
    447       include(Done, jFarm);
    448     if Tile and fTerImp = tiMine then
    449       include(Done, jMine);
    450     if Tile and fPoll = 0 then
    451       include(Done, jPoll);
    452 
    453     if MyUn[uix].Job = jNone then
    454       result := eJobDone
    455     else
    456       result := eOK;
    457     while (result <> eOK) and (result <> eDied) do
     430var
     431  stage, NextJob, Tile: integer;
     432  Done: set of jNone .. jPoll;
     433begin
     434  Done := [];
     435  Tile := MyMap[MyUn[uix].Loc];
     436  if Tile and fRoad <> 0 then
     437    include(Done, jRoad);
     438  if Tile and fRR <> 0 then
     439    include(Done, jRR);
     440  if (Tile and fTerImp = tiIrrigation) or (Tile and fTerImp = tiFarm) then
     441    include(Done, jIrr);
     442  if Tile and fTerImp = tiFarm then
     443    include(Done, jFarm);
     444  if Tile and fTerImp = tiMine then
     445    include(Done, jMine);
     446  if Tile and fPoll = 0 then
     447    include(Done, jPoll);
     448
     449  if MyUn[uix].Job = jNone then
     450    Result := eJobDone
     451  else
     452    Result := eOK;
     453  while (Result <> eOK) and (Result <> eDied) do
     454  begin
     455    stage := -1;
     456    repeat
     457      if stage = -1 then
     458        NextJob := jPoll
     459      else
     460        NextJob := Jobs[Tile and fTerrain, stage];
     461      if (NextJob = jNone) or not (NextJob in Done) then
     462        Break;
     463      Inc(stage);
     464    until stage = 5;
     465    if (stage = 5) or (NextJob = jNone) then
    458466    begin
    459       stage := -1;
    460       repeat
    461         if stage = -1 then
    462           NextJob := jPoll
    463         else
    464           NextJob := Jobs[Tile and fTerrain, stage];
    465         if (NextJob = jNone) or not(NextJob in Done) then
    466           Break;
    467         inc(stage);
    468       until stage = 5;
    469       if (stage = 5) or (NextJob = jNone) then
     467      Result := eJobDone;
     468      Break;
     469    end; // tile enhancement complete
     470    Result := Server(sStartJob + NextJob shl 4, me, uix, nil^);
     471    include(Done, NextJob);
     472  end;
     473end;
     474
     475function AutoBuild(cix: integer; const ImpOrder: TImpOrder): boolean;
     476var
     477  i, NewProject: integer;
     478begin
     479  Result := False;
     480  if (MyCity[cix].Project and (cpImp + cpIndex) = cpImp + imTrGoods) or
     481    (MyCity[cix].Flags and chProduction <> 0) then
     482  begin
     483    i := 0;
     484    repeat
     485      while (ImpOrder[i] >= 0) and (MyCity[cix].Built[ImpOrder[i]] > 0) do
     486        Inc(i);
     487      if ImpOrder[i] < 0 then
     488        Break;
     489      assert(i < nImp);
     490      NewProject := cpImp + ImpOrder[i];
     491      if Server(sSetCityProject, me, cix, NewProject) >= rExecuted then
    470492      begin
    471         result := eJobDone;
     493        Result := True;
     494        CityOptimizer_CityChange(cix);
    472495        Break;
    473       end; // tile enhancement complete
    474       result := Server(sStartJob + NextJob shl 4, me, uix, nil^);
    475       include(Done, NextJob)
     496      end;
     497      Inc(i);
     498    until False;
     499  end;
     500end;
     501
     502procedure CalculateAdvValues;
     503var
     504  i, j: integer;
     505  known: array [0 .. nAdv - 1] of integer;
     506
     507  procedure MarkPreqs(i: integer);
     508  begin
     509    if known[i] = 0 then
     510    begin
     511      known[i] := 1;
     512      if (i <> adScience) and (i <> adMassProduction) then
     513      begin
     514        if (AdvPreq[i, 0] >= 0) then
     515          MarkPreqs(AdvPreq[i, 0]);
     516        if (AdvPreq[i, 1] >= 0) then
     517          MarkPreqs(AdvPreq[i, 1]);
     518      end;
    476519    end;
    477520  end;
    478521
    479   function AutoBuild(cix: integer; const ImpOrder: TImpOrder): boolean;
    480   var
    481     i, NewProject: integer;
    482   begin
    483     result := false;
    484     if (MyCity[cix].Project and (cpImp + cpIndex) = cpImp + imTrGoods) or
    485       (MyCity[cix].Flags and chProduction <> 0) then
     522begin
     523  FillChar(AdvValue, SizeOf(AdvValue), 0);
     524  for i := 0 to nAdv - 1 do
     525  begin
     526    FillChar(known, SizeOf(known), 0);
     527    MarkPreqs(i);
     528    for j := 0 to nAdv - 1 do
     529      if known[j] > 0 then
     530        Inc(AdvValue[i]);
     531    if i in FutureTech then
     532      Inc(AdvValue[i], 3000)
     533    else if known[adMassProduction] > 0 then
     534      Inc(AdvValue[i], 2000)
     535    else if known[adScience] > 0 then
     536      Inc(AdvValue[i], 1000);
     537  end;
     538end;
     539
     540procedure DebugMessage(Level: integer; Text: string);
     541begin
     542  Server(sMessage, me, Level, PChar(Text)^);
     543end;
     544
     545function MarkCitiesAround(Loc, cixExcept: integer): boolean;
     546  // return whether a city was marked
     547var
     548  cix: integer;
     549begin
     550  Result := False;
     551  for cix := 0 to MyRO.nCity - 1 do
     552    if (cix <> cixExcept) and (MyCity[cix].Loc >= 0) and
     553      (MyCity[cix].Flags and chCaptured = 0) and
     554      (Distance(MyCity[cix].Loc, Loc) <= 5) then
    486555    begin
    487       i := 0;
    488       repeat
    489         while (ImpOrder[i] >= 0) and (MyCity[cix].Built[ImpOrder[i]] > 0) do
    490           inc(i);
    491         if ImpOrder[i] < 0 then
    492           Break;
    493         assert(i < nImp);
    494         NewProject := cpImp + ImpOrder[i];
    495         if Server(sSetCityProject, me, cix, NewProject) >= rExecuted then
     556      CityNeedsOptimize[cix] := True;
     557      Result := True;
     558    end;
     559end;
     560
     561procedure OptimizeCities(CheckOnly: boolean);
     562var
     563  cix, fix, dx, dy, Loc1, OptiType: integer;
     564  Done: boolean;
     565  Advice: TCityTileAdviceData;
     566begin
     567  repeat
     568    Done := True;
     569    for cix := 0 to MyRO.nCity - 1 do
     570      if CityNeedsOptimize[cix] then
     571      begin
     572        OptiType := (MyCity[cix].Status shr 4) and $0F;
     573        if OptiType <> 0 then
    496574        begin
    497           result := true;
    498           CityOptimizer_CityChange(cix);
    499           Break;
     575          Advice.ResourceWeights := OfferedResourceWeights[OptiType];
     576          Server(sGetCityTileAdvice, me, cix, Advice);
     577          if Advice.Tiles <> MyCity[cix].Tiles then
     578            if CheckOnly then
     579            begin
     580              // TODO: What is this assert for?
     581              // Need to optimize city tiles but CheckOnly true?
     582              //assert(false)
     583            end
     584            else
     585            begin
     586              for fix := 1 to 26 do
     587                if MyCity[cix].Tiles and not Advice.Tiles and
     588                  (1 shl fix) <> 0 then
     589                begin // tile no longer used by this city -- check using it by another
     590                  dy := fix shr 2 - 3;
     591                  dx := fix and 3 shl 1 - 3 + (dy + 3) and 1;
     592                  Loc1 := dLoc(MyCity[cix].Loc, dx, dy);
     593                  if MarkCitiesAround(Loc1, cix) then
     594                    Done := False;
     595                end;
     596              Server(sSetCityTiles, me, cix, Advice.Tiles);
     597            end;
    500598        end;
    501         inc(i);
    502       until false end end;
    503 
    504       procedure CalculateAdvValues;
    505       var
    506         i, j: integer;
    507         known: array [0 .. nAdv - 1] of integer;
    508 
    509         procedure MarkPreqs(i: integer);
    510         begin
    511           if known[i] = 0 then
    512           begin
    513             known[i] := 1;
    514             if (i <> adScience) and (i <> adMassProduction) then
    515             begin
    516               if (AdvPreq[i, 0] >= 0) then
    517                 MarkPreqs(AdvPreq[i, 0]);
    518               if (AdvPreq[i, 1] >= 0) then
    519                 MarkPreqs(AdvPreq[i, 1]);
    520             end
    521           end
    522         end;
    523 
     599        CityNeedsOptimize[cix] := False;
     600      end;
     601  until Done;
     602end;
     603
     604procedure CityOptimizer_BeginOfTurn;
     605var
     606  cix: integer;
     607begin
     608  FillChar(CityNeedsOptimize, MyRO.nCity - 1, 0); // false
     609  if MyRO.Government <> gAnarchy then
     610  begin
     611    for cix := 0 to MyRO.nCity - 1 do
     612      if (MyCity[cix].Loc >= 0) and (MyCity[cix].Flags and chCaptured = 0)
     613      then
     614        CityNeedsOptimize[cix] := True;
     615    OptimizeCities(False); // optimize all cities
     616  end;
     617end;
     618
     619procedure CityOptimizer_CityChange(cix: integer);
     620begin
     621  if (MyRO.Government <> gAnarchy) and (MyCity[cix].Flags and
     622    chCaptured = 0) then
     623  begin
     624    CityNeedsOptimize[cix] := True;
     625    OptimizeCities(False);
     626  end;
     627end;
     628
     629procedure CityOptimizer_TileBecomesAvailable(Loc: integer);
     630begin
     631  if (MyRO.Government <> gAnarchy) and MarkCitiesAround(Loc, -1) then
     632    OptimizeCities(False);
     633end;
     634
     635procedure CityOptimizer_ReleaseCityTiles(cix, ReleasedTiles: integer);
     636var
     637  fix, dx, dy, Loc1: integer;
     638  Done: boolean;
     639begin
     640  if (MyRO.Government <> gAnarchy) and (ReleasedTiles <> 0) then
     641  begin
     642    Done := True;
     643    for fix := 1 to 26 do
     644      if ReleasedTiles and (1 shl fix) <> 0 then
    524645      begin
    525         FillChar(AdvValue, SizeOf(AdvValue), 0);
    526         for i := 0 to nAdv - 1 do
    527         begin
    528           FillChar(known, SizeOf(known), 0);
    529           MarkPreqs(i);
    530           for j := 0 to nAdv - 1 do
    531             if known[j] > 0 then
    532               inc(AdvValue[i]);
    533           if i in FutureTech then
    534             inc(AdvValue[i], 3000)
    535           else if known[adMassProduction] > 0 then
    536             inc(AdvValue[i], 2000)
    537           else if known[adScience] > 0 then
    538             inc(AdvValue[i], 1000)
    539         end;
     646        dy := fix shr 2 - 3;
     647        dx := fix and 3 shl 1 - 3 + (dy + 3) and 1;
     648        Loc1 := dLoc(MyCity[cix].Loc, dx, dy);
     649        if MarkCitiesAround(Loc1, cix) then
     650          Done := False;
    540651      end;
    541 
    542       procedure DebugMessage(Level: integer; Text: string);
    543       begin
    544         Server(sMessage, me, Level, pchar(Text)^)
    545       end;
    546 
    547       function MarkCitiesAround(Loc, cixExcept: integer): boolean;
    548       // return whether a city was marked
    549       var
    550         cix: integer;
    551       begin
    552         result := false;
    553         for cix := 0 to MyRO.nCity - 1 do
    554           if (cix <> cixExcept) and (MyCity[cix].Loc >= 0) and
    555             (MyCity[cix].Flags and chCaptured = 0) and
    556             (Distance(MyCity[cix].Loc, Loc) <= 5) then
    557           begin
    558             CityNeedsOptimize[cix] := true;
    559             result := true;
    560           end
    561       end;
    562 
    563       procedure OptimizeCities(CheckOnly: boolean);
    564       var
    565         cix, fix, dx, dy, Loc1, OptiType: integer;
    566         Done: boolean;
    567         Advice: TCityTileAdviceData;
    568       begin
    569         repeat
    570           Done := true;
    571           for cix := 0 to MyRO.nCity - 1 do
    572             if CityNeedsOptimize[cix] then begin
    573               OptiType := (MyCity[cix].Status shr 4) and $0F;
    574               if OptiType <> 0 then begin
    575                 Advice.ResourceWeights := OfferedResourceWeights[OptiType];
    576                 Server(sGetCityTileAdvice, me, cix, Advice);
    577                 if Advice.Tiles <> MyCity[cix].Tiles then
    578                   if CheckOnly then begin
    579                     // TODO: What is this assert for?
    580                     // Need to optimize city tiles but CheckOnly true?
    581                     //assert(false)
    582                   end else begin
    583                     for fix := 1 to 26 do
    584                       if MyCity[cix].Tiles and not Advice.Tiles and
    585                         (1 shl fix) <> 0 then
    586                       begin // tile no longer used by this city -- check using it by another
    587                         dy := fix shr 2 - 3;
    588                         dx := fix and 3 shl 1 - 3 + (dy + 3) and 1;
    589                         Loc1 := dLoc(MyCity[cix].Loc, dx, dy);
    590                         if MarkCitiesAround(Loc1, cix) then
    591                           Done := false;
    592                       end;
    593                     Server(sSetCityTiles, me, cix, Advice.Tiles);
    594                   end;
    595               end;
    596               CityNeedsOptimize[cix] := false;
    597             end;
    598         until Done;
    599       end;
    600 
    601       procedure CityOptimizer_BeginOfTurn;
    602       var
    603         cix: integer;
    604       begin
    605         FillChar(CityNeedsOptimize, MyRO.nCity - 1, 0); // false
    606         if MyRO.Government <> gAnarchy then
    607         begin
    608           for cix := 0 to MyRO.nCity - 1 do
    609             if (MyCity[cix].Loc >= 0) and (MyCity[cix].Flags and chCaptured = 0)
    610             then
    611               CityNeedsOptimize[cix] := true;
    612           OptimizeCities(false); // optimize all cities
    613         end
    614       end;
    615 
    616       procedure CityOptimizer_CityChange(cix: integer);
    617       begin
    618         if (MyRO.Government <> gAnarchy) and
    619           (MyCity[cix].Flags and chCaptured = 0) then
    620         begin
    621           CityNeedsOptimize[cix] := true;
    622           OptimizeCities(false);
    623         end
    624       end;
    625 
    626       procedure CityOptimizer_TileBecomesAvailable(Loc: integer);
    627       begin
    628         if (MyRO.Government <> gAnarchy) and MarkCitiesAround(Loc, -1) then
    629           OptimizeCities(false);
    630       end;
    631 
    632       procedure CityOptimizer_ReleaseCityTiles(cix, ReleasedTiles: integer);
    633       var
    634         fix, dx, dy, Loc1: integer;
    635         Done: boolean;
    636       begin
    637         if (MyRO.Government <> gAnarchy) and (ReleasedTiles <> 0) then
    638         begin
    639           Done := true;
    640           for fix := 1 to 26 do
    641             if ReleasedTiles and (1 shl fix) <> 0 then
    642             begin
    643               dy := fix shr 2 - 3;
    644               dx := fix and 3 shl 1 - 3 + (dy + 3) and 1;
    645               Loc1 := dLoc(MyCity[cix].Loc, dx, dy);
    646               if MarkCitiesAround(Loc1, cix) then
    647                 Done := false;
    648             end;
    649           if not Done then
    650             OptimizeCities(false);
    651         end
    652       end;
    653 
    654       procedure CityOptimizer_BeforeRemoveUnit(uix: integer);
    655       var
    656         uix1: integer;
    657       begin
    658         if MyRO.Government <> gAnarchy then
    659         begin
    660           if MyUn[uix].Home >= 0 then
    661             CityNeedsOptimize[MyUn[uix].Home] := true;
    662 
    663           // transported units are also removed
    664           for uix1 := 0 to MyRO.nUn - 1 do
    665             if (MyUn[uix1].Loc >= 0) and (MyUn[uix1].Master = uix) and
    666               (MyUn[uix1].Home >= 0) then
    667               CityNeedsOptimize[MyUn[uix1].Home] := true;
    668         end
    669       end;
    670 
    671       procedure CityOptimizer_AfterRemoveUnit;
    672       begin
    673         if MyRO.Government <> gAnarchy then
    674           OptimizeCities(false);
    675       end;
    676 
    677       procedure CityOptimizer_EndOfTurn;
    678       // all cities should already be optimized here -- only check this
    679       var
    680         cix: integer;
    681       begin
     652    if not Done then
     653      OptimizeCities(False);
     654  end;
     655end;
     656
     657procedure CityOptimizer_BeforeRemoveUnit(uix: integer);
     658var
     659  uix1: integer;
     660begin
     661  if MyRO.Government <> gAnarchy then
     662  begin
     663    if MyUn[uix].Home >= 0 then
     664      CityNeedsOptimize[MyUn[uix].Home] := True;
     665
     666    // transported units are also removed
     667    for uix1 := 0 to MyRO.nUn - 1 do
     668      if (MyUn[uix1].Loc >= 0) and (MyUn[uix1].Master = uix) and
     669        (MyUn[uix1].Home >= 0) then
     670        CityNeedsOptimize[MyUn[uix1].Home] := True;
     671  end;
     672end;
     673
     674procedure CityOptimizer_AfterRemoveUnit;
     675begin
     676  if MyRO.Government <> gAnarchy then
     677    OptimizeCities(False);
     678end;
     679
     680procedure CityOptimizer_EndOfTurn;
     681// all cities should already be optimized here -- only check this
     682var
     683  cix: integer;
     684begin
    682685{$IFOPT O-}
    683         if MyRO.Government <> gAnarchy then
    684         begin
    685           FillChar(CityNeedsOptimize, MyRO.nCity - 1, 0); // false
    686           for cix := 0 to MyRO.nCity - 1 do
    687             if (MyCity[cix].Loc >= 0) and (MyCity[cix].Flags and chCaptured = 0)
    688             then
    689               CityNeedsOptimize[cix] := true;
    690           OptimizeCities(true); // check all cities
    691         end;
     686  if MyRO.Government <> gAnarchy then
     687  begin
     688    FillChar(CityNeedsOptimize, MyRO.nCity - 1, 0); // false
     689    for cix := 0 to MyRO.nCity - 1 do
     690      if (MyCity[cix].Loc >= 0) and (MyCity[cix].Flags and chCaptured = 0)
     691      then
     692        CityNeedsOptimize[cix] := True;
     693    OptimizeCities(True); // check all cities
     694  end;
    692695{$ENDIF}
    693       end;
     696end;
    694697
    695698initialization
    696699
    697 Assert(nImp < 128);
    698 CalculateAdvValues;
     700  Assert(nImp < 128);
     701  CalculateAdvValues;
    699702
    700703end.
Note: See TracChangeset for help on using the changeset viewer.