Changeset 6


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

Legend:

Unmodified
Added
Removed
  • trunk/Back.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit Back;
    43
     
    2423
    2524uses
    26 Directories,ScreenTools,Start;
     25  Directories, ScreenTools, Start;
    2726
    2827{$R *.DFM}
     
    3029procedure TBackground.FormCreate(Sender: TObject);
    3130begin
    32 img:=nil;
     31  img := nil;
    3332end;
    3433
    3534procedure TBackground.FormShow(Sender: TObject);
    3635begin
    37 img:=nil;
    38 if FullScreen then
     36  img := nil;
     37  if FullScreen then
    3938  begin
    40   if FileExists(HomeDir+'Graphics\Background.bmp')
    41     or FileExists(HomeDir+'Graphics\Background.png') then
     39    if FileExists(HomeDir + 'Graphics\Background.bmp') or
     40      FileExists(HomeDir + 'Graphics\Background.png') then
    4241    begin
    43     img:=TBitmap.Create;
    44     LoadGraphicFile(img, HomeDir+'Graphics\Background');
     42      img := TBitmap.Create;
     43      LoadGraphicFile(img, HomeDir + 'Graphics\Background');
    4544    end
    4645  end
    47 else
     46  else
    4847  begin
    49   WindowState:=wsNormal;
    50   Width:=StartDlg.Width+16;
    51   Height:=StartDlg.Height+16;
    52   Left:=StartDlg.Left-8;
    53   Top:=StartDlg.Top-8;
     48    WindowState := wsNormal;
     49    Width := StartDlg.Width + 16;
     50    Height := StartDlg.Height + 16;
     51    Left := StartDlg.Left - 8;
     52    Top := StartDlg.Top - 8;
    5453  end
    5554end;
     
    5756procedure TBackground.FormPaint(Sender: TObject);
    5857begin
    59 if img<>nil then
    60   BitBlt(Canvas.Handle,
    61     Screen.Width-img.Width-(Screen.Width-800)*3 div 8,
    62     (Screen.Height-600) div 3,
    63     img.Width,img.Height,
    64     img.Canvas.Handle,0,0,SRCCOPY);
     58  if img <> nil then
     59    BitBlt(Canvas.Handle, Screen.Width - img.Width - (Screen.Width - 800) *
     60      3 div 8, (Screen.Height - 600) div 3, img.Width, img.Height,
     61      img.Canvas.Handle, 0, 0, SRCCOPY);
    6562end;
    6663
    6764procedure TBackground.FormClose(Sender: TObject; var Action: TCloseAction);
    6865begin
    69 if img<>nil then begin img.Free; img:=nil end;
     66  if img <> nil then
     67  begin
     68    img.Free;
     69    img := nil
     70  end;
    7071end;
    7172
    7273end.
    73 
  • trunk/CityProcessing.pas

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

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit CmdList;
    43
     
    65
    76uses
    8 Classes;
     7  Classes;
    98
    109const
    11 MaxDataSize=1024;
     10  MaxDataSize = 1024;
    1211
    1312type
    14 TLogData=array[0..999999999] of Byte;
    15 
    16 TCmdListState=record
    17   nLog, {used size of LogData in bytes}
    18   LoadPos, {position in LogData when loading a game}
    19   LastMovingUnit: integer;
    20   MoveCode, LoadMoveCode: Cardinal;
     13  TLogData = array [0 .. 999999999] of Byte;
     14
     15  TCmdListState = record
     16    nLog, { used size of LogData in bytes }
     17    LoadPos, { position in LogData when loading a game }
     18    LastMovingUnit: integer;
     19    MoveCode, LoadMoveCode: Cardinal;
    2120  end;
    2221
    23 TCmdList=class
    24   constructor Create;
    25   destructor Destroy; override;
    26   procedure Get(var Command, Player, Subject: integer; var Data: pointer);
    27   procedure GetDataChanges(Data: pointer; DataSize: integer);
    28   procedure Put(Command, Player, Subject: integer; Data: pointer);
    29   procedure PutDataChanges(Command, Player: integer; OldData, NewData: pointer; DataSize: integer);
    30   procedure LoadFromFile(const f: TFileStream);
    31   procedure SaveToFile(const f: TFileStream);
    32   procedure AppendToFile(const f: TFileStream; const OldState: TCmdListState);
    33   procedure Cut;
    34   function Progress: integer;
    35 private
    36   LogAlloc: integer; {allocated size of LogData in bytes}
    37   LogData: ^TLogData;
    38   FState: TCmdListState;
    39   procedure PutData(Data: pointer; Length: integer);
    40   procedure CompleteMoveCode;
    41 public
    42   property State: TCmdListState read FState write FState;
     22  TCmdList = class
     23    constructor Create;
     24    destructor Destroy; override;
     25    procedure Get(var Command, Player, Subject: integer; var Data: pointer);
     26    procedure GetDataChanges(Data: pointer; DataSize: integer);
     27    procedure Put(Command, Player, Subject: integer; Data: pointer);
     28    procedure PutDataChanges(Command, Player: integer;
     29      OldData, NewData: pointer; DataSize: integer);
     30    procedure LoadFromFile(const f: TFileStream);
     31    procedure SaveToFile(const f: TFileStream);
     32    procedure AppendToFile(const f: TFileStream; const OldState: TCmdListState);
     33    procedure Cut;
     34    function Progress: integer;
     35  private
     36    LogAlloc: integer; { allocated size of LogData in bytes }
     37    LogData: ^TLogData;
     38    FState: TCmdListState;
     39    procedure PutData(Data: pointer; Length: integer);
     40    procedure CompleteMoveCode;
     41  public
     42    property State: TCmdListState read FState write FState;
    4343  end;
    4444
     
    4646
    4747uses
    48 Protocol;
     48  Protocol;
    4949
    5050const
    51 LogGrow=1 shl 18;
     51  LogGrow = 1 shl 18;
    5252
    5353type
    54 TData=array[0..MaxDataSize-1] of Cardinal;
    55 PData=^TData;
     54  TData = array [0 .. MaxDataSize - 1] of Cardinal;
     55  PData = ^TData;
    5656
    5757constructor TCmdList.Create;
    5858begin
    59 inherited Create;
    60 FState.nLog:=0;
    61 LogAlloc:=0;
    62 LogData:=nil;
    63 FState.LastMovingUnit:=-1;
    64 FState.MoveCode:=0;
    65 FState.LoadMoveCode:=0;
     59  inherited Create;
     60  FState.nLog := 0;
     61  LogAlloc := 0;
     62  LogData := nil;
     63  FState.LastMovingUnit := -1;
     64  FState.MoveCode := 0;
     65  FState.LoadMoveCode := 0;
    6666end;
    6767
    6868destructor TCmdList.Destroy;
    6969begin
    70 ReallocMem(LogData, 0);
    71 inherited Destroy;
    72 end;
    73 
    74 procedure TCmdList.Get(var Command, Player, Subject: integer; var Data: pointer);
     70  ReallocMem(LogData, 0);
     71  inherited Destroy;
     72end;
     73
     74procedure TCmdList.Get(var Command, Player, Subject: integer;
     75  var Data: pointer);
    7576var
    76 DirCode, code: Cardinal;
    77 begin
    78 if FState.LoadMoveCode>0 then
    79   begin
    80   Player:=-1;
    81   if FState.LoadMoveCode and 1=1 then
     77  DirCode, code: Cardinal;
     78begin
     79  if FState.LoadMoveCode > 0 then
     80  begin
     81    Player := -1;
     82    if FState.LoadMoveCode and 1 = 1 then
    8283    begin // FM
    83     DirCode:=FState.LoadMoveCode shr 1 and 7;
    84     Subject:=FState.LastMovingUnit;
    85     FState.LoadMoveCode:=FState.LoadMoveCode shr 4;
    86     end
     84      DirCode := FState.LoadMoveCode shr 1 and 7;
     85      Subject := FState.LastMovingUnit;
     86      FState.LoadMoveCode := FState.LoadMoveCode shr 4;
     87    end
     88    else
     89    begin // M
     90      DirCode := FState.LoadMoveCode shr 3 and 7;
     91      Subject := FState.LoadMoveCode shr 6 and $FFF;
     92      FState.LoadMoveCode := FState.LoadMoveCode shr 18;
     93      FState.LastMovingUnit := Subject
     94    end;
     95    case DirCode of
     96      0:
     97        Command := sMoveUnit + $090;
     98      1:
     99        Command := sMoveUnit + $0F0;
     100      2:
     101        Command := sMoveUnit + $390;
     102      3:
     103        Command := sMoveUnit + $3F0;
     104      4:
     105        Command := sMoveUnit + $020;
     106      5:
     107        Command := sMoveUnit + $060;
     108      6:
     109        Command := sMoveUnit + $100;
     110      7:
     111        Command := sMoveUnit + $300;
     112    end;
     113    Data := nil;
     114  end
    87115  else
    88     begin // M
    89     DirCode:=FState.LoadMoveCode shr 3 and 7;
    90     Subject:=FState.LoadMoveCode shr 6 and $FFF;
    91     FState.LoadMoveCode:=FState.LoadMoveCode shr 18;
    92     FState.LastMovingUnit:=Subject
    93     end;
    94   case DirCode of
    95     0: Command:=sMoveUnit+$090;
    96     1: Command:=sMoveUnit+$0F0;
    97     2: Command:=sMoveUnit+$390;
    98     3: Command:=sMoveUnit+$3F0;
    99     4: Command:=sMoveUnit+$020;
    100     5: Command:=sMoveUnit+$060;
    101     6: Command:=sMoveUnit+$100;
    102     7: Command:=sMoveUnit+$300;
    103     end;
    104   Data:=nil;
    105   end
    106 else
    107   begin
    108   code:=Cardinal((@LogData[FState.LoadPos])^);
    109   if code and 3=0 then
     116  begin
     117    code := Cardinal((@LogData[FState.LoadPos])^);
     118    if code and 3 = 0 then
    110119    begin // non-clientex command
    111     Command:=code shr 2 and $3FFF +sExecute;
    112     Player:=code shr 16 and $f;
    113     Subject:=code shr 20 and $FFF;
    114     inc(FState.LoadPos,4);
    115     end
    116   else if code and 7=2 then
     120      Command := code shr 2 and $3FFF + sExecute;
     121      Player := code shr 16 and $F;
     122      Subject := code shr 20 and $FFF;
     123      inc(FState.LoadPos, 4);
     124    end
     125    else if code and 7 = 2 then
    117126    begin // clientex command
    118     Command:=code shr 3 and $FFFF;
    119     Player:=code shr 19 and $f;
    120     Subject:=0;
    121     inc(FState.LoadPos,3);
    122     end
    123   else
     127      Command := code shr 3 and $FFFF;
     128      Player := code shr 19 and $F;
     129      Subject := 0;
     130      inc(FState.LoadPos, 3);
     131    end
     132    else
    124133    begin // move command shortcut
    125     if (code and 1=1) and (code and (7 shl 4)<>6 shl 4) then
    126       begin FState.LoadMoveCode:=code and $FF; inc(FState.LoadPos) end
    127     else begin FState.LoadMoveCode:=code and $FFFFFF; inc(FState.LoadPos,3); end;
    128     Get(Command, Player, Subject, Data);
    129     exit;
    130     end;
    131 
    132   if Command and $f=0 then Data:=nil
    133   else
    134     begin
    135     Data:=@LogData[FState.LoadPos];
    136     inc(FState.LoadPos,Command and $f *4);
     134      if (code and 1 = 1) and (code and (7 shl 4) <> 6 shl 4) then
     135      begin
     136        FState.LoadMoveCode := code and $FF;
     137        inc(FState.LoadPos)
     138      end
     139      else
     140      begin
     141        FState.LoadMoveCode := code and $FFFFFF;
     142        inc(FState.LoadPos, 3);
     143      end;
     144      Get(Command, Player, Subject, Data);
     145      exit;
     146    end;
     147
     148    if Command and $F = 0 then
     149      Data := nil
     150    else
     151    begin
     152      Data := @LogData[FState.LoadPos];
     153      inc(FState.LoadPos, Command and $F * 4);
    137154    end
    138155  end
     
    141158procedure TCmdList.GetDataChanges(Data: pointer; DataSize: integer);
    142159var
    143 b0, b1: integer;
    144 Map0, Map1: Cardinal;
    145 begin
    146 Map0:=Cardinal((@LogData[FState.LoadPos])^);
    147 inc(FState.LoadPos,4);
    148 b0:=0;
    149 while Map0>0 do
    150   begin
    151   if Map0 and 1<>0 then
    152     begin
    153     Map1:=Cardinal((@LogData[FState.LoadPos])^);
    154     inc(FState.LoadPos,4);
    155     for b1:=0 to 31 do if 1 shl b1 and Map1<>0 then
    156       begin
    157       if b0*32+b1<DataSize then
    158         PData(Data)[b0*32+b1]:=Cardinal((@LogData[FState.LoadPos])^);
    159       inc(FState.LoadPos,4);
    160       end;
    161     end;
    162   inc(b0);
    163   Map0:=Map0 shr 1;
     160  b0, b1: integer;
     161  Map0, Map1: Cardinal;
     162begin
     163  Map0 := Cardinal((@LogData[FState.LoadPos])^);
     164  inc(FState.LoadPos, 4);
     165  b0 := 0;
     166  while Map0 > 0 do
     167  begin
     168    if Map0 and 1 <> 0 then
     169    begin
     170      Map1 := Cardinal((@LogData[FState.LoadPos])^);
     171      inc(FState.LoadPos, 4);
     172      for b1 := 0 to 31 do
     173        if 1 shl b1 and Map1 <> 0 then
     174        begin
     175          if b0 * 32 + b1 < DataSize then
     176            PData(Data)[b0 * 32 + b1] := Cardinal((@LogData[FState.LoadPos])^);
     177          inc(FState.LoadPos, 4);
     178        end;
     179    end;
     180    inc(b0);
     181    Map0 := Map0 shr 1;
    164182  end
    165183end;
     
    167185procedure TCmdList.Put(Command, Player, Subject: integer; Data: pointer);
    168186var
    169 DirCode, code: Cardinal;
    170 begin
    171 if Command and $FC00=sMoveUnit then
     187  DirCode, code: Cardinal;
     188begin
     189  if Command and $FC00 = sMoveUnit then
    172190  begin // move command shortcut
    173   case Command of
    174     sMoveUnit+$090: DirCode:=0;
    175     sMoveUnit+$0F0: DirCode:=1;
    176     sMoveUnit+$390: DirCode:=2;
    177     sMoveUnit+$3F0: DirCode:=3;
    178     sMoveUnit+$020: DirCode:=4;
    179     sMoveUnit+$060: DirCode:=5;
    180     sMoveUnit+$100: DirCode:=6;
    181     sMoveUnit+$300: DirCode:=7;
    182     end;
    183   if Subject=FState.LastMovingUnit then code:=1+DirCode shl 1
    184   else code:=6+DirCode shl 3+Cardinal(Subject) shl 6;
    185   if FState.MoveCode=0 then FState.MoveCode:=code
    186   else if FState.MoveCode and 1=1 then
     191    case Command of
     192      sMoveUnit + $090:
     193        DirCode := 0;
     194      sMoveUnit + $0F0:
     195        DirCode := 1;
     196      sMoveUnit + $390:
     197        DirCode := 2;
     198      sMoveUnit + $3F0:
     199        DirCode := 3;
     200      sMoveUnit + $020:
     201        DirCode := 4;
     202      sMoveUnit + $060:
     203        DirCode := 5;
     204      sMoveUnit + $100:
     205        DirCode := 6;
     206      sMoveUnit + $300:
     207        DirCode := 7;
     208    end;
     209    if Subject = FState.LastMovingUnit then
     210      code := 1 + DirCode shl 1
     211    else
     212      code := 6 + DirCode shl 3 + Cardinal(Subject) shl 6;
     213    if FState.MoveCode = 0 then
     214      FState.MoveCode := code
     215    else if FState.MoveCode and 1 = 1 then
    187216    begin // FM + this
    188     FState.MoveCode:=FState.MoveCode+code shl 4;
    189     if code and 1=1 then PutData(@FState.MoveCode, 1) // FM + FM
    190     else PutData(@FState.MoveCode, 3); // FM + M
    191     FState.MoveCode:=0;
    192     end
    193   else if code and 1=1 then
     217      FState.MoveCode := FState.MoveCode + code shl 4;
     218      if code and 1 = 1 then
     219        PutData(@FState.MoveCode, 1) // FM + FM
     220      else
     221        PutData(@FState.MoveCode, 3); // FM + M
     222      FState.MoveCode := 0;
     223    end
     224    else if code and 1 = 1 then
    194225    begin // M + FM
    195     FState.MoveCode:=FState.MoveCode+code shl 18;
    196     PutData(@FState.MoveCode, 3);
    197     FState.MoveCode:=0;
    198     end
    199   else // M + M
    200     begin
    201     PutData(@FState.MoveCode, 3);
    202     FState.MoveCode:=code
    203     end;
    204   FState.LastMovingUnit:=Subject;
     226      FState.MoveCode := FState.MoveCode + code shl 18;
     227      PutData(@FState.MoveCode, 3);
     228      FState.MoveCode := 0;
     229    end
     230    else // M + M
     231    begin
     232      PutData(@FState.MoveCode, 3);
     233      FState.MoveCode := code
     234    end;
     235    FState.LastMovingUnit := Subject;
    205236  end
    206 else
    207   begin
     237  else
     238  begin
     239    CompleteMoveCode;
     240    if Command >= cClientEx then
     241    begin
     242      code := 2 + Command shl 3 + Player shl 19;
     243      PutData(@code, 3);
     244    end
     245    else
     246    begin
     247      code := Cardinal(Command - sExecute) shl 2 + Cardinal(Player) shl 16 +
     248        Cardinal(Subject) shl 20;
     249      PutData(@code, 4);
     250    end;
     251  end;
     252  if Command and $F <> 0 then
     253    PutData(Data, Command and $F * 4);
     254end;
     255
     256procedure TCmdList.PutDataChanges(Command, Player: integer;
     257  OldData, NewData: pointer; DataSize: integer);
     258var
     259  MapPos, LogPos, b0, b1, RowEnd: integer;
     260  Map0, Map1, code: Cardinal;
     261begin
     262  if DataSize <= 0 then
     263    exit;
     264  if DataSize > MaxDataSize then
     265    DataSize := MaxDataSize;
    208266  CompleteMoveCode;
    209   if Command>=cClientEx then
    210     begin
    211     code:=2+Command shl 3+Player shl 19;
    212     PutData(@code, 3);
    213     end
    214   else
    215     begin
    216     code:=Cardinal(Command-sExecute) shl 2+Cardinal(Player) shl 16
    217       +Cardinal(Subject) shl 20;
    218     PutData(@code, 4);
     267  MapPos := FState.nLog + 8;
     268  LogPos := MapPos + 4;
     269  Map0 := 0;
     270  for b0 := 0 to (DataSize - 1) div 32 do
     271  begin
     272    if LogPos + 4 * 32 > LogAlloc then
     273    begin
     274      inc(LogAlloc, LogGrow);
     275      ReallocMem(LogData, LogAlloc);
     276    end;
     277    Map0 := Map0 shr 1;
     278    Map1 := 0;
     279    RowEnd := DataSize - 1;
     280    if RowEnd > b0 * 32 + 31 then
     281      RowEnd := b0 * 32 + 31;
     282    for b1 := b0 * 32 to RowEnd do
     283    begin
     284      Map1 := Map1 shr 1;
     285      if PData(NewData)[b1] <> PData(OldData)[b1] then
     286      begin
     287        Cardinal((@LogData[LogPos])^) := PData(NewData)[b1];
     288        inc(LogPos, 4);
     289        inc(Map1, $80000000);
     290      end;
     291    end;
     292    if Map1 > 0 then
     293    begin
     294      Map1 := Map1 shr (b0 * 32 + 31 - RowEnd);
     295      Cardinal((@LogData[MapPos])^) := Map1;
     296      MapPos := LogPos;
     297      inc(LogPos, 4);
     298      inc(Map0, $80000000);
    219299    end;
    220300  end;
    221 if Command and $f<>0 then PutData(Data, Command and $f *4);
    222 end;
    223 
    224 procedure TCmdList.PutDataChanges(Command, Player: integer; OldData,
    225   NewData: pointer; DataSize: integer);
    226 var
    227 MapPos, LogPos, b0, b1, RowEnd: integer;
    228 Map0, Map1, code: Cardinal;
    229 begin
    230 if DataSize<=0 then exit;
    231 if DataSize>MaxDataSize then DataSize:=MaxDataSize;
    232 CompleteMoveCode;
    233 MapPos:=FState.nLog+8;
    234 LogPos:=MapPos+4;
    235 Map0:=0;
    236 for b0:=0 to (DataSize-1) div 32 do
    237   begin
    238   if LogPos+4*32>LogAlloc then
    239     begin
     301  if Map0 = 0 then
     302    exit; // no changes
     303
     304  Map0 := Map0 shr (31 - (DataSize - 1) div 32);
     305  Cardinal((@LogData[FState.nLog + 4])^) := Map0;
     306  code := Cardinal(Command - sExecute) shl 2 + Cardinal(Player) shl 16;
     307  Cardinal((@LogData[FState.nLog])^) := code;
     308  FState.nLog := MapPos
     309end;
     310
     311procedure TCmdList.PutData(Data: pointer; Length: integer);
     312begin
     313  if FState.nLog + Length > LogAlloc then
     314  begin
    240315    inc(LogAlloc, LogGrow);
    241316    ReallocMem(LogData, LogAlloc);
    242     end;
    243   Map0:=Map0 shr 1;
    244   Map1:=0;
    245   RowEnd:=DataSize-1;
    246   if RowEnd>b0*32+31 then RowEnd:=b0*32+31;
    247   for b1:=b0*32 to RowEnd do
    248     begin
    249     Map1:=Map1 shr 1;
    250     if PData(NewData)[b1]<>PData(OldData)[b1] then
    251       begin
    252       Cardinal((@LogData[LogPos])^):=PData(NewData)[b1];
    253       inc(LogPos,4);
    254       inc(Map1,$80000000);
    255       end;
    256     end;
    257   if Map1>0 then
    258     begin
    259     Map1:=Map1 shr (b0*32+31-RowEnd);
    260     Cardinal((@LogData[MapPos])^):=Map1;
    261     MapPos:=LogPos;
    262     inc(LogPos,4);
    263     inc(Map0,$80000000);
    264     end;
    265317  end;
    266 if Map0=0 then exit; // no changes
    267 
    268 Map0:=Map0 shr (31-(DataSize-1) div 32);
    269 Cardinal((@LogData[FState.nLog+4])^):=Map0;
    270 code:=Cardinal(Command-sExecute) shl 2+Cardinal(Player) shl 16;
    271 Cardinal((@LogData[FState.nLog])^):=code;
    272 FState.nLog:=MapPos
    273 end;
    274 
    275 procedure TCmdList.PutData(Data: pointer; Length: integer);
    276 begin
    277 if FState.nLog+Length>LogAlloc then
    278   begin
    279   inc(LogAlloc, LogGrow);
     318  move(Data^, LogData[FState.nLog], Length);
     319  inc(FState.nLog, Length);
     320end;
     321
     322procedure TCmdList.CompleteMoveCode;
     323begin
     324  if FState.MoveCode > 0 then
     325  begin
     326    if FState.MoveCode and 1 = 1 then
     327      PutData(@FState.MoveCode, 1) // Single FM
     328    else
     329      PutData(@FState.MoveCode, 3); // Single M
     330    FState.MoveCode := 0;
     331  end
     332end;
     333
     334procedure TCmdList.LoadFromFile(const f: TFileStream);
     335begin
     336  f.read(FState.nLog, 4);
     337  LogData := nil;
     338  LogAlloc := ((FState.nLog + 2) div LogGrow + 1) * LogGrow;
    280339  ReallocMem(LogData, LogAlloc);
    281   end;
    282 move(Data^, LogData[FState.nLog], Length);
    283 inc(FState.nLog, Length);
    284 end;
    285 
    286 procedure TCmdList.CompleteMoveCode;
    287 begin
    288 if FState.MoveCode>0 then
    289   begin
    290   if FState.MoveCode and 1=1 then PutData(@FState.MoveCode, 1) // Single FM
    291   else PutData(@FState.MoveCode, 3); // Single M
    292   FState.MoveCode:=0;
    293   end
    294 end;
    295 
    296 procedure TCmdList.LoadFromFile(const f: TFileStream);
    297 begin
    298 f.read(FState.nLog, 4);
    299 LogData:=nil;
    300 LogAlloc:=((FState.nLog+2) div LogGrow +1)*LogGrow;
    301 ReallocMem(LogData, LogAlloc);
    302 f.read(LogData^, FState.nLog);
    303 FState.LoadPos:=0;
     340  f.read(LogData^, FState.nLog);
     341  FState.LoadPos := 0;
    304342end;
    305343
    306344procedure TCmdList.SaveToFile(const f: TFileStream);
    307345begin
    308 CompleteMoveCode;
    309 f.write(FState.nLog, 4);
    310 f.write(LogData^, FState.nLog)
    311 end;
    312 
    313 procedure TCmdList.AppendToFile(const f: TFileStream; const OldState: TCmdListState);
    314 begin
    315 CompleteMoveCode;
    316 f.write(FState.nLog, 4);
    317 f.Position:=f.Position+OldState.nLog;
    318 f.write(LogData[OldState.nLog], FState.nLog-OldState.nLog)
     346  CompleteMoveCode;
     347  f.write(FState.nLog, 4);
     348  f.write(LogData^, FState.nLog)
     349end;
     350
     351procedure TCmdList.AppendToFile(const f: TFileStream;
     352  const OldState: TCmdListState);
     353begin
     354  CompleteMoveCode;
     355  f.write(FState.nLog, 4);
     356  f.Position := f.Position + OldState.nLog;
     357  f.write(LogData[OldState.nLog], FState.nLog - OldState.nLog)
    319358end;
    320359
    321360procedure TCmdList.Cut;
    322361begin
    323 FState.nLog:=FState.LoadPos;
     362  FState.nLog := FState.LoadPos;
    324363end;
    325364
    326365function TCmdList.Progress: integer;
    327366begin
    328 if (FState.LoadPos=FState.nLog) and (FState.LoadMoveCode=0) then
    329   result:=1000 // loading complete
    330 else if FState.nLog>1 shl 20 then
    331   result:=(FState.LoadPos shr 8)*999 div (FState.nLog shr 8)
    332 else result:=FState.LoadPos*999 div FState.nLog
    333 end;
    334 
    335 {Format Specification:
    336 
    337 Non-ClientEx-Command:
     367  if (FState.LoadPos = FState.nLog) and (FState.LoadMoveCode = 0) then
     368    result := 1000 // loading complete
     369  else if FState.nLog > 1 shl 20 then
     370    result := (FState.LoadPos shr 8) * 999 div (FState.nLog shr 8)
     371  else
     372    result := FState.LoadPos * 999 div FState.nLog
     373end;
     374
     375{ Format Specification:
     376
     377  Non-ClientEx-Command:
    338378  Byte3    Byte2    Byte1    Byte0
    339379  ssssssss sssspppp cccccccc cccccc00
    340380  (c = Command-sExecute, p = Player, s = Subject)
    341381
    342 ClientEx-Command:
     382  ClientEx-Command:
    343383  Byte2    Byte1    Byte0
    344384  0ppppccc cccccccc ccccc010
    345385  (c = Command, p = Player)
    346386
    347 Single Move:
     387  Single Move:
    348388  Byte2    Byte1    Byte0
    349389  000000ss ssssssss ssaaa110
    350390  (a = Direction, s = Subject)
    351391
    352 Move + Follow Move:
     392  Move + Follow Move:
    353393  Byte2    Byte1    Byte0
    354394  00bbb1ss ssssssss ssaaa110
    355395  (a = Direction 1, s = Subject 1, b = Direction 2)
    356396
    357 Follow Move + Move:
     397  Follow Move + Move:
    358398  Byte2    Byte1    Byte0
    359399  00ssssss ssssssbb b110aaa1
    360400  (a = Direction 1, b = Direction 2, s = Subject 2)
    361401
    362 Single Follow Move:
     402  Single Follow Move:
    363403  Byte0
    364404  0000aaa1
    365405  (a = Direction)
    366406
    367 Double Follow Move:
     407  Double Follow Move:
    368408  Byte0
    369409  bbb1aaa1
     
    372412
    373413end.
    374 
  • trunk/Database.pas

    r2 r6  
    11{$INCLUDE switches}
    2 //{$DEFINE TEXTLOG}
    3 //{$DEFINE LOADPERF}
     2// {$DEFINE TEXTLOG}
     3// {$DEFINE LOADPERF}
    44unit Database;
    55
     
    77
    88uses
    9 Protocol,CmdList;
     9  Protocol, CmdList;
    1010
    1111const
    12 // additional test flags
    13 FastContact=false; {extra small world with railroad everywhere}
    14 
    15 neumax=4096;
    16 necmax=1024;
    17 nemmax=1024;
    18 
    19 lNoObserve=0; lObserveUnhidden=1; lObserveAll=2; lObserveSuper=3; //observe levels
    20 
    21 TerrType_Canalable=[fGrass,fDesert,fPrairie,fTundra,fSwamp,fForest,fHills];
    22 
    23 nStartUn=1;
    24 StartUn: array[0..nStartUn-1] of integer=(0); //mix of start units
    25 
    26 CityOwnTile=13;
    27 
    28 var
    29 GAlive, {players alive; bitset of 1 shl p}
    30 GWatching,
    31 GInitialized,
    32 GAI,
    33 RND, {world map randseed}
    34 lx,ly,
    35 MapSize, // = lx*ly
    36 LandMass,
    37 {$IFOPT O-}InvalidTreatyMap,{$ENDIF}
    38 SaveMapCenterLoc,
    39 PeaceEnded,
    40 GTurn, {current turn}
    41 GTestFlags: integer;
    42 Mode: (moLoading_Fast, moLoading, moMovie, moPlaying);
    43 GWonder: array[0..27] of TWonderInfo;
    44 ServerVersion: array[0..nPl-1] of integer;
    45 ProcessClientData: array[0..nPl-1] of boolean;
    46 CL: TCmdList;
    47 {$IFDEF TEXTLOG}CmdInfo: string; TextLog: TextFile;{$ENDIF}
    48 {$IFDEF LOADPERF}time_total,time_total0,time_x0,time_x1,time_a,time_b,time_c: int64;{$ENDIF}
    49 
    50 // map data
    51 RealMap: array[0..lxmax*lymax-1] of Cardinal;
    52 Continent:array[0..lxmax*lymax-1] of integer; {continent id for each tile}
    53 Occupant:array[0..lxmax*lymax-1] of ShortInt; {occupying player for each tile}
    54 ZoCMap:array[0..lxmax*lymax-1] of ShortInt;
    55 ObserveLevel:array[0..lxmax*lymax-1] of Cardinal;
    56   {Observe Level of player p in bits 2*p and 2*p+1}
    57 UsedByCity:array[0..lxmax*lymax-1] of integer; {location of exploiting city for
    58   each tile, =-1 if not exploited}
    59 
    60 // player data
    61 RW: array[0..nPl-1] of TPlayerContext;{player data}
    62 Difficulty: array[0..nPl-1] of integer;
    63 GShip: array[0..nPl-1] of TShipInfo;
    64 ResourceMask: array[0..nPl-1] of Cardinal;
    65 Founded: array[0..nPl-1] of integer; {number of cities founded}
    66 TerritoryCount: array[0..nPl] of integer;
    67 LastValidStat,
    68 Researched,
    69 Discovered, // number of tiles discovered
    70 GrWallContinent: array[0..nPl-1] of integer;
    71 RWemix: array[0..nPl-1, 0..nPl-1, 0..nmmax-1] of SmallInt;
     12  // additional test flags
     13  FastContact = false; { extra small world with railroad everywhere }
     14
     15  neumax = 4096;
     16  necmax = 1024;
     17  nemmax = 1024;
     18
     19  lNoObserve = 0;
     20  lObserveUnhidden = 1;
     21  lObserveAll = 2;
     22  lObserveSuper = 3; // observe levels
     23
     24  TerrType_Canalable = [fGrass, fDesert, fPrairie, fTundra, fSwamp,
     25    fForest, fHills];
     26
     27  nStartUn = 1;
     28  StartUn: array [0 .. nStartUn - 1] of integer = (0); // mix of start units
     29
     30  CityOwnTile = 13;
     31
     32var
     33  GAlive, { players alive; bitset of 1 shl p }
     34  GWatching, GInitialized, GAI, RND, { world map randseed }
     35  lx, ly, MapSize, // = lx*ly
     36  LandMass,
     37{$IFOPT O-}InvalidTreatyMap, {$ENDIF}
     38  SaveMapCenterLoc, PeaceEnded, GTurn, { current turn }
     39  GTestFlags: integer;
     40  Mode: (moLoading_Fast, moLoading, moMovie, moPlaying);
     41  GWonder: array [0 .. 27] of TWonderInfo;
     42  ServerVersion: array [0 .. nPl - 1] of integer;
     43  ProcessClientData: array [0 .. nPl - 1] of boolean;
     44  CL: TCmdList;
     45{$IFDEF TEXTLOG}CmdInfo: string;
     46  TextLog: TextFile; {$ENDIF}
     47{$IFDEF LOADPERF}time_total, time_total0, time_x0, time_x1, time_a, time_b, time_c: int64; {$ENDIF}
     48  // map data
     49  RealMap: array [0 .. lxmax * lymax - 1] of Cardinal;
     50  Continent: array [0 .. lxmax * lymax - 1] of integer;
     51  { continent id for each tile }
     52  Occupant: array [0 .. lxmax * lymax - 1] of ShortInt;
     53  { occupying player for each tile }
     54  ZoCMap: array [0 .. lxmax * lymax - 1] of ShortInt;
     55  ObserveLevel: array [0 .. lxmax * lymax - 1] of Cardinal;
     56  { Observe Level of player p in bits 2*p and 2*p+1 }
     57  UsedByCity: array [0 .. lxmax * lymax - 1] of integer;
     58  { location of exploiting city for
     59    each tile, =-1 if not exploited }
     60
     61  // player data
     62  RW: array [0 .. nPl - 1] of TPlayerContext; { player data }
     63  Difficulty: array [0 .. nPl - 1] of integer;
     64  GShip: array [0 .. nPl - 1] of TShipInfo;
     65  ResourceMask: array [0 .. nPl - 1] of Cardinal;
     66  Founded: array [0 .. nPl - 1] of integer; { number of cities founded }
     67  TerritoryCount: array [0 .. nPl] of integer;
     68  LastValidStat, Researched, Discovered, // number of tiles discovered
     69  GrWallContinent: array [0 .. nPl - 1] of integer;
     70  RWemix: array [0 .. nPl - 1, 0 .. nPl - 1, 0 .. nmmax - 1] of SmallInt;
    7271  // [p1,p2,mix] -> index of p2's model mix in p1's enemy model list
    73 Destroyed: array[0..nPl-1, 0..nPl-1, 0..nmmax-1] of SmallInt;
     72  Destroyed: array [0 .. nPl - 1, 0 .. nPl - 1, 0 .. nmmax - 1] of SmallInt;
    7473  // [p1,p2,mix] -> number of p2's units with model mix that p1 has destroyed
    75 nTech: array[0..nPl-1] of integer; {number of known techs}
    76 //NewContact: array[0..nPl-1,0..nPl-1] of boolean;
     74  nTech: array [0 .. nPl - 1] of integer; { number of known techs }
     75  // NewContact: array[0..nPl-1,0..nPl-1] of boolean;
    7776
    7877type
    79 TVicinity8Loc=array[0..7] of integer;
    80 TVicinity21Loc=array[0..27] of integer;
     78  TVicinity8Loc = array [0 .. 7] of integer;
     79  TVicinity21Loc = array [0 .. 27] of integer;
    8180
    8281procedure MaskD(var x; Count, Mask: Cardinal);
    83 procedure IntServer(Command,Player,Subject:integer;var Data);
     82procedure IntServer(Command, Player, Subject: integer; var Data);
    8483procedure CompactLists(p: integer);
    8584procedure ClearTestFlags(ClearFlags: integer);
    86 procedure SetTestFlags(p,SetFlags: integer);
     85procedure SetTestFlags(p, SetFlags: integer);
    8786
    8887// Tech Related Functions
    89 function TechBaseCost(nTech,diff: integer): integer;
     88function TechBaseCost(nTech, diff: integer): integer;
    9089function TechCost(p: integer): integer;
    9190procedure CalculateModel(var m: TModel);
    92 procedure CheckSpecialModels(p,pre: integer);
     91procedure CheckSpecialModels(p, pre: integer);
    9392procedure EnableDevModel(p: integer);
    94 procedure SeeTech(p,ad: integer);
    95 procedure DiscoverTech(p,ad: integer);
     93procedure SeeTech(p, ad: integer);
     94procedure DiscoverTech(p, ad: integer);
    9695procedure CheckExpiration(Wonder: integer);
    9796
    9897// Location Navigation
    99 function dLoc(Loc,dx,dy: integer): integer;
    100 procedure dxdy(Loc0,Loc1: integer; var dx,dy: integer);
    101 function Distance(Loc0,Loc1: integer): integer;
     98function dLoc(Loc, dx, dy: integer): integer;
     99procedure dxdy(Loc0, Loc1: integer; var dx, dy: integer);
     100function Distance(Loc0, Loc1: integer): integer;
    102101procedure V8_to_Loc(Loc0: integer; var VicinityLoc: TVicinity8Loc);
    103102procedure V21_to_Loc(Loc0: integer; var VicinityLoc: TVicinity21Loc);
     
    118117// Map Revealing
    119118function GetTileInfo(p, cix, Loc: integer; var Info: TTileInfo): integer;
    120 procedure Strongest(Loc:integer;var uix,Strength,Bonus,Cnt:integer);
     119procedure Strongest(Loc: integer; var uix, Strength, Bonus, Cnt: integer);
    121120function UnitSpeed(p, mix, Health: integer): integer;
    122 procedure GetUnitReport(p,uix: integer; var UnitReport: TUnitReport);
    123 procedure SearchCity(Loc: integer; var p,cix: integer);
    124 procedure TellAboutModel(p,taOwner,tamix: integer);
    125 function emixSafe(p,taOwner,tamix: integer): integer;
    126 function Discover9(Loc,p,Level: integer; TellAllied, EnableContact: boolean): boolean;
    127 function Discover21(Loc,p,AdjacentLevel: integer; TellAllied, EnableContact: boolean): boolean;
     121procedure GetUnitReport(p, uix: integer; var UnitReport: TUnitReport);
     122procedure SearchCity(Loc: integer; var p, cix: integer);
     123procedure TellAboutModel(p, taOwner, tamix: integer);
     124function emixSafe(p, taOwner, tamix: integer): integer;
     125function Discover9(Loc, p, Level: integer;
     126  TellAllied, EnableContact: boolean): boolean;
     127function Discover21(Loc, p, AdjacentLevel: integer;
     128  TellAllied, EnableContact: boolean): boolean;
    128129procedure DiscoverAll(p, Level: integer);
    129130procedure DiscoverViewAreas(p: integer);
    130 function GetUnitStack(p,Loc: integer): integer;
     131function GetUnitStack(p, Loc: integer): integer;
    131132procedure UpdateUnitMap(Loc: integer; CityChange: boolean = false);
    132 procedure RecalcV8ZoC(p,Loc: integer);
     133procedure RecalcV8ZoC(p, Loc: integer);
    133134procedure RecalcMapZoC(p: integer);
    134135procedure RecalcPeaceMap(p: integer);
     
    136137// Territory Calculation
    137138procedure CheckBorders(OriginLoc: integer; PlayerLosingCity: integer = -1);
    138 procedure LogCheckBorders(p,cix: integer; PlayerLosingCity: integer = -1);
     139procedure LogCheckBorders(p, cix: integer; PlayerLosingCity: integer = -1);
    139140
    140141// Map Processing
    141 procedure CreateUnit(p,mix: integer);
    142 procedure FreeUnit(p,uix: integer);
    143 procedure PlaceUnit(p,uix: integer);
    144 procedure RemoveUnit(p,uix: integer; Enemy: integer = -1);
    145 procedure RemoveUnit_UpdateMap(p,uix: integer);
    146 procedure RemoveAllUnits(p,Loc: integer; Enemy: integer = -1);
    147 procedure RemoveDomainUnits(d,p,Loc: integer);
    148 procedure FoundCity(p,FoundLoc: integer);
    149 procedure DestroyCity(p,cix: integer; SaveUnits: boolean);
    150 procedure ChangeCityOwner(pOld,cixOld,pNew: integer);
    151 procedure CompleteJob(p,Loc,Job: integer);
     142procedure CreateUnit(p, mix: integer);
     143procedure FreeUnit(p, uix: integer);
     144procedure PlaceUnit(p, uix: integer);
     145procedure RemoveUnit(p, uix: integer; Enemy: integer = -1);
     146procedure RemoveUnit_UpdateMap(p, uix: integer);
     147procedure RemoveAllUnits(p, Loc: integer; Enemy: integer = -1);
     148procedure RemoveDomainUnits(d, p, Loc: integer);
     149procedure FoundCity(p, FoundLoc: integer);
     150procedure DestroyCity(p, cix: integer; SaveUnits: boolean);
     151procedure ChangeCityOwner(pOld, cixOld, pNew: integer);
     152procedure CompleteJob(p, Loc, Job: integer);
    152153
    153154// Diplomacy
    154 procedure IntroduceEnemy(p1,p2: integer);
     155procedure IntroduceEnemy(p1, p2: integer);
    155156procedure GiveCivilReport(p, pAbout: integer);
    156157procedure GiveMilReport(p, pAbout: integer);
     
    158159function PayPrice(pSender, pTarget, Price: integer; execute: boolean): boolean;
    159160procedure CancelTreaty(p, pWith: integer; DecreaseCredibility: boolean = true);
    160 function DoSpyMission(p,pCity,cix,Mission: integer): Cardinal;
    161 
     161function DoSpyMission(p, pCity, cix, Mission: integer): Cardinal;
    162162
    163163implementation
    164164
    165165uses
    166 {$IFDEF LOADPERF}SysUtils, Windows,{$ENDIF}
    167 {$IFDEF TEXTLOG}SysUtils,{$ENDIF}
    168 IPQ;
    169 
    170 var
    171 UnBuilt: array[0..nPl-1] of integer; {number of units built}
    172 
     166{$IFDEF LOADPERF}SysUtils, Windows, {$ENDIF}
     167{$IFDEF TEXTLOG}SysUtils, {$ENDIF}
     168  IPQ;
     169
     170var
     171  UnBuilt: array [0 .. nPl - 1] of integer; { number of units built }
    173172
    174173procedure MaskD(var x; Count, Mask: Cardinal); Register;
    175174asm
    176 sub eax,4
     175  sub eax,4
    177176@r: and [eax+edx*4],ecx
    178     dec edx
    179     jnz @r
     177  dec edx
     178  jnz @r
    180179end;
    181180
    182181procedure CompactLists(p: integer);
    183182var
    184 uix,uix1,cix: integer;
    185 {$IFOPT O-}V21: integer; Radius: TVicinity21Loc;{$ENDIF}
    186 begin
    187 with RW[p] do
    188   begin
    189   // compact unit list
    190   uix:=0;
    191   while uix<nUn do
    192     if Un[uix].Loc<0 then
    193       begin
    194       dec(nUn);
    195       Un[uix]:=Un[nUn]; {replace removed unit by last}
    196       if (Un[uix].TroopLoad>0) or (Un[uix].AirLoad>0) then
    197         for uix1:=0 to nUn-1 do
    198           if Un[uix1].Master=nUn then Un[uix1].Master:=uix;
    199             // index of last unit changes
     183  uix, uix1, cix: integer;
     184{$IFOPT O-}V21: integer;
     185  Radius: TVicinity21Loc; {$ENDIF}
     186begin
     187  with RW[p] do
     188  begin
     189    // compact unit list
     190    uix := 0;
     191    while uix < nUn do
     192      if Un[uix].Loc < 0 then
     193      begin
     194        dec(nUn);
     195        Un[uix] := Un[nUn]; { replace removed unit by last }
     196        if (Un[uix].TroopLoad > 0) or (Un[uix].AirLoad > 0) then
     197          for uix1 := 0 to nUn - 1 do
     198            if Un[uix1].Master = nUn then
     199              Un[uix1].Master := uix;
     200        // index of last unit changes
    200201      end
    201     else inc(uix);
    202 
    203   // compact city list
    204   cix:=0;
    205   while cix<nCity do
    206     if City[cix].Loc<0 then
    207       begin
    208       dec(nCity);
    209       City[cix]:=City[nCity]; {replace city by last}
    210       for uix1:=0 to nUn-1 do
    211         if Un[uix1].Home=nCity then Un[uix1].Home:=cix;
    212           {index of last city changes}
     202      else
     203        inc(uix);
     204
     205    // compact city list
     206    cix := 0;
     207    while cix < nCity do
     208      if City[cix].Loc < 0 then
     209      begin
     210        dec(nCity);
     211        City[cix] := City[nCity]; { replace city by last }
     212        for uix1 := 0 to nUn - 1 do
     213          if Un[uix1].Home = nCity then
     214            Un[uix1].Home := cix;
     215        { index of last city changes }
    213216      end
    214     else inc(cix);
    215 
    216   // compact enemy city list
    217   cix:=0;
    218   while cix<nEnemyCity do
    219     if EnemyCity[cix].Loc<0 then
    220       begin
    221       dec(nEnemyCity);
    222       EnemyCity[cix]:=EnemyCity[nEnemyCity]; {replace city by last}
     217      else
     218        inc(cix);
     219
     220    // compact enemy city list
     221    cix := 0;
     222    while cix < nEnemyCity do
     223      if EnemyCity[cix].Loc < 0 then
     224      begin
     225        dec(nEnemyCity);
     226        EnemyCity[cix] := EnemyCity[nEnemyCity]; { replace city by last }
    223227      end
    224     else inc(cix);
     228      else
     229        inc(cix);
    225230
    226231{$IFOPT O-}
    227   for cix:=0 to nCity-1 do with City[cix] do
    228     begin
    229     V21_to_Loc(Loc,Radius);
    230     for V21:=1 to 26 do if Tiles and (1 shl V21)<>0 then
    231       assert(UsedByCity[Radius[V21]]=Loc);
     232    for cix := 0 to nCity - 1 do
     233      with City[cix] do
     234      begin
     235        V21_to_Loc(Loc, Radius);
     236        for V21 := 1 to 26 do
     237          if Tiles and (1 shl V21) <> 0 then
     238            assert(UsedByCity[Radius[V21]] = Loc);
     239      end
     240{$ENDIF}
     241  end;
     242end; // CompactLists
     243
     244{
     245  Tech Related Functions
     246  ____________________________________________________________________
     247}
     248function TechBaseCost(nTech, diff: integer): integer;
     249var
     250  c0: single;
     251begin
     252  c0 := TechFormula_M[diff] * (nTech + 4) *
     253    exp((nTech + 4) / TechFormula_D[diff]);
     254  if c0 >= $10000000 then
     255    result := $10000000
     256  else
     257    result := trunc(c0)
     258end;
     259
     260function TechCost(p: integer): integer;
     261begin
     262  with RW[p] do
     263  begin
     264    result := TechBaseCost(nTech[p], Difficulty[p]);
     265    if ResearchTech >= 0 then
     266      if (ResearchTech = adMilitary) or (Tech[ResearchTech] = tsSeen) then
     267        result := result shr 1
     268      else if ResearchTech in FutureTech then
     269        if Government = gFuture then
     270          result := result * 2
     271        else
     272          result := result * 4;
     273  end
     274end;
     275
     276procedure SetModelFlags(var m: TModel);
     277begin
     278  m.Flags := 0;
     279  if (m.Domain = dGround) and (m.Kind <> mkDiplomat) then
     280    m.Flags := m.Flags or mdZOC;
     281  if (m.Kind = mkDiplomat) or (m.Attack + m.Cap[mcBombs] = 0) then
     282    m.Flags := m.Flags or mdCivil;
     283  if (m.Cap[mcOver] > 0) or (m.Domain = dSea) and (m.Weight >= 6) then
     284    m.Flags := m.Flags or mdDoubleSupport;
     285end;
     286
     287procedure CalculateModel(var m: TModel);
     288{ calculate attack, defense, cost... of a model by features }
     289var
     290  i: integer;
     291begin
     292  with m do
     293  begin
     294    Attack := (Cap[mcOffense] + Cap[mcOver]) * MStrength;
     295    Defense := (Cap[mcDefense] + Cap[mcOver]) * MStrength;
     296    case Domain of
     297      dGround:
     298        Speed := 150 + Cap[mcMob] * 50;
     299      dSea:
     300        begin
     301          Speed := 350 + 200 * Cap[mcNP] + 200 * Cap[mcTurbines];
     302          if Cap[mcNP] = 0 then
     303            inc(Speed, 100 * Cap[mcSE]);
     304        end;
     305      dAir:
     306        Speed := 850 + 400 * Cap[mcJet];
     307    end;
     308    Cost := 0;
     309    for i := 0 to nFeature - 1 do
     310      if 1 shl Domain and Feature[i].Domains <> 0 then
     311        inc(Cost, Cap[i] * Feature[i].Cost);
     312    Cost := Cost * MCost;
     313    Weight := 0;
     314    for i := 0 to nFeature - 1 do
     315      if 1 shl Domain and Feature[i].Domains <> 0 then
     316        if (Domain = dGround) and (i = mcDefense) then
     317          inc(Weight, Cap[i] * 2)
     318        else
     319          inc(Weight, Cap[i] * Feature[i].Weight);
     320  end;
     321  SetModelFlags(m);
     322end;
     323
     324procedure CheckSpecialModels(p, pre: integer);
     325var
     326  i, mix1: integer;
     327  HasAlready: boolean;
     328begin
     329  for i := 0 to nSpecialModel -
     330    1 do { check whether new special model available }
     331    if (SpecialModelPreq[i] = pre) and (RW[p].nModel < nmmax) then
     332    begin
     333      HasAlready := false;
     334      for mix1 := 0 to RW[p].nModel - 1 do
     335        if (RW[p].Model[mix1].Kind = SpecialModel[i].Kind) and
     336          (RW[p].Model[mix1].Attack = SpecialModel[i].Attack) and
     337          (RW[p].Model[mix1].Speed = SpecialModel[i].Speed) then
     338          HasAlready := true;
     339      if not HasAlready then
     340      begin
     341        RW[p].Model[RW[p].nModel] := SpecialModel[i];
     342        SetModelFlags(RW[p].Model[RW[p].nModel]);
     343        with RW[p].Model[RW[p].nModel] do
     344        begin
     345          Status := 0;
     346          SavedStatus := 0;
     347          IntroTurn := GTurn;
     348          Built := 0;
     349          Lost := 0;
     350          ID := p shl 12 + RW[p].nModel;
     351          if (Kind = mkSpecial_Boat) and (ServerVersion[p] < $000EF0) then
     352            Speed := 350; // old longboat
     353        end;
     354        inc(RW[p].nModel);
     355      end
     356    end;
     357end;
     358
     359procedure EnableDevModel(p: integer);
     360begin
     361  with RW[p] do
     362    if nModel < nmmax then
     363    begin
     364      Model[nModel] := DevModel;
     365      with Model[nModel] do
     366      begin
     367        Status := 0;
     368        SavedStatus := 0;
     369        IntroTurn := GTurn;
     370        Built := 0;
     371        Lost := 0;
     372        ID := p shl 12 + nModel
     373      end;
     374      inc(nModel);
     375      inc(Researched[p])
    232376    end
    233 {$ENDIF}
    234   end;
    235 end; // CompactLists
    236 
    237 {
    238                          Tech Related Functions
    239  ____________________________________________________________________
    240 }
    241 function TechBaseCost(nTech,diff: integer): integer;
    242 var
    243 c0: single;
    244 begin
    245 c0:=TechFormula_M[diff]*(nTech+4)*exp((nTech+4)/TechFormula_D[diff]);
    246 if c0>=$10000000 then result:=$10000000
    247 else result:=trunc(c0)
    248 end;
    249 
    250 function TechCost(p: integer): integer;
    251 begin
    252 with RW[p] do
    253   begin
    254   result:=TechBaseCost(nTech[p],Difficulty[p]);
    255   if ResearchTech>=0 then
    256     if (ResearchTech=adMilitary) or (Tech[ResearchTech]=tsSeen) then
    257       result:=result shr 1
    258     else if ResearchTech in FutureTech then
    259       if Government=gFuture then
    260         result:=result*2
    261       else result:=result*4;
     377end;
     378
     379procedure SeeTech(p, ad: integer);
     380begin
     381{$IFDEF TEXTLOG}CmdInfo := CmdInfo + Format(' P%d:A%d', [p, ad]); {$ENDIF}
     382  RW[p].Tech[ad] := tsSeen;
     383  // inc(nTech[p]);
     384  inc(Researched[p])
     385end;
     386
     387procedure FreeSlaves;
     388var
     389  p1, uix: integer;
     390begin
     391  for p1 := 0 to nPl - 1 do
     392    if (GAlive and (1 shl p1) <> 0) then
     393      for uix := 0 to RW[p1].nUn - 1 do
     394        if RW[p1].Model[RW[p1].Un[uix].mix].Kind = mkSlaves then
     395          RW[p1].Un[uix].Job := jNone
     396end;
     397
     398procedure DiscoverTech(p, ad: integer);
     399
     400  procedure TellAboutKeyTech(p, Source: integer);
     401  var
     402    i, p1: integer;
     403  begin
     404    for i := 1 to 3 do
     405      if ad = AgePreq[i] then
     406        for p1 := 0 to nPl - 1 do
     407          if (p1 <> p) and ((GAlive or GWatching) and (1 shl p1) <> 0) then
     408            RW[p1].EnemyReport[p].Tech[ad] := Source;
     409  end;
     410
     411var
     412  i: integer;
     413begin
     414  if ad in FutureTech then
     415  begin
     416    if RW[p].Tech[ad] < tsApplicable then
     417      RW[p].Tech[ad] := 1
     418    else
     419      inc(RW[p].Tech[ad]);
     420    if ad <> futResearchTechnology then
     421      inc(nTech[p], 2);
     422    inc(Researched[p], 8);
     423    exit;
     424  end;
     425
     426  if RW[p].Tech[ad] = tsSeen then
     427  begin
     428    inc(nTech[p]);
     429    inc(Researched[p]);
    262430  end
    263 end;
    264 
    265 procedure SetModelFlags(var m: TModel);
    266 begin
    267 m.Flags:=0;
    268 if (m.Domain=dGround) and (m.Kind<>mkDiplomat) then
    269   m.Flags:=m.Flags or mdZOC;
    270 if (m.Kind=mkDiplomat) or (m.Attack+m.Cap[mcBombs]=0) then
    271   m.Flags:=m.Flags or mdCivil;
    272 if (m.Cap[mcOver]>0) or (m.Domain=dSea) and (m.Weight>=6) then
    273   m.Flags:=m.Flags or mdDoubleSupport;
    274 end;
    275 
    276 procedure CalculateModel(var m: TModel);
    277 {calculate attack, defense, cost... of a model by features}
    278 var
    279 i: integer;
    280 begin
    281 with m do
    282   begin
    283   Attack:=(Cap[mcOffense]+Cap[mcOver])*MStrength;
    284   Defense:=(Cap[mcDefense]+Cap[mcOver])*MStrength;
    285   case Domain of
    286     dGround: Speed:=150+Cap[mcMob]*50;
    287     dSea:
    288       begin
    289       Speed:=350+200*Cap[mcNP]+200*Cap[mcTurbines];
    290       if Cap[mcNP]=0 then
    291         inc(Speed,100*Cap[mcSE]);
    292       end;
    293     dAir: Speed:=850+400*Cap[mcJet];
    294     end;
    295   Cost:=0;
    296   for i:=0 to nFeature-1 do
    297     if 1 shl Domain and Feature[i].Domains<>0 then
    298       inc(Cost,Cap[i]*Feature[i].Cost);
    299   Cost:=Cost*MCost;
    300   Weight:=0;
    301   for i:=0 to nFeature-1 do
    302     if 1 shl Domain and Feature[i].Domains<>0 then
    303       if (Domain=dGround) and (i=mcDefense) then inc(Weight,Cap[i]*2)
    304       else inc(Weight,Cap[i]*Feature[i].Weight);
    305   end;
    306 SetModelFlags(m);
    307 end;
    308 
    309 procedure CheckSpecialModels(p,pre: integer);
    310 var
    311 i,mix1: integer;
    312 HasAlready: boolean;
    313 begin
    314 for i:=0 to nSpecialModel-1 do {check whether new special model available}
    315   if (SpecialModelPreq[i]=pre) and (RW[p].nModel<nmmax) then
    316     begin
    317     HasAlready:=false;
    318     for mix1:=0 to RW[p].nModel-1 do
    319       if (RW[p].Model[mix1].Kind=SpecialModel[i].Kind)
    320         and (RW[p].Model[mix1].Attack=SpecialModel[i].Attack)
    321         and (RW[p].Model[mix1].Speed=SpecialModel[i].Speed) then
    322         HasAlready:=true;
    323     if not HasAlready then
    324       begin
    325       RW[p].Model[RW[p].nModel]:=SpecialModel[i];
    326       SetModelFlags(RW[p].Model[RW[p].nModel]);
    327       with RW[p].Model[RW[p].nModel] do
    328         begin
    329         Status:=0;
    330         SavedStatus:=0;
    331         IntroTurn:=GTurn;
    332         Built:=0;
    333         Lost:=0;
    334         ID:=p shl 12+RW[p].nModel;
    335         if (Kind=mkSpecial_Boat) and (ServerVersion[p]<$000EF0) then
    336           Speed:=350; // old longboat
    337         end;
    338       inc(RW[p].nModel);
    339       end
    340     end;
    341 end;
    342 
    343 procedure EnableDevModel(p: integer);
    344 begin
    345 with RW[p] do if nModel<nmmax then
    346   begin
    347   Model[nModel]:=DevModel;
    348   with Model[nModel] do
    349     begin
    350     Status:=0;
    351     SavedStatus:=0;
    352     IntroTurn:=GTurn;
    353     Built:=0;
    354     Lost:=0;
    355     ID:=p shl 12+nModel
    356     end;
    357   inc(nModel);
    358   inc(Researched[p])
    359   end
    360 end;
    361 
    362 procedure SeeTech(p,ad: integer);
    363 begin
    364 {$IFDEF TEXTLOG}CmdInfo:=CmdInfo+Format(' P%d:A%d', [p,ad]);{$ENDIF}
    365 RW[p].Tech[ad]:=tsSeen;
    366 //inc(nTech[p]);
    367 inc(Researched[p])
    368 end;
    369 
    370 procedure FreeSlaves;
    371 var
    372 p1,uix: integer;
    373 begin
    374 for p1:=0 to nPl-1 do if (GAlive and (1 shl p1)<>0) then
    375   for uix:=0 to RW[p1].nUn-1 do
    376     if RW[p1].Model[RW[p1].Un[uix].mix].Kind=mkSlaves then
    377       RW[p1].Un[uix].Job:=jNone
    378 end;
    379 
    380 procedure DiscoverTech(p,ad: integer);
    381 
    382   procedure TellAboutKeyTech(p,Source: integer);
    383   var
    384   i,p1: integer;
    385   begin
    386   for i:=1 to 3 do if ad=AgePreq[i] then
    387     for p1:=0 to nPl-1 do if (p1<>p) and ((GAlive or GWatching) and (1 shl p1)<>0) then
    388       RW[p1].EnemyReport[p].Tech[ad]:=Source;
    389   end;
    390 
    391 var
    392 i: integer;
    393 begin
    394 if ad in FutureTech then
    395   begin
    396   if RW[p].Tech[ad]<tsApplicable then RW[p].Tech[ad]:=1
    397   else inc(RW[p].Tech[ad]);
    398   if ad<>futResearchTechnology then inc(nTech[p],2);
    399   inc(Researched[p],8);
    400   exit;
    401   end;
    402 
    403 if RW[p].Tech[ad]=tsSeen then
    404   begin inc(nTech[p]); inc(Researched[p]); end
    405 else begin inc(nTech[p],2); inc(Researched[p],2); end;
    406 RW[p].Tech[ad]:=tsResearched;
    407 TellAboutKeyTech(p,tsResearched);
    408 CheckSpecialModels(p,ad);
    409 if ad=adScience then
    410   ResourceMask[p]:=ResourceMask[p] or fSpecial2;
    411 if ad=adMassProduction then
    412   ResourceMask[p]:=ResourceMask[p] or fModern;
    413 
    414 for i:=0 to 27 do {check whether wonders expired}
    415   if (GWonder[i].EffectiveOwner<>GWonder[woEiffel].EffectiveOwner)
    416     and (Imp[i].Expiration=ad) then
    417     begin
    418     GWonder[i].EffectiveOwner:=-1;
    419     if i=woPyramids then FreeSlaves;
     431  else
     432  begin
     433    inc(nTech[p], 2);
     434    inc(Researched[p], 2);
     435  end;
     436  RW[p].Tech[ad] := tsResearched;
     437  TellAboutKeyTech(p, tsResearched);
     438  CheckSpecialModels(p, ad);
     439  if ad = adScience then
     440    ResourceMask[p] := ResourceMask[p] or fSpecial2;
     441  if ad = adMassProduction then
     442    ResourceMask[p] := ResourceMask[p] or fModern;
     443
     444  for i := 0 to 27 do { check whether wonders expired }
     445    if (GWonder[i].EffectiveOwner <> GWonder[woEiffel].EffectiveOwner) and
     446      (Imp[i].Expiration = ad) then
     447    begin
     448      GWonder[i].EffectiveOwner := -1;
     449      if i = woPyramids then
     450        FreeSlaves;
    420451    end;
    421452end;
     
    424455// GWonder[Wonder].EffectiveOwner must be set before!
    425456var
    426 p: integer;
    427 begin
    428 if (Imp[Wonder].Expiration>=0)
    429   and (GWonder[woEiffel].EffectiveOwner<>GWonder[Wonder].EffectiveOwner) then
    430   for p:=0 to nPl-1 do // check if already expired
    431     if (1 shl p and GAlive<>0) and (RW[p].Tech[Imp[Wonder].Expiration]>=tsApplicable) then
    432       begin
    433       GWonder[Wonder].EffectiveOwner:=-1;
    434       if Wonder=woPyramids then FreeSlaves
     457  p: integer;
     458begin
     459  if (Imp[Wonder].Expiration >= 0) and
     460    (GWonder[woEiffel].EffectiveOwner <> GWonder[Wonder].EffectiveOwner) then
     461    for p := 0 to nPl - 1 do // check if already expired
     462      if (1 shl p and GAlive <> 0) and
     463        (RW[p].Tech[Imp[Wonder].Expiration] >= tsApplicable) then
     464      begin
     465        GWonder[Wonder].EffectiveOwner := -1;
     466        if Wonder = woPyramids then
     467          FreeSlaves
    435468      end
    436469end;
    437470
    438471{
    439                           Location Navigation
    440  ____________________________________________________________________
     472  Location Navigation
     473  ____________________________________________________________________
    441474}
    442 function dLoc(Loc,dx,dy: integer): integer;
    443 {relative location, dx in hor and dy in ver direction from Loc}
    444 var
    445 y0: integer;
    446 begin
    447 assert((Loc>=0) and (Loc<MapSize) and (dx+lx>=0));
    448 y0:=Loc div lx;
    449 result:=(Loc+(dx+y0 and 1+lx+lx) shr 1) mod lx +lx*(y0+dy);
    450 if (result<0) or (result>=MapSize) then result:=-1;
    451 end;
    452 
    453 procedure dxdy(Loc0,Loc1: integer; var dx,dy: integer);
    454 begin
    455 dx:=((Loc1 mod lx *2 +Loc1 div lx and 1)
    456   -(Loc0 mod lx *2 +Loc0 div lx and 1)+3*lx) mod (2*lx) -lx;
    457 dy:=Loc1 div lx-Loc0 div lx;
    458 end;
    459 
    460 function Distance(Loc0,Loc1: integer): integer;
    461 var
    462 dx,dy: integer;
    463 begin
    464 dxdy(Loc0,Loc1,dx,dy);
    465 dx:=abs(dx);
    466 dy:=abs(dy);
    467 result:=dx+dy+abs(dx-dy) shr 1;
     475function dLoc(Loc, dx, dy: integer): integer;
     476{ relative location, dx in hor and dy in ver direction from Loc }
     477var
     478  y0: integer;
     479begin
     480  assert((Loc >= 0) and (Loc < MapSize) and (dx + lx >= 0));
     481  y0 := Loc div lx;
     482  result := (Loc + (dx + y0 and 1 + lx + lx) shr 1) mod lx + lx * (y0 + dy);
     483  if (result < 0) or (result >= MapSize) then
     484    result := -1;
     485end;
     486
     487procedure dxdy(Loc0, Loc1: integer; var dx, dy: integer);
     488begin
     489  dx := ((Loc1 mod lx * 2 + Loc1 div lx and 1) -
     490    (Loc0 mod lx * 2 + Loc0 div lx and 1) + 3 * lx) mod (2 * lx) - lx;
     491  dy := Loc1 div lx - Loc0 div lx;
     492end;
     493
     494function Distance(Loc0, Loc1: integer): integer;
     495var
     496  dx, dy: integer;
     497begin
     498  dxdy(Loc0, Loc1, dx, dy);
     499  dx := abs(dx);
     500  dy := abs(dy);
     501  result := dx + dy + abs(dx - dy) shr 1;
    468502end;
    469503
    470504procedure V8_to_Loc(Loc0: integer; var VicinityLoc: TVicinity8Loc);
    471505var
    472 x0,y0,lx0: integer;
    473 begin
    474 lx0:=lx; // put in register!
    475 y0:=Loc0 div lx0;
    476 x0:=Loc0-y0*lx0; // Loc0 mod lx;
    477 y0:=y0 and 1;
    478 VicinityLoc[1]:=Loc0+lx0*2;
    479 VicinityLoc[3]:=Loc0-1;
    480 VicinityLoc[5]:=Loc0-lx0*2;
    481 VicinityLoc[7]:=Loc0+1;
    482 inc(Loc0,y0);
    483 VicinityLoc[0]:=Loc0+lx0;
    484 VicinityLoc[2]:=Loc0+lx0-1;
    485 VicinityLoc[4]:=Loc0-lx0-1;
    486 VicinityLoc[6]:=Loc0-lx0;
    487 
    488 // world is round!
    489 if x0<lx0-1 then
    490   begin
    491   if x0=0 then
    492     begin
    493     inc(VicinityLoc[3],lx0);
    494     if y0=0 then
    495       begin
    496       inc(VicinityLoc[2],lx0);
    497       inc(VicinityLoc[4],lx0);
     506  x0, y0, lx0: integer;
     507begin
     508  lx0 := lx; // put in register!
     509  y0 := Loc0 div lx0;
     510  x0 := Loc0 - y0 * lx0; // Loc0 mod lx;
     511  y0 := y0 and 1;
     512  VicinityLoc[1] := Loc0 + lx0 * 2;
     513  VicinityLoc[3] := Loc0 - 1;
     514  VicinityLoc[5] := Loc0 - lx0 * 2;
     515  VicinityLoc[7] := Loc0 + 1;
     516  inc(Loc0, y0);
     517  VicinityLoc[0] := Loc0 + lx0;
     518  VicinityLoc[2] := Loc0 + lx0 - 1;
     519  VicinityLoc[4] := Loc0 - lx0 - 1;
     520  VicinityLoc[6] := Loc0 - lx0;
     521
     522  // world is round!
     523  if x0 < lx0 - 1 then
     524  begin
     525    if x0 = 0 then
     526    begin
     527      inc(VicinityLoc[3], lx0);
     528      if y0 = 0 then
     529      begin
     530        inc(VicinityLoc[2], lx0);
     531        inc(VicinityLoc[4], lx0);
    498532      end
    499533    end
    500534  end
    501 else
    502   begin
    503   dec(VicinityLoc[7],lx0);
    504   if y0=1 then
    505     begin
    506     dec(VicinityLoc[0],lx0);
    507     dec(VicinityLoc[6],lx0);
     535  else
     536  begin
     537    dec(VicinityLoc[7], lx0);
     538    if y0 = 1 then
     539    begin
     540      dec(VicinityLoc[0], lx0);
     541      dec(VicinityLoc[6], lx0);
    508542    end
    509543  end;
     
    512546procedure V21_to_Loc(Loc0: integer; var VicinityLoc: TVicinity21Loc);
    513547var
    514 dx,dy,bit,y0,xComp,yComp,xComp0,xCompSwitch: integer;
    515 dst: ^integer;
    516 begin
    517 y0:=Loc0 div lx;
    518 xComp0:=Loc0-y0*lx-1; // Loc0 mod lx -1
    519 xCompSwitch:=xComp0-1+y0 and 1;
    520 if xComp0<0 then inc(xComp0,lx);
    521 if xCompSwitch<0 then inc(xCompSwitch,lx);
    522 xCompSwitch:=xCompSwitch xor xComp0;
    523 yComp:=lx*(y0-3);
    524 dst:=@VicinityLoc;
    525 bit:=1;
    526 for dy:=0 to 6 do
    527   begin
    528   xComp0:=xComp0 xor xCompSwitch;
    529   xComp:=xComp0;
    530   for dx:=0 to 3 do
    531     begin
    532     if bit and $67F7F76<>0 then dst^:=xComp+yComp
    533     else dst^:=-1;
    534     inc(xComp);
    535     if xComp>=lx then dec(xComp, lx);
    536     inc(dst);
    537     bit:=bit shl 1;
    538     end;
    539   inc(yComp,lx);
    540   end;
    541 end;
    542 
     548  dx, dy, bit, y0, xComp, yComp, xComp0, xCompSwitch: integer;
     549  dst: ^integer;
     550begin
     551  y0 := Loc0 div lx;
     552  xComp0 := Loc0 - y0 * lx - 1; // Loc0 mod lx -1
     553  xCompSwitch := xComp0 - 1 + y0 and 1;
     554  if xComp0 < 0 then
     555    inc(xComp0, lx);
     556  if xCompSwitch < 0 then
     557    inc(xCompSwitch, lx);
     558  xCompSwitch := xCompSwitch xor xComp0;
     559  yComp := lx * (y0 - 3);
     560  dst := @VicinityLoc;
     561  bit := 1;
     562  for dy := 0 to 6 do
     563  begin
     564    xComp0 := xComp0 xor xCompSwitch;
     565    xComp := xComp0;
     566    for dx := 0 to 3 do
     567    begin
     568      if bit and $67F7F76 <> 0 then
     569        dst^ := xComp + yComp
     570      else
     571        dst^ := -1;
     572      inc(xComp);
     573      if xComp >= lx then
     574        dec(xComp, lx);
     575      inc(dst);
     576      bit := bit shl 1;
     577    end;
     578    inc(yComp, lx);
     579  end;
     580end;
    543581
    544582{
    545                              Map Creation
    546  ____________________________________________________________________
     583  Map Creation
     584  ____________________________________________________________________
    547585}
    548586var
    549 primitive: integer;
    550 StartLoc, StartLoc2: array[0..nPl-1] of integer; {starting coordinates}
    551 Elevation: array[0..lxmax*lymax-1] of Byte; {map elevation}
    552 ElCount: array[Byte] of integer; {count of elevation occurance}
     587  primitive: integer;
     588  StartLoc, StartLoc2: array [0 .. nPl - 1] of integer; { starting coordinates }
     589  Elevation: array [0 .. lxmax * lymax - 1] of Byte; { map elevation }
     590  ElCount: array [Byte] of integer; { count of elevation occurance }
    553591
    554592procedure CalculatePrimitive;
    555593var
    556 i,j: integer;
    557 begin
    558 primitive:=1;
    559 i:=2;
    560 while i*i<=MapSize+1 do // test whether prime
    561   begin if (MapSize+1) mod i=0 then primitive:=0; inc(i) end;
    562 
    563 if primitive>0 then
    564   repeat
    565     inc(primitive);
    566     i:=1;
    567     j:=0;
    568     repeat inc(j); i:=i*primitive mod (MapSize+1) until (i=1) or (j=MapSize+1);
    569   until j=MapSize;
     594  i, j: integer;
     595begin
     596  primitive := 1;
     597  i := 2;
     598  while i * i <= MapSize + 1 do // test whether prime
     599  begin
     600    if (MapSize + 1) mod i = 0 then
     601      primitive := 0;
     602    inc(i)
     603  end;
     604
     605  if primitive > 0 then
     606    repeat
     607      inc(primitive);
     608      i := 1;
     609      j := 0;
     610      repeat
     611        inc(j);
     612        i := i * primitive mod (MapSize + 1)
     613      until (i = 1) or (j = MapSize + 1);
     614    until j = MapSize;
    570615end;
    571616
    572617function MapGeneratorAvailable: boolean;
    573618begin
    574 result:=(primitive>0) and (lx>=20) and (ly>=40)
     619  result := (primitive > 0) and (lx >= 20) and (ly >= 40)
    575620end;
    576621
    577622procedure CreateElevation;
    578623const
    579 d=64;
    580 Smooth=0.049;{causes low amplitude of short waves}
    581 Detail=0.095;{causes short period of short waves}
    582 Merge=5;{elevation merging range at the connection line of the
    583   round world,in relation to lx}
    584 
    585 var
    586 sa,ca,f1,f2:array[1..d] of single;
    587 imerge,x,y:integer;
    588 v,maxv:single;
    589 
    590   function Value(x,y:integer):single;{elevation formula}
     624  d = 64;
     625  Smooth = 0.049; { causes low amplitude of short waves }
     626  Detail = 0.095; { causes short period of short waves }
     627  Merge = 5; { elevation merging range at the connection line of the
     628    round world,in relation to lx }
     629
     630var
     631  sa, ca, f1, f2: array [1 .. d] of single;
     632  imerge, x, y: integer;
     633  v, maxv: single;
     634
     635  function Value(x, y: integer): single; { elevation formula }
    591636  var
    592   i:integer;
    593   begin
    594   result:=0;
    595   for i:=1 to d do result:=result+sin(f1[i]*((x*2+y and 1)*sa[i]+y*1.5*ca[i]))
    596     *f2[i];
    597    {x values effectively multiplied with 2 to get 2 horizantal periods
    598    of the prime waves}
    599   end;
    600 
    601 begin
    602 for x:=1 to d do {prepare formula parameters}
    603   begin
    604   {$IFNDEF SCR}if x=1 then v:=pi/2 {first wave goes horizontal}
    605   else{$ENDIF} v:=Random*2*pi;
    606   sa[x]:=sin(v)/lx;
    607   ca[x]:=cos(v)/ly;
    608   f1[x]:=2*pi*Exp(Detail*(x-1));
    609   f2[x]:=Exp(-x*Smooth)
    610   end;
    611 
    612 imerge:=2*lx div Merge;
    613 FillChar(ElCount,SizeOf(ElCount),0);
    614 maxv:=0;
    615 for x:=0 to lx-1 do for y:=0 to ly-1 do
    616   begin
    617   v:=Value(x,y);
    618   if x*2<imerge then v:=(x*2*v+(imerge-x*2)*Value(x+lx,y))/imerge;
    619   v:=v-sqr(sqr(2*y/ly-1));{soft cut at poles}
    620   if v>maxv then maxv:=v;
    621 
    622   if v<-4 then Elevation[x+lx*y]:=0
    623   else if v>8.75 then Elevation[x+lx*y]:=255
    624   else Elevation[x+lx*y]:=Round((v+4)*20);
    625   inc(ElCount[Elevation[x+lx*y]])
    626   end;
     637    i: integer;
     638  begin
     639    result := 0;
     640    for i := 1 to d do
     641      result := result + sin(f1[i] * ((x * 2 + y and 1) * sa[i] + y * 1.5 *
     642        ca[i])) * f2[i];
     643    { x values effectively multiplied with 2 to get 2 horizantal periods
     644      of the prime waves }
     645  end;
     646
     647begin
     648  for x := 1 to d do { prepare formula parameters }
     649  begin
     650{$IFNDEF SCR} if x = 1 then
     651      v := pi / 2 { first wave goes horizontal }
     652    else {$ENDIF} v := Random * 2 * pi;
     653    sa[x] := sin(v) / lx;
     654    ca[x] := cos(v) / ly;
     655    f1[x] := 2 * pi * exp(Detail * (x - 1));
     656    f2[x] := exp(-x * Smooth)
     657  end;
     658
     659  imerge := 2 * lx div Merge;
     660  FillChar(ElCount, SizeOf(ElCount), 0);
     661  maxv := 0;
     662  for x := 0 to lx - 1 do
     663    for y := 0 to ly - 1 do
     664    begin
     665      v := Value(x, y);
     666      if x * 2 < imerge then
     667        v := (x * 2 * v + (imerge - x * 2) * Value(x + lx, y)) / imerge;
     668      v := v - sqr(sqr(2 * y / ly - 1)); { soft cut at poles }
     669      if v > maxv then
     670        maxv := v;
     671
     672      if v < -4 then
     673        Elevation[x + lx * y] := 0
     674      else if v > 8.75 then
     675        Elevation[x + lx * y] := 255
     676      else
     677        Elevation[x + lx * y] := Round((v + 4) * 20);
     678      inc(ElCount[Elevation[x + lx * y]])
     679    end;
    627680end;
    628681
    629682procedure FindContinents;
    630683
    631   procedure ReplaceCont(a,b,Stop:integer);
    632   {replace continent name a by b}
     684  procedure ReplaceCont(a, b, Stop: integer);
     685  { replace continent name a by b }
    633686  // make sure always continent[loc]<=loc
    634687  var
    635   i: integer;
    636   begin
    637   if a<b then begin i:=a; a:=b; b:=i end;
    638   if a>b then
    639     for i:=a to Stop do if Continent[i]=a then Continent[i]:=b
    640   end;
    641 
    642 var
    643 x,y,Loc,Wrong:integer;
    644 begin
    645 for y:=1 to ly-2 do for x:=0 to lx-1 do
    646   begin
    647   Loc:=x+lx*y;
    648   Continent[Loc]:=-1;
    649   if RealMap[Loc] and fTerrain>=fGrass then
    650     begin
    651     if (y-2>=1) and (RealMap[Loc-2*lx] and fTerrain>=fGrass) then
    652       Continent[Loc]:=Continent[Loc-2*lx];
    653     if (x-1+y and 1>=0) and (y-1>=1)
    654       and (RealMap[Loc-1+y and 1-lx] and fTerrain>=fGrass) then
    655       Continent[Loc]:=Continent[Loc-1+y and 1-lx];
    656     if (x+y and 1<lx) and (y-1>=1)
    657       and (RealMap[Loc+y and 1-lx] and fTerrain>=fGrass) then
    658       Continent[Loc]:=Continent[Loc+y and 1-lx];
    659     if (x-1>=0) and (RealMap[Loc-1] and fTerrain>=fGrass) then
    660       if Continent[Loc]=-1 then Continent[Loc]:=Continent[Loc-1]
    661       else ReplaceCont(Continent[Loc-1],Continent[Loc],Loc);
    662     if Continent[Loc]=-1 then Continent[Loc]:=Loc
    663     end
    664   end;
    665 
    666 {connect continents due to round earth}
    667 for y:=1 to ly-2 do if RealMap[lx*y] and fTerrain>=fGrass then
    668   begin
    669   Wrong:=-1;
    670   if RealMap[lx-1+lx*y] and fTerrain>=fGrass then Wrong:=Continent[lx-1+lx*y];
    671   if (y and 1=0) and (y-1>=1) and (RealMap[lx-1+lx*(y-1)] and fTerrain>=fGrass) then
    672     Wrong:=Continent[lx-1+lx*(y-1)];
    673   if (y and 1=0) and (y+1<ly-1)
    674     and (RealMap[lx-1+lx*(y+1)] and fTerrain>=fGrass) then
    675     Wrong:=Continent[lx-1+lx*(y+1)];
    676   if Wrong>=0 then ReplaceCont(Wrong,Continent[lx*y],MapSize-1)
    677   end;
     688    i: integer;
     689  begin
     690    if a < b then
     691    begin
     692      i := a;
     693      a := b;
     694      b := i
     695    end;
     696    if a > b then
     697      for i := a to Stop do
     698        if Continent[i] = a then
     699          Continent[i] := b
     700  end;
     701
     702var
     703  x, y, Loc, Wrong: integer;
     704begin
     705  for y := 1 to ly - 2 do
     706    for x := 0 to lx - 1 do
     707    begin
     708      Loc := x + lx * y;
     709      Continent[Loc] := -1;
     710      if RealMap[Loc] and fTerrain >= fGrass then
     711      begin
     712        if (y - 2 >= 1) and (RealMap[Loc - 2 * lx] and fTerrain >= fGrass) then
     713          Continent[Loc] := Continent[Loc - 2 * lx];
     714        if (x - 1 + y and 1 >= 0) and (y - 1 >= 1) and
     715          (RealMap[Loc - 1 + y and 1 - lx] and fTerrain >= fGrass) then
     716          Continent[Loc] := Continent[Loc - 1 + y and 1 - lx];
     717        if (x + y and 1 < lx) and (y - 1 >= 1) and
     718          (RealMap[Loc + y and 1 - lx] and fTerrain >= fGrass) then
     719          Continent[Loc] := Continent[Loc + y and 1 - lx];
     720        if (x - 1 >= 0) and (RealMap[Loc - 1] and fTerrain >= fGrass) then
     721          if Continent[Loc] = -1 then
     722            Continent[Loc] := Continent[Loc - 1]
     723          else
     724            ReplaceCont(Continent[Loc - 1], Continent[Loc], Loc);
     725        if Continent[Loc] = -1 then
     726          Continent[Loc] := Loc
     727      end
     728    end;
     729
     730  { connect continents due to round earth }
     731  for y := 1 to ly - 2 do
     732    if RealMap[lx * y] and fTerrain >= fGrass then
     733    begin
     734      Wrong := -1;
     735      if RealMap[lx - 1 + lx * y] and fTerrain >= fGrass then
     736        Wrong := Continent[lx - 1 + lx * y];
     737      if (y and 1 = 0) and (y - 1 >= 1) and
     738        (RealMap[lx - 1 + lx * (y - 1)] and fTerrain >= fGrass) then
     739        Wrong := Continent[lx - 1 + lx * (y - 1)];
     740      if (y and 1 = 0) and (y + 1 < ly - 1) and
     741        (RealMap[lx - 1 + lx * (y + 1)] and fTerrain >= fGrass) then
     742        Wrong := Continent[lx - 1 + lx * (y + 1)];
     743      if Wrong >= 0 then
     744        ReplaceCont(Wrong, Continent[lx * y], MapSize - 1)
     745    end;
    678746end;
    679747
     
    682750// must be done after FindContinents
    683751var
    684 i,j,Cnt,x,y,dx,dy,Loc0,Loc1,xworst,yworst,totalrare,RareMaxWater,RareType,
    685   iBest,jbest,MinDist,xBlock,yBlock,V8: integer;
    686 AreaCount, RareByArea, RareAdjacent: array[0..7,0..4] of integer;
    687 RareLoc: array[0..11] of integer;
    688 Dist: array[0..11,0..11] of integer;
    689 Adjacent: TVicinity8Loc;
    690 begin
    691 RareMaxWater:=0;
    692 repeat
    693   FillChar(AreaCount, SizeOf(AreaCount), 0);
    694   for y:=1 to ly-2 do
    695     begin
    696     yBlock:=y*5 div ly;
    697     if yBlock=(y+1)*5 div ly then for x:=0 to lx-1 do
    698       begin
    699       xBlock:=x*8 div lx;
    700       if xBlock=(x+1)*8 div lx then
    701         begin
    702         Loc0:=x+lx*y;
    703         if RealMap[Loc0] and fTerrain>=fGrass then
     752  i, j, Cnt, x, y, dx, dy, Loc0, Loc1, xworst, yworst, totalrare, RareMaxWater,
     753    RareType, iBest, jbest, MinDist, xBlock, yBlock, V8: integer;
     754  AreaCount, RareByArea, RareAdjacent: array [0 .. 7, 0 .. 4] of integer;
     755  RareLoc: array [0 .. 11] of integer;
     756  Dist: array [0 .. 11, 0 .. 11] of integer;
     757  Adjacent: TVicinity8Loc;
     758begin
     759  RareMaxWater := 0;
     760  repeat
     761    FillChar(AreaCount, SizeOf(AreaCount), 0);
     762    for y := 1 to ly - 2 do
     763    begin
     764      yBlock := y * 5 div ly;
     765      if yBlock = (y + 1) * 5 div ly then
     766        for x := 0 to lx - 1 do
     767        begin
     768          xBlock := x * 8 div lx;
     769          if xBlock = (x + 1) * 8 div lx then
    704770          begin
    705           Cnt:=0;
    706           V8_to_Loc(Loc0,Adjacent);
    707           for V8:=0 to 7 do
     771            Loc0 := x + lx * y;
     772            if RealMap[Loc0] and fTerrain >= fGrass then
    708773            begin
    709             Loc1:=Adjacent[V8];
    710             if (Loc1>=0) and (Loc1<MapSize)
    711               and (RealMap[Loc1] and fTerrain<fGrass) then
    712               inc(Cnt); // count adjacent water
     774              Cnt := 0;
     775              V8_to_Loc(Loc0, Adjacent);
     776              for V8 := 0 to 7 do
     777              begin
     778                Loc1 := Adjacent[V8];
     779                if (Loc1 >= 0) and (Loc1 < MapSize) and
     780                  (RealMap[Loc1] and fTerrain < fGrass) then
     781                  inc(Cnt); // count adjacent water
     782              end;
     783              if Cnt <= RareMaxWater then // inner land
     784              begin
     785                inc(AreaCount[xBlock, yBlock]);
     786                if Random(AreaCount[xBlock, yBlock]) = 0 then
     787                  RareByArea[xBlock, yBlock] := Loc0
     788              end
    713789            end;
    714           if Cnt<=RareMaxWater then // inner land
     790          end;
     791        end
     792    end;
     793    totalrare := 0;
     794    for x := 0 to 7 do
     795      for y := 0 to 4 do
     796        if AreaCount[x, y] > 0 then
     797          inc(totalrare);
     798    inc(RareMaxWater);
     799  until totalrare >= 12;
     800
     801  while totalrare > 12 do
     802  begin // remove rarebyarea resources too close to each other
     803    FillChar(RareAdjacent, SizeOf(RareAdjacent), 0);
     804    for x := 0 to 7 do
     805      for y := 0 to 4 do
     806        if AreaCount[x, y] > 0 then
     807        begin
     808          if (AreaCount[(x + 1) mod 8, y] > 0) and
     809            (Continent[RareByArea[x, y]] = Continent
     810            [RareByArea[(x + 1) mod 8, y]]) then
     811          begin
     812            inc(RareAdjacent[x, y]);
     813            inc(RareAdjacent[(x + 1) mod 8, y]);
     814          end;
     815          if y < 4 then
     816          begin
     817            if (AreaCount[x, y + 1] > 0) and
     818              (Continent[RareByArea[x, y]] = Continent[RareByArea[x, y + 1]])
     819            then
    715820            begin
    716             inc(AreaCount[xBlock,yBlock]);
    717             if Random(AreaCount[xBlock,yBlock])=0 then
    718               RareByArea[xBlock,yBlock]:=Loc0
     821              inc(RareAdjacent[x, y]);
     822              inc(RareAdjacent[x, y + 1]);
     823            end;
     824            if (AreaCount[(x + 1) mod 8, y + 1] > 0) and
     825              (Continent[RareByArea[x, y]] = Continent[RareByArea[(x + 1) mod 8,
     826              y + 1]]) then
     827            begin
     828              inc(RareAdjacent[x, y]);
     829              inc(RareAdjacent[(x + 1) mod 8, y + 1]);
     830            end;
     831            if (AreaCount[(x + 7) mod 8, y + 1] > 0) and
     832              (Continent[RareByArea[x, y]] = Continent[RareByArea[(x + 7) mod 8,
     833              y + 1]]) then
     834            begin
     835              inc(RareAdjacent[x, y]);
     836              inc(RareAdjacent[(x + 7) mod 8, y + 1]);
     837            end;
     838          end
     839        end;
     840    xworst := 0;
     841    yworst := 0;
     842    Cnt := 0;
     843    for x := 0 to 7 do
     844      for y := 0 to 4 do
     845        if AreaCount[x, y] > 0 then
     846        begin
     847          if (Cnt = 0) or (RareAdjacent[x, y] > RareAdjacent[xworst, yworst])
     848          then
     849          begin
     850            xworst := x;
     851            yworst := y;
     852            Cnt := 1
     853          end
     854          else if (RareAdjacent[x, y] = RareAdjacent[xworst, yworst]) then
     855          begin
     856            inc(Cnt);
     857            if Random(Cnt) = 0 then
     858            begin
     859              xworst := x;
     860              yworst := y;
    719861            end
    720862          end;
    721863        end;
    722       end
    723     end;
    724   totalrare:=0;
    725   for x:=0 to 7 do for y:=0 to 4 do if AreaCount[x,y]>0 then
    726     inc(totalrare);
    727   inc(RareMaxWater);
    728 until totalrare>=12;
    729 
    730 while totalrare>12 do
    731   begin // remove rarebyarea resources too close to each other
    732   FillChar(RareAdjacent,SizeOf(RareAdjacent),0);
    733   for x:=0 to 7 do for y:=0 to 4 do if AreaCount[x,y]>0 then
    734     begin
    735     if (AreaCount[(x+1) mod 8,y]>0)
    736       and (Continent[RareByArea[x,y]]=Continent[RareByArea[(x+1) mod 8,y]]) then
    737       begin
    738       inc(RareAdjacent[x,y]);
    739       inc(RareAdjacent[(x+1) mod 8,y]);
    740       end;
    741     if y<4 then
    742       begin
    743       if (AreaCount[x,y+1]>0)
    744         and (Continent[RareByArea[x,y]]=Continent[RareByArea[x,y+1]]) then
    745         begin
    746         inc(RareAdjacent[x,y]);
    747         inc(RareAdjacent[x,y+1]);
    748         end;
    749       if (AreaCount[(x+1) mod 8,y+1]>0)
    750         and (Continent[RareByArea[x,y]]=Continent[RareByArea[(x+1) mod 8,y+1]]) then
    751         begin
    752         inc(RareAdjacent[x,y]);
    753         inc(RareAdjacent[(x+1) mod 8,y+1]);
    754         end;
    755       if (AreaCount[(x+7) mod 8,y+1]>0)
    756         and (Continent[RareByArea[x,y]]=Continent[RareByArea[(x+7) mod 8,y+1]]) then
    757         begin
    758         inc(RareAdjacent[x,y]);
    759         inc(RareAdjacent[(x+7) mod 8,y+1]);
    760         end;
    761       end
    762     end;
    763   xworst:=0; yworst:=0;
    764   Cnt:=0;
    765   for x:=0 to 7 do for y:=0 to 4 do if AreaCount[x,y]>0 then
    766     begin
    767     if (Cnt=0) or (RareAdjacent[x,y]>RareAdjacent[xworst,yworst]) then
    768       begin xworst:=x; yworst:=y; Cnt:=1 end
    769     else if (RareAdjacent[x,y]=RareAdjacent[xworst,yworst]) then
    770       begin
    771       inc(Cnt);
    772       if Random(Cnt)=0 then begin xworst:=x; yworst:=y; end
    773       end;
    774     end;
    775   AreaCount[xworst,yworst]:=0;
    776   dec(totalrare)
    777   end;
    778 
    779 Cnt:=0;
    780 for x:=0 to 7 do for y:=0 to 4 do if AreaCount[x,y]>0 then
    781   begin RareLoc[Cnt]:=RareByArea[x,y]; inc(Cnt) end;
    782 for i:=0 to 11 do
    783   begin
    784   RealMap[RareLoc[i]]:=RealMap[RareLoc[i]]
    785     and not (fTerrain or fSpecial) or (fDesert or fDeadLands);
    786   for dy:=-1 to 1 do for dx:=-1 to 1 do if (dx+dy) and 1=0 then
    787     begin
    788     Loc1:=dLoc(RareLoc[i],dx,dy);
    789     if (Loc1>=0) and (RealMap[Loc1] and fTerrain=fMountains) then
    790       RealMap[Loc1]:=RealMap[Loc1] and not fTerrain or fHills;
    791     end
    792   end;
    793 for i:=0 to 11 do for j:=0 to 11 do
    794   Dist[i,j]:=Distance(RareLoc[i],RareLoc[j]);
    795 
    796 MinDist:=Distance(0,MapSize-lx shr 1) shr 1;
    797 for RareType:=1 to 3 do
    798   begin
    799   Cnt:=0;
    800   for i:=0 to 11 do if RareLoc[i]>=0 then
    801     for j:=0 to 11 do if RareLoc[j]>=0 then
    802       if (Cnt>0) and (Dist[iBest,jbest]>=MinDist) then
    803         begin
    804         if Dist[i,j]>=MinDist then
    805           begin
    806           inc(Cnt);
    807           if Random(Cnt)=0 then
    808             begin iBest:=i; jbest:=j end
    809           end
     864    AreaCount[xworst, yworst] := 0;
     865    dec(totalrare)
     866  end;
     867
     868  Cnt := 0;
     869  for x := 0 to 7 do
     870    for y := 0 to 4 do
     871      if AreaCount[x, y] > 0 then
     872      begin
     873        RareLoc[Cnt] := RareByArea[x, y];
     874        inc(Cnt)
     875      end;
     876  for i := 0 to 11 do
     877  begin
     878    RealMap[RareLoc[i]] := RealMap[RareLoc[i]] and not(fTerrain or fSpecial) or
     879      (fDesert or fDeadLands);
     880    for dy := -1 to 1 do
     881      for dx := -1 to 1 do
     882        if (dx + dy) and 1 = 0 then
     883        begin
     884          Loc1 := dLoc(RareLoc[i], dx, dy);
     885          if (Loc1 >= 0) and (RealMap[Loc1] and fTerrain = fMountains) then
     886            RealMap[Loc1] := RealMap[Loc1] and not fTerrain or fHills;
    810887        end
    811       else if (Cnt=0) or (Dist[i,j]>Dist[iBest,jbest]) then
    812         begin iBest:=i; jbest:=j; Cnt:=1; end;
    813   RealMap[RareLoc[iBest]]:=RealMap[RareLoc[iBest]] or Cardinal(RareType) shl 25;
    814   RealMap[RareLoc[jbest]]:=RealMap[RareLoc[jbest]] or Cardinal(RareType) shl 25;
    815   RareLoc[iBest]:=-1;
    816   RareLoc[jbest]:=-1;
     888  end;
     889  for i := 0 to 11 do
     890    for j := 0 to 11 do
     891      Dist[i, j] := Distance(RareLoc[i], RareLoc[j]);
     892
     893  MinDist := Distance(0, MapSize - lx shr 1) shr 1;
     894  for RareType := 1 to 3 do
     895  begin
     896    Cnt := 0;
     897    for i := 0 to 11 do
     898      if RareLoc[i] >= 0 then
     899        for j := 0 to 11 do
     900          if RareLoc[j] >= 0 then
     901            if (Cnt > 0) and (Dist[iBest, jbest] >= MinDist) then
     902            begin
     903              if Dist[i, j] >= MinDist then
     904              begin
     905                inc(Cnt);
     906                if Random(Cnt) = 0 then
     907                begin
     908                  iBest := i;
     909                  jbest := j
     910                end
     911              end
     912            end
     913            else if (Cnt = 0) or (Dist[i, j] > Dist[iBest, jbest]) then
     914            begin
     915              iBest := i;
     916              jbest := j;
     917              Cnt := 1;
     918            end;
     919    RealMap[RareLoc[iBest]] := RealMap[RareLoc[iBest]] or
     920      Cardinal(RareType) shl 25;
     921    RealMap[RareLoc[jbest]] := RealMap[RareLoc[jbest]] or
     922      Cardinal(RareType) shl 25;
     923    RareLoc[iBest] := -1;
     924    RareLoc[jbest] := -1;
    817925  end;
    818926end; // RarePositions
     
    820928function CheckShore(Loc: integer): boolean;
    821929var
    822 Loc1,OldTile,V21: integer;
    823 Radius: TVicinity21Loc;
    824 begin
    825 result:=false;
    826 OldTile:=RealMap[Loc];
    827 if OldTile and fTerrain<fGrass then
    828   begin
    829   RealMap[Loc]:=RealMap[Loc] and not fTerrain or fOcean;
    830   V21_to_Loc(Loc,Radius);
    831   for V21:=1 to 26 do
    832     begin
    833     Loc1:=Radius[V21];
    834     if (Loc1>=0) and (Loc1<MapSize)
    835       and (RealMap[Loc1] and fTerrain>=fGrass)
    836       and (RealMap[Loc1] and fTerrain<>fArctic) then
    837       RealMap[Loc]:=RealMap[Loc] and not fTerrain or fShore;
    838     end;
    839   if (RealMap[Loc] xor Cardinal(OldTile)) and fTerrain<>0 then
    840     result:=true
     930  Loc1, OldTile, V21: integer;
     931  Radius: TVicinity21Loc;
     932begin
     933  result := false;
     934  OldTile := RealMap[Loc];
     935  if OldTile and fTerrain < fGrass then
     936  begin
     937    RealMap[Loc] := RealMap[Loc] and not fTerrain or fOcean;
     938    V21_to_Loc(Loc, Radius);
     939    for V21 := 1 to 26 do
     940    begin
     941      Loc1 := Radius[V21];
     942      if (Loc1 >= 0) and (Loc1 < MapSize) and
     943        (RealMap[Loc1] and fTerrain >= fGrass) and
     944        (RealMap[Loc1] and fTerrain <> fArctic) then
     945        RealMap[Loc] := RealMap[Loc] and not fTerrain or fShore;
     946    end;
     947    if (RealMap[Loc] xor Cardinal(OldTile)) and fTerrain <> 0 then
     948      result := true
    841949  end;
    842950end;
     
    844952function ActualSpecialTile(Loc: integer): Cardinal;
    845953begin
    846 result:=SpecialTile(Loc, RealMap[Loc] and fTerrain, lx);
     954  result := SpecialTile(Loc, RealMap[Loc] and fTerrain, lx);
    847955end;
    848956
    849957procedure CreateMap(preview: boolean);
    850958const
    851 ShHiHills=6; {of land}
    852 ShMountains=6; {of land}
    853 ShRandHills=12; {of land}
    854 ShTestRiver=40;
    855 ShSwamp=25; {of grassland}
    856 MinRivLen=3;
    857 unification=70;
    858 hotunification=50; // min. 25
    859 
    860 Zone:array[0..3,2..9] of single= {terrain distribution}
    861  ((0.25,0,   0,   0.4 ,0,0,0,0.35),
    862   (0.55,0,   0.1 ,0,   0,0,0,0.35),
    863   (0.4, 0,   0.35,0,   0,0,0,0.25),
    864   (0,   0.7, 0,   0,   0,0,0,0.3));
    865   {Grs  Dst  Pra  Tun  - - - For}
    866 
    867   function RndLow(y:integer):Cardinal;
    868   {random lowland appropriate to climate}
     959  ShHiHills = 6; { of land }
     960  ShMountains = 6; { of land }
     961  ShRandHills = 12; { of land }
     962  ShTestRiver = 40;
     963  ShSwamp = 25; { of grassland }
     964  MinRivLen = 3;
     965  unification = 70;
     966  hotunification = 50; // min. 25
     967
     968  Zone: array [0 .. 3, 2 .. 9] of single = { terrain distribution }
     969    ((0.25, 0, 0, 0.4, 0, 0, 0, 0.35), (0.55, 0, 0.1, 0, 0, 0, 0, 0.35),
     970    (0.4, 0, 0.35, 0, 0, 0, 0, 0.25), (0, 0.7, 0, 0, 0, 0, 0, 0.3));
     971  { Grs  Dst  Pra  Tun  - - - For }
     972
     973  function RndLow(y: integer): Cardinal;
     974  { random lowland appropriate to climate }
    869975  var
    870   z0,i:integer;
    871   p,p0,ZPlus:single;
    872   begin
    873   if ly-1-y>y then begin z0:=6*y div ly;ZPlus:=6*y/ly -z0 end
    874   else begin z0:=6*(ly-1-y) div ly;ZPlus:=6*(ly-1-y)/ly -z0 end;
    875   p0:=1;
    876   for i:=2 to 9 do
    877     begin
    878     p:=Zone[z0,i]*(1-ZPlus)+Zone[z0+1,i]*ZPlus;
    879     {weight between zones z0 and z0+1}
    880     if Random*p0<p then begin RndLow:=i;Break end;
    881     p0:=p0-p
     976    z0, i: integer;
     977    p, p0, ZPlus: single;
     978  begin
     979    if ly - 1 - y > y then
     980    begin
     981      z0 := 6 * y div ly;
     982      ZPlus := 6 * y / ly - z0
     983    end
     984    else
     985    begin
     986      z0 := 6 * (ly - 1 - y) div ly;
     987      ZPlus := 6 * (ly - 1 - y) / ly - z0
     988    end;
     989    p0 := 1;
     990    for i := 2 to 9 do
     991    begin
     992      p := Zone[z0, i] * (1 - ZPlus) + Zone[z0 + 1, i] * ZPlus;
     993      { weight between zones z0 and z0+1 }
     994      if Random * p0 < p then
     995      begin
     996        RndLow := i;
     997        Break
     998      end;
     999      p0 := p0 - p
    8821000    end;
    8831001  end;
    8841002
    8851003  function RunRiver(Loc0: integer): integer;
    886   {runs river from start point Loc0; return value: length}
     1004  { runs river from start point Loc0; return value: length }
    8871005  var
    888   Dir,T,Loc,Loc1,Cost: integer;
    889   Q: TIPQ;
    890   From: array[0..lxmax*lymax-1] of integer;
    891   Time: array[0..lxmax*lymax-1] of integer;
    892   OneTileLake: boolean;
    893   begin
    894   FillChar(Time,SizeOf(Time),255); {-1}
    895   Q:=TIPQ.Create(MapSize);
    896   Q.Put(Loc0,0);
    897   while Q.Get(Loc,T) and (RealMap[Loc] and fRiver=0) do
    898     begin
    899     if (RealMap[Loc] and fTerrain<fGrass) then
    900       begin
    901       OneTileLake:=true;
    902       for Dir:=0 to 3 do
    903         begin
    904         Loc1:=dLoc(Loc,Dir and 1 *2 -1,Dir shr 1 *2 -1);
    905         if (Loc1>=0) and (RealMap[Loc1] and fTerrain<fGrass) then
    906           OneTileLake:=false;
     1006    Dir, T, Loc, Loc1, Cost: integer;
     1007    Q: TIPQ;
     1008    From: array [0 .. lxmax * lymax - 1] of integer;
     1009    Time: array [0 .. lxmax * lymax - 1] of integer;
     1010    OneTileLake: boolean;
     1011  begin
     1012    FillChar(Time, SizeOf(Time), 255); { -1 }
     1013    Q := TIPQ.Create(MapSize);
     1014    Q.Put(Loc0, 0);
     1015    while Q.Get(Loc, T) and (RealMap[Loc] and fRiver = 0) do
     1016    begin
     1017      if (RealMap[Loc] and fTerrain < fGrass) then
     1018      begin
     1019        OneTileLake := true;
     1020        for Dir := 0 to 3 do
     1021        begin
     1022          Loc1 := dLoc(Loc, Dir and 1 * 2 - 1, Dir shr 1 * 2 - 1);
     1023          if (Loc1 >= 0) and (RealMap[Loc1] and fTerrain < fGrass) then
     1024            OneTileLake := false;
    9071025        end;
    908       if not OneTileLake then Break;
    909       end;
    910     Time[Loc]:=T;
    911     for Dir:=0 to 3 do
    912       begin
    913       Loc1:=dLoc(Loc,Dir and 1 *2 -1,Dir shr 1 *2 -1);
    914       if (Loc1>=lx) and (Loc1<lx*(ly-1)) and (Time[Loc1]<0) then
    915         begin
    916         if RealMap[Loc1] and fRiver=0 then
     1026        if not OneTileLake then
     1027          Break;
     1028      end;
     1029      Time[Loc] := T;
     1030      for Dir := 0 to 3 do
     1031      begin
     1032        Loc1 := dLoc(Loc, Dir and 1 * 2 - 1, Dir shr 1 * 2 - 1);
     1033        if (Loc1 >= lx) and (Loc1 < lx * (ly - 1)) and (Time[Loc1] < 0) then
     1034        begin
     1035          if RealMap[Loc1] and fRiver = 0 then
    9171036          begin
    918           Cost:=Elevation[Loc1]-Elevation[Loc];
    919           if Cost<0 then Cost:=0;
     1037            Cost := Elevation[Loc1] - Elevation[Loc];
     1038            if Cost < 0 then
     1039              Cost := 0;
    9201040          end
    921         else Cost:=0;
    922         if Q.Put(Loc1,T+Cost shl 8+1) then From[Loc1]:=Loc
     1041          else
     1042            Cost := 0;
     1043          if Q.Put(Loc1, T + Cost shl 8 + 1) then
     1044            From[Loc1] := Loc
    9231045        end
    9241046      end
    9251047    end;
    926   Loc1:=Loc;
    927   result:=0;
    928   while Loc<>Loc0 do begin Loc:=From[Loc]; inc(result); end;
    929   if (result>1) and ((result>=MinRivLen) or (RealMap[Loc1] and fTerrain>=fGrass)) then
    930     begin
    931     Loc:=Loc1;
    932     while Loc<>Loc0 do
    933       begin
    934       Loc:=From[Loc];
    935       if RealMap[Loc] and fTerrain in [fHills,fMountains] then
    936         RealMap[Loc]:=fGrass or fRiver
    937       else if RealMap[Loc] and fTerrain>=fGrass then
    938         RealMap[Loc]:=RealMap[Loc] or fRiver;
     1048    Loc1 := Loc;
     1049    result := 0;
     1050    while Loc <> Loc0 do
     1051    begin
     1052      Loc := From[Loc];
     1053      inc(result);
     1054    end;
     1055    if (result > 1) and ((result >= MinRivLen) or
     1056      (RealMap[Loc1] and fTerrain >= fGrass)) then
     1057    begin
     1058      Loc := Loc1;
     1059      while Loc <> Loc0 do
     1060      begin
     1061        Loc := From[Loc];
     1062        if RealMap[Loc] and fTerrain in [fHills, fMountains] then
     1063          RealMap[Loc] := fGrass or fRiver
     1064        else if RealMap[Loc] and fTerrain >= fGrass then
     1065          RealMap[Loc] := RealMap[Loc] or fRiver;
    9391066      end
    9401067    end
    941   else result:=0;
    942   Q.Free
    943   end;
    944 
    945 var
    946 x,y,n,Dir,plus,Count,Loc0,Loc1,bLand,bHills,bMountains,V8: integer;
    947 CopyFrom: array[0..lxmax*lymax-1] of integer;
    948 Adjacent: TVicinity8Loc;
    949 
    950 begin
    951 FillChar(RealMap,MapSize*4,0);
    952 plus:=0;
    953 bMountains:=256;
    954 while plus<MapSize*LandMass*ShMountains div 10000 do
    955   begin dec(bMountains);inc(plus,ElCount[bMountains]) end;
    956 Count:=plus;
    957 plus:=0;
    958 bHills:=bMountains;
    959 while plus<MapSize*LandMass*ShHiHills div 10000 do
    960   begin dec(bHills);inc(plus,ElCount[bHills]) end;
    961 inc(Count,plus);
    962 bLand:=bHills;
    963 while Count<MapSize*LandMass div 100 do
    964   begin dec(bLand);inc(Count,ElCount[bLand]) end;
    965 
    966 for Loc0:=lx to lx*(ly-1)-1 do
    967   if Elevation[Loc0]>=bMountains then RealMap[Loc0]:=fMountains
    968   else if Elevation[Loc0]>=bHills then RealMap[Loc0]:=fHills
    969   else if Elevation[Loc0]>=bLand then RealMap[Loc0]:=fGrass;
    970 
    971 // remove one-tile islands
    972 for Loc0:=0 to MapSize-1 do
    973   if RealMap[Loc0]>=fGrass then
    974     begin
    975     Count:=0;
    976     V8_to_Loc(Loc0,Adjacent);
    977     for V8:=0 to 7 do
    978       begin
    979       Loc1:=Adjacent[V8];
    980       if (Loc1<0) or (Loc1>=MapSize)
    981         or (RealMap[Loc1] and fTerrain<fGrass)
    982         or (RealMap[Loc1] and fTerrain=fArctic) then
    983         inc(Count); // count adjacent water
    984       end;
    985     if Count=8 then RealMap[Loc0]:=fOcean
    986     end;
    987 
    988 if not preview then
    989   begin
    990   plus:=36*56*20*ShTestRiver div (LandMass*100);
    991   if plus>MapSize then plus:=MapSize;
    992   Loc0:=Random(MapSize);
    993   for n:=0 to plus-1 do
    994     begin
    995     if (RealMap[Loc0] and fTerrain>=fGrass) and (Loc0>=lx) and (Loc0<MapSize-lx) then
    996       RunRiver(Loc0);
    997     Loc0:=(Loc0+1)*primitive mod (MapSize+1) -1;
    998     end;
    999   end;
    1000 
    1001 for Loc0:=0 to MapSize-1 do
    1002   if (RealMap[Loc0]=fGrass) and (Random(100)<ShRandHills) then
    1003     RealMap[Loc0]:=RealMap[Loc0] or fHills;
    1004 
    1005 // make terrain types coherent
    1006 for Loc0:=0 to MapSize-1 do CopyFrom[Loc0]:=Loc0;
    1007 
    1008 for n:=0 to unification*MapSize div 100 do
    1009   begin
    1010   y:=Random(ly);
    1011   if abs(y-(ly shr 1))>ly div 4+Random(ly*hotunification div 100) then
    1012     if y<ly shr 1 then y:=ly shr 1-y
    1013     else y:=3*ly shr 1-y;
    1014   Loc0:=lx*y+Random(lx);
    1015   if RealMap[Loc0] and fTerrain=fGrass then
    1016     begin
    1017     Dir:=Random(4);
    1018     Loc1:=dLoc(Loc0,Dir and 1 *2 -1,Dir shr 1 *2 -1);
    1019     if (Loc1>=0) and (RealMap[Loc1] and fTerrain=fGrass) then
    1020       begin
    1021       while CopyFrom[Loc0]<>Loc0 do Loc0:=CopyFrom[Loc0];
    1022       while CopyFrom[Loc1]<>Loc1 do Loc1:=CopyFrom[Loc1];
    1023       if Loc1<Loc0 then CopyFrom[Loc0]:=Loc1
    1024       else CopyFrom[Loc1]:=Loc0;
    1025       end;
    1026     end;
    1027   end;
    1028 
    1029 for Loc0:=0 to MapSize-1 do
    1030   if (RealMap[Loc0] and fTerrain=fGrass) and (CopyFrom[Loc0]=Loc0) then
    1031     RealMap[Loc0]:=RealMap[Loc0] and not fTerrain or RndLow(Loc0 div lx);
    1032 
    1033 for Loc0:=0 to MapSize-1 do
    1034   if RealMap[Loc0] and fTerrain=fGrass then
    1035     begin
    1036     Loc1:=Loc0;
    1037     while CopyFrom[Loc1]<>Loc1 do Loc1:=CopyFrom[Loc1];
    1038     RealMap[Loc0]:=RealMap[Loc0] and not fTerrain or RealMap[Loc1] and fTerrain
    1039     end;
    1040 
    1041 for Loc0:=0 to MapSize-1 do
    1042   if RealMap[Loc0] and fTerrain=fGrass then
     1068    else
     1069      result := 0;
     1070    Q.Free
     1071  end;
     1072
     1073var
     1074  x, y, n, Dir, plus, Count, Loc0, Loc1, bLand, bHills, bMountains, V8: integer;
     1075  CopyFrom: array [0 .. lxmax * lymax - 1] of integer;
     1076  Adjacent: TVicinity8Loc;
     1077
     1078begin
     1079  FillChar(RealMap, MapSize * 4, 0);
     1080  plus := 0;
     1081  bMountains := 256;
     1082  while plus < MapSize * LandMass * ShMountains div 10000 do
     1083  begin
     1084    dec(bMountains);
     1085    inc(plus, ElCount[bMountains])
     1086  end;
     1087  Count := plus;
     1088  plus := 0;
     1089  bHills := bMountains;
     1090  while plus < MapSize * LandMass * ShHiHills div 10000 do
     1091  begin
     1092    dec(bHills);
     1093    inc(plus, ElCount[bHills])
     1094  end;
     1095  inc(Count, plus);
     1096  bLand := bHills;
     1097  while Count < MapSize * LandMass div 100 do
     1098  begin
     1099    dec(bLand);
     1100    inc(Count, ElCount[bLand])
     1101  end;
     1102
     1103  for Loc0 := lx to lx * (ly - 1) - 1 do
     1104    if Elevation[Loc0] >= bMountains then
     1105      RealMap[Loc0] := fMountains
     1106    else if Elevation[Loc0] >= bHills then
     1107      RealMap[Loc0] := fHills
     1108    else if Elevation[Loc0] >= bLand then
     1109      RealMap[Loc0] := fGrass;
     1110
     1111  // remove one-tile islands
     1112  for Loc0 := 0 to MapSize - 1 do
     1113    if RealMap[Loc0] >= fGrass then
     1114    begin
     1115      Count := 0;
     1116      V8_to_Loc(Loc0, Adjacent);
     1117      for V8 := 0 to 7 do
     1118      begin
     1119        Loc1 := Adjacent[V8];
     1120        if (Loc1 < 0) or (Loc1 >= MapSize) or
     1121          (RealMap[Loc1] and fTerrain < fGrass) or
     1122          (RealMap[Loc1] and fTerrain = fArctic) then
     1123          inc(Count); // count adjacent water
     1124      end;
     1125      if Count = 8 then
     1126        RealMap[Loc0] := fOcean
     1127    end;
     1128
     1129  if not preview then
     1130  begin
     1131    plus := 36 * 56 * 20 * ShTestRiver div (LandMass * 100);
     1132    if plus > MapSize then
     1133      plus := MapSize;
     1134    Loc0 := Random(MapSize);
     1135    for n := 0 to plus - 1 do
     1136    begin
     1137      if (RealMap[Loc0] and fTerrain >= fGrass) and (Loc0 >= lx) and
     1138        (Loc0 < MapSize - lx) then
     1139        RunRiver(Loc0);
     1140      Loc0 := (Loc0 + 1) * primitive mod (MapSize + 1) - 1;
     1141    end;
     1142  end;
     1143
     1144  for Loc0 := 0 to MapSize - 1 do
     1145    if (RealMap[Loc0] = fGrass) and (Random(100) < ShRandHills) then
     1146      RealMap[Loc0] := RealMap[Loc0] or fHills;
     1147
     1148  // make terrain types coherent
     1149  for Loc0 := 0 to MapSize - 1 do
     1150    CopyFrom[Loc0] := Loc0;
     1151
     1152  for n := 0 to unification * MapSize div 100 do
     1153  begin
     1154    y := Random(ly);
     1155    if abs(y - (ly shr 1)) > ly div 4 + Random(ly * hotunification div 100) then
     1156      if y < ly shr 1 then
     1157        y := ly shr 1 - y
     1158      else
     1159        y := 3 * ly shr 1 - y;
     1160    Loc0 := lx * y + Random(lx);
     1161    if RealMap[Loc0] and fTerrain = fGrass then
     1162    begin
     1163      Dir := Random(4);
     1164      Loc1 := dLoc(Loc0, Dir and 1 * 2 - 1, Dir shr 1 * 2 - 1);
     1165      if (Loc1 >= 0) and (RealMap[Loc1] and fTerrain = fGrass) then
     1166      begin
     1167        while CopyFrom[Loc0] <> Loc0 do
     1168          Loc0 := CopyFrom[Loc0];
     1169        while CopyFrom[Loc1] <> Loc1 do
     1170          Loc1 := CopyFrom[Loc1];
     1171        if Loc1 < Loc0 then
     1172          CopyFrom[Loc0] := Loc1
     1173        else
     1174          CopyFrom[Loc1] := Loc0;
     1175      end;
     1176    end;
     1177  end;
     1178
     1179  for Loc0 := 0 to MapSize - 1 do
     1180    if (RealMap[Loc0] and fTerrain = fGrass) and (CopyFrom[Loc0] = Loc0) then
     1181      RealMap[Loc0] := RealMap[Loc0] and not fTerrain or RndLow(Loc0 div lx);
     1182
     1183  for Loc0 := 0 to MapSize - 1 do
     1184    if RealMap[Loc0] and fTerrain = fGrass then
     1185    begin
     1186      Loc1 := Loc0;
     1187      while CopyFrom[Loc1] <> Loc1 do
     1188        Loc1 := CopyFrom[Loc1];
     1189      RealMap[Loc0] := RealMap[Loc0] and not fTerrain or
     1190        RealMap[Loc1] and fTerrain
     1191    end;
     1192
     1193  for Loc0 := 0 to MapSize - 1 do
     1194    if RealMap[Loc0] and fTerrain = fGrass then
    10431195    begin // change grassland to swamp
    1044     if Random(100)<ShSwamp then
    1045       RealMap[Loc0]:=RealMap[Loc0] and not fTerrain or fSwamp;
    1046     end;
    1047 
    1048 for Loc0:=0 to MapSize-1 do // change desert to prairie 1
    1049   if RealMap[Loc0] and fTerrain=fDesert then
    1050     begin
    1051     if RealMap[Loc0] and fRiver<>0 then Count:=5
    1052     else
    1053       begin
    1054       Count:=0;
    1055       for Dir:=0 to 3 do
    1056         begin
    1057         Loc1:=dLoc(Loc0,Dir and 1 *2 -1,Dir shr 1 *2 -1);
    1058         if Loc1>=0 then
    1059           if RealMap[Loc1] and fTerrain<fGrass then inc(Count,2)
     1196      if Random(100) < ShSwamp then
     1197        RealMap[Loc0] := RealMap[Loc0] and not fTerrain or fSwamp;
     1198    end;
     1199
     1200  for Loc0 := 0 to MapSize - 1 do // change desert to prairie 1
     1201    if RealMap[Loc0] and fTerrain = fDesert then
     1202    begin
     1203      if RealMap[Loc0] and fRiver <> 0 then
     1204        Count := 5
     1205      else
     1206      begin
     1207        Count := 0;
     1208        for Dir := 0 to 3 do
     1209        begin
     1210          Loc1 := dLoc(Loc0, Dir and 1 * 2 - 1, Dir shr 1 * 2 - 1);
     1211          if Loc1 >= 0 then
     1212            if RealMap[Loc1] and fTerrain < fGrass then
     1213              inc(Count, 2)
    10601214        end;
    10611215      end;
    1062     if Count>=4 then RealMap[Loc0]:=RealMap[Loc0] and not fTerrain or fPrairie
    1063     end;
    1064 
    1065 for Loc0:=0 to MapSize-1 do // change desert to prairie 2
    1066   if RealMap[Loc0] and fTerrain=fDesert then
    1067     begin
    1068     Count:=0;
    1069     for Dir:=0 to 3 do
    1070       begin
    1071       Loc1:=dLoc(Loc0,Dir and 1 *2 -1,Dir shr 1 *2 -1);
    1072       if Loc1>=0 then
    1073         if RealMap[Loc1] and fTerrain<>fDesert then inc(Count)
    1074       end;
    1075     if Count>=4 then RealMap[Loc0]:=RealMap[Loc0] and not fTerrain or fPrairie
    1076     end;
    1077 
    1078 for Loc0:=0 to MapSize-1 do CheckShore(Loc0); // change ocean to shore
    1079 for x:=0 to lx-1 do
    1080   begin
    1081   RealMap[x+lx*0]:=fArctic;
    1082   if RealMap[x+lx*1]>=fGrass then
    1083     RealMap[x+lx*1]:=RealMap[x+lx*1] and not fTerrain or fTundra;
    1084   if RealMap[x+lx*(ly-2)]>=fGrass then
    1085     RealMap[x+lx*(ly-2)]:=RealMap[x+lx*(ly-2)] and not fTerrain or fTundra;
    1086   RealMap[x+lx*(ly-1)]:=fArctic
    1087   end;
    1088 
    1089 for Loc0:=0 to MapSize-1 do //define special terrain tiles
    1090   RealMap[Loc0]:=RealMap[Loc0] or ActualSpecialTile(Loc0) shl 5 or ($F shl 27);
    1091 
    1092 if not preview then
    1093   begin FindContinents; RarePositions; end;
     1216      if Count >= 4 then
     1217        RealMap[Loc0] := RealMap[Loc0] and not fTerrain or fPrairie
     1218    end;
     1219
     1220  for Loc0 := 0 to MapSize - 1 do // change desert to prairie 2
     1221    if RealMap[Loc0] and fTerrain = fDesert then
     1222    begin
     1223      Count := 0;
     1224      for Dir := 0 to 3 do
     1225      begin
     1226        Loc1 := dLoc(Loc0, Dir and 1 * 2 - 1, Dir shr 1 * 2 - 1);
     1227        if Loc1 >= 0 then
     1228          if RealMap[Loc1] and fTerrain <> fDesert then
     1229            inc(Count)
     1230      end;
     1231      if Count >= 4 then
     1232        RealMap[Loc0] := RealMap[Loc0] and not fTerrain or fPrairie
     1233    end;
     1234
     1235  for Loc0 := 0 to MapSize - 1 do
     1236    CheckShore(Loc0); // change ocean to shore
     1237  for x := 0 to lx - 1 do
     1238  begin
     1239    RealMap[x + lx * 0] := fArctic;
     1240    if RealMap[x + lx * 1] >= fGrass then
     1241      RealMap[x + lx * 1] := RealMap[x + lx * 1] and not fTerrain or fTundra;
     1242    if RealMap[x + lx * (ly - 2)] >= fGrass then
     1243      RealMap[x + lx * (ly - 2)] := RealMap[x + lx * (ly - 2)] and
     1244        not fTerrain or fTundra;
     1245    RealMap[x + lx * (ly - 1)] := fArctic
     1246  end;
     1247
     1248  for Loc0 := 0 to MapSize - 1 do // define special terrain tiles
     1249    RealMap[Loc0] := RealMap[Loc0] or ActualSpecialTile(Loc0) shl 5 or
     1250      ($F shl 27);
     1251
     1252  if not preview then
     1253  begin
     1254    FindContinents;
     1255    RarePositions;
     1256  end;
    10941257end;
    10951258
     
    10991262
    11001263var
    1101 CountGood:(cgBest,cgFlat,cgLand);
     1264  CountGood: (cgBest, cgFlat, cgLand);
    11021265
    11031266  function IsGoodTile(Loc: integer): boolean;
    11041267  var
    1105   xLoc,yLoc: integer;
    1106   begin
    1107   xLoc:=Loc mod lx; yLoc:=Loc div lx;
    1108   if RealMap[Loc] and fDeadLands<>0 then result:=false
    1109   else
    1110     case CountGood of
    1111       cgBest:
    1112         result:=(RealMap[Loc] and fTerrain in [fGrass,fPrairie,fTundra,fSwamp,fForest])
    1113           and Odd((lymax+xLoc-yLoc shr 1) shr 1+xLoc+(yLoc+1) shr 1);
    1114       cgFlat:
    1115         result:=(RealMap[Loc] and fTerrain in [fGrass,fPrairie,fTundra,fSwamp,fForest]);
    1116       cgLand:
    1117         result:= RealMap[Loc] and fTerrain>=fGrass;
     1268    xLoc, yLoc: integer;
     1269  begin
     1270    xLoc := Loc mod lx;
     1271    yLoc := Loc div lx;
     1272    if RealMap[Loc] and fDeadLands <> 0 then
     1273      result := false
     1274    else
     1275      case CountGood of
     1276        cgBest:
     1277          result := (RealMap[Loc] and fTerrain in [fGrass, fPrairie, fTundra,
     1278            fSwamp, fForest]) and Odd((lymax + xLoc - yLoc shr 1) shr 1 + xLoc +
     1279            (yLoc + 1) shr 1);
     1280        cgFlat:
     1281          result := (RealMap[Loc] and fTerrain in [fGrass, fPrairie, fTundra,
     1282            fSwamp, fForest]);
     1283        cgLand:
     1284          result := RealMap[Loc] and fTerrain >= fGrass;
    11181285      end;
    11191286  end;
    11201287
    11211288const
    1122 MaxCityLoc=64;
    1123 
    1124 var
    1125 p1,p2,nAlive,c,Loc,Loc1,CntGood,CntGoodGrass,MinDist,Tries,i,j,n,nsc,TestLoc,
    1126   V21,V8,BestDist,TestDist,MinGood,nIrrLoc,xLoc,yLoc,qx,qy,FineDistSQR,
    1127   nRest:integer;
    1128 ccount:array[0..lxmax*lymax-1] of word;
    1129 sc,StartLoc0,sccount: array[1..nPl] of integer;
    1130 TestStartLoc: array[0..nPl-1] of integer;
    1131 CityLoc: array[1..nPl,0..MaxCityLoc-1] of integer;
    1132 nCityLoc: array[1..nPl] of integer;
    1133 RestLoc: array[0..MaxCityLoc-1] of integer;
    1134 IrrLoc: array[0..20] of integer;
    1135 Radius: TVicinity21Loc;
    1136 Adjacent: TVicinity8Loc;
    1137 ok: boolean;
    1138 
    1139 begin
    1140 nAlive:=0;
    1141 for p1:=0 to nPl-1 do if 1 shl p1 and GAlive<>0 then inc(nAlive);
    1142 if nAlive=0 then exit;
    1143 
    1144 {count good tiles}
    1145 FillChar(ccount,MapSize*2,0);
    1146 for Loc:=0 to MapSize-1 do
    1147   if RealMap[Loc] and fTerrain=fGrass then
    1148     if ActualSpecialTile(Loc)=1 then inc(ccount[Continent[Loc]],3)
    1149     else inc(ccount[Continent[Loc]],2)
    1150   else if RealMap[Loc] and fTerrain in [fPrairie,fSwamp,fForest,fHills] then
    1151     inc(ccount[Continent[Loc]]);
    1152 
    1153 Loc:=0;while ccount[Loc]>0 do inc(Loc);
    1154 for i:=1 to nAlive do begin sc[i]:=Loc; sccount[i]:=1 end;
    1155   {init with zero size start continents, then search bigger ones}
    1156 for Loc:=0 to MapSize-1 do if ccount[Loc]>0 then
    1157   begin // search biggest continents
    1158   p1:=nAlive+1;
    1159   while (p1>1) and (ccount[Loc]>ccount[sc[p1-1]]) do
    1160     begin if p1<nAlive+1 then sc[p1]:=sc[p1-1]; dec(p1) end;
    1161   if p1<nAlive+1 then sc[p1]:=Loc;
    1162   end;
    1163 nsc:=nAlive;
    1164 repeat
    1165   c:=1; // search least crowded continent after smallest
    1166   for i:=2 to nsc-1 do
    1167     if ccount[sc[i]]*(2*sccount[c]+1)>ccount[sc[c]]*(2*sccount[i]+1) then
    1168       c:=i;
    1169   if ccount[sc[nsc]]*(2*sccount[c]+1)>ccount[sc[c]] then
    1170     Break; // even least crowded continent is more crowded than smallest
    1171   inc(sccount[c]);
    1172   dec(nsc)
    1173 until sccount[nsc]>1;
    1174 
    1175 MinGood:=7;
    1176 CountGood:=cgBest;
    1177 repeat
    1178   dec(MinGood);
    1179   if (MinGood=3) and (CountGood<cgLand) then // too demanding!
    1180     begin inc(CountGood); MinGood:=6 end;
    1181   FillChar(nCityLoc,SizeOf(nCityLoc),0);
    1182   Loc:=Random(MapSize);
    1183   for i:=0 to MapSize-1 do
    1184     begin
    1185     if ((Loc>=4*lx) and (Loc<MapSize-4*lx) or (CountGood>=cgLand))
    1186       and IsGoodTile(Loc) then
    1187       begin
    1188       c:=nsc;
    1189       while (c>0) and (Continent[Loc]<>sc[c]) do dec(c);
    1190       if (c>0) and (nCityLoc[c]<MaxCityLoc) then
    1191         begin
    1192         CntGood:=1;
    1193         V21_to_Loc(Loc,Radius);
    1194         for V21:=1 to 26 do if V21<>CityOwnTile then
     1289  MaxCityLoc = 64;
     1290
     1291var
     1292  p1, p2, nAlive, c, Loc, Loc1, CntGood, CntGoodGrass, MinDist, Tries, i, j, n,
     1293    nsc, TestLoc, V21, V8, BestDist, TestDist, MinGood, nIrrLoc, xLoc, yLoc, qx,
     1294    qy, FineDistSQR, nRest: integer;
     1295  ccount: array [0 .. lxmax * lymax - 1] of word;
     1296  sc, StartLoc0, sccount: array [1 .. nPl] of integer;
     1297  TestStartLoc: array [0 .. nPl - 1] of integer;
     1298  CityLoc: array [1 .. nPl, 0 .. MaxCityLoc - 1] of integer;
     1299  nCityLoc: array [1 .. nPl] of integer;
     1300  RestLoc: array [0 .. MaxCityLoc - 1] of integer;
     1301  IrrLoc: array [0 .. 20] of integer;
     1302  Radius: TVicinity21Loc;
     1303  Adjacent: TVicinity8Loc;
     1304  ok: boolean;
     1305
     1306begin
     1307  nAlive := 0;
     1308  for p1 := 0 to nPl - 1 do
     1309    if 1 shl p1 and GAlive <> 0 then
     1310      inc(nAlive);
     1311  if nAlive = 0 then
     1312    exit;
     1313
     1314  { count good tiles }
     1315  FillChar(ccount, MapSize * 2, 0);
     1316  for Loc := 0 to MapSize - 1 do
     1317    if RealMap[Loc] and fTerrain = fGrass then
     1318      if ActualSpecialTile(Loc) = 1 then
     1319        inc(ccount[Continent[Loc]], 3)
     1320      else
     1321        inc(ccount[Continent[Loc]], 2)
     1322    else if RealMap[Loc] and fTerrain in [fPrairie, fSwamp, fForest, fHills]
     1323    then
     1324      inc(ccount[Continent[Loc]]);
     1325
     1326  Loc := 0;
     1327  while ccount[Loc] > 0 do
     1328    inc(Loc);
     1329  for i := 1 to nAlive do
     1330  begin
     1331    sc[i] := Loc;
     1332    sccount[i] := 1
     1333  end;
     1334  { init with zero size start continents, then search bigger ones }
     1335  for Loc := 0 to MapSize - 1 do
     1336    if ccount[Loc] > 0 then
     1337    begin // search biggest continents
     1338      p1 := nAlive + 1;
     1339      while (p1 > 1) and (ccount[Loc] > ccount[sc[p1 - 1]]) do
     1340      begin
     1341        if p1 < nAlive + 1 then
     1342          sc[p1] := sc[p1 - 1];
     1343        dec(p1)
     1344      end;
     1345      if p1 < nAlive + 1 then
     1346        sc[p1] := Loc;
     1347    end;
     1348  nsc := nAlive;
     1349  repeat
     1350    c := 1; // search least crowded continent after smallest
     1351    for i := 2 to nsc - 1 do
     1352      if ccount[sc[i]] * (2 * sccount[c] + 1) > ccount[sc[c]] *
     1353        (2 * sccount[i] + 1) then
     1354        c := i;
     1355    if ccount[sc[nsc]] * (2 * sccount[c] + 1) > ccount[sc[c]] then
     1356      Break; // even least crowded continent is more crowded than smallest
     1357    inc(sccount[c]);
     1358    dec(nsc)
     1359  until sccount[nsc] > 1;
     1360
     1361  MinGood := 7;
     1362  CountGood := cgBest;
     1363  repeat
     1364    dec(MinGood);
     1365    if (MinGood = 3) and (CountGood < cgLand) then // too demanding!
     1366    begin
     1367      inc(CountGood);
     1368      MinGood := 6
     1369    end;
     1370    FillChar(nCityLoc, SizeOf(nCityLoc), 0);
     1371    Loc := Random(MapSize);
     1372    for i := 0 to MapSize - 1 do
     1373    begin
     1374      if ((Loc >= 4 * lx) and (Loc < MapSize - 4 * lx) or (CountGood >= cgLand))
     1375        and IsGoodTile(Loc) then
     1376      begin
     1377        c := nsc;
     1378        while (c > 0) and (Continent[Loc] <> sc[c]) do
     1379          dec(c);
     1380        if (c > 0) and (nCityLoc[c] < MaxCityLoc) then
     1381        begin
     1382          CntGood := 1;
     1383          V21_to_Loc(Loc, Radius);
     1384          for V21 := 1 to 26 do
     1385            if V21 <> CityOwnTile then
     1386            begin
     1387              Loc1 := Radius[V21];
     1388              if (Loc1 >= 0) and (Loc1 < MapSize) and IsGoodTile(Loc1) then
     1389                inc(CntGood)
     1390            end;
     1391          if CntGood >= MinGood then
    11951392          begin
    1196           Loc1:=Radius[V21];
    1197           if (Loc1>=0) and (Loc1<MapSize) and IsGoodTile(Loc1) then
    1198             inc(CntGood)
    1199           end;
    1200         if CntGood>=MinGood then
    1201           begin
    1202           CityLoc[c,nCityLoc[c]]:=Loc;
    1203           inc(nCityLoc[c])
     1393            CityLoc[c, nCityLoc[c]] := Loc;
     1394            inc(nCityLoc[c])
    12041395          end
    12051396        end
    12061397      end;
    1207     Loc:=(Loc+1)*primitive mod (MapSize+1) -1;
    1208     end;
    1209 
    1210   ok:=true;
    1211   for c:=1 to nsc do
    1212     if nCityLoc[c]<sccount[c]*(8-MinGood) div (7-MinGood) then ok:=false;
    1213 until ok;
    1214 
    1215 FineDistSQR:=MapSize*LandMass*9 div (nAlive*100);
    1216 p1:=1;
    1217 for c:=1 to nsc do
     1398      Loc := (Loc + 1) * primitive mod (MapSize + 1) - 1;
     1399    end;
     1400
     1401    ok := true;
     1402    for c := 1 to nsc do
     1403      if nCityLoc[c] < sccount[c] * (8 - MinGood) div (7 - MinGood) then
     1404        ok := false;
     1405  until ok;
     1406
     1407  FineDistSQR := MapSize * LandMass * 9 div (nAlive * 100);
     1408  p1 := 1;
     1409  for c := 1 to nsc do
    12181410  begin // for all start continents
    1219   if sccount[c]=1 then StartLoc0[p1]:=CityLoc[c,Random(nCityLoc[c])]
    1220   else
    1221     begin
    1222     BestDist:=0;
    1223     n:=1 shl sccount[c] *32; // number of tries to find good distribution
    1224     if n>1 shl 12 then n:=1 shl 12;
    1225     while (n>0) and (BestDist*BestDist<FineDistSQR) do
    1226       begin
    1227       MinDist:=MaxInt;
    1228       nRest:=nCityLoc[c];
    1229       for i:=0 to nRest-1 do RestLoc[i]:=CityLoc[c,i];
    1230       for i:=0 to sccount[c]-1 do
    1231         begin
    1232         if nRest=0 then break;
    1233         j:=Random(nRest);
    1234         TestStartLoc[i]:=RestLoc[j];
    1235         RestLoc[j]:=RestLoc[nRest-1];
    1236         dec(nRest);
    1237         for j:=0 to i-1 do
     1411    if sccount[c] = 1 then
     1412      StartLoc0[p1] := CityLoc[c, Random(nCityLoc[c])]
     1413    else
     1414    begin
     1415      BestDist := 0;
     1416      n := 1 shl sccount[c] * 32; // number of tries to find good distribution
     1417      if n > 1 shl 12 then
     1418        n := 1 shl 12;
     1419      while (n > 0) and (BestDist * BestDist < FineDistSQR) do
     1420      begin
     1421        MinDist := MaxInt;
     1422        nRest := nCityLoc[c];
     1423        for i := 0 to nRest - 1 do
     1424          RestLoc[i] := CityLoc[c, i];
     1425        for i := 0 to sccount[c] - 1 do
     1426        begin
     1427          if nRest = 0 then
     1428            Break;
     1429          j := Random(nRest);
     1430          TestStartLoc[i] := RestLoc[j];
     1431          RestLoc[j] := RestLoc[nRest - 1];
     1432          dec(nRest);
     1433          for j := 0 to i - 1 do
    12381434          begin
    1239           TestDist:=Distance(TestStartLoc[i],TestStartLoc[j]);
    1240           if TestDist<MinDist then MinDist:=TestDist
     1435            TestDist := Distance(TestStartLoc[i], TestStartLoc[j]);
     1436            if TestDist < MinDist then
     1437              MinDist := TestDist
    12411438          end;
    1242         if i=sccount[c]-1 then
     1439          if i = sccount[c] - 1 then
    12431440          begin
    1244           assert(MinDist>BestDist);
    1245           BestDist:=MinDist;
    1246           for j:=0 to sccount[c]-1 do StartLoc0[p1+j]:=TestStartLoc[j];
     1441            assert(MinDist > BestDist);
     1442            BestDist := MinDist;
     1443            for j := 0 to sccount[c] - 1 do
     1444              StartLoc0[p1 + j] := TestStartLoc[j];
    12471445          end
    1248         else if BestDist>0 then
     1446          else if BestDist > 0 then
    12491447          begin
    1250           j:=0;
    1251           while j<nRest do
     1448            j := 0;
     1449            while j < nRest do
    12521450            begin // remove all locs from rest which have too little distance to this one
    1253             TestDist:=Distance(TestStartLoc[i],RestLoc[j]);
    1254             if TestDist<=BestDist then
    1255               begin RestLoc[j]:=RestLoc[nRest-1]; dec(nRest); end
    1256             else inc(j);
     1451              TestDist := Distance(TestStartLoc[i], RestLoc[j]);
     1452              if TestDist <= BestDist then
     1453              begin
     1454                RestLoc[j] := RestLoc[nRest - 1];
     1455                dec(nRest);
     1456              end
     1457              else
     1458                inc(j);
    12571459            end;
    12581460          end;
    12591461        end;
    1260       dec(n)
    1261       end;
    1262     end;
    1263   p1:=p1+sccount[c]
    1264   end;
    1265 
    1266 // make start locs fertile
    1267 for p1:=1 to nAlive do
    1268   begin
    1269   RealMap[StartLoc0[p1]]:=RealMap[StartLoc0[p1]] and not (fTerrain or fSpecial)
    1270     or fGrass or fSpecial1;
    1271   CntGood:=1;
    1272   CntGoodGrass:=1;
    1273   V21_to_Loc(StartLoc0[p1],Radius);
    1274   for V21:=1 to 26 do if V21<>CityOwnTile then
    1275     begin
    1276     Loc1:=Radius[V21];
    1277     if (Loc1>=0) and (Loc1<MapSize) and IsGoodTile(Loc1) then
    1278       if RealMap[Loc1] and fTerrain=fGrass then inc(CntGoodGrass)
    1279       else inc(CntGood);
    1280     end;
    1281   for V21:=1 to 26 do if V21<>CityOwnTile then
    1282     begin
    1283     Loc1:=Radius[V21];
    1284     if (Loc1>=0) and (Loc1<MapSize) and (RealMap[Loc1] and fDeadLands=0) then
    1285       if IsGoodTile(Loc1) and (random(CntGood)<MinGood-CntGoodGrass+1) then
    1286         begin
    1287         RealMap[Loc1]:=RealMap[Loc1] and not (fTerrain or fSpecial) or fGrass;
    1288         RealMap[Loc1]:=RealMap[Loc1] or ActualSpecialTile(Loc1) shl 5;
     1462        dec(n)
     1463      end;
     1464    end;
     1465    p1 := p1 + sccount[c]
     1466  end;
     1467
     1468  // make start locs fertile
     1469  for p1 := 1 to nAlive do
     1470  begin
     1471    RealMap[StartLoc0[p1]] := RealMap[StartLoc0[p1]] and
     1472      not(fTerrain or fSpecial) or fGrass or fSpecial1;
     1473    CntGood := 1;
     1474    CntGoodGrass := 1;
     1475    V21_to_Loc(StartLoc0[p1], Radius);
     1476    for V21 := 1 to 26 do
     1477      if V21 <> CityOwnTile then
     1478      begin
     1479        Loc1 := Radius[V21];
     1480        if (Loc1 >= 0) and (Loc1 < MapSize) and IsGoodTile(Loc1) then
     1481          if RealMap[Loc1] and fTerrain = fGrass then
     1482            inc(CntGoodGrass)
     1483          else
     1484            inc(CntGood);
     1485      end;
     1486    for V21 := 1 to 26 do
     1487      if V21 <> CityOwnTile then
     1488      begin
     1489        Loc1 := Radius[V21];
     1490        if (Loc1 >= 0) and (Loc1 < MapSize) and
     1491          (RealMap[Loc1] and fDeadLands = 0) then
     1492          if IsGoodTile(Loc1) and (Random(CntGood) < MinGood - CntGoodGrass + 1)
     1493          then
     1494          begin
     1495            RealMap[Loc1] := RealMap[Loc1] and not(fTerrain or fSpecial)
     1496              or fGrass;
     1497            RealMap[Loc1] := RealMap[Loc1] or ActualSpecialTile(Loc1) shl 5;
     1498          end
     1499          else if RealMap[Loc1] and fTerrain = fDesert then
     1500            RealMap[Loc1] := RealMap[Loc1] and not fTerrain or fPrairie
     1501          else if (RealMap[Loc1] and fTerrain in [fPrairie, fTundra, fSwamp])
     1502            and (Random(2) = 0) then
     1503            RealMap[Loc1] := RealMap[Loc1] and not fTerrain or fForest;
     1504      end;
     1505
     1506    // first irrigation
     1507    nIrrLoc := 0;
     1508    for V21 := 1 to 26 do
     1509      if V21 <> CityOwnTile then
     1510      begin
     1511        Loc1 := Radius[V21];
     1512        if (Loc1 >= 0) and (Loc1 < MapSize) and
     1513          (RealMap[Loc1] and (fTerrain or fSpecial) = fGrass or fSpecial1) then
     1514        begin
     1515          IrrLoc[nIrrLoc] := Loc1;
     1516          inc(nIrrLoc);
     1517        end;
     1518      end;
     1519    i := 2;
     1520    if i > nIrrLoc then
     1521      i := nIrrLoc;
     1522    while i > 0 do
     1523    begin
     1524      j := Random(nIrrLoc);
     1525      RealMap[IrrLoc[j]] := RealMap[IrrLoc[j]] or tiIrrigation;
     1526      IrrLoc[j] := IrrLoc[nIrrLoc - 1];
     1527      dec(nIrrLoc);
     1528      dec(i)
     1529    end;
     1530  end;
     1531
     1532  StartLoc[0] := 0;
     1533  for p1 := 0 to nPl - 1 do
     1534    if 1 shl p1 and GAlive <> 0 then
     1535    begin
     1536      repeat
     1537        i := Random(nAlive) + 1
     1538      until StartLoc0[i] >= 0;
     1539      StartLoc[p1] := StartLoc0[i];
     1540      StartLoc0[i] := -1
     1541    end;
     1542  SaveMapCenterLoc := StartLoc[0];
     1543
     1544  // second unit starting position
     1545  for p1 := 0 to nPl - 1 do
     1546    if 1 shl p1 and GAlive <> 0 then
     1547    begin
     1548      StartLoc2[p1] := StartLoc[p1];
     1549      V8_to_Loc(StartLoc[p1], Adjacent);
     1550      for V8 := 0 to 7 do
     1551      begin
     1552        Loc1 := Adjacent[V8];
     1553        for p2 := 0 to nPl - 1 do
     1554          if (1 shl p2 and GAlive <> 0) and (StartLoc[p2] = Loc1) then
     1555            Loc1 := -1;
     1556        for p2 := 0 to p1 - 1 do
     1557          if (1 shl p2 and GAlive <> 0) and (StartLoc2[p2] = Loc1) then
     1558            Loc1 := -1;
     1559        if (Loc1 < 0) or (Loc1 >= MapSize) or
     1560          (RealMap[Loc1] and fTerrain in [fOcean, fShore, fDesert, fArctic,
     1561          fMountains]) or (RealMap[Loc1] and fDeadLands <> 0) then
     1562          TestDist := -1
     1563        else if RealMap[Loc1] and fTerrain = fGrass then
     1564          TestDist := 2
     1565        else if Terrain[RealMap[Loc1] and fTerrain].IrrEff > 0 then
     1566          TestDist := 1
     1567        else
     1568          TestDist := 0;
     1569        if (StartLoc2[p1] = StartLoc[p1]) or (TestDist > BestDist) then
     1570        begin
     1571          StartLoc2[p1] := Loc1;
     1572          BestDist := TestDist;
     1573          n := 1;
    12891574        end
    1290       else if RealMap[Loc1] and fTerrain=fDesert then
    1291         RealMap[Loc1]:=RealMap[Loc1] and not fTerrain or fPrairie
    1292       else if (RealMap[Loc1] and fTerrain in [fPrairie,fTundra,fSwamp])
    1293         and (random(2)=0) then
    1294         RealMap[Loc1]:=RealMap[Loc1] and not fTerrain or fForest;
    1295     end;
    1296 
    1297   // first irrigation
    1298   nIrrLoc:=0;
    1299   for V21:=1 to 26 do if V21<>CityOwnTile then
    1300     begin
    1301     Loc1:=Radius[V21];
    1302     if (Loc1>=0) and (Loc1<MapSize)
    1303       and (RealMap[Loc1] and (fTerrain or fSpecial)=fGrass or fSpecial1) then
    1304       begin
    1305       IrrLoc[nIrrLoc]:=Loc1;
    1306       inc(nIrrLoc);
    1307       end;
    1308     end;
    1309   i:=2;
    1310   if i>nIrrLoc then i:=nIrrLoc;
    1311   while i>0 do
    1312     begin
    1313     j:=random(nIrrLoc);
    1314     RealMap[IrrLoc[j]]:=RealMap[IrrLoc[j]] or tiIrrigation;
    1315     IrrLoc[j]:=IrrLoc[nIrrLoc-1];
    1316     dec(nIrrLoc);
    1317     dec(i)
    1318     end;
    1319   end;
    1320 
    1321 StartLoc[0]:=0;
    1322 for p1:=0 to nPl-1 do if 1 shl p1 and GAlive<>0 then
    1323   begin
    1324   repeat i:=Random(nAlive)+1 until StartLoc0[i]>=0;
    1325   StartLoc[p1]:=StartLoc0[i];
    1326   StartLoc0[i]:=-1
    1327   end;
    1328 SaveMapCenterLoc:=StartLoc[0];
    1329 
    1330 // second unit starting position
    1331 for p1:=0 to nPl-1 do if 1 shl p1 and GAlive<>0 then
    1332   begin
    1333   StartLoc2[p1]:=StartLoc[p1];
    1334   V8_to_Loc(StartLoc[p1],Adjacent);
    1335   for V8:=0 to 7 do
    1336     begin
    1337     Loc1:=Adjacent[V8];
    1338     for p2:=0 to nPl-1 do
    1339       if (1 shl p2 and GAlive<>0) and (StartLoc[p2]=Loc1) then Loc1:=-1;
    1340     for p2:=0 to p1-1 do
    1341       if (1 shl p2 and GAlive<>0) and (StartLoc2[p2]=Loc1) then Loc1:=-1;
    1342     if (Loc1<0) or (Loc1>=MapSize)
    1343       or (RealMap[Loc1] and fTerrain in [fOcean, fShore, fDesert, fArctic, fMountains])
    1344       or (RealMap[Loc1] and fDeadLands<>0) then
    1345       TestDist:=-1
    1346     else if RealMap[Loc1] and fTerrain=fGrass then TestDist:=2
    1347     else if Terrain[RealMap[Loc1] and fTerrain].IrrEff>0 then TestDist:=1
    1348     else TestDist:=0;
    1349     if (StartLoc2[p1]=StartLoc[p1]) or (TestDist>BestDist) then
    1350       begin StartLoc2[p1]:=Loc1; BestDist:=TestDist; n:=1; end
    1351     else if TestDist=BestDist then
    1352       begin inc(n); if random(n)=0 then StartLoc2[p1]:=Loc1; end;
    1353     end
    1354   end;
    1355 end; {StartPositions}
     1575        else if TestDist = BestDist then
     1576        begin
     1577          inc(n);
     1578          if Random(n) = 0 then
     1579            StartLoc2[p1] := Loc1;
     1580        end;
     1581      end
     1582    end;
     1583end; { StartPositions }
    13561584
    13571585procedure PredefinedStartPositions(Human: integer);
    13581586// use predefined nation start positions
    13591587var
    1360 i,p1,Loc1,nAlive,nStartLoc0,nPrefStartLoc0,imax: integer;
    1361 StartLoc0: array[0..lxmax*lymax-1] of integer;
    1362 ishuman: boolean;
    1363 begin
    1364 nAlive:=0;
    1365 for p1:=0 to nPl-1 do if 1 shl p1 and GAlive<>0 then inc(nAlive);
    1366 if nAlive=0 then exit;
    1367 
    1368 // calculate starting positions
    1369 nStartLoc0:=0;
    1370 nPrefStartLoc0:=0;
    1371 for Loc1:=0 to MapSize-1 do
    1372   if RealMap[Loc1] and fPrefStartPos<>0 then
    1373     begin
    1374     StartLoc0[nStartLoc0]:=StartLoc0[nPrefStartLoc0];
    1375     StartLoc0[nPrefStartLoc0]:=Loc1;
    1376     inc(nPrefStartLoc0);
    1377     inc(nStartLoc0);
    1378     RealMap[Loc1]:=RealMap[Loc1] and not fPrefStartPos;
     1588  i, p1, Loc1, nAlive, nStartLoc0, nPrefStartLoc0, imax: integer;
     1589  StartLoc0: array [0 .. lxmax * lymax - 1] of integer;
     1590  ishuman: boolean;
     1591begin
     1592  nAlive := 0;
     1593  for p1 := 0 to nPl - 1 do
     1594    if 1 shl p1 and GAlive <> 0 then
     1595      inc(nAlive);
     1596  if nAlive = 0 then
     1597    exit;
     1598
     1599  // calculate starting positions
     1600  nStartLoc0 := 0;
     1601  nPrefStartLoc0 := 0;
     1602  for Loc1 := 0 to MapSize - 1 do
     1603    if RealMap[Loc1] and fPrefStartPos <> 0 then
     1604    begin
     1605      StartLoc0[nStartLoc0] := StartLoc0[nPrefStartLoc0];
     1606      StartLoc0[nPrefStartLoc0] := Loc1;
     1607      inc(nPrefStartLoc0);
     1608      inc(nStartLoc0);
     1609      RealMap[Loc1] := RealMap[Loc1] and not fPrefStartPos;
    13791610    end
    1380   else if RealMap[Loc1] and fStartPos<>0 then
    1381     begin
    1382     StartLoc0[nStartLoc0]:=Loc1;
    1383     inc(nStartLoc0);
    1384     RealMap[Loc1]:=RealMap[Loc1] and not fStartPos;
    1385     end;
    1386 assert(nStartLoc0>=nAlive);
    1387 
    1388 StartLoc[0]:=0;
    1389 for ishuman:=true downto false do for p1:=0 to nPl-1 do
    1390   if (1 shl p1 and GAlive<>0) and ((1 shl p1 and Human<>0)=ishuman) then
    1391     begin
    1392     dec(nStartLoc0);
    1393     imax:=nStartLoc0;
    1394     if nPrefStartLoc0>0 then
    1395       begin
    1396       dec(nPrefStartLoc0);
    1397       imax:=nPrefStartLoc0;
    1398       end;
    1399     i:=Random(imax+1);
    1400     StartLoc[p1]:=StartLoc0[i];
    1401     StartLoc2[p1]:=StartLoc0[i];
    1402     StartLoc0[i]:=StartLoc0[imax];
    1403     StartLoc0[imax]:=StartLoc0[nStartLoc0];
    1404     end;
    1405 SaveMapCenterLoc:=StartLoc[0];
    1406 end; {PredefinedStartPositions}
     1611    else if RealMap[Loc1] and fStartPos <> 0 then
     1612    begin
     1613      StartLoc0[nStartLoc0] := Loc1;
     1614      inc(nStartLoc0);
     1615      RealMap[Loc1] := RealMap[Loc1] and not fStartPos;
     1616    end;
     1617  assert(nStartLoc0 >= nAlive);
     1618
     1619  StartLoc[0] := 0;
     1620  for ishuman := true downto false do
     1621    for p1 := 0 to nPl - 1 do
     1622      if (1 shl p1 and GAlive <> 0) and ((1 shl p1 and Human <> 0) = ishuman)
     1623      then
     1624      begin
     1625        dec(nStartLoc0);
     1626        imax := nStartLoc0;
     1627        if nPrefStartLoc0 > 0 then
     1628        begin
     1629          dec(nPrefStartLoc0);
     1630          imax := nPrefStartLoc0;
     1631        end;
     1632        i := Random(imax + 1);
     1633        StartLoc[p1] := StartLoc0[i];
     1634        StartLoc2[p1] := StartLoc0[i];
     1635        StartLoc0[i] := StartLoc0[imax];
     1636        StartLoc0[imax] := StartLoc0[nStartLoc0];
     1637      end;
     1638  SaveMapCenterLoc := StartLoc[0];
     1639end; { PredefinedStartPositions }
    14071640
    14081641procedure InitGame;
    14091642var
    1410 i, p, p1, uix, Loc1: integer;
    1411 begin
    1412 if FastContact then {Railroad everywhere}
    1413   for Loc1:=0 to MapSize-1 do
    1414     if RealMap[Loc1] and fTerrain>=fGrass then RealMap[Loc1]:=RealMap[Loc1] or fRR;
    1415 
    1416 {!!!for Loc1:=0 to MapSize-1 do
    1417   if RealMap[Loc1] and fterrain>=fGrass then
     1643  i, p, p1, uix, Loc1: integer;
     1644begin
     1645  if FastContact then { Railroad everywhere }
     1646    for Loc1 := 0 to MapSize - 1 do
     1647      if RealMap[Loc1] and fTerrain >= fGrass then
     1648        RealMap[Loc1] := RealMap[Loc1] or fRR;
     1649
     1650  { !!!for Loc1:=0 to MapSize-1 do
     1651    if RealMap[Loc1] and fterrain>=fGrass then
    14181652    if random(3)=0 then RealMap[Loc1]:=RealMap[Loc1] or fRoad
    14191653    else if random(3)=0 then RealMap[Loc1]:=RealMap[Loc1] or fRR;
    1420     {random Road and Railroad}
    1421 {!!!for Loc1:=0 to MapSize-1 do
    1422   if (RealMap[Loc1] and fterrain>=fGrass) and (random(20)=0) then
    1423     RealMap[Loc1]:=RealMap[Loc1] or fPoll;}
    1424 
    1425 FillChar(Occupant,MapSize,-1);
    1426 FillChar(ZoCMap,MapSize,0);
    1427 FillChar(ObserveLevel,MapSize*4,0);
    1428 FillChar(UsedByCity,MapSize*4,-1);
    1429 GTestFlags:=0;
    1430 GInitialized:=GAlive or GWatching;
    1431 for p:=0 to nPl-1 do if 1 shl p and GInitialized<>0 then with RW[p] do
    1432   begin
    1433   Researched[p]:=0;
    1434   Discovered[p]:=0;
    1435   TerritoryCount[p]:=0;
    1436   nTech[p]:=0;
    1437   if Difficulty[p]=0 then ResourceMask[p]:=$FFFFFFFF
    1438   else ResourceMask[p]:=$FFFFFFFF and not (fSpecial2 or fModern);
    1439   GrWallContinent[p]:=-1;
    1440 
    1441   GetMem(Map,4*MapSize);
    1442   GetMem(MapObservedLast,2*MapSize);
    1443   FillChar(MapObservedLast^,2*MapSize,-1);
    1444   GetMem(Territory,MapSize);
    1445   FillChar(Territory^,MapSize,$FF);
    1446   GetMem(Un,numax*SizeOf(TUn));
    1447   GetMem(Model,(nmmax+1)*SizeOf(TModel)); // draft needs one model behind last
    1448   GetMem(City,ncmax*SizeOf(TCity));
    1449   GetMem(EnemyUn,neumax*SizeOf(TUnitInfo));
    1450   GetMem(EnemyCity,necmax*SizeOf(TCityInfo));
    1451   GetMem(EnemyModel,nemmax*SizeOf(TModelInfo));
    1452   for p1:=0 to nPl-1 do
    1453     begin
    1454     if 1 shl p1 and GInitialized<>0 then
    1455       begin
    1456       FillChar(RWemix[p,p1],SizeOf(RWemix[p,p1]),255); {-1}
    1457       FillChar(Destroyed[p,p1],SizeOf(Destroyed[p,p1]),0);
    1458       end;
    1459     Attitude[p1]:=atNeutral;
    1460     Treaty[p1]:=trNoContact;
    1461     LastCancelTreaty[p1]:=-CancelTreatyTurns-1;
    1462     EvaStart[p1]:=-PeaceEvaTurns-1;
    1463     Tribute[p1]:=0;
    1464     TributePaid[p1]:=0;
    1465     if (p1<>p) and (1 shl p1 and GAlive<>0) then
    1466       begin // initialize enemy report
    1467       GetMem(EnemyReport[p1],SizeOf(TEnemyReport)-2*(INFIN+1-nmmax));
    1468       FillChar(EnemyReport[p1].Tech,nAdv,tsNA);
    1469       EnemyReport[p1].TurnOfContact:=-1;
    1470       EnemyReport[p1].TurnOfCivilReport:=-1;
    1471       EnemyReport[p1].TurnOfMilReport:=-1;
    1472       EnemyReport[p1].Attitude:=atNeutral;
    1473       EnemyReport[p1].Government:=gDespotism;
    1474       if 1 shl p and GAlive=0 then Treaty[p1]:=trNone // supervisor
    1475       end
    1476     else EnemyReport[p1]:=nil;
    1477     end;
    1478   TestFlags:=GTestFlags;
    1479   Credibility:=InitialCredibility;
    1480   MaxCredibility:=100;
    1481   nUn:=0;
    1482   nModel:=0;
    1483   nCity:=0;
    1484   nEnemyUn:=0;
    1485   nEnemyCity:=0;
    1486   nEnemyModel:=0;
    1487   for Loc1:=0 to MapSize-1 do Map[Loc1]:=fUNKNOWN;
    1488   FillChar(Tech,nAdv,tsNA);
    1489   FillChar(NatBuilt,SizeOf(NatBuilt),0);
    1490   end;
    1491 
    1492 // create initial models and units
    1493 for p:=0 to nPl-1 do if (1 shl p and GAlive<>0) then with RW[p] do
    1494   begin
    1495   nModel:=0;
    1496   for i:=0 to nSpecialModel-1 do if SpecialModelPreq[i]=preNone then
    1497     begin
    1498     Model[nModel]:=SpecialModel[i];
    1499     Model[nModel].Status:=0;
    1500     Model[nModel].IntroTurn:=0;
    1501     Model[nModel].Built:=0;
    1502     Model[nModel].Lost:=0;
    1503     Model[nModel].ID:=p shl 12+nModel;
    1504     SetModelFlags(Model[nModel]);
    1505     inc(nModel)
    1506     end;
    1507   nUn:=0;
    1508   UnBuilt[p]:=0;
    1509   for uix:=0 to nStartUn-1 do
    1510     begin
    1511     CreateUnit(p, StartUn[uix]);
    1512     dec(Model[StartUn[uix]].Built);
    1513     Un[uix].Loc:=StartLoc2[p];
    1514     PlaceUnit(p,uix);
    1515     end;
    1516   FoundCity(p,StartLoc[p]); // capital
    1517   Founded[p]:=1;
    1518   with City[0] do
    1519     begin
    1520     ID:=p shl 12;
    1521     Flags:=chFounded;
    1522     end;
    1523   end;
    1524 
    1525 TerritoryCount[nPl]:=MapSize;
    1526 //fillchar(NewContact, sizeof(NewContact), false);
     1654    {random Road and Railroad }
     1655  { !!!for Loc1:=0 to MapSize-1 do
     1656    if (RealMap[Loc1] and fterrain>=fGrass) and (random(20)=0) then
     1657    RealMap[Loc1]:=RealMap[Loc1] or fPoll; }
     1658
     1659  FillChar(Occupant, MapSize, -1);
     1660  FillChar(ZoCMap, MapSize, 0);
     1661  FillChar(ObserveLevel, MapSize * 4, 0);
     1662  FillChar(UsedByCity, MapSize * 4, -1);
     1663  GTestFlags := 0;
     1664  GInitialized := GAlive or GWatching;
     1665  for p := 0 to nPl - 1 do
     1666    if 1 shl p and GInitialized <> 0 then
     1667      with RW[p] do
     1668      begin
     1669        Researched[p] := 0;
     1670        Discovered[p] := 0;
     1671        TerritoryCount[p] := 0;
     1672        nTech[p] := 0;
     1673        if Difficulty[p] = 0 then
     1674          ResourceMask[p] := $FFFFFFFF
     1675        else
     1676          ResourceMask[p] := $FFFFFFFF and not(fSpecial2 or fModern);
     1677        GrWallContinent[p] := -1;
     1678
     1679        GetMem(Map, 4 * MapSize);
     1680        GetMem(MapObservedLast, 2 * MapSize);
     1681        FillChar(MapObservedLast^, 2 * MapSize, -1);
     1682        GetMem(Territory, MapSize);
     1683        FillChar(Territory^, MapSize, $FF);
     1684        GetMem(Un, numax * SizeOf(TUn));
     1685        GetMem(Model, (nmmax + 1) * SizeOf(TModel));
     1686        // draft needs one model behind last
     1687        GetMem(City, ncmax * SizeOf(TCity));
     1688        GetMem(EnemyUn, neumax * SizeOf(TUnitInfo));
     1689        GetMem(EnemyCity, necmax * SizeOf(TCityInfo));
     1690        GetMem(EnemyModel, nemmax * SizeOf(TModelInfo));
     1691        for p1 := 0 to nPl - 1 do
     1692        begin
     1693          if 1 shl p1 and GInitialized <> 0 then
     1694          begin
     1695            FillChar(RWemix[p, p1], SizeOf(RWemix[p, p1]), 255); { -1 }
     1696            FillChar(Destroyed[p, p1], SizeOf(Destroyed[p, p1]), 0);
     1697          end;
     1698          Attitude[p1] := atNeutral;
     1699          Treaty[p1] := trNoContact;
     1700          LastCancelTreaty[p1] := -CancelTreatyTurns - 1;
     1701          EvaStart[p1] := -PeaceEvaTurns - 1;
     1702          Tribute[p1] := 0;
     1703          TributePaid[p1] := 0;
     1704          if (p1 <> p) and (1 shl p1 and GAlive <> 0) then
     1705          begin // initialize enemy report
     1706            GetMem(EnemyReport[p1], SizeOf(TEnemyReport) - 2 *
     1707              (INFIN + 1 - nmmax));
     1708            FillChar(EnemyReport[p1].Tech, nAdv, tsNA);
     1709            EnemyReport[p1].TurnOfContact := -1;
     1710            EnemyReport[p1].TurnOfCivilReport := -1;
     1711            EnemyReport[p1].TurnOfMilReport := -1;
     1712            EnemyReport[p1].Attitude := atNeutral;
     1713            EnemyReport[p1].Government := gDespotism;
     1714            if 1 shl p and GAlive = 0 then
     1715              Treaty[p1] := trNone // supervisor
     1716          end
     1717          else
     1718            EnemyReport[p1] := nil;
     1719        end;
     1720        TestFlags := GTestFlags;
     1721        Credibility := InitialCredibility;
     1722        MaxCredibility := 100;
     1723        nUn := 0;
     1724        nModel := 0;
     1725        nCity := 0;
     1726        nEnemyUn := 0;
     1727        nEnemyCity := 0;
     1728        nEnemyModel := 0;
     1729        for Loc1 := 0 to MapSize - 1 do
     1730          Map[Loc1] := fUNKNOWN;
     1731        FillChar(Tech, nAdv, tsNA);
     1732        FillChar(NatBuilt, SizeOf(NatBuilt), 0);
     1733      end;
     1734
     1735  // create initial models and units
     1736  for p := 0 to nPl - 1 do
     1737    if (1 shl p and GAlive <> 0) then
     1738      with RW[p] do
     1739      begin
     1740        nModel := 0;
     1741        for i := 0 to nSpecialModel - 1 do
     1742          if SpecialModelPreq[i] = preNone then
     1743          begin
     1744            Model[nModel] := SpecialModel[i];
     1745            Model[nModel].Status := 0;
     1746            Model[nModel].IntroTurn := 0;
     1747            Model[nModel].Built := 0;
     1748            Model[nModel].Lost := 0;
     1749            Model[nModel].ID := p shl 12 + nModel;
     1750            SetModelFlags(Model[nModel]);
     1751            inc(nModel)
     1752          end;
     1753        nUn := 0;
     1754        UnBuilt[p] := 0;
     1755        for uix := 0 to nStartUn - 1 do
     1756        begin
     1757          CreateUnit(p, StartUn[uix]);
     1758          dec(Model[StartUn[uix]].Built);
     1759          Un[uix].Loc := StartLoc2[p];
     1760          PlaceUnit(p, uix);
     1761        end;
     1762        FoundCity(p, StartLoc[p]); // capital
     1763        Founded[p] := 1;
     1764        with City[0] do
     1765        begin
     1766          ID := p shl 12;
     1767          Flags := chFounded;
     1768        end;
     1769      end;
     1770
     1771  TerritoryCount[nPl] := MapSize;
     1772  // fillchar(NewContact, sizeof(NewContact), false);
    15271773end; // InitGame
    15281774
    15291775procedure InitRandomGame;
    15301776begin
    1531 RandSeed:=RND;
    1532 CalculatePrimitive;
    1533 CreateElevation;
    1534 CreateMap(false);
    1535 StartPositions;
    1536 InitGame;
    1537 end; {InitRandomGame}
     1777  RandSeed := RND;
     1778  CalculatePrimitive;
     1779  CreateElevation;
     1780  CreateMap(false);
     1781  StartPositions;
     1782  InitGame;
     1783end; { InitRandomGame }
    15381784
    15391785procedure InitMapGame(Human: integer);
    15401786begin
    1541 RandSeed:=RND;
    1542 FindContinents;
    1543 PredefinedStartPositions(Human);
    1544 InitGame;
    1545 end; {InitMapGame}
     1787  RandSeed := RND;
     1788  FindContinents;
     1789  PredefinedStartPositions(Human);
     1790  InitGame;
     1791end; { InitMapGame }
    15461792
    15471793procedure ReleaseGame;
    15481794var
    1549 p1,p2: integer;
    1550 begin
    1551 for p1:=0 to nPl-1 do if 1 shl p1 and GInitialized<>0 then
    1552   begin
    1553   for p2:=0 to nPl-1 do
    1554     if RW[p1].EnemyReport[p2]<>nil then
    1555       FreeMem(RW[p1].EnemyReport[p2]);
    1556   FreeMem(RW[p1].EnemyUn);
    1557   FreeMem(RW[p1].EnemyCity);
    1558   FreeMem(RW[p1].EnemyModel);
    1559   FreeMem(RW[p1].Un);
    1560   FreeMem(RW[p1].City);
    1561   FreeMem(RW[p1].Model);
    1562   FreeMem(RW[p1].Territory);
    1563   FreeMem(RW[p1].MapObservedLast);
    1564   FreeMem(RW[p1].Map);
    1565   end
     1795  p1, p2: integer;
     1796begin
     1797  for p1 := 0 to nPl - 1 do
     1798    if 1 shl p1 and GInitialized <> 0 then
     1799    begin
     1800      for p2 := 0 to nPl - 1 do
     1801        if RW[p1].EnemyReport[p2] <> nil then
     1802          FreeMem(RW[p1].EnemyReport[p2]);
     1803      FreeMem(RW[p1].EnemyUn);
     1804      FreeMem(RW[p1].EnemyCity);
     1805      FreeMem(RW[p1].EnemyModel);
     1806      FreeMem(RW[p1].Un);
     1807      FreeMem(RW[p1].City);
     1808      FreeMem(RW[p1].Model);
     1809      FreeMem(RW[p1].Territory);
     1810      FreeMem(RW[p1].MapObservedLast);
     1811      FreeMem(RW[p1].Map);
     1812    end
    15661813end;
    15671814
    15681815procedure InitMapEditor;
    15691816var
    1570 p1: integer;
    1571 begin
    1572 CalculatePrimitive;
    1573 FillChar(Occupant,MapSize,-1);
    1574 FillChar(ObserveLevel,MapSize*4,0);
    1575 with RW[0] do
    1576   begin
    1577   ResourceMask[0]:=$FFFFFFFF;
    1578   GetMem(Map,4*MapSize);
    1579   GetMem(MapObservedLast,2*MapSize);
    1580   FillChar(MapObservedLast^,2*MapSize,-1);
    1581   GetMem(Territory,MapSize);
    1582   FillChar(Territory^,MapSize,$FF);
    1583   Un:=nil;
    1584   Model:=nil;
    1585   City:=nil;
    1586   EnemyUn:=nil;
    1587   EnemyCity:=nil;
    1588   EnemyModel:=nil;
    1589   for p1:=0 to nPl-1 do EnemyReport[p1]:=nil;
    1590   nUn:=0;
    1591   nModel:=0;
    1592   nCity:=0;
    1593   nEnemyUn:=0;
    1594   nEnemyCity:=0;
    1595   nEnemyModel:=0;
     1817  p1: integer;
     1818begin
     1819  CalculatePrimitive;
     1820  FillChar(Occupant, MapSize, -1);
     1821  FillChar(ObserveLevel, MapSize * 4, 0);
     1822  with RW[0] do
     1823  begin
     1824    ResourceMask[0] := $FFFFFFFF;
     1825    GetMem(Map, 4 * MapSize);
     1826    GetMem(MapObservedLast, 2 * MapSize);
     1827    FillChar(MapObservedLast^, 2 * MapSize, -1);
     1828    GetMem(Territory, MapSize);
     1829    FillChar(Territory^, MapSize, $FF);
     1830    Un := nil;
     1831    Model := nil;
     1832    City := nil;
     1833    EnemyUn := nil;
     1834    EnemyCity := nil;
     1835    EnemyModel := nil;
     1836    for p1 := 0 to nPl - 1 do
     1837      EnemyReport[p1] := nil;
     1838    nUn := 0;
     1839    nModel := 0;
     1840    nCity := 0;
     1841    nEnemyUn := 0;
     1842    nEnemyCity := 0;
     1843    nEnemyModel := 0;
    15961844  end;
    15971845end;
     
    15991847procedure ReleaseMapEditor;
    16001848begin
    1601 FreeMem(RW[0].Territory);
    1602 FreeMem(RW[0].MapObservedLast);
    1603 FreeMem(RW[0].Map);
     1849  FreeMem(RW[0].Territory);
     1850  FreeMem(RW[0].MapObservedLast);
     1851  FreeMem(RW[0].Map);
    16041852end;
    16051853
    16061854procedure EditTile(Loc, NewTile: integer);
    16071855var
    1608 Loc1,V21: integer;
    1609 Radius: TVicinity21Loc;
    1610 begin
    1611 if NewTile and fDeadLands<>0 then
    1612   NewTile:=NewTile and (fDeadLands or fModern or fRiver) or fDesert;
    1613 case NewTile and fTerrain of
    1614   fOcean, fShore: NewTile:=NewTile and (fTerrain or fSpecial);
    1615   fMountains,fArctic: NewTile:=NewTile and not fRiver;
    1616   end;
    1617 with Terrain[NewTile and fTerrain] do
    1618   if (ClearTerrain>=0) or (AfforestTerrain>=0) or (TransTerrain>=0) then
    1619     NewTile:=NewTile or fSpecial; // only automatic special resources for transformable tiles
    1620 if NewTile and fRR<>0 then NewTile:=NewTile and not fRoad;
    1621 if not ((NewTile and fTerrain) in TerrType_Canalable) then
    1622   NewTile:=NewTile and not fCanal;
    1623 if Terrain[NewTile and fTerrain].IrrEff=0 then
    1624   begin
    1625   NewTile:=NewTile and not (fPrefStartPos or fStartPos);
    1626   if (NewTile and fTerImp=tiIrrigation) or (NewTile and fTerImp=tiFarm) then
    1627     NewTile:=NewTile and not fTerImp
    1628   end;
    1629 if (Terrain[NewTile and fTerrain].MineEff=0)
    1630   and (NewTile and fTerImp=tiMine) then
    1631   NewTile:=NewTile and not fTerImp;
    1632 
    1633 RealMap[Loc]:=NewTile;
    1634 if NewTile and fSpecial=fSpecial then // standard special resource distribution
    1635   RealMap[Loc]:=RealMap[Loc] and not fSpecial or ActualSpecialTile(Loc) shl 5;
    1636 
    1637 // automatic shore tiles
    1638 V21_to_Loc(Loc,Radius);
    1639 for V21:=1 to 26 do
    1640   begin
    1641   Loc1:=Radius[V21];
    1642   if (Loc1>=0) and (Loc1<MapSize) then
    1643     begin
    1644     if CheckShore(Loc1) then
    1645       RealMap[Loc1]:=RealMap[Loc1] and not fSpecial or ActualSpecialTile(Loc1) shl 5;
    1646     RealMap[Loc1]:=RealMap[Loc1] or ($F shl 27);
    1647     RW[0].Map[Loc1]:=RealMap[Loc1] and $07FFFFFF or fObserved;
     1856  Loc1, V21: integer;
     1857  Radius: TVicinity21Loc;
     1858begin
     1859  if NewTile and fDeadLands <> 0 then
     1860    NewTile := NewTile and (fDeadLands or fModern or fRiver) or fDesert;
     1861  case NewTile and fTerrain of
     1862    fOcean, fShore:
     1863      NewTile := NewTile and (fTerrain or fSpecial);
     1864    fMountains, fArctic:
     1865      NewTile := NewTile and not fRiver;
     1866  end;
     1867  with Terrain[NewTile and fTerrain] do
     1868    if (ClearTerrain >= 0) or (AfforestTerrain >= 0) or (TransTerrain >= 0) then
     1869      NewTile := NewTile or fSpecial;
     1870  // only automatic special resources for transformable tiles
     1871  if NewTile and fRR <> 0 then
     1872    NewTile := NewTile and not fRoad;
     1873  if not((NewTile and fTerrain) in TerrType_Canalable) then
     1874    NewTile := NewTile and not fCanal;
     1875  if Terrain[NewTile and fTerrain].IrrEff = 0 then
     1876  begin
     1877    NewTile := NewTile and not(fPrefStartPos or fStartPos);
     1878    if (NewTile and fTerImp = tiIrrigation) or (NewTile and fTerImp = tiFarm)
     1879    then
     1880      NewTile := NewTile and not fTerImp
     1881  end;
     1882  if (Terrain[NewTile and fTerrain].MineEff = 0) and
     1883    (NewTile and fTerImp = tiMine) then
     1884    NewTile := NewTile and not fTerImp;
     1885
     1886  RealMap[Loc] := NewTile;
     1887  if NewTile and fSpecial = fSpecial then
     1888  // standard special resource distribution
     1889    RealMap[Loc] := RealMap[Loc] and not fSpecial or
     1890      ActualSpecialTile(Loc) shl 5;
     1891
     1892  // automatic shore tiles
     1893  V21_to_Loc(Loc, Radius);
     1894  for V21 := 1 to 26 do
     1895  begin
     1896    Loc1 := Radius[V21];
     1897    if (Loc1 >= 0) and (Loc1 < MapSize) then
     1898    begin
     1899      if CheckShore(Loc1) then
     1900        RealMap[Loc1] := RealMap[Loc1] and not fSpecial or
     1901          ActualSpecialTile(Loc1) shl 5;
     1902      RealMap[Loc1] := RealMap[Loc1] or ($F shl 27);
     1903      RW[0].Map[Loc1] := RealMap[Loc1] and $07FFFFFF or fObserved;
    16481904    end
    16491905  end;
    1650 //RealMap[Loc]:=RealMap[Loc] and not fSpecial;
    1651 //RW[0].Map[Loc]:=RealMap[Loc] or fObserved;
     1906  // RealMap[Loc]:=RealMap[Loc] and not fSpecial;
     1907  // RW[0].Map[Loc]:=RealMap[Loc] or fObserved;
    16521908end;
    16531909
    16541910{
    1655                             Map Revealing
    1656  ____________________________________________________________________
     1911  Map Revealing
     1912  ____________________________________________________________________
    16571913}
    16581914function GetTileInfo(p, cix, Loc: integer; var Info: TTileInfo): integer;
     
    16611917// cix=-2 - don't search city, don't calculate city benefits, just government of player p
    16621918var
    1663 p0,Tile,special: integer;
    1664 begin
    1665 with Info do
    1666   begin
    1667   p0:=p;
    1668   if cix>=0 then Tile:=RealMap[Loc]
    1669   else
    1670     begin
    1671     Tile:=RW[p].Map[Loc];
    1672     if Tile and fTerrain=fUNKNOWN then begin result:=eNoPreq; exit end;
    1673     end;
    1674 
    1675   if (cix=-1) and (UsedByCity[Loc]>=0) then
     1919  p0, Tile, special: integer;
     1920begin
     1921  with Info do
     1922  begin
     1923    p0 := p;
     1924    if cix >= 0 then
     1925      Tile := RealMap[Loc]
     1926    else
     1927    begin
     1928      Tile := RW[p].Map[Loc];
     1929      if Tile and fTerrain = fUNKNOWN then
     1930      begin
     1931        result := eNoPreq;
     1932        exit
     1933      end;
     1934    end;
     1935
     1936    if (cix = -1) and (UsedByCity[Loc] >= 0) then
    16761937    begin // search exploiting player and city
    1677     SearchCity(UsedByCity[Loc],p,cix);
    1678     if not ((p=p0) or (ObserveLevel[UsedByCity[Loc]] shr (2*p0) and 3=lObserveSuper)) then
    1679       cix:=-1
    1680     end;
    1681   if cix=-1 then begin result:=eInvalid; exit end; // no city found here
    1682 
    1683   special:=Tile and fSpecial and ResourceMask[p] shr 5;
    1684   with Terrain[Tile and fTerrain] do
    1685     begin
    1686     Food:=FoodRes[special];
    1687     Prod:=ProdRes[special];
    1688     Trade:=TradeRes[special];
    1689     if (special>0) and (Tile and fTerrain<>fGrass)
    1690       and (RW[p].NatBuilt[imSpacePort]>0) then
     1938      SearchCity(UsedByCity[Loc], p, cix);
     1939      if not((p = p0) or (ObserveLevel[UsedByCity[Loc]] shr (2 * p0) and
     1940        3 = lObserveSuper)) then
     1941        cix := -1
     1942    end;
     1943    if cix = -1 then
     1944    begin
     1945      result := eInvalid;
     1946      exit
     1947    end; // no city found here
     1948
     1949    special := Tile and fSpecial and ResourceMask[p] shr 5;
     1950    with Terrain[Tile and fTerrain] do
     1951    begin
     1952      Food := FoodRes[special];
     1953      Prod := ProdRes[special];
     1954      Trade := TradeRes[special];
     1955      if (special > 0) and (Tile and fTerrain <> fGrass) and
     1956        (RW[p].NatBuilt[imSpacePort] > 0) then
    16911957      begin // GeoSat effect
    1692       Food:=2*Food-FoodRes[0];
    1693       Prod:=2*Prod-ProdRes[0];
    1694       Trade:=2*Trade-TradeRes[0];
    1695       end;
    1696 
    1697     if (Tile and fTerImp=tiIrrigation) or (Tile and fTerImp=tiFarm)
    1698       or (Tile and fCity<>0) then
    1699       inc(Food,IrrEff); {irrigation effect}
    1700     if Tile and fTerImp=tiMine then inc(Prod,MineEff); {mining effect}
    1701     if (Tile and fRiver<>0) and (RW[p].Tech[adMapMaking]>=tsApplicable) then
    1702       inc(Trade); {river effect}
    1703     if (Tile and (fRoad or fRR)<>0) and (MoveCost=1)
    1704       and (RW[p].Tech[adWheel]>=tsApplicable) then
    1705       inc(Trade); {road effect}
    1706     if (Tile and (fRR or fCity)<>0) and (RW[p].Tech[adRailroad]>=tsApplicable) then
    1707       inc(Prod,Prod shr 1); {railroad effect}
    1708 
    1709     ExplCity:=-1;
    1710     if (cix>=0) and (p=p0) then ExplCity:=cix;
    1711     if cix>=0 then
    1712       if Tile and fTerrain>=fGrass then
    1713         begin
    1714         if ((Tile and fTerImp=tiFarm) or (Tile and fCity<>0))
    1715           and (RW[p].City[cix].Built[imSupermarket]>0) then
    1716           inc(Food,Food shr 1); {farmland effect}
    1717         if (Tile and (fRoad or fRR)<>0) and (MoveCost=1)
    1718           and (RW[p].City[cix].Built[imHighways]>0) then
    1719           inc(Trade,1); {superhighway effect}
     1958        Food := 2 * Food - FoodRes[0];
     1959        Prod := 2 * Prod - ProdRes[0];
     1960        Trade := 2 * Trade - TradeRes[0];
     1961      end;
     1962
     1963      if (Tile and fTerImp = tiIrrigation) or (Tile and fTerImp = tiFarm) or
     1964        (Tile and fCity <> 0) then
     1965        inc(Food, IrrEff); { irrigation effect }
     1966      if Tile and fTerImp = tiMine then
     1967        inc(Prod, MineEff); { mining effect }
     1968      if (Tile and fRiver <> 0) and (RW[p].Tech[adMapMaking] >= tsApplicable)
     1969      then
     1970        inc(Trade); { river effect }
     1971      if (Tile and (fRoad or fRR) <> 0) and (MoveCost = 1) and
     1972        (RW[p].Tech[adWheel] >= tsApplicable) then
     1973        inc(Trade); { road effect }
     1974      if (Tile and (fRR or fCity) <> 0) and
     1975        (RW[p].Tech[adRailroad] >= tsApplicable) then
     1976        inc(Prod, Prod shr 1); { railroad effect }
     1977
     1978      ExplCity := -1;
     1979      if (cix >= 0) and (p = p0) then
     1980        ExplCity := cix;
     1981      if cix >= 0 then
     1982        if Tile and fTerrain >= fGrass then
     1983        begin
     1984          if ((Tile and fTerImp = tiFarm) or (Tile and fCity <> 0)) and
     1985            (RW[p].City[cix].Built[imSupermarket] > 0) then
     1986            inc(Food, Food shr 1); { farmland effect }
     1987          if (Tile and (fRoad or fRR) <> 0) and (MoveCost = 1) and
     1988            (RW[p].City[cix].Built[imHighways] > 0) then
     1989            inc(Trade, 1); { superhighway effect }
    17201990        end
    1721       else
    1722         begin
    1723         if RW[p].City[cix].Built[imHarbor]>0 then inc(Food); {harbour effect}
    1724         if RW[p].City[cix].Built[imPlatform]>0 then inc(Prod); {oil platform effect}
    1725         if GWonder[woLighthouse].EffectiveOwner=p then inc(Prod);
     1991        else
     1992        begin
     1993          if RW[p].City[cix].Built[imHarbor] > 0 then
     1994            inc(Food); { harbour effect }
     1995          if RW[p].City[cix].Built[imPlatform] > 0 then
     1996            inc(Prod); { oil platform effect }
     1997          if GWonder[woLighthouse].EffectiveOwner = p then
     1998            inc(Prod);
    17261999        end;
    17272000    end;
    17282001
    1729   {good government influence}
    1730   if (RW[p].Government in [gRepublic,gDemocracy,gFuture]) and (Trade>0) then
    1731     inc(Trade);
    1732   if (RW[p].Government=gCommunism) and (Prod>1) then
    1733     inc(Prod);
    1734 
    1735   if RW[p].Government in [gAnarchy,gDespotism] then
    1736     begin {bad government influence}
    1737     if Food>3 then Food:=3;
    1738     if Prod>2 then Prod:=2;
    1739     if Trade>2 then Trade:=2;
    1740     end;
    1741 
    1742   if Tile and (fTerrain or fPoll)>fPoll then
    1743     begin {pollution - decrease ressources}
    1744     dec(Food,Food shr 1);
    1745     dec(Prod,Prod shr 1);
    1746     dec(Trade,Trade shr 1);
    1747     end;
    1748 
    1749   if Tile and fCity<>0 then Trade:=0
    1750   else if (cix>=0)
    1751     and (RW[p].City[cix].Built[imCourt]+RW[p].City[cix].Built[imPalace]=0) then
    1752     if RW[p].City[cix].Built[imTownHall]=0 then Trade:=0
    1753     else if Trade>3 then Trade:=3;
    1754   end;
    1755 result:=eOK;
    1756 end; {GetTileInfo}
    1757 
    1758 procedure Strongest(Loc:integer;var uix,Strength,Bonus,Cnt:integer);
    1759 {find strongest defender at Loc}
    1760 var
    1761 Defender,uix1,Det,Cost,TestStrength,TestBonus,TestDet,TestCost,Domain: integer;
    1762 PUn: ^TUn;
    1763 PModel: ^TModel;
    1764 begin
    1765 Defender:=Occupant[Loc];
    1766 Cnt:=0;
    1767 Det:=-1;
    1768 for uix1:=0 to RW[Defender].nUn-1 do
    1769   begin
    1770   PUn:=@RW[Defender].Un[uix1];
    1771   PModel:=@RW[Defender].Model[PUn.mix];
    1772   if PModel.Kind=mkSpecial_Glider then Domain:=dGround
    1773   else Domain:=PModel.Domain;
    1774   if PUn.Loc=Loc then
    1775     begin
    1776     inc(Cnt);
    1777     if PUn.Master<0 then
    1778       begin
    1779       if Domain<dSea then
    1780         begin
    1781         TestBonus:=Terrain[RealMap[Loc] and fTerrain].Defense;
    1782         if RealMap[Loc] and fTerImp=tiFort then inc(TestBonus,4);
    1783         if PUn.Flags and unFortified<>0 then inc(TestBonus,2);
    1784         if (PModel.Kind=mkSpecial_TownGuard) and (RealMap[Loc] and fCity<>0) then
    1785           inc(TestBonus,4);
     2002    { good government influence }
     2003    if (RW[p].Government in [gRepublic, gDemocracy, gFuture]) and (Trade > 0)
     2004    then
     2005      inc(Trade);
     2006    if (RW[p].Government = gCommunism) and (Prod > 1) then
     2007      inc(Prod);
     2008
     2009    if RW[p].Government in [gAnarchy, gDespotism] then
     2010    begin { bad government influence }
     2011      if Food > 3 then
     2012        Food := 3;
     2013      if Prod > 2 then
     2014        Prod := 2;
     2015      if Trade > 2 then
     2016        Trade := 2;
     2017    end;
     2018
     2019    if Tile and (fTerrain or fPoll) > fPoll then
     2020    begin { pollution - decrease ressources }
     2021      dec(Food, Food shr 1);
     2022      dec(Prod, Prod shr 1);
     2023      dec(Trade, Trade shr 1);
     2024    end;
     2025
     2026    if Tile and fCity <> 0 then
     2027      Trade := 0
     2028    else if (cix >= 0) and (RW[p].City[cix].Built[imCourt] + RW[p].City[cix]
     2029      .Built[imPalace] = 0) then
     2030      if RW[p].City[cix].Built[imTownHall] = 0 then
     2031        Trade := 0
     2032      else if Trade > 3 then
     2033        Trade := 3;
     2034  end;
     2035  result := eOK;
     2036end; { GetTileInfo }
     2037
     2038procedure Strongest(Loc: integer; var uix, Strength, Bonus, Cnt: integer);
     2039{ find strongest defender at Loc }
     2040var
     2041  Defender, uix1, Det, Cost, TestStrength, TestBonus, TestDet, TestCost,
     2042    Domain: integer;
     2043  PUn: ^TUn;
     2044  PModel: ^TModel;
     2045begin
     2046  Defender := Occupant[Loc];
     2047  Cnt := 0;
     2048  Det := -1;
     2049  for uix1 := 0 to RW[Defender].nUn - 1 do
     2050  begin
     2051    PUn := @RW[Defender].Un[uix1];
     2052    PModel := @RW[Defender].Model[PUn.mix];
     2053    if PModel.Kind = mkSpecial_Glider then
     2054      Domain := dGround
     2055    else
     2056      Domain := PModel.Domain;
     2057    if PUn.Loc = Loc then
     2058    begin
     2059      inc(Cnt);
     2060      if PUn.Master < 0 then
     2061      begin
     2062        if Domain < dSea then
     2063        begin
     2064          TestBonus := Terrain[RealMap[Loc] and fTerrain].Defense;
     2065          if RealMap[Loc] and fTerImp = tiFort then
     2066            inc(TestBonus, 4);
     2067          if PUn.Flags and unFortified <> 0 then
     2068            inc(TestBonus, 2);
     2069          if (PModel.Kind = mkSpecial_TownGuard) and
     2070            (RealMap[Loc] and fCity <> 0) then
     2071            inc(TestBonus, 4);
    17862072        end
    1787       else TestBonus:=4;
    1788       inc(TestBonus,PUn.Exp div ExpCost);
    1789       TestStrength:=PModel.Defense*TestBonus*PUn.Health;
    1790       if (Domain=dAir) and ((RealMap[Loc] and fCity<>0)
    1791         or (RealMap[Loc] and fTerImp=tiBase)) then
    1792         TestStrength:=0;
    1793       if (Domain=dSea) and (RealMap[Loc] and fTerrain>=fGrass) then
    1794         TestStrength:=TestStrength shr 1;
    1795       TestDet:=TestStrength;
    1796       if PModel.Cap[mcStealth]>0 then
    1797       else if PModel.Cap[mcSub]>0 then inc(TestDet,1 shl 28)
    1798       else if (Domain=dGround) and (PModel.Cap[mcFanatic]>0)
    1799         and not (RW[Defender].Government in [gRepublic,gDemocracy,gFuture]) then
    1800         inc(TestDet,4 shl 28) // fanatic ground units always defend
    1801       else if PModel.Flags and mdZOC<>0 then
    1802         inc(TestDet,3 shl 28)
    1803       else inc(TestDet,2 shl 28);
    1804       TestCost:=RW[Defender].Model[PUn.mix].Cost;
    1805       if (TestDet>Det) or (TestDet=Det) and (TestCost<Cost) then
    1806         begin
    1807         uix:=uix1;
    1808         Strength:=TestStrength;
    1809         Bonus:=TestBonus;
    1810         Det:=TestDet;
    1811         Cost:=TestCost;
     2073        else
     2074          TestBonus := 4;
     2075        inc(TestBonus, PUn.exp div ExpCost);
     2076        TestStrength := PModel.Defense * TestBonus * PUn.Health;
     2077        if (Domain = dAir) and ((RealMap[Loc] and fCity <> 0) or
     2078          (RealMap[Loc] and fTerImp = tiBase)) then
     2079          TestStrength := 0;
     2080        if (Domain = dSea) and (RealMap[Loc] and fTerrain >= fGrass) then
     2081          TestStrength := TestStrength shr 1;
     2082        TestDet := TestStrength;
     2083        if PModel.Cap[mcStealth] > 0 then
     2084        else if PModel.Cap[mcSub] > 0 then
     2085          inc(TestDet, 1 shl 28)
     2086        else if (Domain = dGround) and (PModel.Cap[mcFanatic] > 0) and
     2087          not(RW[Defender].Government in [gRepublic, gDemocracy, gFuture]) then
     2088          inc(TestDet, 4 shl 28) // fanatic ground units always defend
     2089        else if PModel.Flags and mdZOC <> 0 then
     2090          inc(TestDet, 3 shl 28)
     2091        else
     2092          inc(TestDet, 2 shl 28);
     2093        TestCost := RW[Defender].Model[PUn.mix].Cost;
     2094        if (TestDet > Det) or (TestDet = Det) and (TestCost < Cost) then
     2095        begin
     2096          uix := uix1;
     2097          Strength := TestStrength;
     2098          Bonus := TestBonus;
     2099          Det := TestDet;
     2100          Cost := TestCost;
    18122101        end
    18132102      end
     
    18182107function UnitSpeed(p, mix, Health: integer): integer;
    18192108begin
    1820 with RW[p].Model[mix] do
    1821   begin
    1822   result:=Speed;
    1823   if Domain=dSea then
    1824     begin
    1825     if GWonder[woMagellan].EffectiveOwner=p then inc(result,200);
    1826     if Health<100 then
    1827       result:=((result-250)*Health div 5000)*50+250;
     2109  with RW[p].Model[mix] do
     2110  begin
     2111    result := Speed;
     2112    if Domain = dSea then
     2113    begin
     2114      if GWonder[woMagellan].EffectiveOwner = p then
     2115        inc(result, 200);
     2116      if Health < 100 then
     2117        result := ((result - 250) * Health div 5000) * 50 + 250;
    18282118    end
    18292119  end
    18302120end;
    18312121
    1832 procedure GetUnitReport(p,uix: integer; var UnitReport: TUnitReport);
    1833 var
    1834 TerrOwner: integer;
    1835 PModel: ^TModel;
    1836 begin
    1837 UnitReport.FoodSupport:=0;
    1838 UnitReport.ProdSupport:=0;
    1839 UnitReport.ReportFlags:=0;
    1840 if RW[p].Government<>gAnarchy then with RW[p].Un[uix] do
    1841   begin
    1842   PModel:=@RW[p].Model[mix];
    1843   if (PModel.Kind=mkSettler) {and (GWonder[woFreeSettlers].EffectiveOwner<>p)} then
    1844     UnitReport.FoodSupport:=SettlerFood[RW[p].Government]
    1845   else if Flags and unConscripts<>0 then UnitReport.FoodSupport:=1;
    1846 
    1847   if RW[p].Government<>gFundamentalism then
    1848     begin
    1849     if GTestFlags and tfImmImprove=0 then
    1850       begin
    1851       if PModel.Flags and mdDoubleSupport<>0 then
    1852         UnitReport.ProdSupport:=2
    1853       else UnitReport.ProdSupport:=1;
    1854       if PModel.Kind=mkSpecial_TownGuard then
    1855         UnitReport.ReportFlags:=UnitReport.ReportFlags or urfAlwaysSupport;
    1856       end;
    1857     if PModel.Flags and mdCivil=0 then
    1858       begin
    1859       TerrOwner:=RealMap[Loc] shr 27;
    1860       case RW[p].Government of
    1861         gRepublic, gFuture:
    1862           if (TerrOwner<>p) and (TerrOwner<nPl)
    1863             and (RW[p].Treaty[TerrOwner]<trAlliance) then
    1864             UnitReport.ReportFlags:=UnitReport.ReportFlags or urfDeployed;
    1865         gDemocracy:
    1866           if (TerrOwner>=nPl) or (TerrOwner<>p)
    1867             and (RW[p].Treaty[TerrOwner]<trAlliance) then
    1868             UnitReport.ReportFlags:=UnitReport.ReportFlags or urfDeployed;
     2122procedure GetUnitReport(p, uix: integer; var UnitReport: TUnitReport);
     2123var
     2124  TerrOwner: integer;
     2125  PModel: ^TModel;
     2126begin
     2127  UnitReport.FoodSupport := 0;
     2128  UnitReport.ProdSupport := 0;
     2129  UnitReport.ReportFlags := 0;
     2130  if RW[p].Government <> gAnarchy then
     2131    with RW[p].Un[uix] do
     2132    begin
     2133      PModel := @RW[p].Model[mix];
     2134      if (PModel.Kind = mkSettler)
     2135      { and (GWonder[woFreeSettlers].EffectiveOwner<>p) } then
     2136        UnitReport.FoodSupport := SettlerFood[RW[p].Government]
     2137      else if Flags and unConscripts <> 0 then
     2138        UnitReport.FoodSupport := 1;
     2139
     2140      if RW[p].Government <> gFundamentalism then
     2141      begin
     2142        if GTestFlags and tfImmImprove = 0 then
     2143        begin
     2144          if PModel.Flags and mdDoubleSupport <> 0 then
     2145            UnitReport.ProdSupport := 2
     2146          else
     2147            UnitReport.ProdSupport := 1;
     2148          if PModel.Kind = mkSpecial_TownGuard then
     2149            UnitReport.ReportFlags := UnitReport.ReportFlags or
     2150              urfAlwaysSupport;
    18692151        end;
    1870       end
    1871     end;
    1872   end;
    1873 end;
    1874 
    1875 procedure SearchCity(Loc: integer; var p,cix: integer);
     2152        if PModel.Flags and mdCivil = 0 then
     2153        begin
     2154          TerrOwner := RealMap[Loc] shr 27;
     2155          case RW[p].Government of
     2156            gRepublic, gFuture:
     2157              if (TerrOwner <> p) and (TerrOwner < nPl) and
     2158                (RW[p].Treaty[TerrOwner] < trAlliance) then
     2159                UnitReport.ReportFlags := UnitReport.ReportFlags or urfDeployed;
     2160            gDemocracy:
     2161              if (TerrOwner >= nPl) or (TerrOwner <> p) and
     2162                (RW[p].Treaty[TerrOwner] < trAlliance) then
     2163                UnitReport.ReportFlags := UnitReport.ReportFlags or urfDeployed;
     2164          end;
     2165        end
     2166      end;
     2167    end;
     2168end;
     2169
     2170procedure SearchCity(Loc: integer; var p, cix: integer);
    18762171// set p to supposed owner before call
    18772172var
    1878 i: integer;
    1879 begin
    1880 if RealMap[Loc]<nPl shl 27 then p:=RealMap[Loc] shr 27;
    1881 for i:=0 to nPl-1 do
    1882   begin
    1883   if 1 shl p and GAlive<>0 then with RW[p] do
    1884     begin
    1885     cix:=nCity-1;
    1886     while (cix>=0) and (City[cix].Loc<>Loc) do dec(cix);
    1887     if cix>=0 then exit;
    1888     end;
    1889   assert(i<nPl-1);
    1890   p:=(p+1) mod nPl;
     2173  i: integer;
     2174begin
     2175  if RealMap[Loc] < nPl shl 27 then
     2176    p := RealMap[Loc] shr 27;
     2177  for i := 0 to nPl - 1 do
     2178  begin
     2179    if 1 shl p and GAlive <> 0 then
     2180      with RW[p] do
     2181      begin
     2182        cix := nCity - 1;
     2183        while (cix >= 0) and (City[cix].Loc <> Loc) do
     2184          dec(cix);
     2185        if cix >= 0 then
     2186          exit;
     2187      end;
     2188    assert(i < nPl - 1);
     2189    p := (p + 1) mod nPl;
    18912190  end;
    18922191end;
     
    18942193procedure MakeCityInfo(p, cix: integer; var ci: TCityInfo);
    18952194begin
    1896 assert((p>=0) and (p<nPl));
    1897 assert((cix>=0) and (cix<RW[p].nCity));
    1898 with RW[p].City[cix] do
    1899   begin
    1900   ci.Loc:=Loc;
    1901   ci.ID:=ID;
    1902   ci.Owner:=p;
    1903   ci.Size:=Size;
    1904   ci.Flags:=0;
    1905   if Built[imPalace]>0 then inc(ci.Flags,ciCapital);
    1906   if (Built[imWalls]>0) or (Continent[Loc]=GrWallContinent[p]) then
    1907     inc(ci.Flags,ciWalled);
    1908   if Built[imCoastalFort]>0 then inc(ci.Flags,ciCoastalFort);
    1909   if Built[imMissileBat]>0 then inc(ci.Flags,ciMissileBat);
    1910   if Built[imBunker]>0 then inc(ci.Flags,ciBunker);
    1911   if Built[imSpacePort]>0 then inc(ci.Flags,ciSpacePort);
    1912   end;
    1913 end;
    1914 
    1915 procedure TellAboutModel(p,taOwner,tamix: integer);
    1916 var
    1917 i: integer;
    1918 begin
    1919 if (p=taOwner) or (Mode<moPlaying) then exit;
    1920 i:=0;
    1921 while (i<RW[p].nEnemyModel)
    1922   and ((RW[p].EnemyModel[i].Owner<>taOwner)
    1923   or (RW[p].EnemyModel[i].mix<>tamix)) do inc(i);
    1924 if i=RW[p].nEnemyModel then
    1925   IntServer(sIntTellAboutModel+p shl 4,taOwner,tamix,nil^);
    1926 end;
    1927 
    1928 function emixSafe(p,taOwner,tamix: integer): integer;
    1929 begin
    1930 result:=RWemix[p,taOwner,tamix];
    1931 if result<0 then
     2195  assert((p >= 0) and (p < nPl));
     2196  assert((cix >= 0) and (cix < RW[p].nCity));
     2197  with RW[p].City[cix] do
     2198  begin
     2199    ci.Loc := Loc;
     2200    ci.ID := ID;
     2201    ci.Owner := p;
     2202    ci.Size := Size;
     2203    ci.Flags := 0;
     2204    if Built[imPalace] > 0 then
     2205      inc(ci.Flags, ciCapital);
     2206    if (Built[imWalls] > 0) or (Continent[Loc] = GrWallContinent[p]) then
     2207      inc(ci.Flags, ciWalled);
     2208    if Built[imCoastalFort] > 0 then
     2209      inc(ci.Flags, ciCoastalFort);
     2210    if Built[imMissileBat] > 0 then
     2211      inc(ci.Flags, ciMissileBat);
     2212    if Built[imBunker] > 0 then
     2213      inc(ci.Flags, ciBunker);
     2214    if Built[imSpacePort] > 0 then
     2215      inc(ci.Flags, ciSpacePort);
     2216  end;
     2217end;
     2218
     2219procedure TellAboutModel(p, taOwner, tamix: integer);
     2220var
     2221  i: integer;
     2222begin
     2223  if (p = taOwner) or (Mode < moPlaying) then
     2224    exit;
     2225  i := 0;
     2226  while (i < RW[p].nEnemyModel) and ((RW[p].EnemyModel[i].Owner <> taOwner) or
     2227    (RW[p].EnemyModel[i].mix <> tamix)) do
     2228    inc(i);
     2229  if i = RW[p].nEnemyModel then
     2230    IntServer(sIntTellAboutModel + p shl 4, taOwner, tamix, nil^);
     2231end;
     2232
     2233function emixSafe(p, taOwner, tamix: integer): integer;
     2234begin
     2235  result := RWemix[p, taOwner, tamix];
     2236  if result < 0 then
    19322237  begin // sIntTellAboutModel comes too late
    1933   assert(Mode=moMovie);
    1934   result:=$FFFF;
    1935   end;
    1936 end;
    1937 
    1938 procedure IntroduceEnemy(p1,p2: integer);
    1939 begin
    1940 RW[p1].Treaty[p2]:=trNone;
    1941 RW[p2].Treaty[p1]:=trNone;
    1942 end;
    1943 
    1944 function DiscoverTile(Loc, p, pTell, Level: integer;
    1945   EnableContact: boolean; euix: integer = -2): boolean;
     2238    assert(Mode = moMovie);
     2239    result := $FFFF;
     2240  end;
     2241end;
     2242
     2243procedure IntroduceEnemy(p1, p2: integer);
     2244begin
     2245  RW[p1].Treaty[p2] := trNone;
     2246  RW[p2].Treaty[p1] := trNone;
     2247end;
     2248
     2249function DiscoverTile(Loc, p, pTell, Level: integer; EnableContact: boolean;
     2250  euix: integer = -2): boolean;
    19462251// euix = -2: full discover
    19472252// euix = -1: unit and city only, append units in EnemyUn
    19482253// euix >= 0: unit and city only, replace EnemyUn[euix]
    19492254
    1950   procedure SetContact(p1,p2: integer);
    1951   begin
    1952   if (Mode<moPlaying) or (p1=p2) or (RW[p1].Treaty[p2]>trNoContact) then exit;
    1953   IntServer(sIntTellAboutNation,p1,p2,nil^);
    1954 //  NewContact[p1,p2]:=true
    1955   end;
    1956 
    1957 var
    1958 i,uix,cix,TerrOwner,TerrOwnerTreaty,Strength,Bonus,Cnt,pFoundCity,
    1959   cixFoundCity,MinLevel,Loc1,V8: integer;
    1960 Tile,AddFlags: Cardinal;
    1961 Adjacent: TVicinity8Loc;
    1962 unx: ^TUn;
    1963 mox: ^TModel;
    1964 begin
    1965 result:=false;
    1966 with RW[pTell] do
    1967   begin
    1968   Tile:=RealMap[Loc] and ResourceMask[pTell];
    1969   if Mode=moLoading_Fast then AddFlags:=0 // don't discover units
    1970   else
    1971     begin
    1972     AddFlags:=Map[Loc] and fInEnemyZoC // always preserve this flag!
    1973       or fObserved;
    1974     if Level=lObserveSuper then
    1975       AddFlags:=AddFlags or fSpiedOut;
    1976     if (GrWallContinent[pTell]>=0) and (Continent[Loc]=GrWallContinent[pTell]) then
    1977       AddFlags:=AddFlags or fGrWall;
    1978     if (Mode=moPlaying) and ((Tile and (nPl shl 27)<>nPl shl 27) and (pTell=p)) then
     2255  procedure SetContact(p1, p2: integer);
     2256  begin
     2257    if (Mode < moPlaying) or (p1 = p2) or (RW[p1].Treaty[p2] > trNoContact) then
     2258      exit;
     2259    IntServer(sIntTellAboutNation, p1, p2, nil^);
     2260    // NewContact[p1,p2]:=true
     2261  end;
     2262
     2263var
     2264  i, uix, cix, TerrOwner, TerrOwnerTreaty, Strength, Bonus, Cnt, pFoundCity,
     2265    cixFoundCity, MinLevel, Loc1, V8: integer;
     2266  Tile, AddFlags: Cardinal;
     2267  Adjacent: TVicinity8Loc;
     2268  unx: ^TUn;
     2269  mox: ^TModel;
     2270begin
     2271  result := false;
     2272  with RW[pTell] do
     2273  begin
     2274    Tile := RealMap[Loc] and ResourceMask[pTell];
     2275    if Mode = moLoading_Fast then
     2276      AddFlags := 0 // don't discover units
     2277    else
     2278    begin
     2279      AddFlags := Map[Loc] and fInEnemyZoC // always preserve this flag!
     2280        or fObserved;
     2281      if Level = lObserveSuper then
     2282        AddFlags := AddFlags or fSpiedOut;
     2283      if (GrWallContinent[pTell] >= 0) and
     2284        (Continent[Loc] = GrWallContinent[pTell]) then
     2285        AddFlags := AddFlags or fGrWall;
     2286      if (Mode = moPlaying) and ((Tile and (nPl shl 27) <> nPl shl 27) and
     2287        (pTell = p)) then
    19792288      begin // set fPeace flag?
    1980       TerrOwner:=Tile shr 27;
    1981       if TerrOwner<>pTell then
    1982         begin
    1983         TerrOwnerTreaty:=RW[pTell].Treaty[TerrOwner];
    1984         if 1 shl TerrOwnerTreaty
    1985           and (1 shl trPeace or 1 shl TrFriendlyContact)<>0 then
    1986           AddFlags:=AddFlags or fPeace;
     2289        TerrOwner := Tile shr 27;
     2290        if TerrOwner <> pTell then
     2291        begin
     2292          TerrOwnerTreaty := RW[pTell].Treaty[TerrOwner];
     2293          if 1 shl TerrOwnerTreaty and
     2294            (1 shl trPeace or 1 shl TrFriendlyContact) <> 0 then
     2295            AddFlags := AddFlags or fPeace;
    19872296        end
    19882297      end;
    19892298
    1990     if Occupant[Loc]>=0 then
    1991       if Occupant[Loc]=pTell then
    1992         begin
    1993         AddFlags:=AddFlags or (fOwned or fUnit);
    1994         if ZoCMap[Loc]>0 then AddFlags:=AddFlags or fOwnZoCUnit;
    1995 //        Level:=lObserveSuper // always see own units
     2299      if Occupant[Loc] >= 0 then
     2300        if Occupant[Loc] = pTell then
     2301        begin
     2302          AddFlags := AddFlags or (fOwned or fUnit);
     2303          if ZoCMap[Loc] > 0 then
     2304            AddFlags := AddFlags or fOwnZoCUnit;
     2305          // Level:=lObserveSuper // always see own units
    19962306        end
    1997       else if Map[Loc] and fUnit<>0 then
    1998         AddFlags:=AddFlags or fUnit
    1999       else
    2000         begin
    2001         Strongest(Loc,uix,Strength,Bonus,Cnt);
    2002         unx:=@RW[Occupant[Loc]].Un[uix];
    2003         mox:=@RW[Occupant[Loc]].Model[unx.mix];
    2004         assert((ZoCMap[Loc]<>0)=(mox.Flags and mdZOC<>0));
    2005         if (mox.Cap[mcStealth]>0) and (Tile and fCity=0)
    2006           and (Tile and fTerImp<>tiBase) then
    2007           MinLevel:=lObserveSuper
    2008         else if (mox.Cap[mcSub]>0) and (Tile and fTerrain<fGrass) then
    2009           MinLevel:=lObserveAll
    2010         else MinLevel:=lObserveUnhidden;
    2011         if Level>=MinLevel then
     2307        else if Map[Loc] and fUnit <> 0 then
     2308          AddFlags := AddFlags or fUnit
     2309        else
     2310        begin
     2311          Strongest(Loc, uix, Strength, Bonus, Cnt);
     2312          unx := @RW[Occupant[Loc]].Un[uix];
     2313          mox := @RW[Occupant[Loc]].Model[unx.mix];
     2314          assert((ZoCMap[Loc] <> 0) = (mox.Flags and mdZOC <> 0));
     2315          if (mox.Cap[mcStealth] > 0) and (Tile and fCity = 0) and
     2316            (Tile and fTerImp <> tiBase) then
     2317            MinLevel := lObserveSuper
     2318          else if (mox.Cap[mcSub] > 0) and (Tile and fTerrain < fGrass) then
     2319            MinLevel := lObserveAll
     2320          else
     2321            MinLevel := lObserveUnhidden;
     2322          if Level >= MinLevel then
    20122323          begin
    2013           AddFlags:=AddFlags or fUnit;
    2014           if euix>=0 then uix:=euix
    2015           else
     2324            AddFlags := AddFlags or fUnit;
     2325            if euix >= 0 then
     2326              uix := euix
     2327            else
    20162328            begin
    2017             uix:=nEnemyUn;
    2018             inc(nEnemyUn);
    2019             assert(nEnemyUn<neumax);
     2329              uix := nEnemyUn;
     2330              inc(nEnemyUn);
     2331              assert(nEnemyUn < neumax);
    20202332            end;
    2021           MakeUnitInfo(Occupant[Loc],unx^,EnemyUn[uix]);
    2022           if Cnt>1 then
    2023             EnemyUn[uix].Flags:=EnemyUn[uix].Flags or unMulti;
    2024           if (mox.Flags and mdZOC<>0) and (pTell=p)
    2025             and (Treaty[Occupant[Loc]]<trAlliance) then
     2333            MakeUnitInfo(Occupant[Loc], unx^, EnemyUn[uix]);
     2334            if Cnt > 1 then
     2335              EnemyUn[uix].Flags := EnemyUn[uix].Flags or unMulti;
     2336            if (mox.Flags and mdZOC <> 0) and (pTell = p) and
     2337              (Treaty[Occupant[Loc]] < trAlliance) then
    20262338            begin // set fInEnemyZoC flags of surrounding tiles
    2027             V8_to_Loc(Loc,Adjacent);
    2028             for V8:=0 to 7 do
     2339              V8_to_Loc(Loc, Adjacent);
     2340              for V8 := 0 to 7 do
    20292341              begin
    2030               Loc1:=Adjacent[V8];
    2031               if (Loc1>=0) and (Loc1<MapSize) then
    2032                 Map[Loc1]:=Map[Loc1] or fInEnemyZoC
     2342                Loc1 := Adjacent[V8];
     2343                if (Loc1 >= 0) and (Loc1 < MapSize) then
     2344                  Map[Loc1] := Map[Loc1] or fInEnemyZoC
    20332345              end
    20342346            end;
    2035           if EnableContact and (mox.Domain=dGround) then
    2036             SetContact(pTell,Occupant[Loc]);
    2037           if Mode>=moMovie then
     2347            if EnableContact and (mox.Domain = dGround) then
     2348              SetContact(pTell, Occupant[Loc]);
     2349            if Mode >= moMovie then
    20382350            begin
    2039             TellAboutModel(pTell,Occupant[Loc],unx.mix);
    2040             EnemyUn[uix].emix:=emixSafe(pTell,Occupant[Loc],unx.mix);
     2351              TellAboutModel(pTell, Occupant[Loc], unx.mix);
     2352              EnemyUn[uix].emix := emixSafe(pTell, Occupant[Loc], unx.mix);
    20412353            end;
    2042 //          Level:=lObserveSuper; // don't discover unit twice
    2043           if (pTell=p)
    2044             and ((Tile and fCity=0) or (1 shl pTell and GAI<>0)) then
    2045             result:=true;
     2354            // Level:=lObserveSuper; // don't discover unit twice
     2355            if (pTell = p) and
     2356              ((Tile and fCity = 0) or (1 shl pTell and GAI <> 0)) then
     2357              result := true;
    20462358          end
    2047         else AddFlags:=AddFlags or Map[Loc] and (fStealthUnit or fHiddenUnit)
     2359          else
     2360            AddFlags := AddFlags or Map[Loc] and (fStealthUnit or fHiddenUnit)
    20482361        end
    20492362    end; // if Mode>moLoading_Fast
    20502363
    2051   if Tile and fCity<>0 then
    2052     if ObserveLevel[Loc] shr (2*pTell) and 3>0 then
    2053       AddFlags:=AddFlags or Map[Loc] and fOwned
    2054     else
    2055       begin
    2056       pFoundCity:=Tile shr 27;
    2057       if pFoundCity=pTell then AddFlags:=AddFlags or fOwned
     2364    if Tile and fCity <> 0 then
     2365      if ObserveLevel[Loc] shr (2 * pTell) and 3 > 0 then
     2366        AddFlags := AddFlags or Map[Loc] and fOwned
    20582367      else
    2059         begin
    2060         if EnableContact then SetContact(pTell,pFoundCity);
    2061         cixFoundCity:=RW[pFoundCity].nCity-1;
    2062         while (cixFoundCity>=0)
    2063           and (RW[pFoundCity].City[cixFoundCity].Loc<>Loc) do
    2064           dec(cixFoundCity);
    2065         assert(cixFoundCity>=0);
    2066         i:=0;
    2067         while (i<nEnemyCity) and (EnemyCity[i].Loc<>Loc) do
    2068           inc(i);
    2069         if i=nEnemyCity then
     2368      begin
     2369        pFoundCity := Tile shr 27;
     2370        if pFoundCity = pTell then
     2371          AddFlags := AddFlags or fOwned
     2372        else
     2373        begin
     2374          if EnableContact then
     2375            SetContact(pTell, pFoundCity);
     2376          cixFoundCity := RW[pFoundCity].nCity - 1;
     2377          while (cixFoundCity >= 0) and
     2378            (RW[pFoundCity].City[cixFoundCity].Loc <> Loc) do
     2379            dec(cixFoundCity);
     2380          assert(cixFoundCity >= 0);
     2381          i := 0;
     2382          while (i < nEnemyCity) and (EnemyCity[i].Loc <> Loc) do
     2383            inc(i);
     2384          if i = nEnemyCity then
    20702385          begin
    2071           inc(nEnemyCity);
    2072           assert(nEnemyCity<necmax);
    2073           EnemyCity[i].Status:=0;
    2074           EnemyCity[i].SavedStatus:=0;
    2075           if pTell=p then result:=true;
     2386            inc(nEnemyCity);
     2387            assert(nEnemyCity < necmax);
     2388            EnemyCity[i].Status := 0;
     2389            EnemyCity[i].SavedStatus := 0;
     2390            if pTell = p then
     2391              result := true;
    20762392          end;
    2077         MakeCityInfo(pFoundCity,cixFoundCity,EnemyCity[i]);
     2393          MakeCityInfo(pFoundCity, cixFoundCity, EnemyCity[i]);
    20782394        end;
    20792395      end
    2080   else if Map[Loc] and fCity<>0 then // remove enemycity
    2081     for cix:=0 to nEnemyCity-1 do
    2082       if EnemyCity[cix].Loc=Loc then
    2083         EnemyCity[cix].Loc:=-1;
    2084 
    2085   if Map[Loc] and fTerrain=fUNKNOWN then inc(Discovered[pTell]);
    2086   if euix>=-1 then
    2087     Map[Loc]:=Map[Loc] and not (fUnit or fCity or fOwned or fOwnZoCUnit)
    2088       or (Tile and $07FFFFFF or AddFlags) and (fUnit or fCity or fOwned or fOwnZoCUnit)
    2089   else
    2090     begin
    2091     Map[Loc]:=Tile and $07FFFFFF or AddFlags;
    2092     if Tile and $78000000=$78000000 then Territory[Loc]:=-1
    2093     else Territory[Loc]:=Tile shr 27;
    2094     MapObservedLast[Loc]:=GTurn
    2095     end;
    2096   ObserveLevel[Loc]:=ObserveLevel[Loc] and not (3 shl (2*pTell))
    2097     or Cardinal(Level) shl (2*pTell);
     2396    else if Map[Loc] and fCity <> 0 then // remove enemycity
     2397      for cix := 0 to nEnemyCity - 1 do
     2398        if EnemyCity[cix].Loc = Loc then
     2399          EnemyCity[cix].Loc := -1;
     2400
     2401    if Map[Loc] and fTerrain = fUNKNOWN then
     2402      inc(Discovered[pTell]);
     2403    if euix >= -1 then
     2404      Map[Loc] := Map[Loc] and not(fUnit or fCity or fOwned or fOwnZoCUnit) or
     2405        (Tile and $07FFFFFF or AddFlags) and
     2406        (fUnit or fCity or fOwned or fOwnZoCUnit)
     2407    else
     2408    begin
     2409      Map[Loc] := Tile and $07FFFFFF or AddFlags;
     2410      if Tile and $78000000 = $78000000 then
     2411        Territory[Loc] := -1
     2412      else
     2413        Territory[Loc] := Tile shr 27;
     2414      MapObservedLast[Loc] := GTurn
     2415    end;
     2416    ObserveLevel[Loc] := ObserveLevel[Loc] and not(3 shl (2 * pTell)) or
     2417      Cardinal(Level) shl (2 * pTell);
    20982418  end
    20992419end; // DiscoverTile
    21002420
    2101 function Discover9(Loc,p,Level: integer; TellAllied, EnableContact: boolean): boolean;
    2102 var
    2103 V9,Loc1,pTell,OldLevel: integer;
    2104 Radius: TVicinity8Loc;
    2105 begin
    2106 assert((Mode>moLoading_Fast) or (RW[p].nEnemyUn=0));
    2107 result:=false;
    2108 V8_to_Loc(Loc,Radius);
    2109 for V9:=0 to 8 do
    2110   begin
    2111   if V9=8 then Loc1:=Loc
    2112   else Loc1:=Radius[V9];
    2113   if (Loc1>=0) and (Loc1<MapSize) then
    2114     if TellAllied then
    2115       begin
    2116       for pTell:=0 to nPl-1 do
    2117         if (pTell=p) or (1 shl pTell and GAlive<>0)
    2118           and (RW[p].Treaty[pTell]=trAlliance) then
     2421function Discover9(Loc, p, Level: integer;
     2422  TellAllied, EnableContact: boolean): boolean;
     2423var
     2424  V9, Loc1, pTell, OldLevel: integer;
     2425  Radius: TVicinity8Loc;
     2426begin
     2427  assert((Mode > moLoading_Fast) or (RW[p].nEnemyUn = 0));
     2428  result := false;
     2429  V8_to_Loc(Loc, Radius);
     2430  for V9 := 0 to 8 do
     2431  begin
     2432    if V9 = 8 then
     2433      Loc1 := Loc
     2434    else
     2435      Loc1 := Radius[V9];
     2436    if (Loc1 >= 0) and (Loc1 < MapSize) then
     2437      if TellAllied then
     2438      begin
     2439        for pTell := 0 to nPl - 1 do
     2440          if (pTell = p) or (1 shl pTell and GAlive <> 0) and
     2441            (RW[p].Treaty[pTell] = trAlliance) then
    21192442          begin
    2120           OldLevel:=ObserveLevel[Loc1] shr (2*pTell) and 3;
    2121           if Level>OldLevel then
    2122             result:=DiscoverTile(Loc1,p,pTell,Level,EnableContact) or result;
     2443            OldLevel := ObserveLevel[Loc1] shr (2 * pTell) and 3;
     2444            if Level > OldLevel then
     2445              result := DiscoverTile(Loc1, p, pTell, Level, EnableContact)
     2446                or result;
    21232447          end
    21242448      end
    2125     else
    2126       begin
    2127       OldLevel:=ObserveLevel[Loc1] shr (2*p) and 3;
    2128       if Level>OldLevel then
    2129         result:=DiscoverTile(Loc1,p,p,Level,EnableContact) or result;
     2449      else
     2450      begin
     2451        OldLevel := ObserveLevel[Loc1] shr (2 * p) and 3;
     2452        if Level > OldLevel then
     2453          result := DiscoverTile(Loc1, p, p, Level, EnableContact) or result;
    21302454      end
    21312455  end;
    21322456end;
    21332457
    2134 function Discover21(Loc,p,AdjacentLevel: integer; TellAllied, EnableContact: boolean): boolean;
    2135 var
    2136 V21,Loc1,pTell,Level,OldLevel,AdjacentFlags: integer;
    2137 Radius: TVicinity21Loc;
    2138 begin
    2139 assert((Mode>moLoading_Fast) or (RW[p].nEnemyUn=0));
    2140 result:=false;
    2141 AdjacentFlags:=$00267620 shr 1;
    2142 V21_to_Loc(Loc,Radius);
    2143 for V21:=1 to 26 do
    2144   begin
    2145   Loc1:=Radius[V21];
    2146   if (Loc1>=0) and (Loc1<MapSize) then
    2147     begin
    2148     if AdjacentFlags and 1<>0 then Level:=AdjacentLevel
    2149     else Level:=lObserveUnhidden;
    2150     if TellAllied then
    2151       begin
    2152       for pTell:=0 to nPl-1 do
    2153         if (pTell=p) or (1 shl pTell and GAlive<>0)
    2154           and (RW[p].Treaty[pTell]=trAlliance) then
     2458function Discover21(Loc, p, AdjacentLevel: integer;
     2459  TellAllied, EnableContact: boolean): boolean;
     2460var
     2461  V21, Loc1, pTell, Level, OldLevel, AdjacentFlags: integer;
     2462  Radius: TVicinity21Loc;
     2463begin
     2464  assert((Mode > moLoading_Fast) or (RW[p].nEnemyUn = 0));
     2465  result := false;
     2466  AdjacentFlags := $00267620 shr 1;
     2467  V21_to_Loc(Loc, Radius);
     2468  for V21 := 1 to 26 do
     2469  begin
     2470    Loc1 := Radius[V21];
     2471    if (Loc1 >= 0) and (Loc1 < MapSize) then
     2472    begin
     2473      if AdjacentFlags and 1 <> 0 then
     2474        Level := AdjacentLevel
     2475      else
     2476        Level := lObserveUnhidden;
     2477      if TellAllied then
     2478      begin
     2479        for pTell := 0 to nPl - 1 do
     2480          if (pTell = p) or (1 shl pTell and GAlive <> 0) and
     2481            (RW[p].Treaty[pTell] = trAlliance) then
    21552482          begin
    2156           OldLevel:=ObserveLevel[Loc1] shr (2*pTell) and 3;
    2157           if Level>OldLevel then
    2158             result:=DiscoverTile(Loc1,p,pTell,Level,EnableContact) or result;
     2483            OldLevel := ObserveLevel[Loc1] shr (2 * pTell) and 3;
     2484            if Level > OldLevel then
     2485              result := DiscoverTile(Loc1, p, pTell, Level, EnableContact)
     2486                or result;
    21592487          end
    21602488      end
    2161     else
    2162       begin
    2163       OldLevel:=ObserveLevel[Loc1] shr (2*p) and 3;
    2164       if Level>OldLevel then
    2165         result:=DiscoverTile(Loc1,p,p,Level,EnableContact) or result;
     2489      else
     2490      begin
     2491        OldLevel := ObserveLevel[Loc1] shr (2 * p) and 3;
     2492        if Level > OldLevel then
     2493          result := DiscoverTile(Loc1, p, p, Level, EnableContact) or result;
    21662494      end
    21672495    end;
    2168   AdjacentFlags:=AdjacentFlags shr 1;
     2496    AdjacentFlags := AdjacentFlags shr 1;
    21692497  end;
    21702498end;
    21712499
    21722500procedure DiscoverAll(p, Level: integer);
    2173 {player p discovers complete playground (for supervisor)}
    2174 var
    2175 Loc, OldLevel: integer;
    2176 begin
    2177 assert((Mode>moLoading_Fast) or (RW[p].nEnemyUn=0));
    2178 for Loc:=0 to MapSize-1 do
    2179   begin
    2180   OldLevel:=ObserveLevel[Loc] shr (2*p) and 3;
    2181   if Level>OldLevel then
    2182     DiscoverTile(Loc,p,p,Level,false);
     2501{ player p discovers complete playground (for supervisor) }
     2502var
     2503  Loc, OldLevel: integer;
     2504begin
     2505  assert((Mode > moLoading_Fast) or (RW[p].nEnemyUn = 0));
     2506  for Loc := 0 to MapSize - 1 do
     2507  begin
     2508    OldLevel := ObserveLevel[Loc] shr (2 * p) and 3;
     2509    if Level > OldLevel then
     2510      DiscoverTile(Loc, p, p, Level, false);
    21832511  end;
    21842512end;
     
    21862514procedure DiscoverViewAreas(p: integer);
    21872515var
    2188 pTell, uix, cix, ecix, Loc, RealOwner: integer;
    2189 PModel: ^TModel;
     2516  pTell, uix, cix, ecix, Loc, RealOwner: integer;
     2517  PModel: ^TModel;
    21902518begin // discover unit and city view areas
    2191 for pTell:=0 to nPl-1 do
    2192   if (pTell=p) or (RW[p].Treaty[pTell]=trAlliance) then
    2193     begin
    2194     for uix:=0 to RW[pTell].nUn-1 do with RW[pTell].Un[uix] do
    2195       if (Loc>=0) and (master<0) and (RealMap[Loc] and fCity=0) then
    2196         begin
    2197         PModel:=@RW[pTell].Model[mix];
    2198         if (PModel.Kind=mkDiplomat) or (PModel.Cap[mcSpy]>0) then
    2199           Discover21(Loc,p,lObserveSuper,false,true)
    2200         else if (PModel.Cap[mcRadar]+PModel.Cap[mcCarrier]>0)
    2201           or (PModel.Domain=dAir) then
    2202           Discover21(Loc,p,lObserveAll,false,false)
    2203         else if (RealMap[Loc] and fTerrain=fMountains)
    2204           or (RealMap[Loc] and fTerImp=tiFort)
    2205           or (RealMap[Loc] and fTerImp=tiBase)
    2206           or (PModel.Cap[mcAcademy]>0) then
    2207           Discover21(Loc,p,lObserveUnhidden,false,PModel.Domain=dGround)
    2208         else Discover9(Loc,p,lObserveUnhidden,false,PModel.Domain=dGround);
    2209         end;
    2210     for cix:=0 to RW[pTell].nCity-1 do if RW[pTell].City[cix].Loc>=0 then
    2211       Discover21(RW[pTell].City[cix].Loc,p,lObserveUnhidden,false,true);
    2212     for ecix:=0 to RW[pTell].nEnemyCity-1 do
     2519  for pTell := 0 to nPl - 1 do
     2520    if (pTell = p) or (RW[p].Treaty[pTell] = trAlliance) then
     2521    begin
     2522      for uix := 0 to RW[pTell].nUn - 1 do
     2523        with RW[pTell].Un[uix] do
     2524          if (Loc >= 0) and (Master < 0) and (RealMap[Loc] and fCity = 0) then
     2525          begin
     2526            PModel := @RW[pTell].Model[mix];
     2527            if (PModel.Kind = mkDiplomat) or (PModel.Cap[mcSpy] > 0) then
     2528              Discover21(Loc, p, lObserveSuper, false, true)
     2529            else if (PModel.Cap[mcRadar] + PModel.Cap[mcCarrier] > 0) or
     2530              (PModel.Domain = dAir) then
     2531              Discover21(Loc, p, lObserveAll, false, false)
     2532            else if (RealMap[Loc] and fTerrain = fMountains) or
     2533              (RealMap[Loc] and fTerImp = tiFort) or
     2534              (RealMap[Loc] and fTerImp = tiBase) or (PModel.Cap[mcAcademy] > 0)
     2535            then
     2536              Discover21(Loc, p, lObserveUnhidden, false,
     2537                PModel.Domain = dGround)
     2538            else
     2539              Discover9(Loc, p, lObserveUnhidden, false,
     2540                PModel.Domain = dGround);
     2541          end;
     2542      for cix := 0 to RW[pTell].nCity - 1 do
     2543        if RW[pTell].City[cix].Loc >= 0 then
     2544          Discover21(RW[pTell].City[cix].Loc, p, lObserveUnhidden, false, true);
     2545      for ecix := 0 to RW[pTell].nEnemyCity - 1 do
    22132546      begin // players know territory, so no use in hiding city owner
    2214       Loc:=RW[pTell].EnemyCity[ecix].Loc;
    2215       if Loc>=0 then
    2216         begin
    2217         RealOwner:=(RealMap[Loc] shr 27) and $F;
    2218         if RealOwner<nPl then
    2219           RW[pTell].EnemyCity[ecix].owner:=RealOwner
    2220         else
     2547        Loc := RW[pTell].EnemyCity[ecix].Loc;
     2548        if Loc >= 0 then
     2549        begin
     2550          RealOwner := (RealMap[Loc] shr 27) and $F;
     2551          if RealOwner < nPl then
     2552            RW[pTell].EnemyCity[ecix].Owner := RealOwner
     2553          else
    22212554          begin
    2222           RW[pTell].EnemyCity[ecix].Loc:=-1;
    2223           RW[pTell].Map[Loc]:=RW[pTell].Map[Loc] and not fCity
     2555            RW[pTell].EnemyCity[ecix].Loc := -1;
     2556            RW[pTell].Map[Loc] := RW[pTell].Map[Loc] and not fCity
    22242557          end
    22252558        end
     
    22282561end;
    22292562
    2230 function GetUnitStack(p,Loc: integer): integer;
    2231 var
    2232 uix: integer;
    2233 unx: ^TUn;
    2234 begin
    2235 result:=0;
    2236 if Occupant[Loc]<0 then exit;
    2237 for uix:=0 to RW[Occupant[Loc]].nUn-1 do
    2238   begin
    2239   unx:=@RW[Occupant[Loc]].Un[uix];
    2240   if unx.Loc=Loc then
    2241     begin
    2242     MakeUnitInfo(Occupant[Loc],unx^,RW[p].EnemyUn[RW[p].nEnemyUn+result]);
    2243     TellAboutModel(p,Occupant[Loc],unx.mix);
    2244     RW[p].EnemyUn[RW[p].nEnemyUn+result].emix:=RWemix[p,Occupant[Loc],unx.mix];
    2245     inc(result);
     2563function GetUnitStack(p, Loc: integer): integer;
     2564var
     2565  uix: integer;
     2566  unx: ^TUn;
     2567begin
     2568  result := 0;
     2569  if Occupant[Loc] < 0 then
     2570    exit;
     2571  for uix := 0 to RW[Occupant[Loc]].nUn - 1 do
     2572  begin
     2573    unx := @RW[Occupant[Loc]].Un[uix];
     2574    if unx.Loc = Loc then
     2575    begin
     2576      MakeUnitInfo(Occupant[Loc], unx^, RW[p].EnemyUn[RW[p].nEnemyUn + result]);
     2577      TellAboutModel(p, Occupant[Loc], unx.mix);
     2578      RW[p].EnemyUn[RW[p].nEnemyUn + result].emix :=
     2579        RWemix[p, Occupant[Loc], unx.mix];
     2580      inc(result);
    22462581    end
    22472582  end
     
    22512586// update maps and enemy units of all players after unit change
    22522587var
    2253 p, euix, OldLevel: integer;
    2254 AddFlags, ClearFlags: Cardinal;
    2255 begin
    2256 if (Mode=moLoading_Fast) and not CityChange then exit;
    2257 for p:=0 to nPl-1 do if 1 shl p and (GAlive or GWatching)<>0 then
    2258   begin
    2259   OldLevel:=ObserveLevel[Loc] shr (2*p) and 3;
    2260   if OldLevel>lNoObserve then
    2261     begin
    2262     if RW[p].Map[Loc] and (fUnit or fOwned)=fUnit then
    2263       begin
    2264       // replace unit located here in EnemyUn
    2265       // do not just set loc:=-1 because total number would be unlimited
    2266       euix:=RW[p].nEnemyUn-1;
    2267       while euix>=0 do
    2268         begin
    2269         if RW[p].EnemyUn[euix].Loc=Loc then
    2270           begin RW[p].EnemyUn[euix].Loc:=-1; Break; end;
    2271         dec(euix);
     2588  p, euix, OldLevel: integer;
     2589  AddFlags, ClearFlags: Cardinal;
     2590begin
     2591  if (Mode = moLoading_Fast) and not CityChange then
     2592    exit;
     2593  for p := 0 to nPl - 1 do
     2594    if 1 shl p and (GAlive or GWatching) <> 0 then
     2595    begin
     2596      OldLevel := ObserveLevel[Loc] shr (2 * p) and 3;
     2597      if OldLevel > lNoObserve then
     2598      begin
     2599        if RW[p].Map[Loc] and (fUnit or fOwned) = fUnit then
     2600        begin
     2601          // replace unit located here in EnemyUn
     2602          // do not just set loc:=-1 because total number would be unlimited
     2603          euix := RW[p].nEnemyUn - 1;
     2604          while euix >= 0 do
     2605          begin
     2606            if RW[p].EnemyUn[euix].Loc = Loc then
     2607            begin
     2608              RW[p].EnemyUn[euix].Loc := -1;
     2609              Break;
     2610            end;
     2611            dec(euix);
     2612          end;
     2613          RW[p].Map[Loc] := RW[p].Map[Loc] and not fUnit
     2614        end
     2615        else
     2616        begin // look for empty slot in EnemyUn
     2617          euix := RW[p].nEnemyUn - 1;
     2618          while (euix >= 0) and (RW[p].EnemyUn[euix].Loc >= 0) do
     2619            dec(euix);
    22722620        end;
    2273       RW[p].Map[Loc]:=RW[p].Map[Loc] and not fUnit
    2274       end
    2275     else
    2276       begin // look for empty slot in EnemyUn
    2277       euix:=RW[p].nEnemyUn-1;
    2278       while (euix>=0) and (RW[p].EnemyUn[euix].Loc>=0) do dec(euix);
    2279       end;
    2280     if (Occupant[Loc]<0) and not CityChange then
    2281       begin // calling DiscoverTile not necessary, only clear map flags
    2282       ClearFlags:=fUnit or fHiddenUnit or fStealthUnit or fOwnZoCUnit;
    2283       if RealMap[Loc] and fCity=0 then
    2284         ClearFlags:=ClearFlags or fOwned;
    2285       RW[p].Map[Loc]:=RW[p].Map[Loc] and not ClearFlags;
    2286       end
    2287     else if (Occupant[Loc]<>p) or CityChange then
    2288       begin // city or enemy unit update necessary, call DiscoverTile
    2289       ObserveLevel[Loc]:=ObserveLevel[Loc] and not (3 shl (2*p));
    2290       DiscoverTile(Loc, p, p, OldLevel, false, euix);
    2291       end
    2292     else {if (Occupant[Loc]=p) and not CityChange then}
    2293       begin // calling DiscoverTile not necessary, only set map flags
    2294       ClearFlags:=0;
    2295       AddFlags:=fUnit or fOwned;
    2296       if ZoCMap[Loc]>0 then AddFlags:=AddFlags or fOwnZoCUnit
    2297       else ClearFlags:=ClearFlags or fOwnZoCUnit;
    2298       RW[p].Map[Loc]:=RW[p].Map[Loc] and not ClearFlags or AddFlags;
    2299       end
    2300     end
    2301   end
    2302 end;
    2303 
    2304 procedure RecalcV8ZoC(p,Loc: integer);
    2305 // recalculate fInEnemyZoC flags around single tile
    2306 var
    2307 v8,V8V8,Loc1,Loc2,p1,ObserveMask: integer;
    2308 Tile1: ^Cardinal;
    2309 Adjacent,AdjacentAdjacent: TVicinity8Loc;
    2310 begin
    2311 if Mode=moLoading_Fast then exit;
    2312 ObserveMask:=3 shl (2*p);
    2313 V8_to_Loc(Loc,Adjacent);
    2314 for V8:=0 to 7 do
    2315   begin
    2316   Loc1:=Adjacent[V8];
    2317   if (Loc1>=0) and (Loc1<MapSize) then
    2318     begin
    2319     Tile1:=@RW[p].Map[Loc1];
    2320     Tile1^:=Tile1^ and not fInEnemyZoC;
    2321     V8_to_Loc(Loc1,AdjacentAdjacent);
    2322     for V8V8:=0 to 7 do
    2323       begin
    2324       Loc2:=AdjacentAdjacent[V8V8];
    2325       if (Loc2>=0) and (Loc2<MapSize) and (ZoCMap[Loc2]>0)
    2326         and (ObserveLevel[Loc2] and ObserveMask<>0) then
    2327         begin
    2328         p1:=Occupant[Loc2];
    2329         assert(p1<>nPl);
    2330         if (p1<>p) and (RW[p].Treaty[p1]<trAlliance) then
    2331           begin Tile1^:=Tile1^ or fInEnemyZoC; break end
     2621        if (Occupant[Loc] < 0) and not CityChange then
     2622        begin // calling DiscoverTile not necessary, only clear map flags
     2623          ClearFlags := fUnit or fHiddenUnit or fStealthUnit or fOwnZoCUnit;
     2624          if RealMap[Loc] and fCity = 0 then
     2625            ClearFlags := ClearFlags or fOwned;
     2626          RW[p].Map[Loc] := RW[p].Map[Loc] and not ClearFlags;
    23322627        end
    2333       end;
    2334     end
    2335   end
    2336 end;
    2337 
    2338 procedure RecalcMapZoC(p: integer);
    2339 // recalculate fInEnemyZoC flags for the whole map
    2340 var
    2341 Loc,Loc1,V8,p1,ObserveMask: integer;
    2342 Adjacent: TVicinity8Loc;
    2343 begin
    2344 if Mode=moLoading_Fast then exit;
    2345 MaskD(RW[p].Map^,MapSize,not Cardinal(fInEnemyZoC));
    2346 ObserveMask:=3 shl (2*p);
    2347 for Loc:=0 to MapSize-1 do
    2348   if (ZoCMap[Loc]>0) and (ObserveLevel[Loc] and ObserveMask<>0) then
    2349     begin
    2350     p1:=Occupant[Loc];
    2351     assert(p1<>nPl);
    2352     if (p1<>p) and (RW[p].Treaty[p1]<trAlliance) then
    2353       begin // this non-allied enemy ZoC unit is known to this player -- set flags!
    2354       V8_to_Loc(Loc,Adjacent);
    2355       for V8:=0 to 7 do
    2356         begin
    2357         Loc1:=Adjacent[V8];
    2358         if (Loc1>=0) and (Loc1<MapSize) then
    2359           RW[p].Map[Loc1]:=RW[p].Map[Loc1] or fInEnemyZoC
     2628        else if (Occupant[Loc] <> p) or CityChange then
     2629        begin // city or enemy unit update necessary, call DiscoverTile
     2630          ObserveLevel[Loc] := ObserveLevel[Loc] and not(3 shl (2 * p));
     2631          DiscoverTile(Loc, p, p, OldLevel, false, euix);
     2632        end
     2633        else { if (Occupant[Loc]=p) and not CityChange then }
     2634        begin // calling DiscoverTile not necessary, only set map flags
     2635          ClearFlags := 0;
     2636          AddFlags := fUnit or fOwned;
     2637          if ZoCMap[Loc] > 0 then
     2638            AddFlags := AddFlags or fOwnZoCUnit
     2639          else
     2640            ClearFlags := ClearFlags or fOwnZoCUnit;
     2641          RW[p].Map[Loc] := RW[p].Map[Loc] and not ClearFlags or AddFlags;
    23602642        end
    23612643      end
     
    23632645end;
    23642646
     2647procedure RecalcV8ZoC(p, Loc: integer);
     2648// recalculate fInEnemyZoC flags around single tile
     2649var
     2650  V8, V8V8, Loc1, Loc2, p1, ObserveMask: integer;
     2651  Tile1: ^Cardinal;
     2652  Adjacent, AdjacentAdjacent: TVicinity8Loc;
     2653begin
     2654  if Mode = moLoading_Fast then
     2655    exit;
     2656  ObserveMask := 3 shl (2 * p);
     2657  V8_to_Loc(Loc, Adjacent);
     2658  for V8 := 0 to 7 do
     2659  begin
     2660    Loc1 := Adjacent[V8];
     2661    if (Loc1 >= 0) and (Loc1 < MapSize) then
     2662    begin
     2663      Tile1 := @RW[p].Map[Loc1];
     2664      Tile1^ := Tile1^ and not fInEnemyZoC;
     2665      V8_to_Loc(Loc1, AdjacentAdjacent);
     2666      for V8V8 := 0 to 7 do
     2667      begin
     2668        Loc2 := AdjacentAdjacent[V8V8];
     2669        if (Loc2 >= 0) and (Loc2 < MapSize) and (ZoCMap[Loc2] > 0) and
     2670          (ObserveLevel[Loc2] and ObserveMask <> 0) then
     2671        begin
     2672          p1 := Occupant[Loc2];
     2673          assert(p1 <> nPl);
     2674          if (p1 <> p) and (RW[p].Treaty[p1] < trAlliance) then
     2675          begin
     2676            Tile1^ := Tile1^ or fInEnemyZoC;
     2677            Break
     2678          end
     2679        end
     2680      end;
     2681    end
     2682  end
     2683end;
     2684
     2685procedure RecalcMapZoC(p: integer);
     2686// recalculate fInEnemyZoC flags for the whole map
     2687var
     2688  Loc, Loc1, V8, p1, ObserveMask: integer;
     2689  Adjacent: TVicinity8Loc;
     2690begin
     2691  if Mode = moLoading_Fast then
     2692    exit;
     2693  MaskD(RW[p].Map^, MapSize, not Cardinal(fInEnemyZoC));
     2694  ObserveMask := 3 shl (2 * p);
     2695  for Loc := 0 to MapSize - 1 do
     2696    if (ZoCMap[Loc] > 0) and (ObserveLevel[Loc] and ObserveMask <> 0) then
     2697    begin
     2698      p1 := Occupant[Loc];
     2699      assert(p1 <> nPl);
     2700      if (p1 <> p) and (RW[p].Treaty[p1] < trAlliance) then
     2701      begin // this non-allied enemy ZoC unit is known to this player -- set flags!
     2702        V8_to_Loc(Loc, Adjacent);
     2703        for V8 := 0 to 7 do
     2704        begin
     2705          Loc1 := Adjacent[V8];
     2706          if (Loc1 >= 0) and (Loc1 < MapSize) then
     2707            RW[p].Map[Loc1] := RW[p].Map[Loc1] or fInEnemyZoC
     2708        end
     2709      end
     2710    end
     2711end;
     2712
    23652713procedure RecalcPeaceMap(p: integer);
    23662714// recalculate fPeace flags for the whole map
    23672715var
    2368 Loc,p1: integer;
    2369 PeacePlayer: array[-1..nPl-1] of boolean;
    2370 begin
    2371 if Mode<>moPlaying then exit;
    2372 MaskD(RW[p].Map^,MapSize,not Cardinal(fPeace));
    2373 for p1:=-1 to nPl-1 do
    2374   PeacePlayer[p1]:= (p1>=0) and (p1<>p) and (1 shl p1 and GAlive<>0)
    2375     and (RW[p].Treaty[p1] in [trPeace,trFriendlyContact]);
    2376 for Loc:=0 to MapSize-1 do
    2377   if PeacePlayer[RW[p].Territory[Loc]] then
    2378     RW[p].Map[Loc]:=RW[p].Map[Loc] or fPeace
    2379 end;
    2380 
     2716  Loc, p1: integer;
     2717  PeacePlayer: array [-1 .. nPl - 1] of boolean;
     2718begin
     2719  if Mode <> moPlaying then
     2720    exit;
     2721  MaskD(RW[p].Map^, MapSize, not Cardinal(fPeace));
     2722  for p1 := -1 to nPl - 1 do
     2723    PeacePlayer[p1] := (p1 >= 0) and (p1 <> p) and (1 shl p1 and GAlive <> 0)
     2724      and (RW[p].Treaty[p1] in [trPeace, TrFriendlyContact]);
     2725  for Loc := 0 to MapSize - 1 do
     2726    if PeacePlayer[RW[p].Territory[Loc]] then
     2727      RW[p].Map[Loc] := RW[p].Map[Loc] or fPeace
     2728end;
    23812729
    23822730{
    2383                          Territory Calculation
    2384  ____________________________________________________________________
     2731  Territory Calculation
     2732  ____________________________________________________________________
    23852733}
    23862734var
    2387 BorderChanges: array[0..sIntExpandTerritory and $F-1] of Cardinal;
     2735  BorderChanges: array [0 .. sIntExpandTerritory and $F - 1] of Cardinal;
    23882736
    23892737procedure ChangeTerritory(Loc, p: integer);
    23902738var
    2391 p1: integer;
    2392 begin
    2393 assert(p>=0); // no player's territory indicated by p=nPl
    2394 dec(TerritoryCount[RealMap[Loc] shr 27]);
    2395 inc(TerritoryCount[p]);
    2396 RealMap[Loc]:=RealMap[Loc] and not ($F shl 27) or Cardinal(p) shl 27;
    2397 if p=$F then p:=-1;
    2398 for p1:=0 to nPl-1 do if 1 shl p1 and (GAlive or GWatching)<>0 then
    2399   if RW[p1].Map[Loc] and fTerrain<>fUNKNOWN then
    2400     begin
    2401     RW[p1].Territory[Loc]:=p;
    2402     if (p<nPl) and (p<>p1) and (1 shl p and GAlive<>0)
    2403       and (RW[p1].Treaty[p] in [trPeace,trFriendlyContact]) then
    2404       RW[p1].Map[Loc]:=RW[p1].Map[Loc] or fPeace
    2405     else RW[p1].Map[Loc]:=RW[p1].Map[Loc] and not fPeace;
    2406     end
     2739  p1: integer;
     2740begin
     2741  assert(p >= 0); // no player's territory indicated by p=nPl
     2742  dec(TerritoryCount[RealMap[Loc] shr 27]);
     2743  inc(TerritoryCount[p]);
     2744  RealMap[Loc] := RealMap[Loc] and not($F shl 27) or Cardinal(p) shl 27;
     2745  if p = $F then
     2746    p := -1;
     2747  for p1 := 0 to nPl - 1 do
     2748    if 1 shl p1 and (GAlive or GWatching) <> 0 then
     2749      if RW[p1].Map[Loc] and fTerrain <> fUNKNOWN then
     2750      begin
     2751        RW[p1].Territory[Loc] := p;
     2752        if (p < nPl) and (p <> p1) and (1 shl p and GAlive <> 0) and
     2753          (RW[p1].Treaty[p] in [trPeace, TrFriendlyContact]) then
     2754          RW[p1].Map[Loc] := RW[p1].Map[Loc] or fPeace
     2755        else
     2756          RW[p1].Map[Loc] := RW[p1].Map[Loc] and not fPeace;
     2757      end
    24072758end;
    24082759
    24092760procedure ExpandTerritory(OriginLoc: integer);
    24102761var
    2411 i,dx,dy,dxMax,dyMax,Loc,NewOwner: integer;
    2412 begin
    2413 i:=0;
    2414 dyMax:=0;
    2415 while (dyMax+1)+(dyMax+1) shr 1<=CountryRadius do
    2416   inc(dyMax);
    2417 for dy:=-dyMax to dyMax do
    2418   begin
    2419   dxMax:=dy and 1;
    2420   while abs(dy)+(dxMax+2)+abs(abs(dy)-(dxMax+2)) shr 1<=CountryRadius do
    2421     inc(dxMax,2);
    2422   for dx:=-dxMax to dxMax do if (dy+dx) and 1=0 then
    2423     begin
    2424     NewOwner:=BorderChanges[i div 8] shr (i mod 8 *4) and $F;
    2425     Loc:=dLoc(OriginLoc,dx,dy);
    2426     if (Loc>=0) and (Cardinal(NewOwner)<>RealMap[Loc] shr 27) then
    2427       ChangeTerritory(Loc,NewOwner);
    2428     inc(i);
    2429     end
     2762  i, dx, dy, dxMax, dyMax, Loc, NewOwner: integer;
     2763begin
     2764  i := 0;
     2765  dyMax := 0;
     2766  while (dyMax + 1) + (dyMax + 1) shr 1 <= CountryRadius do
     2767    inc(dyMax);
     2768  for dy := -dyMax to dyMax do
     2769  begin
     2770    dxMax := dy and 1;
     2771    while abs(dy) + (dxMax + 2) + abs(abs(dy) - (dxMax + 2)) shr 1 <=
     2772      CountryRadius do
     2773      inc(dxMax, 2);
     2774    for dx := -dxMax to dxMax do
     2775      if (dy + dx) and 1 = 0 then
     2776      begin
     2777        NewOwner := BorderChanges[i div 8] shr (i mod 8 * 4) and $F;
     2778        Loc := dLoc(OriginLoc, dx, dy);
     2779        if (Loc >= 0) and (Cardinal(NewOwner) <> RealMap[Loc] shr 27) then
     2780          ChangeTerritory(Loc, NewOwner);
     2781        inc(i);
     2782      end
    24302783  end
    24312784end;
     
    24332786procedure CheckBorders(OriginLoc, PlayerLosingCity: integer);
    24342787// OriginLoc: only changes in CountryRadius around this location possible,
    2435 //   -1 for complete map, -2 for double-check (no more changes allowed)
     2788// -1 for complete map, -2 for double-check (no more changes allowed)
    24362789// PlayerLosingCity: do nothing but remove tiles no longer in reach from this
    2437 //   player's territory, -1 for full border recalculation
    2438 var
    2439 i,r,Loc,Loc1,dx,dy,p1,p2,cix,NewDist,dxMax,dyMax,OldOwner,V8,
    2440   NewOwner: integer;
    2441 Adjacent: TVicinity8Loc;
    2442 AtPeace: array[0..nPl,0..nPl] of boolean;
    2443 Country, FormerCountry, {to who's country a tile belongs}
    2444 Dist, FormerDist, StolenDist: array[0..lxmax*lymax-1] of ShortInt;
    2445 begin
    2446 if PlayerLosingCity>=0 then
    2447   begin
    2448   for Loc:=0 to MapSize-1 do StolenDist[Loc]:=CountryRadius+1;
    2449   for cix:=0 to RW[PlayerLosingCity].nCity-1 do
    2450     if RW[PlayerLosingCity].City[cix].Loc>=0 then
    2451       StolenDist[RW[PlayerLosingCity].City[cix].Loc]:=0;
    2452 
    2453   for r:=1 to CountryRadius shr 1 do
    2454     begin
    2455     move(StolenDist,FormerDist,MapSize);
    2456     for Loc:=0 to MapSize-1 do
    2457       if (FormerDist[Loc]<=CountryRadius-2) // use same conditions as below!
    2458         and ((1 shl (RealMap[Loc] and fTerrain))
    2459         and (1 shl fShore+1 shl fMountains+1 shl fArctic)=0) then
    2460         begin
    2461         V8_to_Loc(Loc,Adjacent);
    2462         for V8:=0 to 7 do
     2790// player's territory, -1 for full border recalculation
     2791var
     2792  i, r, Loc, Loc1, dx, dy, p1, p2, cix, NewDist, dxMax, dyMax, OldOwner, V8,
     2793    NewOwner: integer;
     2794  Adjacent: TVicinity8Loc;
     2795  AtPeace: array [0 .. nPl, 0 .. nPl] of boolean;
     2796  Country, FormerCountry, { to who's country a tile belongs }
     2797  Dist, FormerDist, StolenDist: array [0 .. lxmax * lymax - 1] of ShortInt;
     2798begin
     2799  if PlayerLosingCity >= 0 then
     2800  begin
     2801    for Loc := 0 to MapSize - 1 do
     2802      StolenDist[Loc] := CountryRadius + 1;
     2803    for cix := 0 to RW[PlayerLosingCity].nCity - 1 do
     2804      if RW[PlayerLosingCity].City[cix].Loc >= 0 then
     2805        StolenDist[RW[PlayerLosingCity].City[cix].Loc] := 0;
     2806
     2807    for r := 1 to CountryRadius shr 1 do
     2808    begin
     2809      move(StolenDist, FormerDist, MapSize);
     2810      for Loc := 0 to MapSize - 1 do
     2811        if (FormerDist[Loc] <= CountryRadius - 2)
     2812        // use same conditions as below!
     2813          and ((1 shl (RealMap[Loc] and fTerrain)) and
     2814          (1 shl fShore + 1 shl fMountains + 1 shl fArctic) = 0) then
     2815        begin
     2816          V8_to_Loc(Loc, Adjacent);
     2817          for V8 := 0 to 7 do
    24632818          begin
    2464           Loc1:=Adjacent[V8];
    2465           NewDist:=FormerDist[Loc]+2+V8 and 1;
    2466           if (Loc1>=0) and (Loc1<MapSize) and (NewDist<StolenDist[Loc1]) then
    2467             StolenDist[Loc1]:=NewDist;
     2819            Loc1 := Adjacent[V8];
     2820            NewDist := FormerDist[Loc] + 2 + V8 and 1;
     2821            if (Loc1 >= 0) and (Loc1 < MapSize) and (NewDist < StolenDist[Loc1])
     2822            then
     2823              StolenDist[Loc1] := NewDist;
    24682824          end
    24692825        end
     
    24712827  end;
    24722828
    2473 FillChar(Country,MapSize,-1);
    2474 for Loc:=0 to MapSize-1 do Dist[Loc]:=CountryRadius+1;
    2475 for p1:=0 to nPl-1 do if 1 shl p1 and GAlive<>0 then
    2476   for cix:=0 to RW[p1].nCity-1 do if RW[p1].City[cix].Loc>=0 then
    2477     begin
    2478     Country[RW[p1].City[cix].Loc]:=p1;
    2479     Dist[RW[p1].City[cix].Loc]:=0;
    2480     end;
    2481 
    2482 for r:=1 to CountryRadius shr 1 do
    2483   begin
    2484   move(Country,FormerCountry,MapSize);
    2485   move(Dist,FormerDist,MapSize);
    2486   for Loc:=0 to MapSize-1 do
    2487     if (FormerDist[Loc]<=CountryRadius-2) // use same conditions as above!
    2488       and ((1 shl (RealMap[Loc] and fTerrain))
    2489       and (1 shl fShore+1 shl fMountains+1 shl fArctic)=0) then
    2490       begin
    2491       assert(FormerCountry[Loc]>=0);
    2492       V8_to_Loc(Loc,Adjacent);
    2493       for V8:=0 to 7 do
    2494         begin
    2495         Loc1:=Adjacent[V8];
    2496         NewDist:=FormerDist[Loc]+2+V8 and 1;
    2497         if (Loc1>=0) and (Loc1<MapSize) and (NewDist<Dist[Loc1]) then
     2829  FillChar(Country, MapSize, -1);
     2830  for Loc := 0 to MapSize - 1 do
     2831    Dist[Loc] := CountryRadius + 1;
     2832  for p1 := 0 to nPl - 1 do
     2833    if 1 shl p1 and GAlive <> 0 then
     2834      for cix := 0 to RW[p1].nCity - 1 do
     2835        if RW[p1].City[cix].Loc >= 0 then
     2836        begin
     2837          Country[RW[p1].City[cix].Loc] := p1;
     2838          Dist[RW[p1].City[cix].Loc] := 0;
     2839        end;
     2840
     2841  for r := 1 to CountryRadius shr 1 do
     2842  begin
     2843    move(Country, FormerCountry, MapSize);
     2844    move(Dist, FormerDist, MapSize);
     2845    for Loc := 0 to MapSize - 1 do
     2846      if (FormerDist[Loc] <= CountryRadius - 2) // use same conditions as above!
     2847        and ((1 shl (RealMap[Loc] and fTerrain)) and
     2848        (1 shl fShore + 1 shl fMountains + 1 shl fArctic) = 0) then
     2849      begin
     2850        assert(FormerCountry[Loc] >= 0);
     2851        V8_to_Loc(Loc, Adjacent);
     2852        for V8 := 0 to 7 do
     2853        begin
     2854          Loc1 := Adjacent[V8];
     2855          NewDist := FormerDist[Loc] + 2 + V8 and 1;
     2856          if (Loc1 >= 0) and (Loc1 < MapSize) and (NewDist < Dist[Loc1]) then
    24982857          begin
    2499           Country[Loc1]:=FormerCountry[Loc];
    2500           Dist[Loc1]:=NewDist;
     2858            Country[Loc1] := FormerCountry[Loc];
     2859            Dist[Loc1] := NewDist;
    25012860          end
    25022861        end
     
    25042863  end;
    25052864
    2506 FillChar(AtPeace, sizeof(AtPeace), false);
    2507 for p1:=0 to nPl-1 do if 1 shl p1 and GAlive<>0 then
    2508   for p2:=0 to nPl-1 do
    2509     if (p2<>p1) and (1 shl p2 and GAlive<>0) and (RW[p1].Treaty[p2]>=trPeace) then
    2510     AtPeace[p1,p2]:=true;
    2511 
    2512 if OriginLoc>=0 then
     2865  FillChar(AtPeace, SizeOf(AtPeace), false);
     2866  for p1 := 0 to nPl - 1 do
     2867    if 1 shl p1 and GAlive <> 0 then
     2868      for p2 := 0 to nPl - 1 do
     2869        if (p2 <> p1) and (1 shl p2 and GAlive <> 0) and
     2870          (RW[p1].Treaty[p2] >= trPeace) then
     2871          AtPeace[p1, p2] := true;
     2872
     2873  if OriginLoc >= 0 then
    25132874  begin // update area only
    2514   i:=0;
    2515   fillchar(BorderChanges, sizeof(BorderChanges), 0);
    2516   dyMax:=0;
    2517   while (dyMax+1)+(dyMax+1) shr 1<=CountryRadius do
    2518     inc(dyMax);
    2519   for dy:=-dyMax to dyMax do
    2520     begin
    2521     dxMax:=dy and 1;
    2522     while abs(dy)+(dxMax+2)+abs(abs(dy)-(dxMax+2)) shr 1<=CountryRadius do
    2523       inc(dxMax,2);
    2524     for dx:=-dxMax to dxMax do if (dy+dx) and 1=0 then
    2525       begin
    2526       Loc:=dLoc(OriginLoc,dx,dy);
    2527       if Loc>=0 then
    2528         begin
    2529         OldOwner:=RealMap[Loc] shr 27;
    2530         NewOwner:=Country[Loc] and $f;
    2531         if NewOwner<>OldOwner then
    2532           if AtPeace[NewOwner,OldOwner]
    2533             and not ((OldOwner=PlayerLosingCity) and (StolenDist[Loc]>CountryRadius)) then
    2534             NewOwner:=OldOwner // peace fixes borders
    2535           else ChangeTerritory(Loc,NewOwner);
    2536         inc(BorderChanges[i div 8],NewOwner shl (i mod 8 *4));
    2537         end;
    2538       inc(i);
    2539       end
     2875    i := 0;
     2876    FillChar(BorderChanges, SizeOf(BorderChanges), 0);
     2877    dyMax := 0;
     2878    while (dyMax + 1) + (dyMax + 1) shr 1 <= CountryRadius do
     2879      inc(dyMax);
     2880    for dy := -dyMax to dyMax do
     2881    begin
     2882      dxMax := dy and 1;
     2883      while abs(dy) + (dxMax + 2) + abs(abs(dy) - (dxMax + 2)) shr 1 <=
     2884        CountryRadius do
     2885        inc(dxMax, 2);
     2886      for dx := -dxMax to dxMax do
     2887        if (dy + dx) and 1 = 0 then
     2888        begin
     2889          Loc := dLoc(OriginLoc, dx, dy);
     2890          if Loc >= 0 then
     2891          begin
     2892            OldOwner := RealMap[Loc] shr 27;
     2893            NewOwner := Country[Loc] and $F;
     2894            if NewOwner <> OldOwner then
     2895              if AtPeace[NewOwner, OldOwner] and
     2896                not((OldOwner = PlayerLosingCity) and
     2897                (StolenDist[Loc] > CountryRadius)) then
     2898                NewOwner := OldOwner // peace fixes borders
     2899              else
     2900                ChangeTerritory(Loc, NewOwner);
     2901            inc(BorderChanges[i div 8], NewOwner shl (i mod 8 * 4));
     2902          end;
     2903          inc(i);
     2904        end
    25402905    end
    25412906  end
    2542 else for Loc:=0 to MapSize-1 do // update complete map
    2543   begin
    2544   OldOwner:=RealMap[Loc] shr 27;
    2545   NewOwner:=Country[Loc] and $f;
    2546   if (NewOwner<>OldOwner)
    2547     and (not AtPeace[NewOwner,OldOwner]
    2548       or ((OldOwner=PlayerLosingCity) and (StolenDist[Loc]>CountryRadius))) then
    2549     begin
    2550     assert(OriginLoc<>-2); // test if border saving works
    2551     ChangeTerritory(Loc,NewOwner);
    2552     end;
    2553   end;
    2554 
    2555 {$IFOPT O-}if OriginLoc<>-2 then CheckBorders(-2);{$ENDIF} //check: single pass should do!
    2556 end; //CheckBorders
    2557 
    2558 procedure LogCheckBorders(p,cix,PlayerLosingCity: integer);
    2559 begin
    2560 CheckBorders(RW[p].City[cix].Loc,PlayerLosingCity);
    2561 IntServer(sIntExpandTerritory,p,cix,BorderChanges);
     2907  else
     2908    for Loc := 0 to MapSize - 1 do // update complete map
     2909    begin
     2910      OldOwner := RealMap[Loc] shr 27;
     2911      NewOwner := Country[Loc] and $F;
     2912      if (NewOwner <> OldOwner) and (not AtPeace[NewOwner, OldOwner] or
     2913        ((OldOwner = PlayerLosingCity) and (StolenDist[Loc] > CountryRadius)))
     2914      then
     2915      begin
     2916        assert(OriginLoc <> -2); // test if border saving works
     2917        ChangeTerritory(Loc, NewOwner);
     2918      end;
     2919    end;
     2920
     2921{$IFOPT O-} if OriginLoc <> -2 then
     2922    CheckBorders(-2); {$ENDIF} // check: single pass should do!
     2923end; // CheckBorders
     2924
     2925procedure LogCheckBorders(p, cix, PlayerLosingCity: integer);
     2926begin
     2927  CheckBorders(RW[p].City[cix].Loc, PlayerLosingCity);
     2928  IntServer(sIntExpandTerritory, p, cix, BorderChanges);
    25622929end;
    25632930
    25642931{
    2565                              Map Processing
    2566  ____________________________________________________________________
     2932  Map Processing
     2933  ____________________________________________________________________
    25672934}
    25682935
    2569 procedure CreateUnit(p,mix: integer);
    2570 begin
    2571 with RW[p] do
    2572   begin
    2573   Un[nUn].mix:=mix;
    2574   with Un[nUn] do
    2575     begin
    2576     ID:=UnBuilt[p];
    2577     inc(UnBuilt[p]);
    2578     Status:=0;
    2579     SavedStatus:=0;
    2580     inc(Model[mix].Built);
    2581     Home:=-1;
    2582     Health:=100;
    2583     Flags:=0;
    2584     Movement:=0;
    2585     if Model[mix].Domain=dAir then
    2586       begin
    2587       Fuel:=Model[mix].Cap[mcFuel];
    2588       Flags:=Flags or unBombsLoaded
    2589       end;
    2590     Job:=jNone;
    2591     Exp:=ExpCost shr 1;
    2592     TroopLoad:=0; AirLoad:=0; Master:=-1;
    2593     end;
    2594   inc(nUn);
     2936procedure CreateUnit(p, mix: integer);
     2937begin
     2938  with RW[p] do
     2939  begin
     2940    Un[nUn].mix := mix;
     2941    with Un[nUn] do
     2942    begin
     2943      ID := UnBuilt[p];
     2944      inc(UnBuilt[p]);
     2945      Status := 0;
     2946      SavedStatus := 0;
     2947      inc(Model[mix].Built);
     2948      Home := -1;
     2949      Health := 100;
     2950      Flags := 0;
     2951      Movement := 0;
     2952      if Model[mix].Domain = dAir then
     2953      begin
     2954        Fuel := Model[mix].Cap[mcFuel];
     2955        Flags := Flags or unBombsLoaded
     2956      end;
     2957      Job := jNone;
     2958      exp := ExpCost shr 1;
     2959      TroopLoad := 0;
     2960      AirLoad := 0;
     2961      Master := -1;
     2962    end;
     2963    inc(nUn);
    25952964  end
    25962965end;
    25972966
    2598 procedure FreeUnit(p,uix: integer);
     2967procedure FreeUnit(p, uix: integer);
    25992968// loc or master should be set after call
    26002969// implementation is critical for loading performance, change carefully
    26012970var
    2602 Loc0, uix1: integer;
    2603 Occ, ZoC: boolean;
    2604 begin
    2605 with RW[p].Un[uix] do
    2606   begin
    2607   Job:=jNone;
    2608   Flags:=Flags and not (unFortified or unMountainDelay);
    2609   Loc0:=Loc
    2610   end;
    2611 if Occupant[Loc0]>=0 then
    2612   begin
    2613   assert(Occupant[Loc0]=p);
    2614   Occ:=false;
    2615   ZoC:=false;
    2616   for uix1:=0 to RW[p].nUn-1 do with RW[p].Un[uix1] do
    2617     if (Loc=Loc0) and (Master<0) and (uix1<>uix) then
    2618       begin
    2619       Occ:=true;
    2620       if RW[p].Model[mix].Flags and mdZOC<>0 then
    2621         begin ZoC:=true; Break end
    2622       end;
    2623   if not Occ then Occupant[Loc0]:=-1;
    2624   if not ZoC then ZoCMap[Loc0]:=0;
    2625   end;
    2626 end;
    2627 
    2628 procedure PlaceUnit(p,uix: integer);
    2629 begin
    2630 with RW[p].Un[uix] do
    2631   begin
    2632   Occupant[Loc]:=p;
    2633   if RW[p].Model[mix].Flags and mdZOC<>0 then ZoCMap[Loc]:=1;
     2971  Loc0, uix1: integer;
     2972  Occ, ZoC: boolean;
     2973begin
     2974  with RW[p].Un[uix] do
     2975  begin
     2976    Job := jNone;
     2977    Flags := Flags and not(unFortified or unMountainDelay);
     2978    Loc0 := Loc
     2979  end;
     2980  if Occupant[Loc0] >= 0 then
     2981  begin
     2982    assert(Occupant[Loc0] = p);
     2983    Occ := false;
     2984    ZoC := false;
     2985    for uix1 := 0 to RW[p].nUn - 1 do
     2986      with RW[p].Un[uix1] do
     2987        if (Loc = Loc0) and (Master < 0) and (uix1 <> uix) then
     2988        begin
     2989          Occ := true;
     2990          if RW[p].Model[mix].Flags and mdZOC <> 0 then
     2991          begin
     2992            ZoC := true;
     2993            Break
     2994          end
     2995        end;
     2996    if not Occ then
     2997      Occupant[Loc0] := -1;
     2998    if not ZoC then
     2999      ZoCMap[Loc0] := 0;
     3000  end;
     3001end;
     3002
     3003procedure PlaceUnit(p, uix: integer);
     3004begin
     3005  with RW[p].Un[uix] do
     3006  begin
     3007    Occupant[Loc] := p;
     3008    if RW[p].Model[mix].Flags and mdZOC <> 0 then
     3009      ZoCMap[Loc] := 1;
    26343010  end
    26353011end;
     
    26373013procedure CountLost(p, mix, Enemy: integer);
    26383014begin
    2639 inc(RW[p].Model[mix].Lost);
    2640 TellAboutModel(Enemy,p,mix);
    2641 inc(Destroyed[Enemy,p,mix]);
    2642 end;
    2643 
    2644 procedure RemoveUnit(p,uix: integer; Enemy: integer = -1);
     3015  inc(RW[p].Model[mix].Lost);
     3016  TellAboutModel(Enemy, p, mix);
     3017  inc(Destroyed[Enemy, p, mix]);
     3018end;
     3019
     3020procedure RemoveUnit(p, uix: integer; Enemy: integer = -1);
    26453021// use enemy only from inside sMoveUnit if attack
    26463022var
    2647 uix1: integer;
    2648 begin
    2649 with RW[p].Un[uix] do
    2650   begin
    2651   assert((Loc>=0) or (RW[p].Model[mix].Kind=mkDiplomat)); // already freed when spy mission
    2652   if Loc>=0 then
    2653     FreeUnit(p,uix);
    2654   if Master>=0 then
    2655     if RW[p].Model[mix].Domain=dAir then dec(RW[p].Un[Master].AirLoad)
    2656     else dec(RW[p].Un[Master].TroopLoad);
    2657   if (TroopLoad>0) or (AirLoad>0) then
    2658     for uix1:=0 to RW[p].nUn-1 do
    2659       if (RW[p].Un[uix1].Loc>=0) and (RW[p].Un[uix1].Master=uix) then
    2660         {unit mastered by removed unit -- remove too}
    2661         begin
    2662         RW[p].Un[uix1].Loc:=-1;
    2663         if Enemy>=0 then CountLost(p,RW[p].Un[uix1].mix,Enemy);
     3023  uix1: integer;
     3024begin
     3025  with RW[p].Un[uix] do
     3026  begin
     3027    assert((Loc >= 0) or (RW[p].Model[mix].Kind = mkDiplomat));
     3028    // already freed when spy mission
     3029    if Loc >= 0 then
     3030      FreeUnit(p, uix);
     3031    if Master >= 0 then
     3032      if RW[p].Model[mix].Domain = dAir then
     3033        dec(RW[p].Un[Master].AirLoad)
     3034      else
     3035        dec(RW[p].Un[Master].TroopLoad);
     3036    if (TroopLoad > 0) or (AirLoad > 0) then
     3037      for uix1 := 0 to RW[p].nUn - 1 do
     3038        if (RW[p].Un[uix1].Loc >= 0) and (RW[p].Un[uix1].Master = uix) then
     3039        { unit mastered by removed unit -- remove too }
     3040        begin
     3041          RW[p].Un[uix1].Loc := -1;
     3042          if Enemy >= 0 then
     3043            CountLost(p, RW[p].Un[uix1].mix, Enemy);
    26643044        end;
    2665   Loc:=-1;
    2666   if Enemy>=0 then CountLost(p,mix,Enemy);
     3045    Loc := -1;
     3046    if Enemy >= 0 then
     3047      CountLost(p, mix, Enemy);
    26673048  end
    2668 end;{RemoveUnit}
    2669 
    2670 procedure RemoveUnit_UpdateMap(p,uix: integer);
    2671 var
    2672 Loc0: integer;
    2673 begin
    2674 Loc0:=RW[p].Un[uix].Loc;
    2675 RemoveUnit(p,uix);
    2676 if Mode>moLoading_Fast then UpdateUnitMap(Loc0);
    2677 end;
    2678 
    2679 procedure RemoveAllUnits(p,Loc: integer; Enemy: integer = -1);
    2680 var
    2681 uix: integer;
    2682 begin
    2683 for uix:=0 to RW[p].nUn-1 do
    2684   if RW[p].Un[uix].Loc=Loc then
    2685     begin
    2686     if Enemy>=0 then CountLost(p,RW[p].Un[uix].mix,Enemy);
    2687     RW[p].Un[uix].Loc:=-1
    2688     end;
    2689 Occupant[Loc]:=-1;
    2690 ZoCMap[Loc]:=0;
    2691 end;
    2692 
    2693 procedure RemoveDomainUnits(d,p,Loc: integer);
    2694 var
    2695 uix: integer;
    2696 begin
    2697 for uix:=0 to RW[p].nUn-1 do
    2698   if (RW[p].Model[RW[p].Un[uix].mix].Domain=d) and (RW[p].Un[uix].Loc=Loc) then
    2699     RemoveUnit(p,uix);
    2700 end;
    2701 
    2702 procedure FoundCity(p,FoundLoc: integer);
    2703 var
    2704 p1,cix1,V21,dx,dy: integer;
    2705 begin
    2706 if RW[p].nCity=ncmax then exit;
    2707 inc(RW[p].nCity);
    2708 with RW[p].City[RW[p].nCity-1] do
    2709   begin
    2710   Size:=2;
    2711   Status:=0;
    2712   SavedStatus:=0;
    2713   FillChar(Built,SizeOf(Built),0);
    2714   Food:=0;
    2715   Project:=cpImp+imTrGoods;
    2716   Prod:=0;
    2717   Project0:=Project;
    2718   Prod0:=0;
    2719   Pollution:=0;
    2720   N1:=0;
    2721   Loc:=FoundLoc;
    2722   if UsedByCity[FoundLoc]>=0 then
    2723     begin {central tile is exploited - toggle in exploiting city}
    2724     p1:=p;
    2725     SearchCity(UsedByCity[FoundLoc],p1,cix1);
    2726     dxdy(UsedByCity[FoundLoc],FoundLoc,dx,dy);
    2727     V21:=(dy+3) shl 2+(dx+3) shr 1;
    2728     RW[p1].City[cix1].Tiles:=RW[p1].City[cix1].Tiles and not (1 shl V21);
    2729     end;
    2730   Tiles:=1 shl 13; {exploit central tile}
    2731   UsedByCity[FoundLoc]:=FoundLoc;
    2732   RealMap[FoundLoc]:=RealMap[FoundLoc]
    2733     and (fTerrain or fSpecial or fRiver or nPl shl 27) or fCity;
    2734    
    2735   ChangeTerritory(Loc,p)
    2736   end;
    2737 end; {FoundCity}
    2738 
    2739 procedure StealCity(p,cix: integer; SaveUnits: boolean);
    2740 var
    2741 i,j,uix1,cix1,nearest: integer;
    2742 begin
    2743 for i:=0 to 27 do
    2744   if RW[p].City[cix].Built[i]=1 then
    2745     begin
    2746     GWonder[i].EffectiveOwner:=-1;
    2747     if i=woPyramids then FreeSlaves;
    2748     if i=woEiffel then // deactivate expired wonders
    2749       for j:=0 to 27 do if GWonder[j].EffectiveOwner=p then
    2750         CheckExpiration(j);
    2751     end;
    2752 for i:=28 to nImp-1 do
    2753   if (Imp[i].Kind<>ikCommon) and (RW[p].City[cix].Built[i]>0) then
    2754     begin {destroy national projects}
    2755     RW[p].NatBuilt[i]:=0;
    2756     if i=imGrWall then GrWallContinent[p]:=-1;
    2757     end;
    2758 
    2759 for uix1:=0 to RW[p].nUn-1 do with RW[p].Un[uix1] do
    2760   if (Loc>=0) and (Home=cix) then
    2761     if SaveUnits then
    2762       begin // support units by nearest other city
    2763       nearest:=-1;
    2764       for cix1:=0 to RW[p].nCity-1 do
    2765         if (cix1<>cix) and (RW[p].City[cix1].Loc>=0)
    2766           and ((nearest<0) or (Distance(RW[p].City[cix1].Loc,Loc)
    2767           <Distance(RW[p].City[nearest].Loc,Loc))) then
    2768           nearest:=cix1;
    2769       Home:=nearest
     3049end; { RemoveUnit }
     3050
     3051procedure RemoveUnit_UpdateMap(p, uix: integer);
     3052var
     3053  Loc0: integer;
     3054begin
     3055  Loc0 := RW[p].Un[uix].Loc;
     3056  RemoveUnit(p, uix);
     3057  if Mode > moLoading_Fast then
     3058    UpdateUnitMap(Loc0);
     3059end;
     3060
     3061procedure RemoveAllUnits(p, Loc: integer; Enemy: integer = -1);
     3062var
     3063  uix: integer;
     3064begin
     3065  for uix := 0 to RW[p].nUn - 1 do
     3066    if RW[p].Un[uix].Loc = Loc then
     3067    begin
     3068      if Enemy >= 0 then
     3069        CountLost(p, RW[p].Un[uix].mix, Enemy);
     3070      RW[p].Un[uix].Loc := -1
     3071    end;
     3072  Occupant[Loc] := -1;
     3073  ZoCMap[Loc] := 0;
     3074end;
     3075
     3076procedure RemoveDomainUnits(d, p, Loc: integer);
     3077var
     3078  uix: integer;
     3079begin
     3080  for uix := 0 to RW[p].nUn - 1 do
     3081    if (RW[p].Model[RW[p].Un[uix].mix].Domain = d) and (RW[p].Un[uix].Loc = Loc)
     3082    then
     3083      RemoveUnit(p, uix);
     3084end;
     3085
     3086procedure FoundCity(p, FoundLoc: integer);
     3087var
     3088  p1, cix1, V21, dx, dy: integer;
     3089begin
     3090  if RW[p].nCity = ncmax then
     3091    exit;
     3092  inc(RW[p].nCity);
     3093  with RW[p].City[RW[p].nCity - 1] do
     3094  begin
     3095    Size := 2;
     3096    Status := 0;
     3097    SavedStatus := 0;
     3098    FillChar(Built, SizeOf(Built), 0);
     3099    Food := 0;
     3100    Project := cpImp + imTrGoods;
     3101    Prod := 0;
     3102    Project0 := Project;
     3103    Prod0 := 0;
     3104    Pollution := 0;
     3105    N1 := 0;
     3106    Loc := FoundLoc;
     3107    if UsedByCity[FoundLoc] >= 0 then
     3108    begin { central tile is exploited - toggle in exploiting city }
     3109      p1 := p;
     3110      SearchCity(UsedByCity[FoundLoc], p1, cix1);
     3111      dxdy(UsedByCity[FoundLoc], FoundLoc, dx, dy);
     3112      V21 := (dy + 3) shl 2 + (dx + 3) shr 1;
     3113      RW[p1].City[cix1].Tiles := RW[p1].City[cix1].Tiles and not(1 shl V21);
     3114    end;
     3115    Tiles := 1 shl 13; { exploit central tile }
     3116    UsedByCity[FoundLoc] := FoundLoc;
     3117    RealMap[FoundLoc] := RealMap[FoundLoc] and
     3118      (fTerrain or fSpecial or fRiver or nPl shl 27) or fCity;
     3119
     3120    ChangeTerritory(Loc, p)
     3121  end;
     3122end; { FoundCity }
     3123
     3124procedure StealCity(p, cix: integer; SaveUnits: boolean);
     3125var
     3126  i, j, uix1, cix1, nearest: integer;
     3127begin
     3128  for i := 0 to 27 do
     3129    if RW[p].City[cix].Built[i] = 1 then
     3130    begin
     3131      GWonder[i].EffectiveOwner := -1;
     3132      if i = woPyramids then
     3133        FreeSlaves;
     3134      if i = woEiffel then // deactivate expired wonders
     3135        for j := 0 to 27 do
     3136          if GWonder[j].EffectiveOwner = p then
     3137            CheckExpiration(j);
     3138    end;
     3139  for i := 28 to nImp - 1 do
     3140    if (Imp[i].Kind <> ikCommon) and (RW[p].City[cix].Built[i] > 0) then
     3141    begin { destroy national projects }
     3142      RW[p].NatBuilt[i] := 0;
     3143      if i = imGrWall then
     3144        GrWallContinent[p] := -1;
     3145    end;
     3146
     3147  for uix1 := 0 to RW[p].nUn - 1 do
     3148    with RW[p].Un[uix1] do
     3149      if (Loc >= 0) and (Home = cix) then
     3150        if SaveUnits then
     3151        begin // support units by nearest other city
     3152          nearest := -1;
     3153          for cix1 := 0 to RW[p].nCity - 1 do
     3154            if (cix1 <> cix) and (RW[p].City[cix1].Loc >= 0) and
     3155              ((nearest < 0) or (Distance(RW[p].City[cix1].Loc, Loc) <
     3156              Distance(RW[p].City[nearest].Loc, Loc))) then
     3157              nearest := cix1;
     3158          Home := nearest
     3159        end
     3160        else
     3161          RemoveUnit(p, uix1); // destroy supported units
     3162end; // StealCity
     3163
     3164procedure DestroyCity(p, cix: integer; SaveUnits: boolean);
     3165var
     3166  i, V21: integer;
     3167  Radius: TVicinity21Loc;
     3168begin
     3169  StealCity(p, cix, SaveUnits);
     3170  with RW[p].City[cix] do
     3171  begin
     3172    for i := 0 to 27 do
     3173      if Built[i] > 0 then
     3174        GWonder[i].CityID := -2; // wonder destroyed
     3175    V21_to_Loc(Loc, Radius);
     3176    for V21 := 1 to 26 do
     3177      if 1 shl V21 and Tiles <> 0 then
     3178        UsedByCity[Radius[V21]] := -1;
     3179    RealMap[Loc] := RealMap[Loc] and not fCity;
     3180    Loc := -1
     3181  end
     3182end; // DestroyCity
     3183
     3184procedure ChangeCityOwner(pOld, cixOld, pNew: integer);
     3185var
     3186  i, j, cix1, Loc1, V21: integer;
     3187  Radius: TVicinity21Loc;
     3188begin
     3189  inc(RW[pNew].nCity);
     3190  RW[pNew].City[RW[pNew].nCity - 1] := RW[pOld].City[cixOld];
     3191  StealCity(pOld, cixOld, false);
     3192  RW[pOld].City[cixOld].Loc := -1;
     3193  with RW[pNew].City[(RW[pNew].nCity - 1)] do
     3194  begin
     3195    Food := 0;
     3196    Project := cpImp + imTrGoods;
     3197    Prod := 0;
     3198    Project0 := Project;
     3199    Prod0 := 0;
     3200    Status := 0;
     3201    SavedStatus := 0;
     3202    N1 := 0;
     3203
     3204    // check for siege
     3205    V21_to_Loc(Loc, Radius);
     3206    for V21 := 1 to 26 do
     3207      if Tiles and (1 shl V21) and not(1 shl CityOwnTile) <> 0 then
     3208      begin
     3209        Loc1 := Radius[V21];
     3210        assert((Loc1 >= 0) and (Loc1 < MapSize) and (UsedByCity[Loc1] = Loc));
     3211        if (ZoCMap[Loc1] > 0) and (Occupant[Loc1] <> pNew) and
     3212          (RW[pNew].Treaty[Occupant[Loc1]] < trAlliance) then
     3213        begin // tile can't remain exploited
     3214          Tiles := Tiles and not(1 shl V21);
     3215          UsedByCity[Loc1] := -1;
     3216        end;
     3217        // don't check for siege by peace territory here, because territory
     3218        // might not be up to date -- done in turn beginning anyway
     3219      end;
     3220    Built[imTownHall] := 0;
     3221    Built[imCourt] := 0;
     3222    for i := 28 to nImp - 1 do
     3223      if Imp[i].Kind <> ikCommon then
     3224        Built[i] := 0; { destroy national projects }
     3225    for i := 0 to 27 do
     3226      if Built[i] = 1 then
     3227      begin // new wonder owner!
     3228        GWonder[i].EffectiveOwner := pNew;
     3229        if i = woEiffel then // reactivate expired wonders
     3230        begin
     3231          for j := 0 to 27 do
     3232            if Imp[j].Expiration >= 0 then
     3233              for cix1 := 0 to (RW[pNew].nCity - 1) do
     3234                if RW[pNew].City[cix1].Built[j] = 1 then
     3235                  GWonder[j].EffectiveOwner := pNew
     3236        end
     3237        else
     3238          CheckExpiration(i);
     3239        case i of
     3240          woLighthouse:
     3241            CheckSpecialModels(pNew, preLighthouse);
     3242          woLeo:
     3243            CheckSpecialModels(pNew, preLeo);
     3244          woPyramids:
     3245            CheckSpecialModels(pNew, preBuilder);
     3246        end;
     3247      end;
     3248
     3249    // remove city from enemy cities
     3250    // not done by Discover, because fCity still set!
     3251    cix1 := RW[pNew].nEnemyCity - 1;
     3252    while (cix1 >= 0) and (RW[pNew].EnemyCity[cix1].Loc <> Loc) do
     3253      dec(cix1);
     3254    assert(cix1 >= 0);
     3255    RW[pNew].EnemyCity[cix1].Loc := -1;
     3256
     3257    ChangeTerritory(Loc, pNew);
     3258  end;
     3259end; // ChangeCityOwner
     3260
     3261procedure CompleteJob(p, Loc, Job: integer);
     3262var
     3263  ChangedTerrain, p1: integer;
     3264begin
     3265  assert(Job <> jCity);
     3266  ChangedTerrain := -1;
     3267  case Job of
     3268    jRoad:
     3269      RealMap[Loc] := RealMap[Loc] or fRoad;
     3270    jRR:
     3271      RealMap[Loc] := RealMap[Loc] and not fRoad or fRR;
     3272    jClear:
     3273      begin
     3274        ChangedTerrain := Terrain[RealMap[Loc] and fTerrain].ClearTerrain;
     3275        RealMap[Loc] := RealMap[Loc] and not fTerrain or
     3276          Cardinal(ChangedTerrain);
     3277        RealMap[Loc] := RealMap[Loc] and not(3 shl 5) or
     3278          ActualSpecialTile(Loc) shl 5;
     3279      end;
     3280    jIrr:
     3281      RealMap[Loc] := RealMap[Loc] and not fTerImp or tiIrrigation;
     3282    jFarm:
     3283      RealMap[Loc] := RealMap[Loc] and not fTerImp or tiFarm;
     3284    jAfforest:
     3285      begin
     3286        ChangedTerrain := Terrain[RealMap[Loc] and fTerrain].AfforestTerrain;
     3287        RealMap[Loc] := RealMap[Loc] and not fTerrain or
     3288          Cardinal(ChangedTerrain);
     3289        RealMap[Loc] := RealMap[Loc] and not(3 shl 5) or
     3290          ActualSpecialTile(Loc) shl 5;
     3291      end;
     3292    jMine:
     3293      RealMap[Loc] := RealMap[Loc] and not fTerImp or tiMine;
     3294    jFort:
     3295      RealMap[Loc] := RealMap[Loc] and not fTerImp or tiFort;
     3296    jCanal:
     3297      RealMap[Loc] := RealMap[Loc] or fCanal;
     3298    jTrans:
     3299      begin
     3300        ChangedTerrain := Terrain[RealMap[Loc] and fTerrain].TransTerrain;
     3301        RealMap[Loc] := RealMap[Loc] and not fTerrain or
     3302          Cardinal(ChangedTerrain);
     3303        RealMap[Loc] := RealMap[Loc] and not(3 shl 5) or
     3304          ActualSpecialTile(Loc) shl 5;
     3305        if not(RealMap[Loc] and fTerrain in TerrType_Canalable) then
     3306        begin
     3307          RemoveDomainUnits(dSea, p, Loc);
     3308          RealMap[Loc] := RealMap[Loc] and not fCanal;
     3309        end;
     3310      end;
     3311    jPoll:
     3312      RealMap[Loc] := RealMap[Loc] and not fPoll;
     3313    jBase:
     3314      RealMap[Loc] := RealMap[Loc] and not fTerImp or tiBase;
     3315    jPillage:
     3316      if RealMap[Loc] and fTerImp <> 0 then
     3317      begin
     3318        if RealMap[Loc] and fTerImp = tiBase then
     3319          RemoveDomainUnits(dAir, p, Loc);
     3320        RealMap[Loc] := RealMap[Loc] and not fTerImp
    27703321      end
    2771     else RemoveUnit(p,uix1); // destroy supported units
    2772 end; //StealCity
    2773 
    2774 procedure DestroyCity(p,cix: integer; SaveUnits: boolean);
    2775 var
    2776 i,V21: integer;
    2777 Radius: TVicinity21Loc;
    2778 begin
    2779 StealCity(p,cix,SaveUnits);
    2780 with RW[p].City[cix] do
    2781   begin
    2782   for i:=0 to 27 do
    2783     if Built[i]>0 then GWonder[i].CityID:=-2; // wonder destroyed
    2784   V21_to_Loc(Loc,Radius);
    2785   for V21:=1 to 26 do if 1 shl V21 and Tiles<>0 then
    2786     UsedByCity[Radius[V21]]:=-1;
    2787   RealMap[Loc]:=RealMap[Loc] and not fCity;
    2788   Loc:=-1
    2789   end
    2790 end; //DestroyCity
    2791 
    2792 procedure ChangeCityOwner(pOld,cixOld,pNew: integer);
    2793 var
    2794 i,j,cix1,Loc1,V21: integer;
    2795 Radius: TVicinity21Loc;
    2796 begin
    2797 inc(RW[pNew].nCity);
    2798 RW[pNew].City[RW[pNew].nCity-1]:=RW[pOld].City[cixOld];
    2799 StealCity(pOld,cixOld,false);
    2800 RW[pOld].City[cixOld].Loc:=-1;
    2801 with RW[pNew].City[(RW[pNew].nCity-1)] do
    2802   begin
    2803   Food:=0;
    2804   Project:=cpImp+imTrGoods;
    2805   Prod:=0;
    2806   Project0:=Project;
    2807   Prod0:=0;
    2808   Status:=0;
    2809   SavedStatus:=0;
    2810   N1:=0;
    2811 
    2812   // check for siege
    2813   V21_to_Loc(Loc,Radius);
    2814   for V21:=1 to 26 do if Tiles and (1 shl V21) and not (1 shl CityOwnTile)<>0 then
    2815     begin
    2816     Loc1:=Radius[V21];
    2817     assert((Loc1>=0) and (Loc1<MapSize) and (UsedByCity[Loc1]=Loc));
    2818     if (ZoCMap[Loc1]>0) and (Occupant[Loc1]<>pNew)
    2819       and (RW[pNew].Treaty[Occupant[Loc1]]<trAlliance) then
    2820       begin // tile can't remain exploited
    2821       Tiles:=Tiles and not (1 shl V21);
    2822       UsedByCity[Loc1]:=-1;
    2823       end;
    2824     // don't check for siege by peace territory here, because territory
    2825     // might not be up to date -- done in turn beginning anyway
    2826     end;
    2827   Built[imTownHall]:=0;
    2828   Built[imCourt]:=0;
    2829   for i:=28 to nImp-1 do if Imp[i].Kind<>ikCommon then
    2830     Built[i]:=0; {destroy national projects}
    2831   for i:=0 to 27 do
    2832     if Built[i]=1 then
    2833       begin // new wonder owner!
    2834       GWonder[i].EffectiveOwner:=pNew;
    2835       if i=woEiffel then // reactivate expired wonders
    2836         begin
    2837         for j:=0 to 27 do if Imp[j].Expiration>=0 then
    2838           for cix1:=0 to (RW[pNew].nCity-1) do
    2839             if RW[pNew].City[cix1].Built[j]=1 then
    2840               GWonder[j].EffectiveOwner:=pNew
    2841         end
    2842       else CheckExpiration(i);
    2843       case i of
    2844         woLighthouse: CheckSpecialModels(pNew,preLighthouse);
    2845         woLeo: CheckSpecialModels(pNew,preLeo);
    2846         woPyramids: CheckSpecialModels(pNew,preBuilder);
    2847         end;
    2848       end;
    2849 
    2850   // remove city from enemy cities
    2851   // not done by Discover, because fCity still set!
    2852   cix1:=RW[pNew].nEnemyCity-1;
    2853   while (cix1>=0) and (RW[pNew].EnemyCity[cix1].Loc<>Loc) do dec(cix1);
    2854   assert(cix1>=0);
    2855   RW[pNew].EnemyCity[cix1].Loc:=-1;
    2856 
    2857   ChangeTerritory(Loc,pNew);
    2858   end;
    2859 end; //ChangeCityOwner
    2860 
    2861 procedure CompleteJob(p,Loc,Job: integer);
    2862 var
    2863 ChangedTerrain,p1: integer;
    2864 begin
    2865 assert(Job<>jCity);
    2866 ChangedTerrain:=-1;
    2867 case Job of
    2868   jRoad:
    2869     RealMap[Loc]:=RealMap[Loc] or fRoad;
    2870   jRR:
    2871     RealMap[Loc]:=RealMap[Loc] and not fRoad or fRR;
    2872   jClear:
    2873     begin
    2874     ChangedTerrain:=Terrain[RealMap[Loc] and fTerrain].ClearTerrain;
    2875     RealMap[Loc]:=RealMap[Loc] and not fTerrain or Cardinal(ChangedTerrain);
    2876     RealMap[Loc]:=RealMap[Loc] and not (3 shl 5) or ActualSpecialTile(Loc) shl 5;
    2877     end;
    2878   jIrr:
    2879     RealMap[Loc]:=RealMap[Loc] and not fTerImp or tiIrrigation;
    2880   jFarm:
    2881     RealMap[Loc]:=RealMap[Loc] and not fTerImp or tiFarm;
    2882   jAfforest:
    2883     begin
    2884     ChangedTerrain:=Terrain[RealMap[Loc] and fTerrain].AfforestTerrain;
    2885     RealMap[Loc]:=RealMap[Loc] and not fTerrain or Cardinal(ChangedTerrain);
    2886     RealMap[Loc]:=RealMap[Loc] and not (3 shl 5) or ActualSpecialTile(Loc) shl 5;
    2887     end;
    2888   jMine:
    2889     RealMap[Loc]:=RealMap[Loc] and not fTerImp or tiMine;
    2890   jFort:
    2891     RealMap[Loc]:=RealMap[Loc] and not fTerImp or tiFort;
    2892   jCanal:
    2893     RealMap[Loc]:=RealMap[Loc] or fCanal;
    2894   jTrans:
    2895     begin
    2896     ChangedTerrain:=Terrain[RealMap[Loc] and fTerrain].TransTerrain;
    2897     RealMap[Loc]:=RealMap[Loc] and not fTerrain or Cardinal(ChangedTerrain);
    2898     RealMap[Loc]:=RealMap[Loc] and not (3 shl 5) or ActualSpecialTile(Loc) shl 5;
    2899     if not (RealMap[Loc] and fTerrain in TerrType_Canalable) then
    2900       begin
    2901       RemoveDomainUnits(dSea,p,Loc);
    2902       RealMap[Loc]:=RealMap[Loc] and not fCanal;
    2903       end;
    2904     end;
    2905   jPoll:
    2906     RealMap[Loc]:=RealMap[Loc] and not fPoll;
    2907   jBase:
    2908     RealMap[Loc]:=RealMap[Loc] and not fTerImp or tiBase;
    2909   jPillage:
    2910     if RealMap[Loc] and fTerImp<>0 then
    2911       begin
    2912       if RealMap[Loc] and fTerImp=tiBase then
    2913         RemoveDomainUnits(dAir,p,Loc);
    2914       RealMap[Loc]:=RealMap[Loc] and not fTerImp
     3322      else if RealMap[Loc] and fCanal <> 0 then
     3323      begin
     3324        RemoveDomainUnits(dSea, p, Loc);
     3325        RealMap[Loc] := RealMap[Loc] and not fCanal
    29153326      end
    2916     else if RealMap[Loc] and fCanal<>0 then
    2917       begin
    2918       RemoveDomainUnits(dSea,p,Loc);
    2919       RealMap[Loc]:=RealMap[Loc] and not fCanal
    2920       end
    2921     else if RealMap[Loc] and fRR<>0 then
    2922       RealMap[Loc]:=RealMap[Loc] and not fRR or fRoad
    2923     else if RealMap[Loc] and fRoad<>0 then
    2924       RealMap[Loc]:=RealMap[Loc] and not fRoad;
    2925   end;
    2926 if ChangedTerrain>=0 then
     3327      else if RealMap[Loc] and fRR <> 0 then
     3328        RealMap[Loc] := RealMap[Loc] and not fRR or fRoad
     3329      else if RealMap[Loc] and fRoad <> 0 then
     3330        RealMap[Loc] := RealMap[Loc] and not fRoad;
     3331  end;
     3332  if ChangedTerrain >= 0 then
    29273333  begin // remove terrain improvements if not possible on new terrain
    2928   if ((RealMap[Loc] and fTerImp=tiIrrigation)
    2929       or (RealMap[Loc] and fTerImp=tiFarm))
    2930     and ((Terrain[ChangedTerrain].IrrClearWork=0)
    2931       or (Terrain[ChangedTerrain].ClearTerrain>=0)) then
    2932     RealMap[Loc]:=RealMap[Loc] and not fTerImp;
    2933   if (RealMap[Loc] and fTerImp=tiMine)
    2934     and ((Terrain[ChangedTerrain].MineAfforestWork=0)
    2935       or (Terrain[ChangedTerrain].AfforestTerrain>=0)) then
    2936     RealMap[Loc]:=RealMap[Loc] and not fTerImp;
    2937   end;
    2938 
    2939 // update map of all observing players
    2940 if Mode>moLoading_Fast then
    2941   for p1:=0 to nPl-1 do
    2942     if (1 shl p1 and (GAlive or GWatching)<>0)
    2943       and (ObserveLevel[Loc] shr (2*p1) and 3>lNoObserve) then
    2944       RW[p1].Map[Loc]:=RW[p1].Map[Loc]
    2945         and not (fTerrain or fSpecial or fTerImp or fRoad or fRR or fCanal or fPoll)
    2946         or RealMap[Loc] and (fTerrain or fSpecial or fTerImp or fRoad or fRR or fCanal or fPoll);
    2947 end; //CompleteJob
     3334    if ((RealMap[Loc] and fTerImp = tiIrrigation) or
     3335      (RealMap[Loc] and fTerImp = tiFarm)) and
     3336      ((Terrain[ChangedTerrain].IrrClearWork = 0) or
     3337      (Terrain[ChangedTerrain].ClearTerrain >= 0)) then
     3338      RealMap[Loc] := RealMap[Loc] and not fTerImp;
     3339    if (RealMap[Loc] and fTerImp = tiMine) and
     3340      ((Terrain[ChangedTerrain].MineAfforestWork = 0) or
     3341      (Terrain[ChangedTerrain].AfforestTerrain >= 0)) then
     3342      RealMap[Loc] := RealMap[Loc] and not fTerImp;
     3343  end;
     3344
     3345  // update map of all observing players
     3346  if Mode > moLoading_Fast then
     3347    for p1 := 0 to nPl - 1 do
     3348      if (1 shl p1 and (GAlive or GWatching) <> 0) and
     3349        (ObserveLevel[Loc] shr (2 * p1) and 3 > lNoObserve) then
     3350        RW[p1].Map[Loc] := RW[p1].Map[Loc] and
     3351          not(fTerrain or fSpecial or fTerImp or fRoad or fRR or fCanal or
     3352          fPoll) or RealMap[Loc] and (fTerrain or fSpecial or fTerImp or
     3353          fRoad or fRR or fCanal or fPoll);
     3354end; // CompleteJob
    29483355
    29493356{
    2950                               Diplomacy
    2951  ____________________________________________________________________
     3357  Diplomacy
     3358  ____________________________________________________________________
    29523359}
    29533360procedure GiveCivilReport(p, pAbout: integer);
    29543361begin
    2955 with RW[p].EnemyReport[pAbout]^ do
    2956   begin
    2957   // general info
    2958   TurnOfCivilReport:=LastValidStat[pAbout];
    2959   move(RW[pAbout].Treaty, Treaty, SizeOf(Treaty));
    2960   Government:=RW[pAbout].Government;
    2961   Money:=RW[pAbout].Money;
    2962 
    2963   // tech info
    2964   ResearchTech:=RW[pAbout].ResearchTech;
    2965   ResearchDone:=RW[pAbout].Research*100 div TechCost(pAbout);
    2966   if ResearchDone>100 then
    2967     ResearchDone:=100;
    2968   move(RW[pAbout].Tech, Tech, nAdv);
     3362  with RW[p].EnemyReport[pAbout]^ do
     3363  begin
     3364    // general info
     3365    TurnOfCivilReport := LastValidStat[pAbout];
     3366    move(RW[pAbout].Treaty, Treaty, SizeOf(Treaty));
     3367    Government := RW[pAbout].Government;
     3368    Money := RW[pAbout].Money;
     3369
     3370    // tech info
     3371    ResearchTech := RW[pAbout].ResearchTech;
     3372    ResearchDone := RW[pAbout].Research * 100 div TechCost(pAbout);
     3373    if ResearchDone > 100 then
     3374      ResearchDone := 100;
     3375    move(RW[pAbout].Tech, Tech, nAdv);
    29693376  end;
    29703377end;
     
    29723379procedure GiveMilReport(p, pAbout: integer);
    29733380var
    2974 uix,mix: integer;
    2975 begin
    2976 with RW[p].EnemyReport[pAbout]^ do
    2977   begin
    2978   TurnOfMilReport:=LastValidStat[pAbout];
    2979   nModelCounted:=RW[pAbout].nModel;
    2980   for mix:=0 to RW[pAbout].nModel-1 do
    2981     begin TellAboutModel(p,pAbout,mix); UnCount[mix]:=0 end;
    2982   for uix:=0 to RW[pAbout].nUn-1 do
    2983     if RW[pAbout].Un[uix].Loc>=0 then inc(UnCount[RW[pAbout].Un[uix].mix]);
     3381  uix, mix: integer;
     3382begin
     3383  with RW[p].EnemyReport[pAbout]^ do
     3384  begin
     3385    TurnOfMilReport := LastValidStat[pAbout];
     3386    nModelCounted := RW[pAbout].nModel;
     3387    for mix := 0 to RW[pAbout].nModel - 1 do
     3388    begin
     3389      TellAboutModel(p, pAbout, mix);
     3390      UnCount[mix] := 0
     3391    end;
     3392    for uix := 0 to RW[pAbout].nUn - 1 do
     3393      if RW[pAbout].Un[uix].Loc >= 0 then
     3394        inc(UnCount[RW[pAbout].Un[uix].mix]);
    29843395  end
    29853396end;
     
    29873398procedure ShowPrice(pSender, pTarget, Price: integer);
    29883399begin
    2989 case Price and opMask of
    2990   opTech: // + advance
    2991     with RW[pTarget].EnemyReport[pSender]^ do
    2992       if Tech[Price-opTech]<tsApplicable then
    2993         Tech[Price-opTech]:=tsApplicable;
    2994   opModel: // + model index
    2995     TellAboutModel(pTarget,pSender,Price-opModel);
    2996 { opCity: // + city ID
    2997     begin
    2998     end;}
     3400  case Price and opMask of
     3401    opTech: // + advance
     3402      with RW[pTarget].EnemyReport[pSender]^ do
     3403        if Tech[Price - opTech] < tsApplicable then
     3404          Tech[Price - opTech] := tsApplicable;
     3405    opModel: // + model index
     3406      TellAboutModel(pTarget, pSender, Price - opModel);
     3407    { opCity: // + city ID
     3408      begin
     3409      end; }
    29993410  end
    30003411end;
     
    30023413function CopyCivilReport(pSender, pTarget, pAbout: integer): boolean;
    30033414var
    3004 i: integer;
    3005 rSender, rTarget: ^TEnemyReport;
     3415  i: integer;
     3416  rSender, rTarget: ^TEnemyReport;
    30063417begin // copy third nation civil report
    3007 result:=false;
    3008 if RW[pTarget].Treaty[pAbout]=trNoContact then
    3009   IntroduceEnemy(pTarget, pAbout);
    3010 rSender:=pointer(RW[pSender].EnemyReport[pAbout]);
    3011 rTarget:=pointer(RW[pTarget].EnemyReport[pAbout]);
    3012 if rSender.TurnOfCivilReport>rTarget.TurnOfCivilReport then
     3418  result := false;
     3419  if RW[pTarget].Treaty[pAbout] = trNoContact then
     3420    IntroduceEnemy(pTarget, pAbout);
     3421  rSender := pointer(RW[pSender].EnemyReport[pAbout]);
     3422  rTarget := pointer(RW[pTarget].EnemyReport[pAbout]);
     3423  if rSender.TurnOfCivilReport > rTarget.TurnOfCivilReport then
    30133424  begin // only if newer than current information
    3014   rTarget.TurnOfCivilReport:=rSender.TurnOfCivilReport;
    3015   rTarget.Treaty:=rSender.Treaty;
    3016   rTarget.Government:=rSender.Government;
    3017   rTarget.Money:=rSender.Money;
    3018   rTarget.ResearchTech:=rSender.ResearchTech;
    3019   rTarget.ResearchDone:=rSender.ResearchDone;
    3020   result:=true
    3021   end;
    3022 for i:=0 to nAdv-1 do
    3023   if rTarget.Tech[i]<rSender.Tech[i] then
    3024     begin
    3025     rTarget.Tech[i]:=rSender.Tech[i];
    3026     result:=true
     3425    rTarget.TurnOfCivilReport := rSender.TurnOfCivilReport;
     3426    rTarget.Treaty := rSender.Treaty;
     3427    rTarget.Government := rSender.Government;
     3428    rTarget.Money := rSender.Money;
     3429    rTarget.ResearchTech := rSender.ResearchTech;
     3430    rTarget.ResearchDone := rSender.ResearchDone;
     3431    result := true
     3432  end;
     3433  for i := 0 to nAdv - 1 do
     3434    if rTarget.Tech[i] < rSender.Tech[i] then
     3435    begin
     3436      rTarget.Tech[i] := rSender.Tech[i];
     3437      result := true
    30273438    end
    30283439end;
     
    30303441function CopyMilReport(pSender, pTarget, pAbout: integer): boolean;
    30313442var
    3032 mix: integer;
    3033 rSender, rTarget: ^TEnemyReport;
     3443  mix: integer;
     3444  rSender, rTarget: ^TEnemyReport;
    30343445begin // copy third nation military report
    3035 result:=false;
    3036 if RW[pTarget].Treaty[pAbout]=trNoContact then
    3037   IntroduceEnemy(pTarget, pAbout);
    3038 rSender:=pointer(RW[pSender].EnemyReport[pAbout]);
    3039 rTarget:=pointer(RW[pTarget].EnemyReport[pAbout]);
    3040 if rSender.TurnOfMilReport>rTarget.TurnOfMilReport then
     3446  result := false;
     3447  if RW[pTarget].Treaty[pAbout] = trNoContact then
     3448    IntroduceEnemy(pTarget, pAbout);
     3449  rSender := pointer(RW[pSender].EnemyReport[pAbout]);
     3450  rTarget := pointer(RW[pTarget].EnemyReport[pAbout]);
     3451  if rSender.TurnOfMilReport > rTarget.TurnOfMilReport then
    30413452  begin // only if newer than current information
    3042   rTarget.TurnOfMilReport:=rSender.TurnOfMilReport;
    3043   rTarget.nModelCounted:=rSender.nModelCounted;
    3044   move(rSender.UnCount, rTarget.UnCount, 2*rSender.nModelCounted);
    3045   for mix:=0 to rTarget.nModelCounted-1 do
    3046     TellAboutModel(pTarget,pAbout,mix);
    3047   result:=true
     3453    rTarget.TurnOfMilReport := rSender.TurnOfMilReport;
     3454    rTarget.nModelCounted := rSender.nModelCounted;
     3455    move(rSender.UnCount, rTarget.UnCount, 2 * rSender.nModelCounted);
     3456    for mix := 0 to rTarget.nModelCounted - 1 do
     3457      TellAboutModel(pTarget, pAbout, mix);
     3458    result := true
    30483459  end
    30493460end;
    30503461
    3051 procedure CopyModel(pSender,pTarget,mix: integer);
    3052 var
    3053 i: integer;
    3054 miSender, miTarget: TModelInfo;
    3055 ok: boolean;
    3056 begin
    3057 // only if target doesn't already have a model like this
    3058 ok:= RW[pTarget].nModel<nmmax;
    3059 MakeModelInfo(pSender,mix,RW[pSender].Model[mix],miSender);
    3060 for i:=0 to RW[pTarget].nModel-1 do
    3061   begin
    3062   MakeModelInfo(pTarget,i,RW[pTarget].Model[i],miTarget);
    3063   if IsSameModel(miSender,miTarget) then ok:=false
    3064   end;
    3065 if ok then
    3066   begin
    3067   RW[pTarget].Model[RW[pTarget].nModel]:=RW[pSender].Model[mix];
    3068   with RW[pTarget].Model[RW[pTarget].nModel] do
    3069     begin
    3070     IntroTurn:=GTurn;
    3071     if Kind=mkSelfDeveloped then Kind:=mkEnemyDeveloped;
    3072     Status:=0;
    3073     SavedStatus:=0;
    3074     Built:=0;
    3075     Lost:=0;
    3076     end;
    3077   inc(RW[pTarget].nModel);
    3078   inc(Researched[pTarget]);
    3079   TellAboutModel(pSender,pTarget,RW[pTarget].nModel-1);
     3462procedure CopyModel(pSender, pTarget, mix: integer);
     3463var
     3464  i: integer;
     3465  miSender, miTarget: TModelInfo;
     3466  ok: boolean;
     3467begin
     3468  // only if target doesn't already have a model like this
     3469  ok := RW[pTarget].nModel < nmmax;
     3470  MakeModelInfo(pSender, mix, RW[pSender].Model[mix], miSender);
     3471  for i := 0 to RW[pTarget].nModel - 1 do
     3472  begin
     3473    MakeModelInfo(pTarget, i, RW[pTarget].Model[i], miTarget);
     3474    if IsSameModel(miSender, miTarget) then
     3475      ok := false
     3476  end;
     3477  if ok then
     3478  begin
     3479    RW[pTarget].Model[RW[pTarget].nModel] := RW[pSender].Model[mix];
     3480    with RW[pTarget].Model[RW[pTarget].nModel] do
     3481    begin
     3482      IntroTurn := GTurn;
     3483      if Kind = mkSelfDeveloped then
     3484        Kind := mkEnemyDeveloped;
     3485      Status := 0;
     3486      SavedStatus := 0;
     3487      Built := 0;
     3488      Lost := 0;
     3489    end;
     3490    inc(RW[pTarget].nModel);
     3491    inc(Researched[pTarget]);
     3492    TellAboutModel(pSender, pTarget, RW[pTarget].nModel - 1);
    30803493  end
    30813494end;
     
    30833496procedure CopyMap(pSender, pTarget: integer);
    30843497var
    3085 Loc,i,cix:integer;
    3086 Tile: Cardinal;
    3087 begin
    3088 for Loc:=0 to MapSize-1 do
    3089   if (RW[pSender].MapObservedLast[Loc]>RW[pTarget].MapObservedLast[Loc]) then
    3090     begin
    3091     Tile:=RW[pSender].Map[Loc];
    3092     if Tile and fCity<>0 then
    3093       begin
    3094       i:=0;
    3095       while (i<RW[pTarget].nEnemyCity) and (RW[pTarget].EnemyCity[i].Loc<>Loc) do
    3096         inc(i);
    3097       if i=RW[pTarget].nEnemyCity then
    3098         begin
    3099         inc(RW[pTarget].nEnemyCity);
    3100         assert(RW[pTarget].nEnemyCity<necmax);
    3101         RW[pTarget].EnemyCity[i].Status:=0;
    3102         RW[pTarget].EnemyCity[i].SavedStatus:=0;
     3498  Loc, i, cix: integer;
     3499  Tile: Cardinal;
     3500begin
     3501  for Loc := 0 to MapSize - 1 do
     3502    if (RW[pSender].MapObservedLast[Loc] > RW[pTarget].MapObservedLast[Loc])
     3503    then
     3504    begin
     3505      Tile := RW[pSender].Map[Loc];
     3506      if Tile and fCity <> 0 then
     3507      begin
     3508        i := 0;
     3509        while (i < RW[pTarget].nEnemyCity) and
     3510          (RW[pTarget].EnemyCity[i].Loc <> Loc) do
     3511          inc(i);
     3512        if i = RW[pTarget].nEnemyCity then
     3513        begin
     3514          inc(RW[pTarget].nEnemyCity);
     3515          assert(RW[pTarget].nEnemyCity < necmax);
     3516          RW[pTarget].EnemyCity[i].Status := 0;
     3517          RW[pTarget].EnemyCity[i].SavedStatus := 0;
    31033518        end;
    3104       if Tile and fOwned<>0 then
     3519        if Tile and fOwned <> 0 then
    31053520        begin // city owned by sender -- create new info
    3106         cix:=RW[pSender].nCity-1;
    3107         while (cix>=0) and (RW[pSender].City[cix].Loc<>Loc) do dec(cix);
    3108         MakeCityInfo(pSender, cix, RW[pTarget].EnemyCity[i]);
     3521          cix := RW[pSender].nCity - 1;
     3522          while (cix >= 0) and (RW[pSender].City[cix].Loc <> Loc) do
     3523            dec(cix);
     3524          MakeCityInfo(pSender, cix, RW[pTarget].EnemyCity[i]);
    31093525        end
    3110       else // city not owned by sender -- copy old info
    3111         begin
    3112         cix:=RW[pSender].nEnemyCity-1;
    3113         while (cix>=0) and (RW[pSender].EnemyCity[cix].Loc<>Loc) do dec(cix);
    3114         RW[pTarget].EnemyCity[i]:=RW[pSender].EnemyCity[cix];
     3526        else // city not owned by sender -- copy old info
     3527        begin
     3528          cix := RW[pSender].nEnemyCity - 1;
     3529          while (cix >= 0) and (RW[pSender].EnemyCity[cix].Loc <> Loc) do
     3530            dec(cix);
     3531          RW[pTarget].EnemyCity[i] := RW[pSender].EnemyCity[cix];
    31153532        end;
    31163533      end
    3117     else if RW[pTarget].Map[Loc] and fCity<>0 then // remove enemycity
    3118       for cix:=0 to RW[pTarget].nEnemyCity-1 do
    3119         if RW[pTarget].EnemyCity[cix].Loc=Loc then
    3120           RW[pTarget].EnemyCity[cix].Loc:=-1;
    3121 
    3122     Tile:=Tile and (not (fSpecial or fModern) or ResourceMask[pTarget]);
    3123     Tile:=Tile or (RW[pTarget].Map[Loc] and fModern);
    3124     if (Tile and fTerrain=RW[pTarget].Map[Loc] and fTerrain) then
    3125       Tile:=Tile or (RW[pTarget].Map[Loc] and fSpecial);
    3126 
    3127     if RW[pTarget].Map[Loc] and fTerrain=fUNKNOWN then inc(Discovered[pTarget]);
    3128     RW[pTarget].Map[Loc]:=RW[pTarget].Map[Loc] and fInEnemyZoC // always preserve this flag!
    3129       or Tile and not (fUnit or fHiddenUnit or fStealthUnit
    3130       or fObserved or fSpiedOut or fOwned or fInEnemyZoC or fOwnZoCUnit
    3131       or fPeace or fGrWall);
    3132     if RW[pSender].Territory[Loc]<>RW[pTarget].Territory[Loc] then
    3133       begin
    3134       RW[pTarget].Territory[Loc]:=RW[pSender].Territory[Loc];
    3135       {if RW[pTarget].BorderHelper<>nil then
    3136         RW[pTarget].BorderHelper[Loc]:=0;}
    3137       end;
    3138     RW[pTarget].Territory[Loc]:=RW[pSender].Territory[Loc];
    3139     RW[pTarget].MapObservedLast[Loc]:=RW[pSender].MapObservedLast[Loc];
     3534      else if RW[pTarget].Map[Loc] and fCity <> 0 then // remove enemycity
     3535        for cix := 0 to RW[pTarget].nEnemyCity - 1 do
     3536          if RW[pTarget].EnemyCity[cix].Loc = Loc then
     3537            RW[pTarget].EnemyCity[cix].Loc := -1;
     3538
     3539      Tile := Tile and (not(fSpecial or fModern) or ResourceMask[pTarget]);
     3540      Tile := Tile or (RW[pTarget].Map[Loc] and fModern);
     3541      if (Tile and fTerrain = RW[pTarget].Map[Loc] and fTerrain) then
     3542        Tile := Tile or (RW[pTarget].Map[Loc] and fSpecial);
     3543
     3544      if RW[pTarget].Map[Loc] and fTerrain = fUNKNOWN then
     3545        inc(Discovered[pTarget]);
     3546      RW[pTarget].Map[Loc] := RW[pTarget].Map[Loc] and fInEnemyZoC
     3547      // always preserve this flag!
     3548        or Tile and not(fUnit or fHiddenUnit or fStealthUnit or fObserved or
     3549        fSpiedOut or fOwned or fInEnemyZoC or fOwnZoCUnit or fPeace or fGrWall);
     3550      if RW[pSender].Territory[Loc] <> RW[pTarget].Territory[Loc] then
     3551      begin
     3552        RW[pTarget].Territory[Loc] := RW[pSender].Territory[Loc];
     3553        { if RW[pTarget].BorderHelper<>nil then
     3554          RW[pTarget].BorderHelper[Loc]:=0; }
     3555      end;
     3556      RW[pTarget].Territory[Loc] := RW[pSender].Territory[Loc];
     3557      RW[pTarget].MapObservedLast[Loc] := RW[pSender].MapObservedLast[Loc];
    31403558    end;
    31413559end;
     
    31433561function PayPrice(pSender, pTarget, Price: integer; execute: boolean): boolean;
    31443562var
    3145 pSubject,i,n,NewTreaty: integer;
    3146 begin
    3147 result:=true;
    3148 case Price and opMask of
    3149   opCivilReport: // + turn + concerned player shl 16
    3150     begin
    3151     pSubject:=Price shr 16 and $f;
    3152     if pTarget=pSubject then result:=false
    3153     else if pSender=pSubject then
    3154       begin
    3155       if execute then GiveCivilReport(pTarget,pSender)
    3156       end
    3157     else if RW[pSender].EnemyReport[pSubject].TurnOfCivilReport<0 then
    3158       result:=false
    3159     else if execute then CopyCivilReport(pSender, pTarget, pSubject);
    3160     end;
    3161   opMilReport: // + turn + concerned player shl 16
    3162     begin
    3163     pSubject:=Price shr 16 and $f;
    3164     if pTarget=pSubject then result:=false
    3165     else if pSender=pSubject then
    3166       begin
    3167       if execute then GiveMilReport(pTarget,pSender)
    3168       end
    3169     else if RW[pSender].EnemyReport[pSubject].TurnOfMilReport<0 then
    3170       result:=false
    3171     else if execute then CopyMilReport(pSender, pTarget, pSubject)
    3172     end;
    3173   opMap:
    3174     if execute then
    3175       begin
    3176       CopyMap(pSender, pTarget);
    3177       RecalcPeaceMap(pTarget);
    3178       end;
    3179   opTreaty..opTreaty+trAlliance: // + nation treaty
    3180     begin
    3181     if Price-opTreaty=RW[pSender].Treaty[pTarget]-1 then
    3182       begin // agreed treaty end
    3183       if execute then CancelTreaty(pSender,pTarget,false)
    3184       end
    3185     else
    3186       begin
    3187       NewTreaty:=-1;
    3188       if Price-opTreaty=RW[pSender].Treaty[pTarget]+1 then
    3189         NewTreaty:=Price-opTreaty
    3190       else if (RW[pSender].Treaty[pTarget]=trNone) and (Price-opTreaty=trPeace) then
    3191         NewTreaty:=trPeace;
    3192       if NewTreaty<0 then result:=false
    3193       else if execute then
    3194         begin
    3195         assert(NewTreaty>RW[pSender].Treaty[pTarget]);
    3196         RW[pSender].Treaty[pTarget]:=NewTreaty;
    3197         RW[pTarget].Treaty[pSender]:=NewTreaty;
    3198         if NewTreaty>=trFriendlyContact then
     3563  pSubject, i, n, NewTreaty: integer;
     3564begin
     3565  result := true;
     3566  case Price and opMask of
     3567    opCivilReport: // + turn + concerned player shl 16
     3568      begin
     3569        pSubject := Price shr 16 and $F;
     3570        if pTarget = pSubject then
     3571          result := false
     3572        else if pSender = pSubject then
     3573        begin
     3574          if execute then
     3575            GiveCivilReport(pTarget, pSender)
     3576        end
     3577        else if RW[pSender].EnemyReport[pSubject].TurnOfCivilReport < 0 then
     3578          result := false
     3579        else if execute then
     3580          CopyCivilReport(pSender, pTarget, pSubject);
     3581      end;
     3582    opMilReport: // + turn + concerned player shl 16
     3583      begin
     3584        pSubject := Price shr 16 and $F;
     3585        if pTarget = pSubject then
     3586          result := false
     3587        else if pSender = pSubject then
     3588        begin
     3589          if execute then
     3590            GiveMilReport(pTarget, pSender)
     3591        end
     3592        else if RW[pSender].EnemyReport[pSubject].TurnOfMilReport < 0 then
     3593          result := false
     3594        else if execute then
     3595          CopyMilReport(pSender, pTarget, pSubject)
     3596      end;
     3597    opMap:
     3598      if execute then
     3599      begin
     3600        CopyMap(pSender, pTarget);
     3601        RecalcPeaceMap(pTarget);
     3602      end;
     3603    opTreaty .. opTreaty + trAlliance: // + nation treaty
     3604      begin
     3605        if Price - opTreaty = RW[pSender].Treaty[pTarget] - 1 then
     3606        begin // agreed treaty end
     3607          if execute then
     3608            CancelTreaty(pSender, pTarget, false)
     3609        end
     3610        else
     3611        begin
     3612          NewTreaty := -1;
     3613          if Price - opTreaty = RW[pSender].Treaty[pTarget] + 1 then
     3614            NewTreaty := Price - opTreaty
     3615          else if (RW[pSender].Treaty[pTarget] = trNone) and
     3616            (Price - opTreaty = trPeace) then
     3617            NewTreaty := trPeace;
     3618          if NewTreaty < 0 then
     3619            result := false
     3620          else if execute then
    31993621          begin
    3200           GiveCivilReport(pTarget, pSender);
    3201           GiveCivilReport(pSender, pTarget);
    3202           end;
    3203         if NewTreaty=trAlliance then
     3622            assert(NewTreaty > RW[pSender].Treaty[pTarget]);
     3623            RW[pSender].Treaty[pTarget] := NewTreaty;
     3624            RW[pTarget].Treaty[pSender] := NewTreaty;
     3625            if NewTreaty >= TrFriendlyContact then
     3626            begin
     3627              GiveCivilReport(pTarget, pSender);
     3628              GiveCivilReport(pSender, pTarget);
     3629            end;
     3630            if NewTreaty = trAlliance then
     3631            begin
     3632              GiveMilReport(pTarget, pSender);
     3633              GiveMilReport(pSender, pTarget);
     3634              CopyMap(pSender, pTarget);
     3635              CopyMap(pTarget, pSender);
     3636              RecalcMapZoC(pSender);
     3637              RecalcMapZoC(pTarget);
     3638            end;
     3639            if not(NewTreaty in [trPeace, TrFriendlyContact]) then
     3640            begin
     3641              RW[pSender].EvaStart[pTarget] := -PeaceEvaTurns - 1;
     3642              RW[pTarget].EvaStart[pSender] := -PeaceEvaTurns - 1;
     3643            end;
     3644            RecalcPeaceMap(pSender);
     3645            RecalcPeaceMap(pTarget);
     3646          end
     3647        end
     3648      end;
     3649    opShipParts: // + number + part type shl 16
     3650      begin
     3651        n := Price and $FFFF; // number
     3652        i := Price shr 16 and $F; // type
     3653        if (i < nShipPart) and (GShip[pSender].Parts[i] >= n) then
     3654        begin
     3655          if execute then
    32043656          begin
    3205           GiveMilReport(pTarget, pSender);
    3206           GiveMilReport(pSender, pTarget);
    3207           CopyMap(pSender, pTarget);
    3208           CopyMap(pTarget, pSender);
    3209           RecalcMapZoC(pSender);
    3210           RecalcMapZoC(pTarget);
    3211           end;
    3212         if not (NewTreaty in [trPeace,trFriendlyContact]) then
    3213           begin
    3214           RW[pSender].EvaStart[pTarget]:=-PeaceEvaTurns-1;
    3215           RW[pTarget].EvaStart[pSender]:=-PeaceEvaTurns-1;
    3216           end;
    3217         RecalcPeaceMap(pSender);
    3218         RecalcPeaceMap(pTarget);
     3657            dec(GShip[pSender].Parts[i], n);
     3658            RW[pSender].Ship[pSender].Parts[i] := GShip[pSender].Parts[i];
     3659            RW[pTarget].Ship[pSender].Parts[i] := GShip[pSender].Parts[i];
     3660            if RW[pTarget].NatBuilt[imSpacePort] > 0 then
     3661            begin // space ship control requires space port
     3662              inc(GShip[pTarget].Parts[i], n);
     3663              RW[pSender].Ship[pTarget].Parts[i] := GShip[pTarget].Parts[i];
     3664              RW[pTarget].Ship[pTarget].Parts[i] := GShip[pTarget].Parts[i];
     3665            end
     3666          end
     3667        end
     3668        else
     3669          result := false;
     3670      end;
     3671    opMoney: // + value
     3672      if (Price - opMoney <= MaxMoneyPrice) and
     3673        (RW[pSender].Money >= Price - opMoney) then
     3674      begin
     3675        if execute then
     3676        begin
     3677          dec(RW[pSender].Money, Price - opMoney);
     3678          inc(RW[pTarget].Money, Price - opMoney);
    32193679        end
    32203680      end
    3221     end;
    3222   opShipParts: // + number + part type shl 16
    3223     begin
    3224     n:=Price and $FFFF; // number
    3225     i:=Price shr 16 and $f; // type
    3226     if (i<nShipPart) and (GShip[pSender].Parts[i]>=n) then
    3227       begin
     3681      else
     3682        result := false;
     3683    opTribute: // + value
    32283684      if execute then
    3229         begin
    3230         dec(GShip[pSender].Parts[i],n);
    3231         RW[pSender].Ship[pSender].Parts[i]:=GShip[pSender].Parts[i];
    3232         RW[pTarget].Ship[pSender].Parts[i]:=GShip[pSender].Parts[i];
    3233         if RW[pTarget].NatBuilt[imSpacePort]>0 then
    3234           begin // space ship control requires space port
    3235           inc(GShip[pTarget].Parts[i],n);
    3236           RW[pSender].Ship[pTarget].Parts[i]:=GShip[pTarget].Parts[i];
    3237           RW[pTarget].Ship[pTarget].Parts[i]:=GShip[pTarget].Parts[i];
    3238           end
     3685      begin
     3686      end;
     3687    opTech: // + advance
     3688      if RW[pSender].Tech[Price - opTech] >= tsApplicable then
     3689      begin
     3690        if execute and (RW[pTarget].Tech[Price - opTech] = tsNA) then
     3691        begin
     3692          SeeTech(pTarget, Price - opTech);
     3693          RW[pSender].EnemyReport[pTarget].Tech[Price - opTech] := tsSeen;
    32393694        end
    32403695      end
    3241     else result:=false;
    3242     end;
    3243   opMoney: // + value
    3244     if (Price-opMoney<=MaxMoneyPrice) and (RW[pSender].Money>=Price-opMoney) then
    3245       begin
     3696      else
     3697        result := false;
     3698    opAllTech:
    32463699      if execute then
    3247         begin
    3248         dec(RW[pSender].Money,Price-opMoney);
    3249         inc(RW[pTarget].Money,Price-opMoney);
     3700        for i := 0 to nAdv - 1 do
     3701          if (RW[pSender].Tech[i] >= tsApplicable) and
     3702            (RW[pTarget].Tech[i] = tsNA) then
     3703          begin
     3704            SeeTech(pTarget, i);
     3705            RW[pSender].EnemyReport[pTarget].Tech[i] := tsSeen;
     3706            RW[pTarget].EnemyReport[pSender].Tech[i] := tsApplicable;
     3707          end;
     3708    opModel: // + model index
     3709      if Price - opModel < RW[pSender].nModel then
     3710      begin
     3711        if execute then
     3712          CopyModel(pSender, pTarget, Price - opModel)
     3713      end
     3714      else
     3715        result := false;
     3716    opAllModel:
     3717      if execute then
     3718        for i := 0 to RW[pSender].nModel - 1 do
     3719        begin
     3720          TellAboutModel(pTarget, pSender, i);
     3721          CopyModel(pSender, pTarget, i);
     3722        end;
     3723    { opCity: // + city ID
     3724      begin
     3725      result:=false
     3726      end; }
     3727  end
     3728end;
     3729
     3730procedure CancelTreaty(p, pWith: integer; DecreaseCredibility: boolean);
     3731// side effect: PeaceEnded := bitarray of players with which peace treaty was canceled
     3732var
     3733  p1, OldTreaty: integer;
     3734begin
     3735  OldTreaty := RW[p].Treaty[pWith];
     3736  PeaceEnded := 0;
     3737  if OldTreaty >= trPeace then
     3738    RW[p].LastCancelTreaty[pWith] := GTurn;
     3739  if DecreaseCredibility then
     3740  begin
     3741    case OldTreaty of
     3742      trPeace:
     3743        begin
     3744          RW[p].Credibility := RW[p].Credibility shr 1;
     3745          if RW[p].MaxCredibility > 0 then
     3746            dec(RW[p].MaxCredibility, 10);
     3747          if RW[p].Credibility > RW[p].MaxCredibility then
     3748            RW[p].Credibility := RW[p].MaxCredibility;
     3749        end;
     3750      trAlliance:
     3751        RW[p].Credibility := RW[p].Credibility * 3 div 4;
     3752    end;
     3753    RW[pWith].EnemyReport[p].Credibility := RW[p].Credibility;
     3754  end;
     3755
     3756  if OldTreaty = trPeace then
     3757  begin
     3758    for p1 := 0 to nPl - 1 do
     3759      if (p1 = pWith) or DecreaseCredibility and (p1 <> p) and
     3760        (RW[pWith].Treaty[p1] = trAlliance) and (RW[p].Treaty[p1] >= trPeace)
     3761      then
     3762      begin
     3763        RW[p].Treaty[p1] := trNone;
     3764        RW[p1].Treaty[p] := trNone;
     3765        RW[p].EvaStart[p1] := -PeaceEvaTurns - 1;
     3766        RW[p1].EvaStart[p] := -PeaceEvaTurns - 1;
     3767        inc(PeaceEnded, 1 shl p1);
     3768      end;
     3769    CheckBorders(-1);
     3770    if (Mode > moLoading_Fast) and (PeaceEnded > 0) then
     3771      RecalcMapZoC(p);
     3772  end
     3773  else
     3774  begin
     3775    RW[p].Treaty[pWith] := OldTreaty - 1;
     3776    RW[pWith].Treaty[p] := OldTreaty - 1;
     3777    if OldTreaty = TrFriendlyContact then
     3778    begin // necessary for loading
     3779      GiveCivilReport(p, pWith);
     3780      GiveCivilReport(pWith, p);
     3781    end
     3782    else if OldTreaty = trAlliance then
     3783    begin // necessary for loading
     3784      GiveMilReport(p, pWith);
     3785      GiveMilReport(pWith, p);
     3786    end;
     3787    if (Mode > moLoading_Fast) and (OldTreaty = trAlliance) then
     3788    begin
     3789      RecalcMapZoC(p);
     3790      RecalcMapZoC(pWith);
     3791    end
     3792  end;
     3793  if OldTreaty in [trPeace, trAlliance] then
     3794  begin
     3795    RecalcPeaceMap(p);
     3796    RecalcPeaceMap(pWith);
     3797  end
     3798end;
     3799
     3800function DoSpyMission(p, pCity, cix, Mission: integer): Cardinal;
     3801var
     3802  p1: integer;
     3803begin
     3804  result := 0;
     3805  case Mission of
     3806    smSabotageProd:
     3807      RW[pCity].City[cix].Flags := RW[pCity].City[cix].Flags or
     3808        chProductionSabotaged;
     3809    smStealMap:
     3810      begin
     3811        CopyMap(pCity, p);
     3812        RecalcPeaceMap(p);
     3813      end;
     3814    smStealCivilReport:
     3815      begin
     3816        if RW[p].Treaty[pCity] = trNoContact then
     3817          IntroduceEnemy(p, pCity);
     3818        GiveCivilReport(p, pCity);
     3819      end;
     3820    smStealMilReport:
     3821      begin
     3822        if RW[p].Treaty[pCity] = trNoContact then
     3823          IntroduceEnemy(p, pCity);
     3824        GiveMilReport(p, pCity);
     3825      end;
     3826    smStealForeignReports:
     3827      begin
     3828        for p1 := 0 to nPl - 1 do
     3829          if (p1 <> p) and (p1 <> pCity) and (RW[pCity].EnemyReport[p1] <> nil)
     3830          then
     3831          begin
     3832            if RW[pCity].EnemyReport[p1].TurnOfCivilReport >= 0 then
     3833              if CopyCivilReport(pCity, p, p1) then
     3834                result := result or (1 shl (2 * p1));
     3835            if RW[pCity].EnemyReport[p1].TurnOfMilReport >= 0 then
     3836              if CopyMilReport(pCity, p, p1) then
     3837                result := result or (2 shl (2 * p1));
     3838          end
     3839      end;
     3840  end;
     3841end;
     3842
     3843{
     3844  Test Flags
     3845  ____________________________________________________________________
     3846}
     3847procedure ClearTestFlags(ClearFlags: integer);
     3848var
     3849  p1: integer;
     3850begin
     3851  GTestFlags := GTestFlags and (not ClearFlags or tfTested or tfAllTechs or
     3852    tfAllContact);
     3853  for p1 := 0 to nPl - 1 do
     3854    if 1 shl p1 and (GAlive or GWatching) <> 0 then
     3855      RW[p1].TestFlags := GTestFlags;
     3856end;
     3857
     3858procedure SetTestFlags(p, SetFlags: integer);
     3859var
     3860  i, p1, p2, MoreFlags: integer;
     3861begin
     3862  MoreFlags := SetFlags and not GTestFlags;
     3863  GTestFlags := GTestFlags or (SetFlags and $7FF);
     3864  for p1 := 0 to nPl - 1 do
     3865    if 1 shl p1 and (GAlive or GWatching) <> 0 then
     3866      RW[p1].TestFlags := GTestFlags;
     3867
     3868  if MoreFlags and (tfUncover or tfAllContact) <> 0 then
     3869    for p1 := 0 to nPl - 2 do
     3870      if 1 shl p1 and GAlive <> 0 then
     3871        for p2 := p1 + 1 to nPl - 1 do
     3872          if 1 shl p2 and GAlive <> 0 then
     3873          begin // make p1 and p2 know each other
     3874            if RW[p1].Treaty[p2] = trNoContact then
     3875              IntroduceEnemy(p1, p2)
     3876          end;
     3877
     3878  if MoreFlags and tfAllTechs <> 0 then
     3879    for p1 := 0 to nPl - 1 do
     3880    begin
     3881      ResourceMask[p1] := $FFFFFFFF;
     3882      if 1 shl p1 and GAlive <> 0 then
     3883      begin
     3884        for i := 0 to nAdv - 1 do // give all techs to player p1
     3885          if not(i in FutureTech) and (RW[p1].Tech[i] < tsApplicable) then
     3886          begin
     3887            RW[p1].Tech[i] := tsCheat;
     3888            CheckSpecialModels(p1, i);
     3889          end;
     3890        for p2 := 0 to nPl - 1 do
     3891          if (p2 <> p1) and (1 shl p2 and (GAlive or GWatching) <> 0) then
     3892            for i := 1 to 3 do
     3893              if RW[p2].EnemyReport[p1].Tech[AgePreq[i]] < tsApplicable then
     3894                RW[p2].EnemyReport[p1].Tech[AgePreq[i]] := tsCheat;
     3895      end
     3896    end;
     3897
     3898  if MoreFlags and tfUncover <> 0 then
     3899  begin
     3900    DiscoverAll(p, lObserveSuper);
     3901    for p1 := 0 to nPl - 1 do
     3902      if 1 shl p1 and GAlive <> 0 then
     3903      begin
     3904        ResourceMask[p1] := $FFFFFFFF;
     3905        if p1 <> p then
     3906        begin
     3907          GiveCivilReport(p, p1);
     3908          GiveMilReport(p, p1);
    32503909        end
    32513910      end
    3252     else result:=false;
    3253   opTribute: // + value
    3254     if execute then
    3255       begin
    3256       end;
    3257   opTech: // + advance
    3258     if RW[pSender].Tech[Price-opTech]>=tsApplicable then
    3259       begin
    3260       if execute and (RW[pTarget].Tech[Price-opTech]=tsNA) then
    3261         begin
    3262         SeeTech(pTarget,Price-opTech);
    3263         RW[pSender].EnemyReport[pTarget].Tech[Price-opTech]:=tsSeen;
    3264         end
    3265       end
    3266     else result:=false;
    3267   opAllTech:
    3268     if execute then for i:=0 to nAdv-1 do
    3269       if (RW[pSender].Tech[i]>=tsApplicable) and (RW[pTarget].Tech[i]=tsNA) then
    3270         begin
    3271         SeeTech(pTarget,i);
    3272         RW[pSender].EnemyReport[pTarget].Tech[i]:=tsSeen;
    3273         RW[pTarget].EnemyReport[pSender].Tech[i]:=tsApplicable;
    3274         end;
    3275   opModel: // + model index
    3276     if Price-opModel<RW[pSender].nModel then
    3277       begin
    3278       if execute then CopyModel(pSender,pTarget,Price-opModel)
    3279       end
    3280     else result:=false;
    3281   opAllModel:
    3282     if execute then for i:=0 to RW[pSender].nModel-1 do
    3283       begin
    3284       TellAboutModel(pTarget,pSender,i);
    3285       CopyModel(pSender,pTarget,i);
    3286       end;
    3287 {  opCity: // + city ID
    3288     begin
    3289     result:=false
    3290     end;}
    3291   end
    3292 end;
    3293 
    3294 procedure CancelTreaty(p, pWith: integer; DecreaseCredibility: boolean);
    3295 // side effect: PeaceEnded := bitarray of players with which peace treaty was canceled
    3296 var
    3297 p1,OldTreaty: integer;
    3298 begin
    3299 OldTreaty:=RW[p].Treaty[pWith];
    3300 PeaceEnded:=0;
    3301 if OldTreaty>=trPeace then
    3302   RW[p].LastCancelTreaty[pWith]:=GTurn;
    3303 if DecreaseCredibility then
    3304   begin
    3305   case OldTreaty of
    3306     trPeace:
    3307       begin
    3308       RW[p].Credibility:=RW[p].Credibility shr 1;
    3309       if RW[p].MaxCredibility>0 then
    3310         dec(RW[p].MaxCredibility,10);
    3311       if RW[p].Credibility>RW[p].MaxCredibility then
    3312         RW[p].Credibility:=RW[p].MaxCredibility;
    3313       end;
    3314     trAlliance:
    3315       RW[p].Credibility:=RW[p].Credibility*3 div 4;
    3316     end;
    3317   RW[pWith].EnemyReport[p].Credibility:=RW[p].Credibility;
    3318   end;
    3319 
    3320 if OldTreaty=trPeace then
    3321   begin
    3322   for p1:=0 to nPl-1 do
    3323     if (p1=pWith)
    3324       or DecreaseCredibility and (p1<>p)
    3325       and (RW[pWith].Treaty[p1]=trAlliance)
    3326       and (RW[p].Treaty[p1]>=trPeace) then
    3327       begin
    3328       RW[p].Treaty[p1]:=trNone;
    3329       RW[p1].Treaty[p]:=trNone;
    3330       RW[p].EvaStart[p1]:=-PeaceEvaTurns-1;
    3331       RW[p1].EvaStart[p]:=-PeaceEvaTurns-1;
    3332       inc(PeaceEnded,1 shl p1);
    3333       end;
    3334   CheckBorders(-1);
    3335   if (Mode>moLoading_Fast) and (PeaceEnded>0) then
    3336     RecalcMapZoC(p);
    3337   end
    3338 else
    3339   begin
    3340   RW[p].Treaty[pWith]:=OldTreaty-1;
    3341   RW[pWith].Treaty[p]:=OldTreaty-1;
    3342   if OldTreaty=trFriendlyContact then
    3343     begin // necessary for loading
    3344     GiveCivilReport(p, pWith);
    3345     GiveCivilReport(pWith, p);
    3346     end
    3347   else if OldTreaty=trAlliance then
    3348     begin // necessary for loading
    3349     GiveMilReport(p, pWith);
    3350     GiveMilReport(pWith, p);
    3351     end;
    3352   if (Mode>moLoading_Fast) and (OldTreaty=trAlliance) then
    3353     begin
    3354     RecalcMapZoC(p);
    3355     RecalcMapZoC(pWith);
    3356     end
    3357   end;
    3358 if OldTreaty in [trPeace,trAlliance] then
    3359   begin
    3360   RecalcPeaceMap(p);
    3361   RecalcPeaceMap(pWith);
    3362   end
    3363 end;
    3364 
    3365 function DoSpyMission(p,pCity,cix,Mission: integer): Cardinal;
    3366 var
    3367 p1: integer;
    3368 begin
    3369 result:=0;
    3370 case Mission of
    3371   smSabotageProd: RW[pCity].City[cix].Flags:=
    3372     RW[pCity].City[cix].Flags or chProductionSabotaged;
    3373   smStealMap:
    3374     begin
    3375     CopyMap(pCity,p);
    3376     RecalcPeaceMap(p);
    3377     end;
    3378   smStealCivilReport:
    3379     begin
    3380     if RW[p].Treaty[pCity]=trNoContact then IntroduceEnemy(p,pCity);
    3381     GiveCivilReport(p,pCity);
    3382     end;
    3383   smStealMilReport:
    3384     begin
    3385     if RW[p].Treaty[pCity]=trNoContact then IntroduceEnemy(p,pCity);
    3386     GiveMilReport(p,pCity);
    3387     end;
    3388   smStealForeignReports:
    3389     begin
    3390     for p1:=0 to nPl-1 do if (p1<>p) and (p1<>pCity)
    3391       and (RW[pCity].EnemyReport[p1]<>nil) then
    3392       begin
    3393       if RW[pCity].EnemyReport[p1].TurnOfCivilReport>=0 then
    3394         if CopyCivilReport(pCity,p,p1) then
    3395           result:=result or (1 shl (2*p1));
    3396       if RW[pCity].EnemyReport[p1].TurnOfMilReport>=0 then
    3397         if CopyMilReport(pCity,p,p1) then
    3398           result:=result or (2 shl (2*p1));
    3399       end
    3400     end;
    34013911  end;
    34023912end;
    34033913
    34043914{
    3405                               Test Flags
    3406  ____________________________________________________________________
     3915  Internal Command Processing
     3916  ____________________________________________________________________
    34073917}
    3408 procedure ClearTestFlags(ClearFlags: integer);
    3409 var
    3410 p1: integer;
    3411 begin
    3412 GTestFlags:=GTestFlags and (not ClearFlags or tfTested or tfAllTechs or tfAllContact);
    3413 for p1:=0 to nPl-1 do if 1 shl p1 and (GAlive or GWatching)<>0 then
    3414   RW[p1].TestFlags:=GTestFlags;
    3415 end;
    3416 
    3417 procedure SetTestFlags(p,SetFlags: integer);
    3418 var
    3419 i,p1,p2,MoreFlags: integer;
    3420 begin
    3421 MoreFlags:=SetFlags and not GTestFlags;
    3422 GTestFlags:=GTestFlags or (SetFlags and $7FF);
    3423 for p1:=0 to nPl-1 do if 1 shl p1 and (GAlive or GWatching)<>0 then
    3424   RW[p1].TestFlags:=GTestFlags;
    3425 
    3426 if MoreFlags and (tfUncover or tfAllContact)<>0 then
    3427   for p1:=0 to nPl-2 do
    3428     if 1 shl p1 and GAlive<>0 then
    3429       for p2:=p1+1 to nPl-1 do if 1 shl p2 and GAlive<>0 then
    3430         begin // make p1 and p2 know each other
    3431         if RW[p1].Treaty[p2]=trNoContact then
    3432           IntroduceEnemy(p1,p2)
    3433         end;
    3434 
    3435 if MoreFlags and tfAllTechs<>0 then
    3436   for p1:=0 to nPl-1 do
    3437     begin
    3438     ResourceMask[p1]:=$FFFFFFFF;
    3439     if 1 shl p1 and GAlive<>0 then
    3440       begin
    3441       for i:=0 to nAdv-1 do // give all techs to player p1
    3442         if not (i in FutureTech) and (RW[p1].Tech[i]<tsApplicable) then
    3443           begin
    3444           RW[p1].Tech[i]:=tsCheat;
    3445           CheckSpecialModels(p1,i);
    3446           end;
    3447       for p2:=0 to nPl-1 do if (p2<>p1) and (1 shl p2 and (GAlive or GWatching)<>0) then
    3448         for i:=1 to 3 do
    3449           if RW[p2].EnemyReport[p1].Tech[AgePreq[i]]<tsApplicable then
    3450             RW[p2].EnemyReport[p1].Tech[AgePreq[i]]:=tsCheat;
    3451       end
    3452     end;
    3453 
    3454 if MoreFlags and tfUncover<>0 then
    3455   begin
    3456   DiscoverAll(p,lObserveSuper);
    3457   for p1:=0 to nPl-1 do if 1 shl p1 and GAlive<>0 then
    3458     begin
    3459     ResourceMask[p1]:=$FFFFFFFF;
    3460     if p1<>p then
    3461       begin
    3462       GiveCivilReport(p, p1);
    3463       GiveMilReport(p, p1);
    3464       end
    3465     end
    3466   end;
    3467 end;
    3468 
    3469 {
    3470                       Internal Command Processing
    3471  ____________________________________________________________________
    3472 }
    3473 procedure IntServer(Command,Player,Subject:integer;var Data);
    3474 var
    3475 i,p1: integer;
    3476 
    3477 begin
    3478 if Mode=moPlaying then
    3479   CL.Put(Command, Player, Subject, @Data);
    3480 
    3481 case Command of
    3482 
    3483   sIntTellAboutNation:
    3484     begin
    3485     {$IFDEF TEXTLOG}CmdInfo:=Format('IntTellAboutNation P%d+P%d', [Player,Subject]);{$ENDIF}
    3486     assert((Player>=0) and (Player<nPl) and (Subject>=0) and (Subject<nPl));
    3487     IntroduceEnemy(Player,Subject);
    3488     end;
    3489 
    3490   sIntHaveContact:
    3491     begin
    3492     {$IFDEF TEXTLOG}CmdInfo:=Format('IntHaveContact P%d+P%d', [Player,Subject]);{$ENDIF}
    3493     assert(RW[Player].Treaty[Subject]>trNoContact);
    3494     RW[Player].EnemyReport[Subject].TurnOfContact:=GTurn;
    3495     RW[Subject].EnemyReport[Player].TurnOfContact:=GTurn;
    3496     end;
    3497 
    3498   sIntCancelTreaty:
    3499     begin
    3500     {$IFDEF TEXTLOG}CmdInfo:=Format('IntCancelTreaty P%d with P%d', [Player,Subject]);{$ENDIF}
    3501     CancelTreaty(Player,Subject);
    3502     end;
    3503 
    3504 (*  sIntChoosePeace:
    3505     begin
    3506     {$IFDEF TEXTLOG}CmdInfo:=Format('IntChoosePeace P%d+P%d', [Player,Subject]);{$ENDIF}
    3507     RW[Player].Treaty[Subject]:=trPeace;
    3508     RW[Subject].Treaty[Player]:=trPeace;
    3509     end;*)
    3510 
    3511   sIntTellAboutModel..sIntTellAboutModel+(nPl-1) shl 4:
    3512     begin
    3513     p1:=(Command-sIntTellAboutModel) shr 4; // told player
    3514     {$IFDEF TEXTLOG}CmdInfo:=Format('IntTellAboutModel P%d about P%d Mod%d', [p1,Player,Subject]);{$ENDIF}
    3515     assert((Player>=0) and (Player<nPl));
    3516     assert((Subject>=0) and (Subject<RW[Player].nModel));
    3517     MakeModelInfo(Player,Subject,RW[Player].Model[Subject],
    3518       RW[p1].EnemyModel[RW[p1].nEnemyModel]);
    3519     RWemix[p1,Player,Subject]:=RW[p1].nEnemyModel;
    3520     inc(RW[p1].nEnemyModel);
    3521     assert(RW[p1].nEnemyModel<nemmax);
    3522     end;
    3523 
    3524   sIntDiscoverZOC:
    3525     begin
    3526     {$IFDEF TEXTLOG}CmdInfo:=Format('IntDiscoverZOC P%d Loc%d', [Player,integer(data)]);{$ENDIF}
    3527     Discover9(integer(Data),Player,lObserveUnhidden,true,false);
    3528     end;
    3529 
    3530   sIntExpandTerritory:
    3531     if Mode<moPlaying then
    3532       begin
    3533       {$IFDEF TEXTLOG}CmdInfo:=Format('IntExpandTerritory P%d Loc%d', [Player,RW[Player].City[Subject].Loc]);{$ENDIF}
    3534       move(Data,BorderChanges,sizeof(BorderChanges));
    3535       ExpandTerritory(RW[Player].City[Subject].Loc);
    3536       end;
    3537 
    3538   sIntBuyMaterial:
    3539     with RW[Player].City[Subject] do
    3540       begin
    3541       {$IFDEF TEXTLOG}CmdInfo:=Format('IntBuyMaterial P%d Loc%d Cost%d', [Player,Loc,integer(Data)]);{$ENDIF}
    3542       dec(RW[Player].Money,integer(Data));
    3543       if (GWonder[woMich].EffectiveOwner=Player) and (Project and cpImp<>0) then
    3544         inc(Prod,integer(Data) div 2)
    3545       else inc(Prod,integer(Data) div 4);
    3546       if Project0 and not cpAuto<>Project and not cpAuto then
    3547         Project0:=Project;
    3548       Prod0:=Prod;
    3549       end;
    3550 
    3551   sIntPayPrices..sIntPayPrices+12:
    3552     begin
    3553     {$IFDEF TEXTLOG}CmdInfo:=Format('IntPayPrices P%d+P%d', [Player,Subject]);{$ENDIF}
    3554     for i:=0 to TOffer(Data).nDeliver-1 do
    3555       PayPrice(Player,Subject,TOffer(Data).Price[i],true);
    3556     for i:=0 to TOffer(Data).nCost-1 do
    3557       PayPrice(Subject,Player,TOffer(Data).Price[TOffer(Data).nDeliver+i],true);
    3558     for i:=0 to TOffer(Data).nDeliver+TOffer(Data).nCost-1 do
    3559       if TOffer(Data).Price[i]=opTreaty+trAlliance then
    3560         begin // add view area of allied player
    3561         DiscoverViewAreas(Player);
    3562         DiscoverViewAreas(Subject);
    3563         break
    3564         end
    3565     end;
    3566 
    3567   sIntSetDevModel:
    3568     if Mode<moPlaying then
    3569       move(Data, RW[Player].DevModel.Kind, sIntSetDevModel and $F *4);
    3570 
    3571   sIntSetModelStatus: if ProcessClientData[Player] then
    3572     begin
    3573     {$IFDEF TEXTLOG}CmdInfo:=Format('IntSetModelStatus P%d', [Player]);{$ENDIF}
    3574     RW[Player].Model[Subject].Status:=integer(Data);
    3575     end;
    3576 
    3577   sIntSetUnitStatus: if ProcessClientData[Player] then
    3578     begin
    3579     {$IFDEF TEXTLOG}CmdInfo:=Format('IntSetUnitStatus P%d', [Player]);{$ENDIF}
    3580     RW[Player].Un[Subject].Status:=integer(Data);
    3581     end;
    3582 
    3583   sIntSetCityStatus: if ProcessClientData[Player] then
    3584     begin
    3585     {$IFDEF TEXTLOG}CmdInfo:=Format('IntSetCityStatus P%d', [Player]);{$ENDIF}
    3586     RW[Player].City[Subject].Status:=integer(Data);
    3587     end;
    3588 
    3589   sIntSetECityStatus: if ProcessClientData[Player] then
    3590     begin
    3591     {$IFDEF TEXTLOG}CmdInfo:=Format('IntSetECityStatus P%d', [Player]);{$ENDIF}
    3592     RW[Player].EnemyCity[Subject].Status:=integer(Data);
    3593     end;
    3594 
    3595   end;{case command}
    3596 end;{IntServer}
     3918procedure IntServer(Command, Player, Subject: integer; var Data);
     3919var
     3920  i, p1: integer;
     3921
     3922begin
     3923  if Mode = moPlaying then
     3924    CL.Put(Command, Player, Subject, @Data);
     3925
     3926  case Command of
     3927
     3928    sIntTellAboutNation:
     3929      begin
     3930{$IFDEF TEXTLOG}CmdInfo := Format('IntTellAboutNation P%d+P%d', [Player, Subject]); {$ENDIF}
     3931        assert((Player >= 0) and (Player < nPl) and (Subject >= 0) and
     3932          (Subject < nPl));
     3933        IntroduceEnemy(Player, Subject);
     3934      end;
     3935
     3936    sIntHaveContact:
     3937      begin
     3938{$IFDEF TEXTLOG}CmdInfo := Format('IntHaveContact P%d+P%d', [Player, Subject]); {$ENDIF}
     3939        assert(RW[Player].Treaty[Subject] > trNoContact);
     3940        RW[Player].EnemyReport[Subject].TurnOfContact := GTurn;
     3941        RW[Subject].EnemyReport[Player].TurnOfContact := GTurn;
     3942      end;
     3943
     3944    sIntCancelTreaty:
     3945      begin
     3946{$IFDEF TEXTLOG}CmdInfo := Format('IntCancelTreaty P%d with P%d', [Player, Subject]); {$ENDIF}
     3947        CancelTreaty(Player, Subject);
     3948      end;
     3949
     3950    (* sIntChoosePeace:
     3951      begin
     3952      {$IFDEF TEXTLOG}CmdInfo:=Format('IntChoosePeace P%d+P%d', [Player,Subject]);{$ENDIF}
     3953      RW[Player].Treaty[Subject]:=trPeace;
     3954      RW[Subject].Treaty[Player]:=trPeace;
     3955      end; *)
     3956
     3957    sIntTellAboutModel .. sIntTellAboutModel + (nPl - 1) shl 4:
     3958      begin
     3959        p1 := (Command - sIntTellAboutModel) shr 4; // told player
     3960{$IFDEF TEXTLOG}CmdInfo := Format('IntTellAboutModel P%d about P%d Mod%d', [p1, Player, Subject]); {$ENDIF}
     3961        assert((Player >= 0) and (Player < nPl));
     3962        assert((Subject >= 0) and (Subject < RW[Player].nModel));
     3963        MakeModelInfo(Player, Subject, RW[Player].Model[Subject],
     3964          RW[p1].EnemyModel[RW[p1].nEnemyModel]);
     3965        RWemix[p1, Player, Subject] := RW[p1].nEnemyModel;
     3966        inc(RW[p1].nEnemyModel);
     3967        assert(RW[p1].nEnemyModel < nemmax);
     3968      end;
     3969
     3970    sIntDiscoverZOC:
     3971      begin
     3972{$IFDEF TEXTLOG}CmdInfo := Format('IntDiscoverZOC P%d Loc%d', [Player, integer(Data)]); {$ENDIF}
     3973        Discover9(integer(Data), Player, lObserveUnhidden, true, false);
     3974      end;
     3975
     3976    sIntExpandTerritory:
     3977      if Mode < moPlaying then
     3978      begin
     3979{$IFDEF TEXTLOG}CmdInfo := Format('IntExpandTerritory P%d Loc%d', [Player, RW[Player].City[Subject].Loc]); {$ENDIF}
     3980        move(Data, BorderChanges, SizeOf(BorderChanges));
     3981        ExpandTerritory(RW[Player].City[Subject].Loc);
     3982      end;
     3983
     3984    sIntBuyMaterial:
     3985      with RW[Player].City[Subject] do
     3986      begin
     3987{$IFDEF TEXTLOG}CmdInfo := Format('IntBuyMaterial P%d Loc%d Cost%d', [Player, Loc, integer(Data)]); {$ENDIF}
     3988        dec(RW[Player].Money, integer(Data));
     3989        if (GWonder[woMich].EffectiveOwner = Player) and (Project and cpImp <> 0)
     3990        then
     3991          inc(Prod, integer(Data) div 2)
     3992        else
     3993          inc(Prod, integer(Data) div 4);
     3994        if Project0 and not cpAuto <> Project and not cpAuto then
     3995          Project0 := Project;
     3996        Prod0 := Prod;
     3997      end;
     3998
     3999    sIntPayPrices .. sIntPayPrices + 12:
     4000      begin
     4001{$IFDEF TEXTLOG}CmdInfo := Format('IntPayPrices P%d+P%d', [Player, Subject]); {$ENDIF}
     4002        for i := 0 to TOffer(Data).nDeliver - 1 do
     4003          PayPrice(Player, Subject, TOffer(Data).Price[i], true);
     4004        for i := 0 to TOffer(Data).nCost - 1 do
     4005          PayPrice(Subject, Player, TOffer(Data).Price[TOffer(Data).nDeliver
     4006            + i], true);
     4007        for i := 0 to TOffer(Data).nDeliver + TOffer(Data).nCost - 1 do
     4008          if TOffer(Data).Price[i] = opTreaty + trAlliance then
     4009          begin // add view area of allied player
     4010            DiscoverViewAreas(Player);
     4011            DiscoverViewAreas(Subject);
     4012            Break
     4013          end
     4014      end;
     4015
     4016    sIntSetDevModel:
     4017      if Mode < moPlaying then
     4018        move(Data, RW[Player].DevModel.Kind, sIntSetDevModel and $F * 4);
     4019
     4020    sIntSetModelStatus:
     4021      if ProcessClientData[Player] then
     4022      begin
     4023{$IFDEF TEXTLOG}CmdInfo := Format('IntSetModelStatus P%d', [Player]);
     4024        {$ENDIF}
     4025        RW[Player].Model[Subject].Status := integer(Data);
     4026      end;
     4027
     4028    sIntSetUnitStatus:
     4029      if ProcessClientData[Player] then
     4030      begin
     4031{$IFDEF TEXTLOG}CmdInfo := Format('IntSetUnitStatus P%d', [Player]);
     4032        {$ENDIF}
     4033        RW[Player].Un[Subject].Status := integer(Data);
     4034      end;
     4035
     4036    sIntSetCityStatus:
     4037      if ProcessClientData[Player] then
     4038      begin
     4039{$IFDEF TEXTLOG}CmdInfo := Format('IntSetCityStatus P%d', [Player]);
     4040        {$ENDIF}
     4041        RW[Player].City[Subject].Status := integer(Data);
     4042      end;
     4043
     4044    sIntSetECityStatus:
     4045      if ProcessClientData[Player] then
     4046      begin
     4047{$IFDEF TEXTLOG}CmdInfo := Format('IntSetECityStatus P%d', [Player]);
     4048        {$ENDIF}
     4049        RW[Player].EnemyCity[Subject].Status := integer(Data);
     4050      end;
     4051
     4052  end; { case command }
     4053end; { IntServer }
    35974054
    35984055end.
    3599 
  • trunk/Direct.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit Direct;
    43
     
    1110
    1211const
    13 WM_GO = WM_USER;
    14 WM_CHANGECLIENT = WM_USER+1; // hand over control to other client
    15 WM_NEXTPLAYER = WM_USER+2; // active player's turn ended, next player
    16 WM_AIEXCEPTION = WM_USER+3;
     12  WM_GO = WM_USER;
     13  WM_CHANGECLIENT = WM_USER + 1; // hand over control to other client
     14  WM_NEXTPLAYER = WM_USER + 2; // active player's turn ended, next player
     15  WM_AIEXCEPTION = WM_USER + 3;
    1716
    1817type
     
    3029    procedure SetInfo(x: string);
    3130    procedure SetState(x: integer);
    32     procedure OnGo(var m:TMessage); message WM_GO;
    33     procedure OnChangeClient(var m:TMessage); message WM_CHANGECLIENT;
    34     procedure OnNextPlayer(var m:TMessage); message WM_NEXTPLAYER;
    35     procedure OnAIException(var Msg:TMessage); message WM_AIEXCEPTION;
     31    procedure OnGo(var m: TMessage); message WM_GO;
     32    procedure OnChangeClient(var m: TMessage); message WM_CHANGECLIENT;
     33    procedure OnNextPlayer(var m: TMessage); message WM_NEXTPLAYER;
     34    procedure OnAIException(var Msg: TMessage); message WM_AIEXCEPTION;
    3635  end;
    3736
     
    4241
    4342uses
    44 ScreenTools,Protocol,GameServer,Start,LocalPlayer,NoTerm,Back,ShellAPI;
     43  ScreenTools, Protocol, GameServer, Start, LocalPlayer, NoTerm, Back, ShellAPI;
    4544
    4645{$R *.DFM}
     
    4847procedure Notify(ID: integer);
    4948begin
    50 DirectDlg.DlgNotify(ID);
     49  DirectDlg.DlgNotify(ID);
    5150end;
    5251
    5352procedure TDirectDlg.DlgNotify(ID: integer);
    5453var
    55 hMem: Cardinal;
    56 p: pointer;
    57 s: string;
    58 begin
    59 case ID of
    60   ntInitLocalHuman:
     54  hMem: Cardinal;
     55  p: pointer;
     56  s: string;
     57begin
     58  case ID of
     59    ntInitLocalHuman:
     60      begin
     61        SetMainTextureByAge(-1);
     62        State := -1;
     63        Info := Phrases.Lookup('BUSY_MODLH');
     64        Show;
     65        Invalidate;
     66        Update;
     67      end;
     68    ntInitModule .. ntInitModule + maxBrain - 1:
     69      if visible then
     70      begin
     71        s := Format(Phrases.Lookup('BUSY_MOD'),
     72          [Brain[ID - ntInitModule].Name]);
     73        while BiColorTextWidth(Canvas, s) + 64 > ClientWidth do
     74          Delete(s, Length(s), 1);
     75        SetInfo(s);
     76      end;
     77    ntCreateWorld:
     78      if visible then
     79        SetInfo(Phrases.Lookup('BUSY_START'));
     80    ntInitPlayers:
     81      if visible then
     82        SetInfo(Phrases.Lookup('BUSY_INIT'));
     83    ntDeactivationMissing .. ntDeactivationMissing + nPl - 1:
     84      SimpleMessage(Format(Phrases.Lookup('MISSDEACT'),
     85        [ID - ntDeactivationMissing]));
     86    ntSetAIName .. ntSetAIName + nPl - 1:
     87      LocalPlayer.SetAIName(ID - ntSetAIName, NotifyMessage);
     88    ntException .. ntException + maxBrain - 1:
     89      PostMessage(Handle, WM_AIEXCEPTION, ID - ntException, 0);
     90    ntLoadBegin:
     91      begin
     92        Info := Phrases.Lookup('BUSY_LOAD');
     93        SetState(0);
     94      end;
     95    ntLoadState .. ntLoadState + 128:
     96      SetState(ID - ntLoadState);
     97    ntDLLError .. ntDLLError + 128:
     98      SimpleMessage(Format(Phrases.Lookup('DLLERROR'),
     99        [Brain[ID - ntDLLError].FileName]));
     100    ntAIError:
     101      SimpleMessage(Format(Phrases.Lookup('AIERROR'), [NotifyMessage]));
     102    ntClientError .. ntClientError + 128:
     103      SimpleMessage(Format(Phrases.Lookup('CLIENTERROR'),
     104        [Brain[ID - ntClientError].FileName]));
     105    ntEndInfo:
     106      begin
     107        Hide;
     108        background.Update
     109      end;
     110    ntLoadError:
     111      begin
     112        if OpenClipboard(Handle) then
     113        begin // copy file path to clipboard
     114          NotifyMessage := NotifyMessage + #0;
     115          hMem := GlobalAlloc(GMEM_MOVEABLE or GMEM_DDESHARE,
     116            Length(NotifyMessage));
     117          p := GlobalLock(hMem);
     118          if p <> nil then
     119            move(NotifyMessage[1], p^, Length(NotifyMessage));
     120          GlobalUnlock(hMem);
     121          SetClipboardData(CF_TEXT, hMem);
     122          CloseClipboard;
     123        end;
     124        with MessgDlg do
     125        begin
     126          MessgText := Phrases.Lookup('LOADERROR');
     127          Kind := mkYesNo;
     128          ShowModal;
     129          if ModalResult = mrOK then
     130            ShellExecute(Handle, 'open',
     131              'http://c-evo.org/_sg/contact/cevobug.html', '', '',
     132              SW_SHOWNORMAL);
     133        end
     134      end;
     135    ntStartDone:
     136      if not Quick then
     137      begin
     138        StartDlg.Hide;
     139        background.Update
     140      end;
     141    ntStartGo, ntStartGoRefresh, ntStartGoRefreshMaps:
     142      if Quick then
     143        Close
     144      else
     145      begin
     146        if ID = ntStartGoRefresh then
     147          StartDlg.UpdateFormerGames
     148        else if ID = ntStartGoRefreshMaps then
     149          StartDlg.UpdateMaps;
     150        StartDlg.Show;
     151      end;
     152    ntChangeClient:
     153      PostMessage(Handle, WM_CHANGECLIENT, 0, 0);
     154    ntNextPlayer:
     155      PostMessage(Handle, WM_NEXTPLAYER, 0, 0);
     156    ntDeinitModule .. ntDeinitModule + maxBrain - 1:
     157      begin
     158        Info := Format(Phrases2.Lookup('BUSY_DEINIT'),
     159          [Brain[ID - ntDeinitModule].Name]);
     160        while BiColorTextWidth(Canvas, Info) + 64 > ClientWidth do
     161          Delete(Info, Length(Info), 1);
     162        SetMainTextureByAge(-1);
     163        State := -1;
     164        Show;
     165        Invalidate;
     166        Update;
     167      end;
     168    ntBackOn:
     169      begin
     170        background.Show;
     171        background.Update;
     172        sleep(50); // prevent flickering
     173      end;
     174    ntBackOff:
     175      background.Close;
     176  end;
     177end;
     178
     179procedure TDirectDlg.FormCreate(Sender: TObject);
     180begin
     181  Gone := false;
     182  State := -1;
     183  Info := '';
     184  GameServer.Init(Notify);
     185  Brain[bixNoTerm].Client := NoTerm.Client;
     186  Brain[bixSuper_Virtual].Client := nil;
     187  Brain[bixTerm].Client := LocalPlayer.Client;
     188  Brain[bixNoTerm].Name := Phrases.Lookup('AIT');
     189  Brain[bixSuper_Virtual].Name := Phrases.Lookup('SUPER');
     190  Brain[bixTerm].Name := Phrases.Lookup('HUMAN');
     191  Brain[bixRandom].Name := Phrases.Lookup('RANDOMAI');
     192  Canvas.Font.Assign(UniFont[ftNormal]);
     193  Canvas.Brush.Style := bsClear;
     194end;
     195
     196procedure TDirectDlg.FormShow(Sender: TObject);
     197begin
     198  if not Gone then
     199  begin
     200    PostMessage(Handle, WM_GO, 0, 0);
     201    Gone := true
     202  end
     203end;
     204
     205procedure TDirectDlg.FormClose(Sender: TObject; var Action: TCloseAction);
     206begin
     207  GameServer.Done;
     208end;
     209
     210procedure TDirectDlg.OnGo(var m: TMessage);
     211var
     212  i: integer;
     213  s: string;
     214begin
     215  Hide;
     216  if nBrain = 3 then
     217  begin
     218    Application.MessageBox(PChar(Phrases.Lookup('NOAI')), 'C-evo', 0);
     219    Close;
     220    exit
     221  end;
     222  Quick := false;
     223  if ParamCount > 0 then
     224  begin
     225    s := ParamStr(1);
     226    if (s[1] = '-') or (s[1] = '/') then
     227    begin // special mode
     228      Delete(s, 1, 1);
     229      for i := 1 to Length(s) do
     230        if s[i] in ['a' .. 'z'] then
     231          dec(s[i], 32);
     232      if s = 'MAN' then
     233      begin
     234        Quick := true;
     235        DirectHelp(cHelpOnly);
     236        Close
     237      end;
     238    end
     239    else if (FileExists(ParamStr(1))) then
    61240    begin
    62     SetMainTextureByAge(-1);
    63     State:=-1;
    64     Info:=Phrases.Lookup('BUSY_MODLH');
    65     Show; Invalidate; Update;
    66     end;
    67   ntInitModule..ntInitModule+maxBrain-1:
    68     if visible then
    69       begin
    70       s:=Format(Phrases.Lookup('BUSY_MOD'),[Brain[ID-ntInitModule].Name]);
    71       while BiColorTextWidth(Canvas,s)+64>ClientWidth do Delete(s,Length(s),1);
    72       SetInfo(s);
    73       end;
    74   ntCreateWorld:
    75     if visible then SetInfo(Phrases.Lookup('BUSY_START'));
    76   ntInitPlayers:
    77     if visible then SetInfo(Phrases.Lookup('BUSY_INIT'));
    78   ntDeactivationMissing..ntDeactivationMissing+nPl-1:
    79     SimpleMessage(Format(Phrases.Lookup('MISSDEACT'),[ID-ntDeactivationMissing]));
    80   ntSetAIName..ntSetAIName+nPl-1:
    81     LocalPlayer.SetAIName(ID-ntSetAIName, NotifyMessage);
    82   ntException..ntException+maxBrain-1:
    83     PostMessage(Handle,WM_AIEXCEPTION,ID-ntException,0);
    84   ntLoadBegin:
    85     begin Info:=Phrases.Lookup('BUSY_LOAD'); SetState(0); end;
    86   ntLoadState..ntLoadState+128:
    87     SetState(ID-ntLoadState);
    88   ntDLLError..ntDLLError+128:
    89     SimpleMessage(Format(Phrases.Lookup('DLLERROR'),[Brain[ID-ntDLLError].FileName]));
    90   ntAIError:
    91     SimpleMessage(Format(Phrases.Lookup('AIERROR'),[NotifyMessage]));
    92   ntClientError..ntClientError+128:
    93     SimpleMessage(Format(Phrases.Lookup('CLIENTERROR'),[Brain[ID-ntClientError].FileName]));
    94   ntEndInfo:
    95     begin Hide; background.update end;
    96   ntLoadError:
    97     begin
    98     if OpenClipboard(Handle) then
    99       begin // copy file path to clipboard
    100       NotifyMessage:=NotifyMessage+#0;
    101       hMem:=GlobalAlloc(GMEM_MOVEABLE or GMEM_DDESHARE, Length(NotifyMessage));
    102       p:=GlobalLock(hMem);
    103       if p<>nil then
    104         move(NotifyMessage[1],p^,Length(NotifyMessage));
    105       GlobalUnlock(hMem);
    106       SetClipboardData(CF_TEXT, hMem);
    107       CloseClipboard;
    108       end;
    109     with MessgDlg do
    110       begin
    111       MessgText:=Phrases.Lookup('LOADERROR');
    112       Kind:=mkYesNo;
    113       ShowModal;
    114       if ModalResult=mrOK then
    115         ShellExecute(Handle,'open','http://c-evo.org/_sg/contact/cevobug.html',
    116           '','',SW_SHOWNORMAL);
    117       end
    118     end;
    119   ntStartDone:
    120     if not Quick then
    121       begin StartDlg.Hide; background.Update end;
    122   ntStartGo, ntStartGoRefresh, ntStartGoRefreshMaps:
    123     if Quick then Close
    124     else
    125       begin
    126       if ID=ntStartGoRefresh then
    127         StartDlg.UpdateFormerGames
    128       else if ID=ntStartGoRefreshMaps then
    129         StartDlg.UpdateMaps;
    130       StartDlg.Show;
    131       end;
    132   ntChangeClient:
    133     PostMessage(Handle,WM_CHANGECLIENT,0,0);
    134   ntNextPlayer:
    135     PostMessage(Handle,WM_NEXTPLAYER,0,0);
    136   ntDeinitModule..ntDeinitModule+maxBrain-1:
    137     begin
    138     Info:=Format(Phrases2.Lookup('BUSY_DEINIT'),
    139       [Brain[ID-ntDeinitModule].Name]);
    140     while BiColorTextWidth(Canvas,Info)+64>ClientWidth do
    141       Delete(Info,Length(Info),1);
    142     SetMainTextureByAge(-1);
    143     State:=-1;
    144     Show;
    145     Invalidate;
    146     Update;
    147     end;
    148   ntBackOn:
    149     begin
    150     background.Show;
    151     background.update;
    152     sleep(50); // prevent flickering
    153     end;
    154   ntBackOff:
    155     background.Close;
    156   end;
    157 end;
    158 
    159 procedure TDirectDlg.FormCreate(Sender: TObject);
    160 begin
    161 Gone:=false;
    162 State:=-1;
    163 Info:='';
    164 GameServer.Init(Notify);
    165 Brain[bixNoTerm].Client:=NoTerm.Client;
    166 Brain[bixSuper_Virtual].Client:=nil;
    167 Brain[bixTerm].Client:=LocalPlayer.Client;
    168 Brain[bixNoTerm].Name:=Phrases.Lookup('AIT');
    169 Brain[bixSuper_Virtual].Name:=Phrases.Lookup('SUPER');
    170 Brain[bixTerm].Name:=Phrases.Lookup('HUMAN');
    171 Brain[bixRandom].name:=Phrases.Lookup('RANDOMAI');
    172 Canvas.Font.Assign(UniFont[ftNormal]);
    173 Canvas.Brush.Style:=bsClear;
    174 end;
    175 
    176 procedure TDirectDlg.FormShow(Sender: TObject);
    177 begin
    178 if not Gone then
    179   begin PostMessage(Handle,WM_GO,0,0); Gone:=true end
    180 end;
    181 
    182 procedure TDirectDlg.FormClose(Sender: TObject; var Action: TCloseAction);
    183 begin
    184 GameServer.Done;
    185 end;
    186 
    187 procedure TDirectDlg.OnGo(var m:TMessage);
    188 var
    189 i: integer;
    190 s: string;
    191 begin
    192 Hide;
    193 if nBrain=3 then
    194   begin
    195   Application.MessageBox(PChar(Phrases.Lookup('NOAI')), 'C-evo', 0);
    196   close;
    197   exit
    198   end;
    199 Quick:=false;
    200 if ParamCount>0 then
    201   begin
    202   s:=ParamStr(1);
    203   if (s[1]='-') or (s[1]='/') then
    204     begin // special mode
    205     Delete(s,1,1);
    206     for i:=1 to Length(s) do if s[i] in ['a'..'z'] then dec(s[i],32);
    207     if s='MAN' then
    208       begin Quick:=true; DirectHelp(cHelpOnly); Close end;
    209     end
    210   else if (FileExists(ParamStr(1))) then
    211     begin
    212     Quick:=true;
    213     if not LoadGame(ExtractFilePath(ParamStr(1)),ExtractFileName(ParamStr(1)),-1,false) then
    214       begin
    215       SimpleMessage(Phrases.Lookup('LOADERR'));
    216       Close
     241      Quick := true;
     242      if not LoadGame(ExtractFilePath(ParamStr(1)), ExtractFileName(ParamStr(1)
     243        ), -1, false) then
     244      begin
     245        SimpleMessage(Phrases.Lookup('LOADERR'));
     246        Close
    217247      end
    218248    end
    219249  end;
    220 if not Quick then
    221   begin background.Show; StartDlg.Show end
    222 end;
    223 
    224 procedure TDirectDlg.OnChangeClient(var m:TMessage);
    225 begin
    226 ChangeClient;
    227 end;
    228 
    229 procedure TDirectDlg.OnNextPlayer(var m:TMessage);
    230 begin
    231 NextPlayer;
    232 end;
    233 
    234 procedure TDirectDlg.OnAIException(var Msg:TMessage);
    235 begin
    236 Application.MessageBox(PChar(Format(Phrases.Lookup('AIEXCEPTION'),
    237   [Brain[Msg.WParam].Name])), 'C-evo', 0);
     250  if not Quick then
     251  begin
     252    background.Show;
     253    StartDlg.Show
     254  end
     255end;
     256
     257procedure TDirectDlg.OnChangeClient(var m: TMessage);
     258begin
     259  ChangeClient;
     260end;
     261
     262procedure TDirectDlg.OnNextPlayer(var m: TMessage);
     263begin
     264  NextPlayer;
     265end;
     266
     267procedure TDirectDlg.OnAIException(var Msg: TMessage);
     268begin
     269  Application.MessageBox(PChar(Format(Phrases.Lookup('AIEXCEPTION'),
     270    [Brain[Msg.WParam].Name])), 'C-evo', 0);
    238271end;
    239272
    240273procedure TDirectDlg.FormPaint(Sender: TObject);
    241274begin
    242 PaintBackground(self,3,3,ClientWidth-6,ClientHeight-6);
    243 Frame(Canvas,0,0,ClientWidth-1,ClientHeight-1,0,0);
    244 Frame(Canvas,1,1,ClientWidth-2,ClientHeight-2,MainTexture.clBevelLight,
    245   MainTexture.clBevelShade);
    246 Frame(Canvas,2,2,ClientWidth-3,ClientHeight-3,MainTexture.clBevelLight,
    247   MainTexture.clBevelShade);
    248 if State>=0 then
    249   RisedTextOut(Canvas,(ClientWidth-BiColorTextWidth(Canvas,Info)) div 2,16,Info)
    250 else RisedTextOut(Canvas,(ClientWidth-BiColorTextWidth(Canvas,Info)) div 2,
    251   (ClientHeight-Canvas.TextHeight(Info)) div 2,Info);
    252 if State>=0 then
    253   PaintProgressBar(Canvas,3,ClientWidth div 2 -64,40,State,0,128,MainTexture);
     275  PaintBackground(self, 3, 3, ClientWidth - 6, ClientHeight - 6);
     276  Frame(Canvas, 0, 0, ClientWidth - 1, ClientHeight - 1, 0, 0);
     277  Frame(Canvas, 1, 1, ClientWidth - 2, ClientHeight - 2,
     278    MainTexture.clBevelLight, MainTexture.clBevelShade);
     279  Frame(Canvas, 2, 2, ClientWidth - 3, ClientHeight - 3,
     280    MainTexture.clBevelLight, MainTexture.clBevelShade);
     281  if State >= 0 then
     282    RisedTextOut(Canvas, (ClientWidth - BiColorTextWidth(Canvas, Info))
     283      div 2, 16, Info)
     284  else
     285    RisedTextOut(Canvas, (ClientWidth - BiColorTextWidth(Canvas, Info)) div 2,
     286      (ClientHeight - Canvas.TextHeight(Info)) div 2, Info);
     287  if State >= 0 then
     288    PaintProgressBar(Canvas, 3, ClientWidth div 2 - 64, 40, State, 0, 128,
     289      MainTexture);
    254290end;
    255291
    256292procedure TDirectDlg.SetInfo(x: string);
    257293begin
    258 Info:=x;
    259 Invalidate;
    260 Update;
     294  Info := x;
     295  Invalidate;
     296  Update;
    261297end;
    262298
    263299procedure TDirectDlg.SetState(x: integer);
    264300begin
    265 if (x<0)<>(State<0) then
    266   begin State:=x; Invalidate; Update end
    267 else if x<>State then
    268   begin
    269   State:=x;
    270   PaintProgressBar(Canvas,6,ClientWidth div 2 -64,40,State,128-State,128,MainTexture);
     301  if (x < 0) <> (State < 0) then
     302  begin
     303    State := x;
     304    Invalidate;
     305    Update
    271306  end
     307  else if x <> State then
     308  begin
     309    State := x;
     310    PaintProgressBar(Canvas, 6, ClientWidth div 2 - 64, 40, State, 128 - State,
     311      128, MainTexture);
     312  end
    272313end;
    273314
    274315end.
    275 
  • trunk/Directories.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit Directories;
    43
     
    65
    76var
    8 HomeDir, DataDir: string;
     7  HomeDir, DataDir: string;
    98
    109function LocalizedFilePath(path: string): string;
    11 
    1210
    1311implementation
    1412
    1513uses
    16 ShlObj,Windows,SysUtils;
     14  ShlObj, Windows, SysUtils;
    1715
    1816function GetSpecialDirectory(const CSIDL: integer): string;
    1917var
    20 RecPath: PChar;
     18  RecPath: PChar;
    2119begin
    22 RecPath:=StrAlloc(MAX_PATH);
    23 try
    24   FillChar(RecPath^, MAX_PATH, 0);
    25   if SHGetSpecialFolderPath(0, RecPath, CSIDL, false) then
    26     result:=RecPath
    27   else result:='';
    28 finally
    29   StrDispose(RecPath);
     20  RecPath := StrAlloc(MAX_PATH);
     21  try
     22    FillChar(RecPath^, MAX_PATH, 0);
     23    if SHGetSpecialFolderPath(0, RecPath, CSIDL, false) then
     24      result := RecPath
     25    else
     26      result := '';
     27  finally
     28    StrDispose(RecPath);
    3029  end
    3130end;
     
    3332function DirectoryExists(path: string): boolean;
    3433var
    35 f: TSearchRec;
     34  f: TSearchRec;
    3635begin
    37 result:=FindFirst(path,faDirectory,f)=0;
     36  result := FindFirst(path, faDirectory, f) = 0;
    3837end;
    3938
    4039function LocalizedFilePath(path: string): string;
    4140begin
    42 result:=DataDir+'Localization\'+path;
    43 if not FileExists(result) then
    44   result:=HomeDir+path
     41  result := DataDir + 'Localization\' + path;
     42  if not FileExists(result) then
     43    result := HomeDir + path
    4544end;
    4645
    47 
    4846var
    49 AppDataDir: string;
    50 src,dst: TSearchRec;
     47  AppDataDir: string;
     48  src, dst: TSearchRec;
    5149
    5250initialization
    53 HomeDir:=ExtractFilePath(ParamStr(0));
    5451
     52HomeDir := ExtractFilePath(ParamStr(0));
    5553
    56 AppDataDir:=GetSpecialDirectory(CSIDL_APPDATA);
    57 if AppDataDir='' then
    58   DataDir:=HomeDir
     54AppDataDir := GetSpecialDirectory(CSIDL_APPDATA);
     55if AppDataDir = '' then
     56  DataDir := HomeDir
    5957else
    60   begin
    61   if not DirectoryExists(AppDataDir+'\C-evo') then
    62     CreateDir(AppDataDir+'\C-evo');
    63   DataDir:=AppDataDir+'\C-evo\';
    64   end;
    65 if not DirectoryExists(DataDir+'Saved') then
    66   CreateDir(DataDir+'Saved');
    67 if not DirectoryExists(DataDir+'Maps') then
    68   CreateDir(DataDir+'Maps');
     58begin
     59  if not DirectoryExists(AppDataDir + '\C-evo') then
     60    CreateDir(AppDataDir + '\C-evo');
     61  DataDir := AppDataDir + '\C-evo\';
     62end;
     63if not DirectoryExists(DataDir + 'Saved') then
     64  CreateDir(DataDir + 'Saved');
     65if not DirectoryExists(DataDir + 'Maps') then
     66  CreateDir(DataDir + 'Maps');
    6967
    7068// copy appdata if not done yet
    71 if FindFirst(HomeDir+'AppData\Saved\*.cevo',$21,src)=0 then
     69if FindFirst(HomeDir + 'AppData\Saved\*.cevo', $21, src) = 0 then
    7270  repeat
    73     if (FindFirst(DataDir+'Saved\'+src.Name,$21,dst)<>0)
    74       or (dst.Time<src.Time) then
    75       CopyFile(PChar(HomeDir+'AppData\Saved\'+src.Name),
    76         PChar(DataDir+'Saved\'+src.Name),false);
    77   until FindNext(src)<>0;
     71    if (FindFirst(DataDir + 'Saved\' + src.Name, $21, dst) <> 0) or
     72      (dst.Time < src.Time) then
     73      CopyFile(PChar(HomeDir + 'AppData\Saved\' + src.Name),
     74        PChar(DataDir + 'Saved\' + src.Name), false);
     75  until FindNext(src) <> 0;
     76
    7877end.
  • trunk/GameServer.pas

    r2 r6  
    11{$INCLUDE switches}
    2 //{$DEFINE TEXTLOG}
    3 //{$DEFINE LOADPERF}
     2// {$DEFINE TEXTLOG}
     3// {$DEFINE LOADPERF}
    44unit GameServer;
    55
     
    77
    88uses
    9 Protocol, Database;
     9  Protocol, Database;
    1010
    1111const
    12 Version=$010200;
    13 FirstAICompatibleVersion=$000D00;
    14 FirstBookCompatibleVersion=$010103;
    15 
    16 // notifications
    17 ntCreateWorld=0; ntInitModule=$100; ntInitLocalHuman=$1FF;
    18 ntDLLError=$200; ntAIError=$2FF;
    19 ntClientError=$300;
    20 ntInitPlayers=$400; ntDeactivationMissing=$410;
    21 ntSetAIName=$420;
    22 ntException=$500;
    23 ntLoadBegin=$600; ntLoadState=$601;
    24 ntEndInfo=$6FC; ntBackOn=$6FD; ntBackOff=$6FE; ntLoadError=$6FF;
    25 ntStartDone=$700; ntStartGo=$701; ntStartGoRefresh=$702;
    26 ntStartGoRefreshMaps=$703;
    27 ntChangeClient=$800; ntNextPlayer=$810;
    28 ntDeinitModule=$900;
    29 
    30 // module flags
    31 fMultiple=$10000000; fDotNet=$20000000; fUsed=$40000000;
    32 
    33 // save map tile flags
    34 smOwned=$20; smUnit=$40; smCity=$80;
    35 
    36 maxBrain=255;
    37 bixNoTerm=0; bixSuper_Virtual=1; bixTerm=2; bixRandom=3; bixFirstAI=4;
     12  Version = $010200;
     13  FirstAICompatibleVersion = $000D00;
     14  FirstBookCompatibleVersion = $010103;
     15
     16  // notifications
     17  ntCreateWorld = 0;
     18  ntInitModule = $100;
     19  ntInitLocalHuman = $1FF;
     20  ntDLLError = $200;
     21  ntAIError = $2FF;
     22  ntClientError = $300;
     23  ntInitPlayers = $400;
     24  ntDeactivationMissing = $410;
     25  ntSetAIName = $420;
     26  ntException = $500;
     27  ntLoadBegin = $600;
     28  ntLoadState = $601;
     29  ntEndInfo = $6FC;
     30  ntBackOn = $6FD;
     31  ntBackOff = $6FE;
     32  ntLoadError = $6FF;
     33  ntStartDone = $700;
     34  ntStartGo = $701;
     35  ntStartGoRefresh = $702;
     36  ntStartGoRefreshMaps = $703;
     37  ntChangeClient = $800;
     38  ntNextPlayer = $810;
     39  ntDeinitModule = $900;
     40
     41  // module flags
     42  fMultiple = $10000000;
     43  fDotNet = $20000000;
     44  fUsed = $40000000;
     45
     46  // save map tile flags
     47  smOwned = $20;
     48  smUnit = $40;
     49  smCity = $80;
     50
     51  maxBrain = 255;
     52  bixNoTerm = 0;
     53  bixSuper_Virtual = 1;
     54  bixTerm = 2;
     55  bixRandom = 3;
     56  bixFirstAI = 4;
    3857
    3958type
    40 TNotifyFunction = procedure(ID: integer);
    41 
    42 TBrainInfo= record
    43   FileName, DLLName, Name, Credits: string; {filename and full name}
    44   hm, {module handle}
    45   Flags,
    46   ServerVersion,
    47   DataVersion, DataSize: integer;
    48   Client: TClientCall; {client function address}
    49   Initialized: boolean;
     59  TNotifyFunction = procedure(ID: integer);
     60
     61  TBrainInfo = record
     62    FileName, DLLName, Name, Credits: string; { filename and full name }
     63    hm, { module handle }
     64    Flags, ServerVersion, DataVersion, DataSize: integer;
     65    Client: TClientCall; { client function address }
     66    Initialized: boolean;
    5067  end;
    5168
    5269var
    53 // PARAMETERS
    54 bixView: array[0..nPl-1]of integer; {brain index of the players}
    55 Difficulty: array[0..nPl-1]of integer absolute Database.Difficulty; {difficulty}
    56 
    57 // READ ONLY
    58 DotNetClient: TClientCall;
    59 bixBeginner, // AI to use for beginner level
    60 nBrain: integer; {number of brains available}
    61 Brain: array[-1..maxBrain-1] of TBrainInfo; {available brains}
    62 NotifyMessage: string;
     70  // PARAMETERS
     71  bixView: array [0 .. nPl - 1] of integer; { brain index of the players }
     72  Difficulty: array [0 .. nPl - 1] of integer absolute Database.Difficulty;
     73  { difficulty }
     74
     75  // READ ONLY
     76  DotNetClient: TClientCall;
     77  bixBeginner, // AI to use for beginner level
     78  nBrain: integer; { number of brains available }
     79  Brain: array [-1 .. maxBrain - 1] of TBrainInfo; { available brains }
     80  NotifyMessage: string;
    6381
    6482procedure Init(NotifyFunction: TNotifyFunction);
    6583procedure Done;
    6684
    67 procedure StartNewGame(const Path, FileName, Map: string; Newlx, Newly,
    68   NewLandMass, NewMaxTurn: integer);
    69 function LoadGame(const Path, FileName: string; Turn: integer; MovieMode: boolean): boolean;
     85procedure StartNewGame(const Path, FileName, Map: string;
     86  Newlx, Newly, NewLandMass, NewMaxTurn: integer);
     87function LoadGame(const Path, FileName: string; Turn: integer;
     88  MovieMode: boolean): boolean;
    7089procedure EditMap(const Map: string; Newlx, Newly, NewLandMass: integer);
    7190procedure DirectHelp(Command: integer);
     
    7594function PreviewMap(lm: integer): pointer;
    7695
    77 
    7896implementation
    7997
    8098uses
    81 Directories, CityProcessing, UnitProcessing, CmdList,
    82 
    83 Windows,Classes,SysUtils;
    84 
     99  Directories, CityProcessing, UnitProcessing, CmdList,
     100
     101  Windows, Classes, SysUtils;
    85102
    86103var
    87 MaxTurn,
    88 LoadTurn, {turn where to stop loading}
    89 nLogOpened, {nLog of opened book}
    90 {$IFOPT O-}nHandoverStack,{$ENDIF}
    91 LastEndClientCommand,
    92 pContacted, // player contacted for negotiation
    93 pDipActive, // player who's to speak in a negotiation
    94 pTurn, {player who's turn it is}
    95 GWinner,
    96 GColdWarStart,
    97 GStealFrom,
    98 SpyMission,
    99 ZOCTile,
    100 CCCommand,
    101 CCPlayer: integer;
    102 DebugMap: array[0..nPl-1] of pointer;
    103 ExeInfo: TSearchRec;
    104 Stat: array[0..nStat-1, 0..nPl-1] of ^TChart;
    105 AutoSaveState: TCmdListState;
    106 MapField: ^Cardinal; // predefined map
    107 LastOffer: TOffer;
    108 CCData: array[0..14] of integer;
    109 DevModelTurn, {turn of last call to sResetModel}
    110 bix, {brain index of the players}
    111 OriginalDataVersion: array[0..nPl-1] of integer;
    112 SavedTiles{, SavedResourceWeights}: array[0..ncmax-1] of cardinal;
    113 SavedData: array[0..nPl-1] of pointer;
    114 LogFileName, SavePath, {name of file for saving the current game}
    115 MapFileName, // name of map to use, empty for random
    116 AICredits: string;
    117 AIInfo: array[0..nPl-1] of string;
    118 Notify: TNotifyFunction;
    119 PerfFreq, LastClientTime: int64;
    120 {$IFOPT O-}HandoverStack: array[0..31] of Cardinal;{$ENDIF}
    121 AutoSaveExists,
    122 LoadOK, WinOnAlone, PreviewElevation, MovieStopped: boolean;
     104  MaxTurn, LoadTurn, { turn where to stop loading }
     105  nLogOpened, { nLog of opened book }
     106{$IFOPT O-}nHandoverStack, {$ENDIF}
     107  LastEndClientCommand, pContacted, // player contacted for negotiation
     108  pDipActive, // player who's to speak in a negotiation
     109  pTurn, { player who's turn it is }
     110  GWinner, GColdWarStart, GStealFrom, SpyMission, ZOCTile, CCCommand,
     111    CCPlayer: integer;
     112  DebugMap: array [0 .. nPl - 1] of pointer;
     113  ExeInfo: TSearchRec;
     114  Stat: array [0 .. nStat - 1, 0 .. nPl - 1] of ^TChart;
     115  AutoSaveState: TCmdListState;
     116  MapField: ^Cardinal; // predefined map
     117  LastOffer: TOffer;
     118  CCData: array [0 .. 14] of integer;
     119  DevModelTurn, { turn of last call to sResetModel }
     120  bix, { brain index of the players }
     121  OriginalDataVersion: array [0 .. nPl - 1] of integer;
     122  SavedTiles { , SavedResourceWeights } : array [0 .. ncmax - 1] of Cardinal;
     123  SavedData: array [0 .. nPl - 1] of pointer;
     124  LogFileName, SavePath, { name of file for saving the current game }
     125  MapFileName, // name of map to use, empty for random
     126  AICredits: string;
     127  AIInfo: array [0 .. nPl - 1] of string;
     128  Notify: TNotifyFunction;
     129  PerfFreq, LastClientTime: int64;
     130{$IFOPT O-}HandoverStack: array [0 .. 31] of Cardinal; {$ENDIF}
     131  AutoSaveExists, LoadOK, WinOnAlone, PreviewElevation, MovieStopped: boolean;
    123132
    124133const
    125 PreviewRND=41601260; {randseed for preview map}
    126 
    127 function Server(Command,Player,Subject:integer;var Data): integer; stdcall; forward;
    128 
    129 
    130 procedure CallPlayer(Command,p: integer; var Data);
     134  PreviewRND = 41601260; { randseed for preview map }
     135
     136function Server(Command, Player, Subject: integer; var Data): integer;
     137  stdcall; forward;
     138
     139procedure CallPlayer(Command, p: integer; var Data);
    131140begin
    132 if ((Mode<>moMovie) or (p=0)) then
     141  if ((Mode <> moMovie) or (p = 0)) then
    133142  begin
    134   {$IFOPT O-}
    135   HandoverStack[nHandoverStack]:=p;
    136   HandoverStack[nHandoverStack+1]:=Command;
    137   inc(nHandoverStack,2);
    138   Brain[bix[p]].Client(Command,p,Data);
    139   dec(nHandoverStack,2);
    140   {$ELSE}
    141   try
    142     Brain[bix[p]].Client(Command,p,Data);
    143   except
    144     Notify(ntException+bix[p]);
     143{$IFOPT O-}
     144    HandoverStack[nHandoverStack] := p;
     145    HandoverStack[nHandoverStack + 1] := Command;
     146    inc(nHandoverStack, 2);
     147    Brain[bix[p]].Client(Command, p, Data);
     148    dec(nHandoverStack, 2);
     149{$ELSE}
     150    try
     151      Brain[bix[p]].Client(Command, p, Data);
     152    except
     153      Notify(ntException + bix[p]);
    145154    end;
    146   {$ENDIF}
     155{$ENDIF}
    147156  end
    148157end;
    149158
    150 procedure CallClient(bix,Command: integer; var Data);
     159procedure CallClient(bix, Command: integer; var Data);
    151160begin
    152 if ((Mode<>moMovie) or (bix=GameServer.bix[0])) then
     161  if ((Mode <> moMovie) or (bix = GameServer.bix[0])) then
    153162  begin
    154   {$IFOPT O-}
    155   HandoverStack[nHandoverStack]:=bix;
    156   HandoverStack[nHandoverStack+1]:=Command;
    157   inc(nHandoverStack,2);
    158   Brain[bix].Client(Command,-1,Data);
    159   dec(nHandoverStack,2);
    160   {$ELSE}
    161   try
    162     Brain[bix].Client(Command,-1,Data);
    163   except
    164     Notify(ntException+bix);
     163{$IFOPT O-}
     164    HandoverStack[nHandoverStack] := bix;
     165    HandoverStack[nHandoverStack + 1] := Command;
     166    inc(nHandoverStack, 2);
     167    Brain[bix].Client(Command, -1, Data);
     168    dec(nHandoverStack, 2);
     169{$ELSE}
     170    try
     171      Brain[bix].Client(Command, -1, Data);
     172    except
     173      Notify(ntException + bix);
    165174    end;
    166   {$ENDIF}
     175{$ENDIF}
    167176  end
    168177end;
     
    170179procedure Init(NotifyFunction: TNotifyFunction);
    171180var
    172 i: integer;
    173 f: TSearchRec;
    174 T: TextFile;
    175 s: string;
     181  i: integer;
     182  f: TSearchRec;
     183  T: TextFile;
     184  s: string;
    176185
    177186begin
    178 Notify:=NotifyFunction;
    179 PreviewElevation:=false;
    180 
    181 {get available brains}
    182 Brain[bixNoTerm].FileName:=':AIT';
    183 Brain[bixNoTerm].Flags:=0;
    184 Brain[bixNoTerm].Initialized:=false;
    185 Brain[bixSuper_Virtual].FileName:=':Supervisor';
    186 Brain[bixSuper_Virtual].Flags:=0;
    187 Brain[bixSuper_Virtual].Initialized:=false;
    188 Brain[bixTerm].FileName:=':StdIntf';
    189 Brain[bixTerm].Flags:=fMultiple;
    190 Brain[bixTerm].Initialized:=false;
    191 Brain[bixTerm].ServerVersion:=Version;
    192 Brain[bixRandom].FileName:=':Random';
    193 Brain[bixRandom].Flags:=fMultiple;
    194 Brain[bixRandom].Initialized:=false;
    195 nBrain:=bixFirstAI;
    196 bixBeginner:=bixFirstAI;
    197 if FindFirst(HomeDir+'*.ai.txt',$21,f)=0 then
    198   repeat
    199     with Brain[nBrain] do
    200       begin
    201       FileName:=Copy(f.Name,1,Length(f.Name)-7);
    202       DLLName:=HomeDir+FileName;
    203       Name:=Copy(f.Name,1,Length(f.Name)-7);
    204       Credits:='';
    205       Flags:=fMultiple;
    206       Client:=nil;
    207       Initialized:=false;
    208       ServerVersion:=0;
    209       AssignFile(T,HomeDir+f.Name);
    210       Reset(T);
    211       while not EOF(T) do
     187  Notify := NotifyFunction;
     188  PreviewElevation := false;
     189
     190  { get available brains }
     191  Brain[bixNoTerm].FileName := ':AIT';
     192  Brain[bixNoTerm].Flags := 0;
     193  Brain[bixNoTerm].Initialized := false;
     194  Brain[bixSuper_Virtual].FileName := ':Supervisor';
     195  Brain[bixSuper_Virtual].Flags := 0;
     196  Brain[bixSuper_Virtual].Initialized := false;
     197  Brain[bixTerm].FileName := ':StdIntf';
     198  Brain[bixTerm].Flags := fMultiple;
     199  Brain[bixTerm].Initialized := false;
     200  Brain[bixTerm].ServerVersion := Version;
     201  Brain[bixRandom].FileName := ':Random';
     202  Brain[bixRandom].Flags := fMultiple;
     203  Brain[bixRandom].Initialized := false;
     204  nBrain := bixFirstAI;
     205  bixBeginner := bixFirstAI;
     206  if FindFirst(HomeDir + '*.ai.txt', $21, f) = 0 then
     207    repeat
     208      with Brain[nBrain] do
     209      begin
     210        FileName := Copy(f.Name, 1, Length(f.Name) - 7);
     211        DLLName := HomeDir + FileName;
     212        Name := Copy(f.Name, 1, Length(f.Name) - 7);
     213        Credits := '';
     214        Flags := fMultiple;
     215        Client := nil;
     216        Initialized := false;
     217        ServerVersion := 0;
     218        AssignFile(T, HomeDir + f.Name);
     219        Reset(T);
     220        while not EOF(T) do
    212221        begin
    213         ReadLn(T,s);
    214         s:=trim(s);
    215         if Copy(s,1,5)='#NAME' then Name:=Copy(s,7,255)
    216         else if Copy(s,1,10)='#.NET' then
    217           Flags:=Flags or fDotNet
    218         else if Copy(s,1,9)='#BEGINNER' then
    219           bixBeginner:=nBrain
    220         else if Copy(s,1,5)='#PATH' then
    221           DLLName:=HomeDir+trim(Copy(s,7,255))
    222         else if Copy(s,1,12)='#GAMEVERSION' then
    223           for i:=13 to Length(s) do
    224             case s[i] of
    225               '0'..'9': ServerVersion:=ServerVersion and $FFFF00
    226                 +ServerVersion and $FF *10+ord(s[i])-48;
    227               '.': ServerVersion:=ServerVersion shl 8;
     222          ReadLn(T, s);
     223          s := trim(s);
     224          if Copy(s, 1, 5) = '#NAME' then
     225            Name := Copy(s, 7, 255)
     226          else if Copy(s, 1, 10) = '#.NET' then
     227            Flags := Flags or fDotNet
     228          else if Copy(s, 1, 9) = '#BEGINNER' then
     229            bixBeginner := nBrain
     230          else if Copy(s, 1, 5) = '#PATH' then
     231            DLLName := HomeDir + trim(Copy(s, 7, 255))
     232          else if Copy(s, 1, 12) = '#GAMEVERSION' then
     233            for i := 13 to Length(s) do
     234              case s[i] of
     235                '0' .. '9':
     236                  ServerVersion := ServerVersion and $FFFF00 + ServerVersion and
     237                    $FF * 10 + ord(s[i]) - 48;
     238                '.':
     239                  ServerVersion := ServerVersion shl 8;
    228240              end
    229         else if Copy(s,1,8)='#CREDITS' then
    230           Credits:=Copy(s,10,255)
     241          else if Copy(s, 1, 8) = '#CREDITS' then
     242            Credits := Copy(s, 10, 255)
    231243        end;
    232       CloseFile(T);
    233       end;
    234     if (Brain[nBrain].ServerVersion>=FirstAICompatibleVersion)
    235       and (Brain[nBrain].ServerVersion<=Version)
    236       and ((Brain[nBrain].Flags and fDotNet=0) or (@DotNetClient<>nil)) then
    237       inc(nBrain);
    238   until FindNext(f)<>0;
     244        CloseFile(T);
     245      end;
     246      if (Brain[nBrain].ServerVersion >= FirstAICompatibleVersion) and
     247        (Brain[nBrain].ServerVersion <= Version) and
     248        ((Brain[nBrain].Flags and fDotNet = 0) or (@DotNetClient <> nil)) then
     249        inc(nBrain);
     250    until FindNext(f) <> 0;
    239251end;
    240252
    241253procedure Done;
    242254var
    243 i: integer;
     255  i: integer;
    244256begin
    245 for i:=0 to nBrain-1 do if Brain[i].Initialized then
    246   begin
    247   CallClient(i, cReleaseModule, nil^);
    248   if (i>=bixFirstAI) and (Brain[i].Flags and fDotNet=0) then
    249     FreeLibrary(Brain[i].hm);
    250   end;
     257  for i := 0 to nBrain - 1 do
     258    if Brain[i].Initialized then
     259    begin
     260      CallClient(i, cReleaseModule, nil^);
     261      if (i >= bixFirstAI) and (Brain[i].Flags and fDotNet = 0) then
     262        FreeLibrary(Brain[i].hm);
     263    end;
    251264end;
    252265
    253266function PreviewMap(lm: integer): pointer;
    254267begin
    255 lx:=lxmax; ly:=lymax; MapSize:=lx*ly;
    256 LandMass:=lm;
    257 RandSeed:=PreviewRND;
    258 if not PreviewElevation then
     268  lx := lxmax;
     269  ly := lymax;
     270  MapSize := lx * ly;
     271  LandMass := lm;
     272  RandSeed := PreviewRND;
     273  if not PreviewElevation then
    259274  begin
    260   CreateElevation;
    261   PreviewElevation:=true;
     275    CreateElevation;
     276    PreviewElevation := true;
    262277  end;
    263 CreateMap(true);
    264 result:=@RealMap;
     278  CreateMap(true);
     279  result := @RealMap;
    265280end;
    266281
     
    268283  DataSize: integer);
    269284begin
    270 CCCommand:=Command;
    271 CCPlayer:=Player;
    272 if DataSize>0 then move(Data,CCData,DataSize);
    273 Notify(ntChangeClient);
     285  CCCommand := Command;
     286  CCPlayer := Player;
     287  if DataSize > 0 then
     288    move(Data, CCData, DataSize);
     289  Notify(ntChangeClient);
    274290end;
    275291
    276292procedure PutMessage(Level: integer; Text: string);
    277293begin
    278 Brain[bix[0]].Client(cDebugMessage,Level,pchar(Text)^);
     294  Brain[bix[0]].Client(cDebugMessage, Level, pchar(Text)^);
    279295end;
    280296
    281297procedure ForceClientDeactivation;
    282298var
    283 NullOffer: TOffer;
     299  NullOffer: TOffer;
    284300begin
    285 if pDipActive<0 then Server(sTurn,pTurn,0,nil^) // no nego mode
    286 else case LastEndClientCommand of // nego mode
    287   scContact: Server(scReject,pDipActive,0,nil^);
    288   scDipCancelTreaty, scDipBreak: Server(scDipNotice,pDipActive,0,nil^);
     301  if pDipActive < 0 then
     302    Server(sTurn, pTurn, 0, nil^) // no nego mode
    289303  else
    290     begin // make null offer
    291     NullOffer.nDeliver:=0;
    292     NullOffer.nCost:=0;
    293     Server(scDipOffer,pDipActive,0,NullOffer);
     304    case LastEndClientCommand of // nego mode
     305      scContact:
     306        Server(scReject, pDipActive, 0, nil^);
     307      scDipCancelTreaty, scDipBreak:
     308        Server(scDipNotice, pDipActive, 0, nil^);
     309    else
     310      begin // make null offer
     311        NullOffer.nDeliver := 0;
     312        NullOffer.nCost := 0;
     313        Server(scDipOffer, pDipActive, 0, NullOffer);
     314      end
     315    end
     316end;
     317
     318procedure ChangeClient;
     319// hand over control to other client (as specified by CC...)
     320var
     321  p: integer;
     322  T: int64;
     323begin
     324  QueryPerformanceCounter(T);
     325  PutMessage(1 shl 16 + 2, Format('CLIENT: took %.1f ms',
     326    [{$IFDEF VER100}(T.LowPart - LastClientTime.LowPart)
     327{$ELSE}(T - LastClientTime){$ENDIF} * 1000.0 / PerfFreq]));
     328  LastClientTime := T;
     329  PutMessage(1 shl 16 + 2, Format('CLIENT: calling %d (%s)',
     330    [CCPlayer, Brain[bix[CCPlayer]].Name]));
     331  if CCCommand = cTurn then
     332    for p := 0 to nPl - 1 do
     333      if (p <> CCPlayer) and (1 shl p and GWatching <> 0) then
     334        CallPlayer(cShowTurnChange, p, CCPlayer);
     335
     336  p := CCPlayer;
     337  CCPlayer := -1;
     338  CallPlayer(CCCommand, p, CCData);
     339  if (Mode = moPlaying) and (Brain[bix[p]].Flags and aiThreaded = 0) and
     340    (CCPlayer < 0) then
     341  begin
     342    Notify(ntDeactivationMissing + p);
     343    ForceClientDeactivation;
     344  end
     345end;
     346
     347procedure Inform(p: integer);
     348var
     349  i, p1: integer;
     350begin
     351  RW[p].Turn := GTurn;
     352  if (GTurn = MaxTurn) and (p = pTurn) and (p = 0) then
     353    RW[p].Happened := RW[p].Happened or phTimeUp;
     354  if (GWinner > 0) and (p = pTurn) and (p = 0) then
     355    RW[p].Happened := RW[p].Happened or phShipComplete;
     356  RW[p].Alive := GAlive;
     357  move(GWonder, RW[p].Wonder, SizeOf(GWonder));
     358  move(GShip, RW[p].Ship, SizeOf(GShip));
     359  for p1 := 0 to nPl - 1 do
     360    if (p1 <> p) and (bix[p1] >= 0) and (Difficulty[p1] > 0) then
     361      RW[p].EnemyReport[p1].Credibility := RW[p1].Credibility;
     362  for p1 := 0 to nPl - 1 do
     363    if (p1 <> p) and (1 shl p1 and GAlive <> 0) then
     364    begin
     365      if (GTestFlags and tfUncover <> 0) or (Difficulty[p] = 0) or
     366        (RW[p].Treaty[p1] >= trFriendlyContact) then
     367        GiveCivilReport(p, p1);
     368      if (GTestFlags and tfUncover <> 0) or (Difficulty[p] = 0) or
     369        (RW[p].Treaty[p1] = trAlliance) then
     370        GiveMilReport(p, p1)
     371    end;
     372  for i := 0 to RW[p].nEnemyModel - 1 do
     373    with RW[p].EnemyModel[i] do
     374      Lost := Destroyed[p, Owner, mix];
     375end;
     376
     377procedure LogChanges;
     378var
     379  p, ix: integer;
     380begin
     381  for p := 0 to nPl - 1 do
     382    if (1 shl p and GWatching <> 0) and ProcessClientData[p] then
     383    begin
     384      // log unit status changes
     385      for ix := 0 to RW[p].nUn - 1 do
     386        with RW[p].Un[ix] do
     387          if (Loc >= 0) and (SavedStatus <> Status) then
     388          begin
     389            CL.Put(sIntSetUnitStatus, p, ix, @Status);
     390            SavedStatus := Status
     391          end;
     392      // log city status changes
     393      for ix := 0 to RW[p].nCity - 1 do
     394        with RW[p].City[ix] do
     395          if (Loc >= 0) and (SavedStatus <> Status) then
     396          begin
     397            CL.Put(sIntSetCityStatus, p, ix, @Status);
     398            SavedStatus := Status
     399          end;
     400      // log model status changes
     401      for ix := 0 to RW[p].nModel - 1 do
     402        with RW[p].Model[ix] do
     403          if SavedStatus <> Status then
     404          begin
     405            CL.Put(sIntSetModelStatus, p, ix, @Status);
     406            SavedStatus := Status
     407          end;
     408      // log enemy city status changes
     409      for ix := 0 to RW[p].nEnemyCity - 1 do
     410        with RW[p].EnemyCity[ix] do
     411          if (Loc >= 0) and (SavedStatus <> Status) then
     412          begin
     413            CL.Put(sIntSetECityStatus, p, ix, @Status);
     414            SavedStatus := Status
     415          end;
     416      // log data changes
     417      if Brain[bix[p]].DataSize > 0 then
     418      begin
     419        CL.PutDataChanges(sIntDataChange, p, SavedData[p], RW[p].Data,
     420          Brain[bix[p]].DataSize);
     421        move(RW[p].Data^, SavedData[p]^, Brain[bix[p]].DataSize * 4);
     422      end
     423    end;
     424end;
     425
     426procedure NoLogChanges;
     427var
     428  p, ix: integer;
     429begin
     430  for p := 0 to nPl - 1 do
     431    if (1 shl p and GWatching <> 0) and ProcessClientData[p] then
     432    begin
     433      for ix := 0 to RW[p].nUn - 1 do
     434        with RW[p].Un[ix] do
     435          SavedStatus := Status;
     436      for ix := 0 to RW[p].nCity - 1 do
     437        with RW[p].City[ix] do
     438          SavedStatus := Status;
     439      for ix := 0 to RW[p].nModel - 1 do
     440        with RW[p].Model[ix] do
     441          SavedStatus := Status;
     442      for ix := 0 to RW[p].nEnemyCity - 1 do
     443        with RW[p].EnemyCity[ix] do
     444          SavedStatus := Status;
     445      if Brain[bix[p]].DataSize > 0 then
     446        move(RW[p].Data^, SavedData[p]^, Brain[bix[p]].DataSize * 4);
     447    end;
     448end;
     449
     450function HasChanges(p: integer): boolean;
     451type
     452  TDWordList = array [0 .. INFIN] of Cardinal;
     453  PDWortList = ^TDWordList;
     454var
     455  ix: integer;
     456begin
     457  result := false;
     458  for ix := 0 to RW[p].nUn - 1 do
     459    with RW[p].Un[ix] do
     460      if (Loc >= 0) and (SavedStatus <> Status) then
     461        result := true;
     462  for ix := 0 to RW[p].nCity - 1 do
     463    with RW[p].City[ix] do
     464      if (Loc >= 0) and (SavedStatus <> Status) then
     465        result := true;
     466  for ix := 0 to RW[p].nModel - 1 do
     467    with RW[p].Model[ix] do
     468      if SavedStatus <> Status then
     469        result := true;
     470  for ix := 0 to RW[p].nEnemyCity - 1 do
     471    with RW[p].EnemyCity[ix] do
     472      if (Loc >= 0) and (SavedStatus <> Status) then
     473        result := true;
     474  if RW[p].Data <> nil then
     475    for ix := 0 to Brain[bix[p]].DataSize - 1 do
     476      if PDWortList(SavedData[p])[ix] <> PDWortList(RW[p].Data)[ix] then
     477        result := true
     478end;
     479
     480procedure InitBrain(bix: integer);
     481var
     482  InitModuleData: TInitModuleData;
     483begin
     484  assert(bix <> bixSuper_Virtual);
     485  with Brain[bix] do
     486  begin
     487    if Initialized then
     488      exit;
     489    if bix >= bixFirstAI then
     490    begin { get client function }
     491      Notify(ntInitModule + bix);
     492      if Flags and fDotNet > 0 then
     493        Client := DotNetClient
     494      else
     495      begin
     496        hm := LoadLibrary(pchar(DLLName));
     497        if hm = 0 then
     498        begin
     499          Client := nil;
     500          Notify(ntDLLError + bix);
     501        end
     502        else
     503        begin
     504          Client := GetProcAddress(hm, 'client');
     505          if @Client = nil then
     506            Notify(ntClientError + bix);
     507        end
     508      end
     509    end;
     510    if @Client <> nil then
     511    begin
     512      Initialized := true;
     513      InitModuleData.Server := @Server;
     514      InitModuleData.DataVersion := 0;
     515      InitModuleData.DataSize := 0;
     516      InitModuleData.Flags := 0;
     517      CallClient(bix, cInitModule, InitModuleData);
     518      DataVersion := InitModuleData.DataVersion;
     519      DataSize := (InitModuleData.DataSize + 3) div 4;
     520      if DataSize > MaxDataSize then
     521        DataSize := 0;
     522      Flags := Flags or InitModuleData.Flags;
    294523    end
    295524  end
    296525end;
    297526
    298 procedure ChangeClient;
    299 //hand over control to other client (as specified by CC...)
     527procedure SaveMap(FileName: string);
    300528var
    301 p: integer;
    302 T: int64;
     529  i: integer;
     530  MapFile: TFileStream;
     531  s: string[255];
    303532begin
    304 QueryPerformanceCounter(T);
    305 PutMessage(1 shl 16+2, Format('CLIENT: took %.1f ms',
    306   [{$IFDEF VER100}(T.LowPart-LastClientTime.LowPart)
    307   {$ELSE}(T-LastClientTime){$ENDIF}*1000.0/PerfFreq]));
    308 LastClientTime:=T;
    309 PutMessage(1 shl 16+2, Format('CLIENT: calling %d (%s)',
    310   [CCPlayer,Brain[bix[CCPlayer]].Name]));
    311 if CCCommand=cTurn then
    312   for p:=0 to nPl-1 do if (p<>CCPlayer) and (1 shl p and GWatching<>0) then
    313     CallPlayer(cShowTurnChange,p,CCPlayer);
    314 
    315 p:=CCPlayer;
    316 CCPlayer:=-1;
    317 CallPlayer(CCCommand,p,CCData);
    318 if (Mode=moPlaying) and (Brain[bix[p]].Flags and aiThreaded=0) and (CCPlayer<0) then
     533  MapFile := TFileStream.Create(DataDir + 'Maps\' + FileName,
     534    fmCreate or fmShareExclusive);
     535  MapFile.Position := 0;
     536  s := 'cEvoMap'#0;
     537  MapFile.write(s[1], 8); { file id }
     538  i := 0;
     539  MapFile.write(i, 4); { format id }
     540  MapFile.write(MaxTurn, 4);
     541  MapFile.write(lx, 4);
     542  MapFile.write(ly, 4);
     543  MapFile.write(RealMap, MapSize * 4);
     544  MapFile.Free;
     545end;
     546
     547function LoadMap(FileName: string): boolean;
     548var
     549  i, Loc1: integer;
     550  MapFile: TFileStream;
     551  s: string[255];
     552begin
     553  result := false;
     554  MapFile := nil;
     555  try
     556    MapFile := TFileStream.Create(DataDir + 'Maps\' + FileName,
     557      fmOpenRead or fmShareExclusive);
     558    MapFile.Position := 0;
     559    MapFile.read(s[1], 8); { file id }
     560    MapFile.read(i, 4); { format id }
     561    if i = 0 then
     562    begin
     563      MapFile.read(i, 4); // MaxTurn
     564      MapFile.read(lx, 4);
     565      MapFile.read(ly, 4);
     566      ly := ly and not 1;
     567      if lx > lxmax then
     568        lx := lxmax;
     569      if ly > lymax then
     570        ly := lymax;
     571      MapSize := lx * ly;
     572      MapFile.read(RealMap, MapSize * 4);
     573      for Loc1 := 0 to MapSize - 1 do
     574      begin
     575        RealMap[Loc1] := RealMap[Loc1] and
     576          ($7F01FFFF or fPrefStartPos or fStartPos) or ($F shl 27);
     577        if RealMap[Loc1] and (fTerrain or fSpecial) = fSwamp or fSpecial2 then
     578          RealMap[Loc1] := RealMap[Loc1] and not(fTerrain or fSpecial) or
     579            (fSwamp or fSpecial1);
     580        if (RealMap[Loc1] and fDeadLands <> 0) and
     581          (RealMap[Loc1] and fTerrain <> fArctic) then
     582          RealMap[Loc1] := RealMap[Loc1] and not(fTerrain or fSpecial)
     583            or fDesert;
     584      end;
     585      result := true;
     586    end;
     587    MapFile.Free;
     588  except
     589    if MapFile <> nil then
     590      MapFile.Free;
     591  end;
     592end;
     593
     594procedure SaveGame(FileName: string; auto: boolean);
     595var
     596  x, y, i, zero, Tile, nLocal: integer;
     597  LogFile: TFileStream;
     598  s: string[255];
     599  SaveMap: array [0 .. lxmax * lymax - 1] of Byte;
     600begin
     601  nLocal := 0;
     602  for i := 0 to nPl - 1 do
     603    if bix[i] = bixTerm then
     604      inc(nLocal);
     605  if Difficulty[0] = 0 then
     606    nLocal := 0;
     607  if nLocal <= 1 then
     608    for y := 0 to ly - 1 do
     609      for x := 0 to lx - 1 do
     610      begin
     611        Tile := RW[0].Map[(x + SaveMapCenterLoc + lx shr 1) mod lx + lx * y];
     612        SaveMap[x + lx * y] := Tile and fTerrain + Tile and
     613          (fCity or fUnit or fOwned) shr 16;
     614      end;
     615
     616  if auto and AutoSaveExists then // append to existing file
     617    LogFile := TFileStream.Create(SavePath + FileName, fmOpenReadWrite or
     618      fmShareExclusive)
     619  else // create new file
     620    LogFile := TFileStream.Create(SavePath + FileName,
     621      fmCreate or fmShareExclusive);
     622
     623  zero := 0;
     624  LogFile.Position := 0;
     625  s := 'cEvoBook';
     626  LogFile.write(s[1], 8); { file id }
     627  i := Version;
     628  LogFile.write(i, 4); { c-evo version }
     629  LogFile.write(ExeInfo.Time, 4);
     630  LogFile.write(lx, 4);
     631  LogFile.write(ly, 4);
     632  LogFile.write(LandMass, 4);
     633  if LandMass = 0 then
     634    LogFile.write(MapField^, MapSize * 4);
     635
     636  LogFile.write(MaxTurn, 4);
     637  LogFile.write(RND, 4);
     638  LogFile.write(GTurn, 4);
     639  if nLocal > 1 then // multiplayer game -- no quick view
    319640  begin
    320   Notify(ntDeactivationMissing+p);
    321   ForceClientDeactivation;
     641    i := $80;
     642    LogFile.write(i, 4);
     643  end
     644  else
     645    LogFile.write(SaveMap, ((MapSize - 1) div 4 + 1) * 4);
     646  for i := 0 to nPl - 1 do
     647    if bix[i] < 0 then
     648      LogFile.write(zero, 4)
     649    else
     650    begin
     651      if bixView[i] >= bixRandom then
     652        s := Brain[bix[i]].FileName
     653      else
     654        s := Brain[bixView[i]].FileName;
     655      move(zero, s[Length(s) + 1], 4);
     656      LogFile.write(s, (Length(s) div 4 + 1) * 4);
     657      LogFile.write(OriginalDataVersion[i], 4);
     658      s := ''; { behavior }
     659      move(zero, s[Length(s) + 1], 4);
     660      LogFile.write(s, (Length(s) div 4 + 1) * 4);
     661      LogFile.write(Difficulty[i], 4);
     662    end;
     663
     664  if auto and AutoSaveExists then
     665    CL.AppendToFile(LogFile, AutoSaveState)
     666  else
     667    CL.SaveToFile(LogFile);
     668  LogFile.Free;
     669  if auto then
     670  begin
     671    AutoSaveState := CL.State;
     672    AutoSaveExists := true
    322673  end
    323674end;
    324675
    325 procedure Inform(p: integer);
     676procedure StartGame;
    326677var
    327 i,p1: integer;
     678  i, p, p1, Human, nAlive, bixUni: integer;
     679  Game: TNewGameData;
     680  // GameEx: TNewGameExData;
     681  Path: shortstring;
     682  BrainUsed: Set of 0 .. 254; { used brains }
    328683begin
    329 RW[p].Turn:=GTurn;
    330 if (GTurn=MaxTurn) and (p=pTurn) and (p=0) then
    331   RW[p].Happened:=RW[p].Happened or phTimeUp;
    332 if (GWinner>0) and (p=pTurn) and (p=0) then
    333   RW[p].Happened:=RW[p].Happened or phShipComplete;
    334 RW[p].Alive:=GAlive;
    335 move(GWonder,RW[p].Wonder,SizeOf(GWonder));
    336 move(GShip,RW[p].Ship,SizeOf(GShip));
    337 for p1:=0 to nPl-1 do
    338   if (p1<>p) and (bix[p1]>=0) and (Difficulty[p1]>0) then
    339     RW[p].EnemyReport[p1].Credibility:=RW[p1].Credibility;
    340 for p1:=0 to nPl-1 do
    341   if (p1<>p) and (1 shl p1 and GAlive<>0) then
     684  for p1 := 0 to nPl - 1 do
     685  begin
     686    if bixView[p1] = bixSuper_Virtual then
     687      bix[p1] := bixTerm // supervisor and local human use same module
     688    else if bixView[p1] = bixRandom then
     689      if nBrain <= bixFirstAI then
     690        bix[p1] := -1
     691      else
     692        bix[p1] := bixFirstAI + random(nBrain - bixFirstAI)
     693    else
     694      bix[p1] := bixView[p1];
     695    if bixView[p1] < 0 then
     696      Difficulty[p1] := -1;
     697  end;
     698
     699  if bix[0] <> bixNoTerm then
     700    Notify(ntInitLocalHuman);
     701  BrainUsed := [];
     702  for p := 0 to nPl - 1 do
     703    if (bix[p] >= 0) and ((Mode <> moMovie) or (p = 0)) then
     704    begin { initiate selected control module }
     705      AIInfo[p] := Brain[bix[p]].Name + #0;
     706      InitBrain(bix[p]);
     707      if Mode = moPlaying then
     708      begin // new game, this data version is original
     709        OriginalDataVersion[p] := Brain[bix[p]].DataVersion;
     710        ProcessClientData[p] := true;
     711      end
     712      else // loading game, compare with data version read from file
     713        ProcessClientData[p] := ProcessClientData[p] and
     714          (OriginalDataVersion[p] = Brain[bix[p]].DataVersion);
     715      if @Brain[bix[p]].Client = nil then // client function not found
     716        if bix[0] = bixNoTerm then
     717          bix[p] := -1
     718        else
     719        begin
     720          bix[p] := bixTerm;
     721          OriginalDataVersion[p] := -1;
     722          ProcessClientData[p] := false;
     723        end;
     724      if bix[p] >= 0 then
     725        include(BrainUsed, bix[p])
     726    end;
     727
     728  Notify(ntCreateWorld);
     729  nAlive := 0;
     730  GAlive := 0;
     731  if Mode = moMovie then
     732    GWatching := 1
     733  else
     734    GWatching := 0;
     735  GAI := 0;
     736  for p1 := 0 to nPl - 1 do
     737    if bix[p1] >= 0 then
    342738    begin
    343     if (GTestFlags and tfUncover<>0) or (Difficulty[p]=0)
    344       or (RW[p].Treaty[p1]>=trFriendlyContact) then
    345       GiveCivilReport(p, p1);
    346     if (GTestFlags and tfUncover<>0) or (Difficulty[p]=0)
    347       or (RW[p].Treaty[p1]=trAlliance) then
    348       GiveMilReport(p, p1)
     739      if Mode <> moMovie then
     740        inc(GWatching, 1 shl p1);
     741      if bix[p1] >= bixFirstAI then
     742        inc(GAI, 1 shl p1);
     743      if Difficulty[p1] > 0 then
     744      begin
     745        inc(GAlive, 1 shl p1);
     746        inc(nAlive);
     747      end;
     748      ServerVersion[p1] := Brain[bix[p1]].ServerVersion;
    349749    end;
    350 for i:=0 to RW[p].nEnemyModel-1 do with RW[p].EnemyModel[i] do
    351   Lost:=Destroyed[p,Owner,mix];
     750  WinOnAlone := (bix[0] = bixNoTerm) and (nAlive > 1);
     751  GWinner := 0;
     752  GColdWarStart := -ColdWarTurns - 1;
     753  uixSelectedTransport := -1;
     754  SpyMission := smSabotageProd;
     755  for p1 := 0 to nPl - 1 do
     756    DebugMap[p1] := nil;
     757
     758  GTurn := 0;
     759  for i := 0 to 27 do
     760    with GWonder[i] do
     761    begin
     762      CityID := -1;
     763      EffectiveOwner := -1
     764    end;
     765  FillChar(GShip, SizeOf(GShip), 0);
     766
     767  for p := 0 to nPl - 1 do
     768    if 1 shl p and (GAlive or GWatching) <> 0 then
     769      with RW[p] do
     770      begin
     771        Government := gDespotism;
     772        Money := StartMoney;
     773        TaxRate := 30;
     774        LuxRate := 0;
     775        Research := 0;
     776        ResearchTech := -2;
     777        AnarchyStart := -AnarchyTurns - 1;
     778        Happened := 0;
     779        LastValidStat[p] := -1;
     780        Worked[p] := 0;
     781        Founded[p] := 0;
     782        DevModelTurn[p] := -1;
     783        OracleIncome := 0;
     784
     785        if Brain[bix[p]].DataSize > 0 then
     786        begin
     787          GetMem(SavedData[p], Brain[bix[p]].DataSize * 4);
     788          GetMem(Data, Brain[bix[p]].DataSize * 4);
     789          FillChar(SavedData[p]^, Brain[bix[p]].DataSize * 4, 0);
     790          FillChar(Data^, Brain[bix[p]].DataSize * 4, 0);
     791        end
     792        else
     793        begin
     794          Data := nil;
     795          SavedData[p] := nil
     796        end;
     797        nBattleHistory := 0;
     798        BattleHistory := nil;
     799        { if bix[p]=bixTerm then
     800          begin
     801          GetMem(BorderHelper,MapSize);
     802          FillChar(BorderHelper^,MapSize,0);
     803          end
     804          else } BorderHelper := nil;
     805        for i := 0 to nStat - 1 do
     806          GetMem(Stat[i, p], 4 * (MaxTurn + 1));
     807        if Brain[bix[p]].Flags and fDotNet <> 0 then
     808        begin
     809          GetMem(RW[p].DefaultDebugMap, MapSize * 4);
     810          FillChar(RW[p].DefaultDebugMap^, MapSize * 4, 0);
     811          DebugMap[p] := RW[p].DefaultDebugMap;
     812        end
     813        else
     814          RW[p].DefaultDebugMap := nil;
     815
     816        { !!!for i:=0 to nShipPart-1 do GShip[p].Parts[i]:=random((3-i)*2);{ }
     817      end;
     818
     819  if LandMass > 0 then
     820  begin // random map
     821    InitRandomGame;
     822    PreviewElevation := false;
     823    MapField := nil;
     824  end
     825  else
     826  begin // predefined map
     827    if Mode = moPlaying then
     828      LoadMap(MapFileName); // new game -- load map from file
     829    GetMem(MapField, MapSize * 4);
     830    move(RealMap, MapField^, MapSize * 4);
     831    Human := 0;
     832    for p1 := 0 to nPl - 1 do
     833      if bix[p1] = bixTerm then
     834        inc(Human, 1 shl p1);
     835    InitMapGame(Human);
     836  end;
     837  CityProcessing.InitGame;
     838  UnitProcessing.InitGame;
     839  for p := 0 to nPl - 1 do
     840    if 1 shl p and (GAlive or GWatching) <> 0 then
     841      Inform(p);
     842
     843  pTurn := -1;
     844  if bix[0] <> bixNoTerm then
     845    Notify(ntInitLocalHuman);
     846  Game.lx := lx;
     847  Game.ly := ly;
     848  Game.LandMass := LandMass;
     849  Game.MaxTurn := MaxTurn;
     850  move(Difficulty, Game.Difficulty, SizeOf(Difficulty));
     851  // GameEx.lx:=lx; GameEx.ly:=ly; GameEx.LandMass:=LandMass;
     852  // GameEx.MaxTurn:=MaxTurn; GameEx.RND:=RND;
     853  // move(Difficulty,GameEx.Difficulty,SizeOf(Difficulty));
     854  AICredits := '';
     855  for i := 0 to nBrain - 1 do
     856    if Brain[i].Initialized then
     857      if i in BrainUsed then
     858      begin
     859        if i >= bixFirstAI then
     860          Notify(ntInitPlayers);
     861        for p := 0 to nPl - 1 do
     862        begin
     863          if bix[p] = i then
     864            Game.RO[p] := @RW[p]
     865          else
     866            Game.RO[p] := nil;
     867          if (i = bixTerm) and (Difficulty[0] = 0) and (bix[p] >= 0) then
     868            Game.SuperVisorRO[p] := @RW[p]
     869          else
     870            Game.SuperVisorRO[p] := nil;
     871        end;
     872        if Brain[i].Flags and fDotNet > 0 then
     873        begin
     874          Path := Brain[i].DLLName;
     875          move(Path[1], Game.AssemblyPath, Length(Path));
     876          Game.AssemblyPath[Length(Path)] := #0;
     877        end
     878        else
     879          Game.AssemblyPath[0] := #0;
     880        case Mode of
     881          moLoading, moLoading_Fast:
     882            CallClient(i, cLoadGame, Game);
     883          moMovie:
     884            CallClient(i, cMovie, Game);
     885          moPlaying:
     886            CallClient(i, cNewGame, Game);
     887        end;
     888        if (i >= bixFirstAI) and (Brain[i].Credits <> '') then
     889          if AICredits = '' then
     890            AICredits := Brain[i].Credits
     891          else
     892            AICredits := AICredits + '\' + Brain[i].Credits
     893      end
     894      else
     895      begin { module no longer used -- unload }
     896        CallClient(i, cReleaseModule, nil^);
     897        if i >= bixFirstAI then
     898        begin
     899          if Brain[i].Flags and fDotNet = 0 then
     900            FreeLibrary(Brain[i].hm);
     901          Brain[i].Client := nil;
     902        end;
     903        Brain[i].Initialized := false;
     904      end;
     905  AICredits := AICredits + #0;
     906
     907  if bix[0] <> bixNoTerm then
     908  begin
     909    // uni ai?
     910    bixUni := -1;
     911    for p1 := 0 to nPl - 1 do
     912      if bix[p1] >= bixFirstAI then
     913        if bixUni = -1 then
     914          bixUni := bix[p1]
     915        else if bixUni <> bix[p1] then
     916          bixUni := -2;
     917    for p1 := 0 to nPl - 1 do
     918      if bix[p1] >= bixFirstAI then
     919      begin
     920        if bixUni = -2 then
     921          NotifyMessage := Brain[bix[p1]].FileName
     922        else
     923          NotifyMessage := '';
     924        Notify(ntSetAIName + p1);
     925      end
     926  end;
     927
     928  CheckBorders(-1);
     929{$IFOPT O-}InvalidTreatyMap := 0; {$ENDIF}
     930  AutoSaveExists := false;
     931  pDipActive := -1;
     932  pTurn := 0;
     933
     934  if Mode >= moMovie then
     935    Notify(ntEndInfo);
     936end; { StartGame }
     937
     938procedure EndGame;
     939var
     940  i, p1: integer;
     941begin
     942  if LandMass = 0 then
     943    FreeMem(MapField);
     944  for p1 := 0 to nPl - 1 do
     945    if bix[p1] >= 0 then
     946    begin
     947      for i := 0 to nStat - 1 do
     948        FreeMem(Stat[i, p1]);
     949      if RW[p1].BattleHistory <> nil then
     950        FreeMem(RW[p1].BattleHistory);
     951      { if RW[p1].BorderHelper<>nil then FreeMem(RW[p1].BorderHelper); }
     952      FreeMem(RW[p1].Data);
     953      FreeMem(SavedData[p1]);
     954      if RW[p1].DefaultDebugMap <> nil then
     955        FreeMem(RW[p1].DefaultDebugMap);
     956    end;
     957  UnitProcessing.ReleaseGame;
     958  CityProcessing.ReleaseGame;
     959  Database.ReleaseGame;
     960  CL.Free;
    352961end;
    353962
    354 procedure LogChanges;
     963procedure GenerateStat(p: integer);
    355964var
    356 p,ix: integer;
     965  cix, uix: integer;
    357966begin
    358 for p:=0 to nPl-1 do
    359   if (1 shl p and GWatching<>0) and ProcessClientData[p] then
     967  if Difficulty[p] > 0 then
     968    with RW[p] do
    360969    begin
    361     // log unit status changes
    362     for ix:=0 to RW[p].nUn-1 do with RW[p].Un[ix] do
    363       if (Loc>=0) and (SavedStatus<>Status) then
    364         begin
    365         CL.Put(sIntSetUnitStatus, p, ix, @Status);
    366         SavedStatus:=Status
    367         end;
    368     // log city status changes
    369     for ix:=0 to RW[p].nCity-1 do with RW[p].City[ix] do
    370       if (Loc>=0) and (SavedStatus<>Status) then
    371         begin
    372         CL.Put(sIntSetCityStatus, p, ix, @Status);
    373         SavedStatus:=Status
    374         end;
    375     // log model status changes
    376     for ix:=0 to RW[p].nModel-1 do with RW[p].Model[ix] do
    377       if SavedStatus<>Status then
    378         begin
    379         CL.Put(sIntSetModelStatus, p, ix, @Status);
    380         SavedStatus:=Status
    381         end;
    382     // log enemy city status changes
    383     for ix:=0 to RW[p].nEnemyCity-1 do with RW[p].EnemyCity[ix] do
    384       if (Loc>=0) and (SavedStatus<>Status) then
    385         begin
    386         CL.Put(sIntSetECityStatus, p, ix, @Status);
    387         SavedStatus:=Status
    388         end;
    389     // log data changes
    390     if Brain[bix[p]].DataSize>0 then
    391       begin
    392       CL.PutDataChanges(sIntDataChange, p, SavedData[p], RW[p].Data,
    393         Brain[bix[p]].DataSize);
    394       move(RW[p].Data^,SavedData[p]^,Brain[bix[p]].DataSize*4);
    395       end
     970      Stat[stPop, p, GTurn] := 0;
     971      for cix := 0 to nCity - 1 do
     972        if City[cix].Loc >= 0 then
     973          inc(Stat[stPop, p, GTurn], City[cix].Size);
     974      Stat[stScience, p, GTurn] := Researched[p] * 50;
     975      if (RW[p].ResearchTech >= 0) and (RW[p].ResearchTech <> adMilitary) then
     976        inc(Stat[stScience, p, GTurn], Research * 100 div TechBaseCost(nTech[p],
     977          Difficulty[p]));
     978      Stat[stMil, p, GTurn] := 0;
     979      for uix := 0 to nUn - 1 do
     980        if Un[uix].Loc >= 0 then
     981          with Model[Un[uix].mix] do
     982          begin
     983            if (Kind <= mkEnemyDeveloped) and (Un[uix].mix <> 1) then
     984              inc(Stat[stMil, p, GTurn], Weight * MStrength *
     985                Un[uix].Health div 100)
     986            else if Domain = dGround then
     987              inc(Stat[stMil, p, GTurn], (Attack + 2 * Defense) *
     988                Un[uix].Health div 100)
     989            else
     990              inc(Stat[stMil, p, GTurn], (Attack + Defense) *
     991                Un[uix].Health div 100);
     992            case Kind of
     993              mkSlaves:
     994                inc(Stat[stPop, p, GTurn]);
     995              mkSettler:
     996                inc(Stat[stPop, p, GTurn], 2);
     997            end;
     998          end;
     999      Stat[stMil, p, GTurn] := Stat[stMil, p, GTurn] div 16;
     1000      Stat[stExplore, p, GTurn] := Discovered[p];
     1001      Stat[stTerritory, p, GTurn] := TerritoryCount[p];
     1002      Stat[stWork, p, GTurn] := Worked[p];
     1003      LastValidStat[p] := GTurn;
    3961004    end;
    3971005end;
    3981006
    399 procedure NoLogChanges;
     1007procedure LogCityTileChanges;
    4001008var
    401 p,ix: integer;
     1009  cix: integer;
    4021010begin
    403 for p:=0 to nPl-1 do
    404   if (1 shl p and GWatching<>0) and ProcessClientData[p] then
     1011  for cix := 0 to RW[pTurn].nCity - 1 do
     1012    with RW[pTurn].City[cix] do
     1013      if Loc >= 0 then
     1014      begin
     1015        { if SavedResourceWeights[cix]<>ResourceWeights then
     1016          begin // log city resource weight changes
     1017          CL.Put(sSetCityResourceWeights, pTurn, cix, @ResourceWeights);
     1018          SavedResourceWeights[cix]:=ResourceWeights;
     1019          end; }
     1020        if SavedTiles[cix] <> Tiles then
     1021        begin // log city tile changes
     1022          CL.Put(sSetCityTiles, pTurn, cix, @Tiles);
     1023          SavedTiles[cix] := Tiles;
     1024        end;
     1025      end;
     1026end;
     1027
     1028procedure NoLogCityTileChanges;
     1029var
     1030  cix: integer;
     1031begin
     1032  for cix := 0 to RW[pTurn].nCity - 1 do
     1033    with RW[pTurn].City[cix] do
     1034      if Loc >= 0 then
     1035      begin
     1036        // SavedResourceWeights[cix]:=ResourceWeights;
     1037        SavedTiles[cix] := Tiles;
     1038      end;
     1039end;
     1040
     1041function HasCityTileChanges: boolean;
     1042var
     1043  cix: integer;
     1044begin
     1045  result := false;
     1046  for cix := 0 to RW[pTurn].nCity - 1 do
     1047    with RW[pTurn].City[cix] do
     1048      if Loc >= 0 then
     1049      begin
     1050        // if SavedResourceWeights[cix]<>ResourceWeights then result:=true;
     1051        if SavedTiles[cix] <> Tiles then
     1052          result := true;
     1053      end;
     1054end;
     1055
     1056procedure BeforeTurn0;
     1057var
     1058  p1, uix: integer;
     1059begin
     1060  for uix := 0 to RW[pTurn].nUn - 1 do { init movement points for first turn }
     1061    with RW[pTurn].Un[uix] do
     1062      Movement := RW[pTurn].Model[mix].Speed;
     1063
     1064  if Difficulty[pTurn] > 0 then
     1065    DiscoverViewAreas(pTurn)
     1066  else { supervisor }
     1067  begin
     1068    DiscoverAll(pTurn, lObserveSuper);
     1069    for p1 := 1 to nPl - 1 do
     1070      if 1 shl p1 and GAlive <> 0 then
     1071      begin
     1072        GiveCivilReport(pTurn, p1);
     1073        GiveMilReport(pTurn, p1)
     1074      end;
     1075  end;
     1076  // CheckContact;
     1077end;
     1078
     1079function LoadGame(const Path, FileName: string; Turn: integer;
     1080  MovieMode: boolean): boolean;
     1081var
     1082  i, j, ix, d, p1, Command, Subject: integer;
     1083{$IFDEF TEXTLOG}LoadPos0: integer; {$ENDIF}
     1084  Data: pointer;
     1085  LogFile: TFileStream;
     1086  FormerCLState: TCmdListState;
     1087  s: string[255];
     1088  SaveMap: array [0 .. lxmax * lymax - 1] of Byte;
     1089  started, StatRequest: boolean;
     1090begin
     1091  SavePath := Path;
     1092  LogFileName := FileName;
     1093  LoadTurn := Turn;
     1094  LogFile := TFileStream.Create(SavePath + LogFileName, fmOpenRead or
     1095    fmShareExclusive);
     1096  LogFile.Position := 0;
     1097  LogFile.read(s[1], 8); { file id }
     1098  LogFile.read(i, 4); { c-evo version }
     1099  LogFile.read(j, 4); { exe time }
     1100
     1101  if (i >= FirstBookCompatibleVersion) and (i <= Version) then
     1102  begin
     1103    result := true;
     1104    LogFile.read(lx, 4);
     1105    LogFile.read(ly, 4);
     1106    MapSize := lx * ly;
     1107    LogFile.read(LandMass, 4);
     1108    if LandMass = 0 then
     1109      LogFile.read(RealMap, MapSize * 4); // use predefined map
     1110    LogFile.read(MaxTurn, 4);
     1111    LogFile.read(RND, 4);
     1112    LogFile.read(GTurn, 4);
     1113    LogFile.read(SaveMap, 4);
     1114    if SaveMap[0] <> $80 then
     1115      LogFile.read(SaveMap[4], ((MapSize - 1) div 4 + 1) * 4 - 4);
     1116    for p1 := 0 to nPl - 1 do
    4051117    begin
    406     for ix:=0 to RW[p].nUn-1 do with RW[p].Un[ix] do
    407       SavedStatus:=Status;
    408     for ix:=0 to RW[p].nCity-1 do with RW[p].City[ix] do
    409       SavedStatus:=Status;
    410     for ix:=0 to RW[p].nModel-1 do with RW[p].Model[ix] do
    411       SavedStatus:=Status;
    412     for ix:=0 to RW[p].nEnemyCity-1 do with RW[p].EnemyCity[ix] do
    413       SavedStatus:=Status;
    414     if Brain[bix[p]].DataSize>0 then
    415       move(RW[p].Data^,SavedData[p]^,Brain[bix[p]].DataSize*4);
     1118      LogFile.read(s[0], 4);
     1119      if s[0] = #0 then
     1120        bixView[p1] := -1
     1121      else
     1122      begin
     1123        LogFile.read(s[4], Byte(s[0]) div 4 * 4);
     1124        LogFile.read(OriginalDataVersion[p1], 4);
     1125        LogFile.read(d, 4); { behavior }
     1126        LogFile.read(Difficulty[p1], 4);
     1127        j := nBrain - 1;
     1128        while (j >= 0) and (AnsiCompareFileName(Brain[j].FileName, s) <> 0) do
     1129          dec(j);
     1130        if j < 0 then
     1131        begin // ai not found -- replace by local player
     1132          ProcessClientData[p1] := false;
     1133          NotifyMessage := s;
     1134          Notify(ntAIError);
     1135          j := bixTerm;
     1136        end
     1137        else
     1138          ProcessClientData[p1] := true;
     1139        if j = bixNoTerm then
     1140          j := bixSuper_Virtual;
     1141        // crashed tournament -- load as supervisor
     1142        bixView[p1] := j;
     1143      end;
    4161144    end;
    417 end;
    418 
    419 function HasChanges(p: integer): boolean;
    420 type
    421 TDWordList= array[0..INFIN] of Cardinal;
    422 PDWortList=^TDWordList;
     1145  end
     1146  else
     1147    result := false;
     1148
     1149  if result then
     1150  begin
     1151    CL := TCmdList.Create;
     1152    CL.LoadFromFile(LogFile);
     1153  end;
     1154  LogFile.Free;
     1155  if not result then
     1156    exit;
     1157
     1158  Notify(ntStartDone);
     1159  if LoadTurn < 0 then
     1160    LoadTurn := GTurn;
     1161  if MovieMode then
     1162    Mode := moMovie
     1163  else if LoadTurn = 0 then
     1164    Mode := moLoading
     1165  else
     1166    Mode := moLoading_Fast;
     1167{$IFDEF TEXTLOG}AssignFile(TextLog, SavePath + LogFileName + '.txt');
     1168  Rewrite(TextLog); {$ENDIF}
     1169  LoadOK := true;
     1170  StartGame;
     1171  if MovieMode then
     1172  begin
     1173    Brain[bix[0]].Client(cShowGame, 0, nil^);
     1174    Notify(ntBackOff);
     1175  end
     1176  else
     1177    Notify(ntLoadBegin);
     1178
     1179  started := false;
     1180  StatRequest := false;
     1181  MovieStopped := false;
     1182{$IFDEF LOADPERF}QueryPerformanceCounter(time_total0);
     1183  time_a := 0;
     1184  time_b := 0;
     1185  time_c := 0; {$ENDIF}
     1186  while not MovieStopped and (CL.Progress < 1000) do
     1187  begin
     1188    FormerCLState := CL.State;
     1189    CL.Get(Command, p1, Subject, Data);
     1190    if p1 < 0 then
     1191      p1 := pTurn;
     1192    if StatRequest and (Command and (sctMask or sExecute) <> sctInternal or
     1193      sExecute) then
     1194    begin
     1195      GenerateStat(pTurn);
     1196      StatRequest := false
     1197    end;
     1198    // complete all internal commands following an sTurn before generating statistics
     1199    if (Command = sTurn) and not started then
     1200    begin
     1201{$IFDEF TEXTLOG}WriteLn(TextLog, '---Turn 0 P0---'); {$ENDIF}
     1202      for p1 := 0 to nPl - 1 do
     1203        if (bix[p1] >= 0) and ((Mode <> moMovie) or (p1 = 0)) then
     1204          CallPlayer(cReplay, p1, nil^);
     1205      BeforeTurn0;
     1206      if MovieMode then
     1207      begin
     1208        Inform(pTurn);
     1209        CallPlayer(cMovieTurn, 0, nil^);
     1210      end;
     1211      StatRequest := true;
     1212      started := true;
     1213    end
     1214    else if (Command = sTurn) and (pTurn = 0) and (GTurn = LoadTurn) then
     1215    begin
     1216      assert(CL.State.LoadPos = FormerCLState.LoadPos + 4); // size of sTurn
     1217      CL.State := FormerCLState;
     1218      CL.Cut;
     1219      Break;
     1220    end
     1221    else if Command = sIntDataChange then
     1222    begin
     1223{$IFDEF TEXTLOG}LoadPos0 := CL.State.LoadPos; {$ENDIF}
     1224      if ProcessClientData[p1] then
     1225        CL.GetDataChanges(RW[p1].Data, Brain[bix[p1]].DataSize)
     1226      else
     1227        CL.GetDataChanges(nil, 0);
     1228{$IFDEF TEXTLOG}WriteLn(TextLog, Format('Data Changes P%d (%d Bytes)', [p1, CL.State.LoadPos - LoadPos0])); {$ENDIF}
     1229    end
     1230    else
     1231    begin
     1232{$IFDEF TEXTLOG}CmdInfo := Format('Command %x', [Command]); {$ENDIF}
     1233      if Command and (sctMask or sExecute) = sctInternal or sExecute then
     1234        IntServer(Command, p1, Subject, Data^) // internal command
     1235      else
     1236      begin
     1237        StatRequest := Command = sTurn;
     1238        Server(Command, p1, Subject, Data^);
     1239      end;
     1240{$IFDEF TEXTLOG}WriteLn(TextLog, CmdInfo); {$ENDIF}
     1241    end;
     1242    if not MovieMode then
     1243      Notify(ntLoadState + CL.Progress * 128 div 1000);
     1244  end;
     1245
     1246  if MovieMode then
     1247  begin
     1248    Notify(ntBackOn);
     1249    Brain[bix[0]].Client(cBreakGame, -1, nil^);
     1250    EndGame;
     1251    Notify(ntStartGo);
     1252    result := false;
     1253    exit;
     1254  end;
     1255
     1256  if StatRequest then
     1257    GenerateStat(pTurn);
     1258  assert(started);
     1259{$IFDEF TEXTLOG}CloseFile(TextLog); {$ENDIF}
     1260{$IFDEF LOADPERF}QueryPerformanceCounter(time_total); { time in s is: (time_total-time_total0)/PerfFreq }{$ENDIF}
     1261  NoLogChanges;
     1262  NoLogCityTileChanges;
     1263  if LogFileName[1] = '~' then
     1264  begin
     1265    Delete(LogFileName, 1, 1);
     1266    nLogOpened := -1
     1267  end
     1268  else
     1269    nLogOpened := CL.State.nLog;
     1270
     1271  Mode := moPlaying;
     1272  LastEndClientCommand := -1;
     1273  if (GTestFlags and tfUncover <> 0) or (Difficulty[pTurn] = 0) then
     1274    DiscoverAll(pTurn, lObserveSuper) { supervisor - all tiles visible }
     1275  else
     1276    DiscoverViewAreas(pTurn);
     1277
     1278  for p1 := 0 to nPl - 1 do
     1279    if 1 shl p1 and (GAlive or GWatching) <> 0 then
     1280    begin
     1281      RecalcPeaceMap(p1);
     1282      for ix := 0 to RW[p1].nEnemyUn - 1 do
     1283        with RW[p1].EnemyUn[ix] do
     1284          emix := RWemix[p1, Owner, mix];
     1285      Inform(p1);
     1286    end;
     1287{$IFOPT O-}CheckBorders(-2); {$ENDIF} // for testing only
     1288  Notify(ntEndInfo);
     1289  if not LoadOK then
     1290  begin
     1291    NotifyMessage := SavePath + LogFileName;
     1292    Notify(ntLoadError);
     1293  end;
     1294  Brain[bix[0]].Client(cShowGame, 0, nil^);
     1295  Notify(ntBackOff);
     1296  Inform(pTurn);
     1297  ChangeClientWhenDone(cResume, 0, nil^, 0);
     1298end; // LoadGame
     1299
     1300procedure InsertTerritoryUpdateCommands;
    4231301var
    424 ix: integer;
     1302  p1, Command, Subject: integer;
     1303  Data: pointer;
     1304  FormerCLState: TCmdListState;
    4251305begin
    426 result:=false;
    427 for ix:=0 to RW[p].nUn-1 do with RW[p].Un[ix] do
    428   if (Loc>=0) and (SavedStatus<>Status) then result:=true;
    429 for ix:=0 to RW[p].nCity-1 do with RW[p].City[ix] do
    430   if (Loc>=0) and (SavedStatus<>Status) then result:=true;
    431 for ix:=0 to RW[p].nModel-1 do with RW[p].Model[ix] do
    432   if SavedStatus<>Status then result:=true;
    433 for ix:=0 to RW[p].nEnemyCity-1 do with RW[p].EnemyCity[ix] do
    434   if (Loc>=0) and (SavedStatus<>Status) then result:=true;
    435 if RW[p].Data<>nil then for ix:=0 to Brain[bix[p]].DataSize-1 do
    436   if PDWortList(SavedData[p])[ix]<>PDWortList(RW[p].Data)[ix] then result:=true
    437 end;
    438 
    439 procedure InitBrain(bix: integer);
    440 var
    441 InitModuleData: TInitModuleData;
    442 begin
    443 assert(bix<>bixSuper_Virtual);
    444 with Brain[bix] do
     1306  while CL.Progress < 1000 do
    4451307  begin
    446   if Initialized then exit;
    447   if bix>=bixFirstAI then
    448     begin {get client function}
    449     Notify(ntInitModule+bix);
    450     if Flags and fDotNet>0 then
    451       Client:=DotNetClient
     1308    FormerCLState := CL.State;
     1309    CL.Get(Command, p1, Subject, Data);
     1310    if (Command = sIntExpandTerritory) and (p1 = pTurn) then
     1311    begin
     1312      IntServer(Command, p1, Subject, Data^);
     1313{$IFDEF TEXTLOG}WriteLn(TextLog, 'AfterTurn - ExpandTerritory'); {$ENDIF}
     1314    end
    4521315    else
    453       begin
    454       hm:=LoadLibrary(pchar(DLLName));
    455       if hm=0 then
    456         begin
    457         Client:=nil;
    458         Notify(ntDLLError+bix);
    459         end
    460       else
    461         begin
    462         Client:=GetProcAddress(hm,'client');
    463         if @Client=nil then Notify(ntClientError+bix);
    464         end
    465       end
    466     end;
    467   if @Client<>nil then
    4681316    begin
    469     Initialized:=true;
    470     InitModuleData.Server:=@Server;
    471     InitModuleData.DataVersion:=0;
    472     InitModuleData.DataSize:=0;
    473     InitModuleData.Flags:=0;
    474     CallClient(bix, cInitModule, InitModuleData);
    475     DataVersion:=InitModuleData.DataVersion;
    476     DataSize:=(InitModuleData.DataSize+3) div 4;
    477     if DataSize>MaxDataSize then DataSize:=0;
    478     Flags:=Flags or InitModuleData.Flags;
    479     end
    480   end
    481 end;
    482 
    483 procedure SaveMap(FileName: string);
    484 var
    485 i: integer;
    486 MapFile: TFileStream;
    487 s: string[255];
    488 begin
    489 MapFile:=TFileStream.Create(DataDir+'Maps\'+FileName, fmCreate or fmShareExclusive);
    490 MapFile.Position:=0;
    491 s:='cEvoMap'#0; MapFile.write(s[1],8); {file id}
    492 i:=0; MapFile.write(i,4); {format id}
    493 MapFile.write(MaxTurn,4);
    494 MapFile.write(lx,4);
    495 MapFile.write(ly,4);
    496 MapFile.write(RealMap,MapSize*4);
    497 MapFile.Free;
    498 end;
    499 
    500 function LoadMap(FileName: string): boolean;
    501 var
    502 i,Loc1: integer;
    503 MapFile: TFileStream;
    504 s: string[255];
    505 begin
    506 result:=false;
    507 MapFile:=nil;
    508 try
    509   MapFile:=TFileStream.Create(DataDir+'Maps\'+FileName, fmOpenRead or fmShareExclusive);
    510   MapFile.Position:=0;
    511   MapFile.read(s[1],8); {file id}
    512   MapFile.read(i,4); {format id}
    513   if i=0 then
    514     begin
    515     MapFile.read(i,4); //MaxTurn
    516     MapFile.read(lx,4);
    517     MapFile.read(ly,4);
    518     ly:=ly and not 1;
    519     if lx>lxmax then lx:=lxmax;
    520     if ly>lymax then ly:=lymax;
    521     MapSize:=lx*ly;
    522     MapFile.read(RealMap,MapSize*4);
    523     for Loc1:=0 to MapSize-1 do
    524       begin
    525       RealMap[Loc1]:=RealMap[Loc1] and ($7F01FFFF or fPrefStartPos or fStartPos)
    526         or ($F shl 27);
    527       if RealMap[Loc1] and (fTerrain or fSpecial)=fSwamp or fSpecial2 then
    528         RealMap[Loc1]:=RealMap[Loc1] and not (fTerrain or fSpecial) or (fSwamp or fSpecial1);
    529       if (RealMap[Loc1] and fDeadLands<>0) and (RealMap[Loc1] and fTerrain<>fArctic) then
    530         RealMap[Loc1]:=RealMap[Loc1] and not (fTerrain or fSpecial) or fDesert;
    531       end;
    532     result:=true;
    533     end;
    534   MapFile.Free;
    535 except
    536   if MapFile<>nil then MapFile.Free;
    537   end;
    538 end;
    539 
    540 procedure SaveGame(FileName: string; auto: boolean);
    541 var
    542 x,y,i,zero,Tile,nLocal: integer;
    543 LogFile: TFileStream;
    544 s: string[255];
    545 SaveMap: array[0..lxmax*lymax-1] of Byte;
    546 begin
    547 nLocal:=0;
    548 for i:=0 to nPl-1 do if bix[i]=bixTerm then inc(nLocal);
    549 if Difficulty[0]=0 then nLocal:=0;
    550 if nLocal<=1 then for y:=0 to ly-1 do for x:=0 to lx-1 do
    551   begin
    552   Tile:=RW[0].Map[(x+SaveMapCenterLoc+lx shr 1) mod lx +lx*y];
    553   SaveMap[x+lx*y]:=Tile and fTerrain + Tile and (fCity or fUnit or fOwned) shr 16;
    554   end;
    555 
    556 if auto and AutoSaveExists then // append to existing file
    557   LogFile:=TFileStream.Create(SavePath+FileName,
    558     fmOpenReadWrite or fmShareExclusive)
    559 else // create new file
    560   LogFile:=TFileStream.Create(SavePath+FileName, fmCreate or fmShareExclusive);
    561 
    562 zero:=0;
    563 LogFile.Position:=0;
    564 s:='cEvoBook'; LogFile.write(s[1],8); {file id}
    565 i:=Version; LogFile.write(i,4); {c-evo version}
    566 LogFile.write(ExeInfo.Time,4);
    567 LogFile.write(lx,4);
    568 LogFile.write(ly,4);
    569 LogFile.write(LandMass,4);
    570 if LandMass=0 then
    571   LogFile.write(MapField^,MapSize*4);
    572 
    573 LogFile.write(MaxTurn,4);
    574 LogFile.write(RND,4);
    575 LogFile.write(GTurn,4);
    576 if nLocal>1 then // multiplayer game -- no quick view
    577   begin i:=$80; LogFile.write(i,4); end
    578 else LogFile.write(SaveMap,((MapSize-1) div 4+1)*4);
    579 for i:=0 to nPl-1 do
    580   if bix[i]<0 then LogFile.write(zero,4)
    581   else
    582     begin
    583     if bixView[i]>=bixRandom then s:=Brain[bix[i]].FileName
    584     else s:=Brain[bixView[i]].FileName;
    585     move(zero,s[Length(s)+1],4);
    586     LogFile.write(s,(Length(s) div 4+1)*4);
    587     LogFile.write(OriginalDataVersion[i],4);
    588     s:=''; {behavior} move(zero,s[Length(s)+1],4);
    589     LogFile.write(s,(Length(s) div 4+1)*4);
    590     LogFile.write(Difficulty[i],4);
    591     end;
    592 
    593 if auto and AutoSaveExists then CL.AppendToFile(LogFile, AutoSaveState)
    594 else CL.SaveToFile(LogFile);
    595 LogFile.Free;
    596 if auto then
    597   begin AutoSaveState:=CL.State; AutoSaveExists:=true end
    598 end;
    599 
    600 procedure StartGame;
    601 var
    602 i,p,p1,Human,nAlive,bixUni: integer;
    603 Game: TNewGameData;
    604 //GameEx: TNewGameExData;
    605 path: shortstring;
    606 BrainUsed: Set of 0..254; {used brains}
    607 begin
    608 for p1:=0 to nPl-1 do
    609   begin
    610   if bixView[p1]=bixSuper_Virtual then bix[p1]:=bixTerm // supervisor and local human use same module
    611   else if bixView[p1]=bixRandom then
    612     if nBrain<=bixFirstAI then bix[p1]:=-1
    613     else bix[p1]:=bixFirstAI+random(nBrain-bixFirstAI)
    614   else bix[p1]:=bixView[p1];
    615   if bixView[p1]<0 then Difficulty[p1]:=-1;
    616   end;
    617 
    618 if bix[0]<>bixNoTerm then Notify(ntInitLocalHuman);
    619 BrainUsed:=[];
    620 for p:=0 to nPl-1 do
    621   if (bix[p]>=0) and ((Mode<>moMovie) or (p=0)) then
    622     begin {initiate selected control module}
    623     AIInfo[p]:=Brain[bix[p]].Name+#0;
    624     InitBrain(bix[p]);
    625     if Mode=moPlaying then
    626       begin // new game, this data version is original
    627       OriginalDataVersion[p]:=Brain[bix[p]].DataVersion;
    628       ProcessClientData[p]:=true;
    629       end
    630     else // loading game, compare with data version read from file
    631       ProcessClientData[p]:=ProcessClientData[p]
    632         and (OriginalDataVersion[p]=Brain[bix[p]].DataVersion);
    633     if @Brain[bix[p]].Client=nil then // client function not found
    634       if bix[0]=bixNoTerm then
    635         bix[p]:=-1
    636       else
    637         begin
    638         bix[p]:=bixTerm;
    639         OriginalDataVersion[p]:=-1;
    640         ProcessClientData[p]:=false;
    641         end;
    642     if bix[p]>=0 then include(BrainUsed,bix[p])
    643     end;
    644 
    645 Notify(ntCreateWorld);
    646 nAlive:=0;
    647 GAlive:=0;
    648 if Mode=moMovie then GWatching:=1
    649 else GWatching:=0;
    650 GAI:=0;
    651 for p1:=0 to nPl-1 do if bix[p1]>=0 then
    652   begin
    653   if Mode<>moMovie then inc(GWatching,1 shl p1);
    654   if bix[p1]>=bixFirstAI then inc(GAI,1 shl p1);
    655   if Difficulty[p1]>0 then
    656     begin inc(GAlive,1 shl p1); inc(nAlive); end;
    657   ServerVersion[p1]:=Brain[bix[p1]].ServerVersion;
    658   end;
    659 WinOnAlone:= (bix[0]=bixNoTerm) and (nAlive>1);
    660 GWinner:=0;
    661 GColdWarStart:=-ColdWarTurns-1;
    662 uixSelectedTransport:=-1;
    663 SpyMission:=smSabotageProd;
    664 for p1:=0 to nPl-1 do
    665   DebugMap[p1]:=nil;
    666 
    667 GTurn:=0;
    668 for i:=0 to 27 do with GWonder[i] do
    669   begin CityID:=-1; EffectiveOwner:=-1 end;
    670 FillChar(GShip,SizeOf(GShip),0);
    671 
    672 for p:=0 to nPl-1 do if 1 shl p and (GAlive or GWatching)<>0 then with RW[p] do
    673   begin
    674   Government:=gDespotism;
    675   Money:=StartMoney;
    676   TaxRate:=30;
    677   LuxRate:=0;
    678   Research:=0;
    679   ResearchTech:=-2;
    680   AnarchyStart:=-AnarchyTurns-1;
    681   Happened:=0;
    682   LastValidStat[p]:=-1;
    683   Worked[p]:=0;
    684   Founded[p]:=0;
    685   DevModelTurn[p]:=-1;
    686   OracleIncome:=0;
    687 
    688   if Brain[bix[p]].DataSize>0 then
    689     begin
    690     GetMem(SavedData[p], Brain[bix[p]].DataSize*4);
    691     GetMem(Data, Brain[bix[p]].DataSize*4);
    692     FillChar(SavedData[p]^,Brain[bix[p]].DataSize*4,0);
    693     FillChar(Data^,Brain[bix[p]].DataSize*4,0);
    694     end
    695   else begin Data:=nil; SavedData[p]:=nil end;
    696   nBattleHistory:=0;
    697   BattleHistory:=nil;
    698   {if bix[p]=bixTerm then
    699     begin
    700     GetMem(BorderHelper,MapSize);
    701     FillChar(BorderHelper^,MapSize,0);
    702     end
    703   else} BorderHelper:=nil;
    704   for i:=0 to nStat-1 do GetMem(Stat[i,p],4*(MaxTurn+1));
    705   if Brain[bix[p]].Flags and fDotNet<>0 then
    706     begin
    707     GetMem(RW[p].DefaultDebugMap, MapSize*4);
    708     FillChar(RW[p].DefaultDebugMap^, MapSize*4, 0);
    709     DebugMap[p]:=RW[p].DefaultDebugMap;
    710     end
    711   else RW[p].DefaultDebugMap:=nil;
    712 
    713   {!!!for i:=0 to nShipPart-1 do GShip[p].Parts[i]:=random((3-i)*2);{}
    714   end;
    715 
    716 if LandMass>0 then
    717   begin // random map
    718   InitRandomGame;
    719   PreviewElevation:=false;
    720   MapField:=nil;
    721   end
    722 else
    723   begin // predefined map
    724   if Mode=moPlaying then
    725     LoadMap(MapFileName); // new game -- load map from file
    726   GetMem(MapField,MapSize*4);
    727   move(RealMap,MapField^,MapSize*4);
    728   Human:=0;
    729   for p1:=0 to nPl-1 do if bix[p1]=bixTerm then inc(Human,1 shl p1);
    730   InitMapGame(Human);
    731   end;
    732 CityProcessing.InitGame;
    733 UnitProcessing.InitGame;
    734 for p:=0 to nPl-1 do if 1 shl p and (GAlive or GWatching)<>0 then
    735   Inform(p);
    736 
    737 pTurn:=-1;
    738 if bix[0]<>bixNoTerm then
    739   Notify(ntInitLocalHuman);
    740 Game.lx:=lx; Game.ly:=ly; Game.LandMass:=LandMass; Game.MaxTurn:=MaxTurn;
    741 move(Difficulty,Game.Difficulty,SizeOf(Difficulty));
    742 //GameEx.lx:=lx; GameEx.ly:=ly; GameEx.LandMass:=LandMass;
    743 //GameEx.MaxTurn:=MaxTurn; GameEx.RND:=RND;
    744 //move(Difficulty,GameEx.Difficulty,SizeOf(Difficulty));
    745 AICredits:='';
    746 for i:=0 to nBrain-1 do if Brain[i].Initialized then
    747   if i in BrainUsed then
    748     begin
    749     if i>=bixFirstAI then
    750       Notify(ntInitPlayers);
    751     for p:=0 to nPl-1 do
    752       begin
    753       if bix[p]=i then
    754         Game.RO[p]:=@RW[p]
    755       else Game.RO[p]:=nil;
    756       if (i=bixTerm) and (Difficulty[0]=0) and (bix[p]>=0) then
    757         Game.SuperVisorRO[p]:=@RW[p]
    758       else Game.SuperVisorRO[p]:=nil;
    759       end;
    760     if Brain[i].Flags and fDotNet>0 then
    761       begin
    762       path:=Brain[i].DLLName;
    763       move(path[1], Game.AssemblyPath, Length(path));
    764       Game.AssemblyPath[Length(path)]:=#0;
    765       end
    766     else Game.AssemblyPath[0]:=#0;
    767     case Mode of
    768       moLoading, moLoading_Fast: CallClient(i, cLoadGame, Game);
    769       moMovie: CallClient(i, cMovie, Game);
    770       moPlaying: CallClient(i, cNewGame, Game);
    771       end;
    772     if (i>=bixFirstAI) and (Brain[i].Credits<>'') then
    773       if AICredits='' then AICredits:=Brain[i].Credits
    774       else AICredits:=AICredits+'\'+Brain[i].Credits
    775     end
    776   else
    777     begin {module no longer used -- unload}
    778     CallClient(i, cReleaseModule, nil^);
    779     if i>=bixFirstAI then
    780       begin
    781       if Brain[i].Flags and fDotNet=0 then
    782         FreeLibrary(Brain[i].hm);
    783       Brain[i].Client:=nil;
    784       end;
    785     Brain[i].Initialized:=false;
    786     end;
    787 AICredits:=AICredits+#0;
    788 
    789 if bix[0]<>bixNoTerm then
    790   begin
    791   // uni ai?
    792   bixUni:=-1;
    793   for p1:=0 to nPl-1 do if bix[p1]>=bixFirstAI then
    794     if bixUni=-1 then bixUni:=bix[p1]
    795     else if bixUni<>bix[p1] then bixUni:=-2;
    796   for p1:=0 to nPl-1 do if bix[p1]>=bixFirstAI then
    797     begin
    798     if bixUni=-2 then NotifyMessage:=Brain[bix[p1]].FileName
    799     else NotifyMessage:='';
    800     Notify(ntSetAIName+p1);
     1317      CL.State := FormerCLState;
     1318      Break
    8011319    end
    8021320  end;
    803 
    804 CheckBorders(-1);
    805 {$IFOPT O-}InvalidTreatyMap:=0;{$ENDIF}
    806 AutoSaveExists:=false;
    807 pDipActive:=-1;
    808 pTurn:=0;
    809 
    810 if Mode>=moMovie then
     1321{$IFOPT O-}InvalidTreatyMap := 0; {$ENDIF}
     1322end;
     1323
     1324procedure StartNewGame(const Path, FileName, Map: string;
     1325  Newlx, Newly, NewLandMass, NewMaxTurn: integer);
     1326var
     1327  p: integer;
     1328begin
     1329  Notify(ntStartDone);
     1330  SavePath := Path;
     1331  LogFileName := FileName;
     1332  MapFileName := Map;
     1333  if FastContact then
     1334  begin
     1335    lx := 24;
     1336    ly := 42;
     1337  end
     1338  else
     1339  begin
     1340    lx := Newlx;
     1341    ly := Newly
     1342  end;
     1343  MapSize := lx * ly;
     1344  if MapFileName <> '' then
     1345    LandMass := 0
     1346  else
     1347    LandMass := NewLandMass;
     1348  MaxTurn := NewMaxTurn;
     1349  Randomize;
     1350  RND := RandSeed;
     1351  Mode := moPlaying;
     1352  CL := TCmdList.Create;
     1353  StartGame;
     1354  NoLogChanges;
     1355  for p := 0 to nPl - 1 do
     1356    if bix[p] >= 0 then
     1357      CallPlayer(cGetReady, p, nil^);
     1358  LogChanges;
     1359  CL.Put(sTurn, 0, 0, nil);
     1360  BeforeTurn0;
     1361  NoLogCityTileChanges;
     1362  GenerateStat(pTurn);
     1363  nLogOpened := -1;
     1364  LastEndClientCommand := -1;
     1365  Brain[bix[0]].Client(cShowGame, 0, nil^);
     1366  Notify(ntBackOff);
     1367  Inform(pTurn);
     1368  ChangeClientWhenDone(cTurn, 0, nil^, 0)
     1369end;
     1370
     1371procedure DirectHelp(Command: integer);
     1372begin
     1373  InitBrain(bixTerm);
     1374  Brain[bixTerm].Client(Command, -1, nil^);
     1375  AICredits := #0;
     1376end;
     1377
     1378procedure EditMap(const Map: string; Newlx, Newly, NewLandMass: integer);
     1379var
     1380  p1, Loc1: integer;
     1381  Game: TNewGameData;
     1382begin
     1383  Notify(ntStartDone);
     1384  Notify(ntInitLocalHuman);
     1385  MapFileName := Map;
     1386  lx := Newlx;
     1387  ly := Newly;
     1388  MapSize := lx * ly;
     1389  LandMass := NewLandMass;
     1390  bix[0] := bixTerm;
     1391  Difficulty[0] := 0;
     1392  InitBrain(bixTerm);
     1393
     1394  Randomize;
     1395  GAlive := 0;
     1396  GWatching := 1;
     1397  if not LoadMap(MapFileName) then
     1398    for Loc1 := 0 to MapSize - 1 do
     1399      RealMap[Loc1] := fOcean or ($F shl 27);
     1400  CL := nil;
     1401  InitMapEditor;
     1402  RW[0].Data := nil;
     1403  RW[0].BorderHelper := nil;
     1404  RW[0].Alive := 0;
     1405  Game.lx := lx;
     1406  Game.ly := ly;
     1407  Game.RO[0] := @RW[0];
     1408  Game.Difficulty[0] := 0;
     1409  for p1 := 1 to nPl - 1 do
     1410  begin
     1411    Game.RO[p1] := nil;
     1412    Game.Difficulty[p1] := -1
     1413  end;
     1414  Brain[bixTerm].Client(cNewMap, -1, Game);
     1415
     1416  DiscoverAll(0, lObserveSuper);
    8111417  Notify(ntEndInfo);
    812 end;{StartGame}
    813 
    814 procedure EndGame;
     1418  Brain[bix[0]].Client(cShowGame, 0, nil^);
     1419  Notify(ntBackOff);
     1420  ChangeClientWhenDone(cEditMap, 0, nil^, 0)
     1421end;
     1422
     1423procedure DestroySpacePort_TellPlayers(p, pCapturer: integer);
    8151424var
    816 i,p1: integer;
     1425  cix, i, p1: integer;
     1426  ShowShipChange: TShowShipChange;
    8171427begin
    818 if LandMass=0 then FreeMem(MapField);
    819 for p1:=0 to nPl-1 do if bix[p1]>=0 then
     1428  // stop ship part production
     1429  for cix := 0 to RW[p].nCity - 1 do
     1430    with RW[p].City[cix] do
     1431      if (Loc >= 0) and (Project and cpImp <> 0) and
     1432        ((Project and cpIndex = woMIR) or
     1433        (Imp[Project and cpIndex].Kind = ikShipPart)) then
     1434      begin
     1435        inc(RW[p].Money, Prod0);
     1436        Prod := 0;
     1437        Prod0 := 0;
     1438        Project := cpImp + imTrGoods;
     1439        Project0 := cpImp + imTrGoods
     1440      end;
     1441
     1442  // destroy ship
     1443  with GShip[p] do
     1444    if Parts[0] + Parts[1] + Parts[2] > 0 then
     1445    begin
     1446      for i := 0 to nShipPart - 1 do
     1447      begin
     1448        ShowShipChange.Ship1Change[i] := -Parts[i];
     1449        if pCapturer >= 0 then
     1450        begin
     1451          ShowShipChange.Ship2Change[i] := Parts[i];
     1452          inc(GShip[pCapturer].Parts[i], Parts[i]);
     1453        end;
     1454        Parts[i] := 0;
     1455      end;
     1456      if Mode >= moMovie then
     1457      begin
     1458        if pCapturer >= 0 then
     1459          ShowShipChange.Reason := scrCapture
     1460        else
     1461          ShowShipChange.Reason := scrDestruction;
     1462        ShowShipChange.Ship1Owner := p;
     1463        ShowShipChange.Ship2Owner := pCapturer;
     1464        for p1 := 0 to nPl - 1 do
     1465          if 1 shl p1 and (GAlive or GWatching) <> 0 then
     1466          begin
     1467            move(GShip, RW[p1].Ship, SizeOf(GShip));
     1468            if 1 shl p1 and GWatching <> 0 then
     1469              CallPlayer(cShowShipChange, p1, ShowShipChange);
     1470          end;
     1471      end
     1472    end
     1473end;
     1474
     1475procedure DestroyCity_TellPlayers(p, cix: integer; SaveUnits: boolean);
     1476begin
     1477  if RW[p].City[cix].built[imSpacePort] > 0 then
     1478    DestroySpacePort_TellPlayers(p, -1);
     1479  DestroyCity(p, cix, SaveUnits);
     1480end;
     1481
     1482procedure ChangeCityOwner_TellPlayers(pOld, cixOld, pNew: integer);
     1483begin
     1484  if RW[pOld].City[cixOld].built[imSpacePort] > 0 then
     1485    if RW[pNew].NatBuilt[imSpacePort] > 0 then
     1486      DestroySpacePort_TellPlayers(pOld, pNew)
     1487    else
     1488      DestroySpacePort_TellPlayers(pOld, -1);
     1489  ChangeCityOwner(pOld, cixOld, pNew);
     1490end;
     1491
     1492procedure CheckWin(p: integer);
     1493var
     1494  i: integer;
     1495  ShipComplete: boolean;
     1496begin
     1497  ShipComplete := true;
     1498  for i := 0 to nShipPart - 1 do
     1499    if GShip[p].Parts[i] < ShipNeed[i] then
     1500      ShipComplete := false;
     1501  if ShipComplete then
     1502    GWinner := GWinner or 1 shl p; // game won!
     1503end;
     1504
     1505procedure BeforeTurn;
     1506var
     1507  i, p1, uix, cix, V21, Loc1, Cost, Job0, nAlive, nAppliers, ad, OldLoc,
     1508    SiegedTiles, nUpdateLoc: integer;
     1509  UpdateLoc: array [0 .. numax - 1] of integer;
     1510  Radius: TVicinity21Loc;
     1511  ShowShipChange: TShowShipChange;
     1512  TribeExtinct, JobDone, MirBuilt: boolean;
     1513begin
     1514{$IFOPT O-}assert(1 shl pTurn and InvalidTreatyMap = 0); {$ENDIF}
     1515  assert(1 shl pTurn and (GAlive or GWatching) <> 0);
     1516  if (1 shl pTurn and GAlive = 0) and (Difficulty[pTurn] > 0) then
     1517    exit;
     1518
     1519  if (GWonder[woGrLibrary].EffectiveOwner = pTurn) and (GWinner = 0) then
     1520  begin // check great library effect
     1521    nAlive := 0;
     1522    for p1 := 0 to nPl - 1 do
     1523      if 1 shl p1 and GAlive <> 0 then
     1524        inc(nAlive);
     1525    for ad := 0 to nAdv - 5 do
     1526      if RW[pTurn].Tech[ad] < tsSeen then
     1527      begin
     1528        nAppliers := 0;
     1529        for p1 := 0 to nPl - 1 do
     1530          if (p1 <> pTurn) and (1 shl p1 and GAlive <> 0) and
     1531            (RW[p1].Tech[ad] >= tsApplicable) then
     1532            inc(nAppliers);
     1533        if nAppliers * 2 > nAlive then
     1534        begin
     1535          SeeTech(pTurn, ad);
     1536          inc(nTech[pTurn]);
     1537          if Mode >= moMovie then
     1538            CallPlayer(cShowGreatLibTech, pTurn, ad);
     1539          // do not call CallPlayer(pTurn) while map is invalid
     1540        end;
     1541      end;
     1542  end;
     1543
     1544  MaskD(ObserveLevel, MapSize, not Cardinal(3 shl (2 * pTurn)));
     1545  if Mode > moLoading_Fast then
     1546    MaskD(RW[pTurn].Map^, MapSize, not Cardinal(fUnit or fHiddenUnit or
     1547      fStealthUnit or fObserved or fSpiedOut or fOwned or fOwnZoCUnit or
     1548      fInEnemyZoC));
     1549  RW[pTurn].nEnemyUn := 0;
     1550
     1551  MirBuilt := false;
     1552  if (Difficulty[pTurn] > 0) and (GWinner = 0) then
     1553    with RW[pTurn] do
     1554    begin
     1555      if nCity > 0 then
     1556        for p1 := 0 to nPl - 1 do
     1557          if GTurn = EvaStart[p1] + PeaceEvaTurns then
     1558          begin // peace contract -- remove all units from p1's territory
     1559            Loc1 := City[0].Loc; // search destination for homeless units
     1560            for cix := 1 to nCity - 1 do
     1561              if (City[cix].Loc >= 0) and
     1562                ((Loc1 < 0) or (City[cix].built[imPalace] > 0)) then
     1563                Loc1 := City[cix].Loc;
     1564            for uix := 0 to nUn - 1 do
     1565              with Un[uix] do
     1566                if (Loc >= 0) and (Model[mix].Kind <> mkDiplomat) and
     1567                  ((Home >= 0) or (Loc1 >= 0)) and
     1568                  (RealMap[Loc] shr 27 = Cardinal(p1)) then
     1569                begin
     1570                  OldLoc := Loc;
     1571                  if Master >= 0 then
     1572                  begin // transport unload
     1573                    if Model[mix].Domain = dAir then
     1574                      dec(Un[Master].AirLoad)
     1575                    else
     1576                      dec(Un[Master].TroopLoad);
     1577                    Master := -1;
     1578                  end
     1579                  else
     1580                    FreeUnit(pTurn, uix);
     1581
     1582                  if Home >= 0 then
     1583                    Loc := City[Home].Loc
     1584                  else
     1585                    Loc := Loc1;
     1586                  PlaceUnit(pTurn, uix);
     1587                  UpdateUnitMap(OldLoc);
     1588                  UpdateUnitMap(Loc);
     1589                  Flags := Flags or unWithdrawn;
     1590                  Happened := Happened or phPeaceEvacuation;
     1591                end
     1592          end;
     1593
     1594      if Mode >= moMovie then
     1595        FillChar(ShowShipChange, SizeOf(ShowShipChange), 0);
     1596      TribeExtinct := true;
     1597      nUpdateLoc := 0;
     1598      for cix := 0 to nCity - 1 do
     1599        with City[cix] do
     1600          if Loc >= 0 then
     1601          begin { next turn for all cities - city loop 1 }
     1602            // if ServerVersion[pTurn]>=$000EF0 then
     1603            // Flags:=Flags and (chFounded or chCaptured or chProductionSabotaged or chDisorder)
     1604            // else Flags:=Flags and (chCaptured or chProductionSabotaged or chDisorder);
     1605            // check for siege
     1606            SiegedTiles := 0;
     1607            V21_to_Loc(Loc, Radius);
     1608            for V21 := 1 to 26 do
     1609              if Tiles and (1 shl V21) and not(1 shl CityOwnTile) <> 0 then
     1610              begin
     1611                Loc1 := Radius[V21];
     1612                assert((Loc1 >= 0) and (Loc1 < MapSize) and
     1613                  (UsedByCity[Loc1] = Loc));
     1614                p1 := RealMap[Loc1] shr 27;
     1615                if (RealMap[Loc1] and fCity <> 0) or (p1 < nPl) and
     1616                  (p1 <> pTurn) and (RW[pTurn].Treaty[p1] >= trPeace) or
     1617                  (ZoCMap[Loc1] > 0) and (Occupant[Loc1] <> pTurn) and
     1618                  (Treaty[Occupant[Loc1]] < trPeace) then
     1619                begin
     1620                  Tiles := Tiles and not(1 shl V21);
     1621                  UsedByCity[Loc1] := -1;
     1622                  Flags := Flags or chSiege;
     1623                  inc(SiegedTiles);
     1624                end;
     1625              end;
     1626            while SiegedTiles > 0 do // replace sieged tiles
     1627            begin
     1628              if not AddBestCityTile(pTurn, cix) then
     1629                Break;
     1630              dec(SiegedTiles);
     1631            end;
     1632
     1633            if Flags and chFounded = 0 then
     1634            begin
     1635              // CollectCityResources(pTurn,cix); // old style
     1636
     1637              if CityTurn(pTurn, cix) then
     1638                TribeExtinct := false
     1639              else
     1640              begin // city is erased
     1641                RemoveDomainUnits(dSea, pTurn, Loc);
     1642                RemoveDomainUnits(dAir, pTurn, Loc);
     1643                Map[Loc] := Map[Loc] and not fCity; // !!! do this in inner core
     1644                UpdateLoc[nUpdateLoc] := Loc;
     1645                inc(nUpdateLoc);
     1646                DestroyCity_TellPlayers(pTurn, cix, true);
     1647              end;
     1648
     1649              if (Flags and chProduction <> 0) and (Project0 and cpImp <> 0)
     1650              then
     1651              begin
     1652                if Project0 and cpIndex = woMIR then // MIR completed
     1653                  MirBuilt := true
     1654                else if Project0 and cpIndex = woManhattan then
     1655                  GColdWarStart := GTurn
     1656                else if Imp[Project0 and cpIndex].Kind = ikShipPart
     1657                then { ship parts produced }
     1658                  inc(ShowShipChange.Ship1Change[Project0 and cpIndex -
     1659                    imShipComp]);
     1660              end
     1661            end
     1662          end; { city loop 1 }
     1663      if nUpdateLoc > 0 then
     1664      begin
     1665        CheckBorders(-1, pTurn);
     1666        for i := 0 to nUpdateLoc - 1 do
     1667          UpdateUnitMap(UpdateLoc[i], true);
     1668        if Mode >= moMovie then
     1669          for p1 := 0 to nPl - 1 do
     1670            if (1 shl p1 and GWatching <> 0) and (p1 <> pTurn) then
     1671              for i := 0 to nUpdateLoc - 1 do
     1672                if ObserveLevel[UpdateLoc[i]] shr (2 * p1) and 3 >= lObserveUnhidden
     1673                then
     1674                  CallPlayer(cShowCityChanged, p1, UpdateLoc[i]);
     1675      end;
     1676
     1677      for uix := 0 to nUn - 1 do
     1678        with Un[uix] do
     1679          if Loc >= 0 then
     1680          begin // unit loop 2
     1681            if Health < 100 then
     1682              Recover(pTurn, uix);
     1683
     1684            if Flags and unMountainDelay <> 0 then
     1685            begin
     1686              Movement := 0;
     1687              Flags := Flags and not unMountainDelay
     1688            end
     1689            else
     1690              Movement := UnitSpeed(pTurn, mix, Health); { refresh movement }
     1691
     1692            assert(Loc >= 0);
     1693            if Model[mix].Kind <> mkDiplomat then
     1694            begin // check treaty violation
     1695              p1 := RealMap[Loc] shr 27;
     1696              if (p1 < nPl) and (p1 <> pTurn) and (Treaty[p1] >= trPeace) then
     1697              begin
     1698                if (Job in [jCity, jPillage, jClear, jAfforest, jTrans]) or
     1699                  (Job in [jIrr, jMine, jFort, jBase]) and
     1700                  (RealMap[Loc] and fTerImp <> 0) then
     1701                  Job := jNone;
     1702                if (GTurn > EvaStart[p1] + PeaceEvaTurns) and
     1703                  (Treaty[p1] <> trAlliance) then
     1704                begin
     1705                  EvaStart[p1] := GTurn;
     1706                  Happened := Happened or phPeaceViolation;
     1707                  if Mode >= moMovie then
     1708                    CallPlayer(cShowPeaceViolation, p1, pTurn);
     1709                end;
     1710              end;
     1711            end;
     1712
     1713            if ServerVersion[pTurn] >= $000EF0 then
     1714            begin
     1715              if (Health <= 0) or TribeExtinct then
     1716                RemoveUnit_UpdateMap(pTurn, uix);
     1717            end
     1718          end;
     1719
     1720      if ServerVersion[pTurn] < $000EF0 then
     1721        for uix := 0 to nUn - 1 do
     1722          with Un[uix] do
     1723            if Loc >= 0 then
     1724            begin // unit loop 3
     1725              Loc1 := Loc;
     1726              Job0 := Job;
     1727              if Job <> jNone then
     1728                JobDone := Work(pTurn, uix);
     1729              { settlers do terrain improvement jobs }
     1730              if (Health <= 0) or TribeExtinct then
     1731                RemoveUnit_UpdateMap(pTurn, uix);
     1732
     1733              if (Job0 = jCity) and JobDone then // new city
     1734              begin
     1735                AddBestCityTile(pTurn, RW[pTurn].nCity - 1);
     1736                UpdateUnitMap(Loc1, true);
     1737                if Mode >= moMovie then // tell enemies
     1738                  for p1 := 0 to nPl - 1 do
     1739                    if (1 shl p1 and GWatching <> 0) and (p1 <> pTurn) and
     1740                      (ObserveLevel[Loc1] and (3 shl (2 * p1)) > 0) then
     1741                      CallPlayer(cShowCityChanged, p1, Loc1);
     1742              end
     1743            end;
     1744
     1745      { pollution - city loop 3 }
     1746      for cix := 0 to nCity - 1 do
     1747        with City[cix] do
     1748          if (Loc >= 0) and (Pollution >= MaxPollution) then
     1749            Pollute(pTurn, cix);
     1750
     1751      CompactLists(pTurn);
     1752      if (nUn = 0) and (nCity = 0) then
     1753      begin // nation made extinct
     1754        Happened := Happened or phExtinct;
     1755        GAlive := GAlive and not(1 shl pTurn);
     1756        Stat[stPop, pTurn, GTurn] := 0;
     1757        Stat[stMil, pTurn, GTurn] := 0;
     1758        Stat[stScience, pTurn, GTurn] := 0;
     1759        Stat[stExplore, pTurn, GTurn] := 0;
     1760        Stat[stTerritory, pTurn, GTurn] := 0;
     1761        Stat[stWork, pTurn, GTurn] := 0;
     1762        for p1 := 0 to nPl - 1 do
     1763          if 1 shl p1 and (GAlive or GWatching) <> 0 then
     1764          begin
     1765            if p1 <> pTurn then
     1766            begin
     1767              GiveCivilReport(p1, pTurn);
     1768              if (GTestFlags and tfUncover <> 0) or (Difficulty[p1] = 0) or
     1769                (RW[p1].Treaty[pTurn] = trAlliance) then
     1770                GiveMilReport(p1, pTurn);
     1771            end;
     1772            with RW[p1] do
     1773            begin
     1774              Alive := GAlive;
     1775              for Loc1 := 0 to MapSize - 1 do
     1776                if Territory[Loc1] = pTurn then
     1777                // remove territory of extinct nation from player maps
     1778                begin
     1779                  Territory[Loc1] := -1;
     1780                  Map[Loc1] := Map[Loc1] and not fPeace
     1781                end
     1782            end;
     1783          end;
     1784        exit
     1785      end;
     1786
     1787      // check research
     1788      Cost := TechCost(pTurn);
     1789      if GTestFlags and tfImmAdvance <> 0 then
     1790        Research := Cost;
     1791      if (Happened and phTech = 0) and (Research >= Cost) then
     1792      begin
     1793        if ResearchTech = adMilitary then
     1794          EnableDevModel(pTurn) { new Unit class initiated }
     1795        else if ResearchTech >= 0 then
     1796          DiscoverTech(pTurn, ResearchTech);
     1797
     1798        dec(Research, Cost);
     1799        Happened := Happened or phTech;
     1800        ResearchTech := -1
     1801      end
     1802      else if (ResearchTech = -2) and (nCity > 0) then
     1803      begin
     1804        Happened := Happened or phTech;
     1805        ResearchTech := -1
     1806      end;
     1807
     1808      if Credibility < MaxCredibility then
     1809        for p1 := 0 to nPl - 1 do
     1810          if (p1 <> pTurn) and (1 shl p1 and GAlive <> 0) and
     1811            (Treaty[p1] >= trPeace) then
     1812          begin
     1813            inc(Credibility);
     1814            Break
     1815          end;
     1816
     1817      if GWinner = 0 then
     1818        CheckWin(pTurn);
     1819      if (Mode >= moMovie) and (GWinner = 0) and
     1820        ((ShowShipChange.Ship1Change[0] > 0) or
     1821        (ShowShipChange.Ship1Change[1] > 0) or
     1822        (ShowShipChange.Ship1Change[2] > 0)) then
     1823      begin
     1824        ShowShipChange.Reason := scrProduction;
     1825        ShowShipChange.Ship1Owner := pTurn;
     1826        ShowShipChange.Ship2Owner := -1;
     1827        for p1 := 0 to nPl - 1 do
     1828          if (p1 <> pTurn) and (1 shl p1 and (GAlive or GWatching) <> 0) then
     1829          begin
     1830            move(GShip, RW[p1].Ship, SizeOf(GShip));
     1831            if 1 shl p1 and GWatching <> 0 then
     1832              CallPlayer(cShowShipChange, p1, ShowShipChange);
     1833          end
     1834      end;
     1835      if WinOnAlone and (GAlive and not(1 shl pTurn or 1) = 0) then
     1836        GWinner := 1 shl pTurn; // break if only one nation left
     1837
     1838      if GTurn = AnarchyStart + AnarchyTurns then
     1839      begin
     1840        AnarchyStart := -AnarchyTurns - 1;
     1841        Government := gDespotism;
     1842        for p1 := 0 to nPl - 1 do
     1843          if (p1 <> pTurn) and ((GAlive or GWatching) and (1 shl p1) <> 0) then
     1844            RW[p1].EnemyReport[pTurn].Government := gDespotism;
     1845        inc(Happened, phChangeGov)
     1846      end;
     1847    end; // if Difficulty[pTurn]>0
     1848
     1849  if (pTurn = 0) and (GWinner > 0) then
     1850  begin // game over, give world map and all reports to player 0
     1851    DiscoverAll(pTurn, lObserveSuper);
     1852    for p1 := 1 to nPl - 1 do
     1853      if 1 shl p1 and GAlive <> 0 then
     1854      begin
     1855        if RW[pTurn].Treaty[p1] < trNone then
     1856        begin
     1857          RW[pTurn].Treaty[p1] := trNone;
     1858          RW[p1].Treaty[pTurn] := trNone;
     1859        end;
     1860        GiveCivilReport(pTurn, p1);
     1861        GiveMilReport(pTurn, p1);
     1862      end;
     1863  end
     1864  else
    8201865  begin
    821   for i:=0 to nStat-1 do FreeMem(Stat[i,p1]);
    822   if RW[p1].BattleHistory<>nil then FreeMem(RW[p1].BattleHistory);
    823   {if RW[p1].BorderHelper<>nil then FreeMem(RW[p1].BorderHelper);}
    824   FreeMem(RW[p1].Data);
    825   FreeMem(SavedData[p1]);
    826   if RW[p1].DefaultDebugMap<>nil then
    827     FreeMem(RW[p1].DefaultDebugMap);
    828   end;
    829 UnitProcessing.ReleaseGame;
    830 CityProcessing.ReleaseGame;
    831 Database.ReleaseGame;
    832 CL.Free;
    833 end;
    834 
    835 procedure GenerateStat(p: integer);
    836 var
    837 cix,uix: integer;
    838 begin
    839 if Difficulty[p]>0 then with RW[p] do
    840   begin
    841   Stat[stPop,p,GTurn]:=0;
    842   for cix:=0 to nCity-1 do if City[cix].Loc>=0 then
    843     inc(Stat[stPop,p,GTurn],City[cix].Size);
    844   Stat[stScience,p,GTurn]:=Researched[p]*50;
    845   if (RW[p].ResearchTech>=0) and (RW[p].ResearchTech<>adMilitary) then
    846     inc(Stat[stScience,p,GTurn],
    847       Research*100 div TechBaseCost(nTech[p],Difficulty[p]));
    848   Stat[stMil,p,GTurn]:=0;
    849   for uix:=0 to nUn-1 do if Un[uix].Loc>=0 then
    850     with Model[Un[uix].mix] do
    851       begin
    852       if (Kind<=mkEnemyDeveloped) and (Un[uix].mix<>1) then
    853         inc(Stat[stMil,p,GTurn],Weight*MStrength*Un[uix].Health div 100)
    854       else if Domain=dGround then inc(Stat[stMil,p,GTurn],(Attack+2*Defense)*Un[uix].Health div 100)
    855       else inc(Stat[stMil,p,GTurn],(Attack+Defense)*Un[uix].Health div 100);
    856       case Kind of
    857         mkSlaves: inc(Stat[stPop,p,GTurn]);
    858         mkSettler: inc(Stat[stPop,p,GTurn],2);
    859         end;
    860       end;
    861   Stat[stMil,p,GTurn]:=Stat[stMil,p,GTurn] div 16;
    862   Stat[stExplore,p,GTurn]:=Discovered[p];
    863   Stat[stTerritory,p,GTurn]:=TerritoryCount[p];
    864   Stat[stWork,p,GTurn]:=Worked[p];
    865   LastValidStat[p]:=GTurn;
    866   end;
    867 end;
    868 
    869 procedure LogCityTileChanges;
    870 var
    871 cix: integer;
    872 begin
    873 for cix:=0 to RW[pTurn].nCity-1 do
    874   with RW[pTurn].City[cix] do if Loc>=0 then
     1866    // show observed areas
     1867    if (GTestFlags and tfUncover <> 0) or (Difficulty[pTurn] = 0)
     1868    then { supervisor - all tiles visible }
    8751869    begin
    876 {    if SavedResourceWeights[cix]<>ResourceWeights then
    877       begin // log city resource weight changes
    878       CL.Put(sSetCityResourceWeights, pTurn, cix, @ResourceWeights);
    879       SavedResourceWeights[cix]:=ResourceWeights;
    880       end;}
    881     if SavedTiles[cix]<>Tiles then
    882       begin // log city tile changes
    883       CL.Put(sSetCityTiles, pTurn, cix, @Tiles);
    884       SavedTiles[cix]:=Tiles;
    885       end;
    886     end;
    887 end;
    888 
    889 procedure NoLogCityTileChanges;
    890 var
    891 cix: integer;
    892 begin
    893 for cix:=0 to RW[pTurn].nCity-1 do
    894   with RW[pTurn].City[cix] do if Loc>=0 then
     1870      if (bix[pTurn] <> bixNoTerm) and
     1871        ((Difficulty[pTurn] > 0) or (Mode > moLoading_Fast)) then
     1872        DiscoverAll(pTurn, lObserveSuper)
     1873    end
     1874    else
    8951875    begin
    896 //    SavedResourceWeights[cix]:=ResourceWeights;
    897     SavedTiles[cix]:=Tiles;
    898     end;
    899 end;
    900 
    901 function HasCityTileChanges: boolean;
    902 var
    903 cix: integer;
    904 begin
    905 result:=false;
    906 for cix:=0 to RW[pTurn].nCity-1 do
    907   with RW[pTurn].City[cix] do if Loc>=0 then
    908     begin
    909 //    if SavedResourceWeights[cix]<>ResourceWeights then result:=true;
    910     if SavedTiles[cix]<>Tiles then result:=true;
    911     end;
    912 end;
    913 
    914 procedure BeforeTurn0;
    915 var
    916 p1,uix: integer;
    917 begin
    918 for uix:=0 to RW[pTurn].nUn-1 do {init movement points for first turn}
    919   with RW[pTurn].Un[uix] do Movement:=RW[pTurn].Model[mix].Speed;
    920 
    921 if Difficulty[pTurn]>0 then
    922   DiscoverViewAreas(pTurn)
    923 else {supervisor}
    924   begin
    925   DiscoverAll(pTurn,lObserveSuper);
    926   for p1:=1 to nPl-1 do
    927     if 1 shl p1 and GAlive<>0 then
    928       begin
    929       GiveCivilReport(pTurn, p1);
    930       GiveMilReport(pTurn, p1)
    931       end;
    932   end;
    933 //CheckContact;
    934 end;
    935 
    936 function LoadGame(const Path, FileName: string; Turn: integer; MovieMode: boolean): boolean;
    937 var
    938 i,j,ix,d,p1,Command,Subject: integer;
    939 {$IFDEF TEXTLOG}LoadPos0: integer;{$ENDIF}
    940 Data: pointer;
    941 LogFile: TFileStream;
    942 FormerCLState: TCmdListState;
    943 s: string[255];
    944 SaveMap: array[0..lxmax*lymax-1] of Byte;
    945 started,StatRequest: boolean;
    946 begin
    947 SavePath:=Path;
    948 LogFileName:=FileName;
    949 LoadTurn:=Turn;
    950 LogFile:=TFileStream.Create(SavePath+LogFileName,fmOpenRead or fmShareExclusive);
    951 LogFile.Position:=0;
    952 LogFile.read(s[1],8); {file id}
    953 LogFile.read(i,4); {c-evo version}
    954 LogFile.read(j,4); {exe time}
    955 
    956 if (i>=FirstBookCompatibleVersion) and (i<=Version) then
    957   begin
    958   result:=true;
    959   LogFile.read(lx,4);
    960   LogFile.read(ly,4);
    961   MapSize:=lx*ly;
    962   LogFile.read(LandMass,4);
    963   if LandMass=0 then
    964     LogFile.read(RealMap,MapSize*4); // use predefined map
    965   LogFile.read(MaxTurn,4);
    966   LogFile.read(RND,4);
    967   LogFile.read(GTurn,4);
    968   LogFile.read(SaveMap,4);
    969   if SaveMap[0]<>$80 then
    970     LogFile.read(SaveMap[4],((MapSize-1) div 4+1)*4-4);
    971   for p1:=0 to nPl-1 do
    972     begin
    973     LogFile.read(s[0],4);
    974     if s[0]=#0 then bixView[p1]:=-1
    975     else
    976       begin
    977       LogFile.read(s[4],Byte(s[0]) div 4 *4);
    978       LogFile.read(OriginalDataVersion[p1],4);
    979       LogFile.read(d,4);{behavior}
    980       LogFile.read(Difficulty[p1],4);
    981       j:=nBrain-1;
    982       while (j>=0) and (AnsiCompareFileName(Brain[j].FileName,s)<>0) do
    983         dec(j);
    984       if j<0 then
    985         begin // ai not found -- replace by local player
    986         ProcessClientData[p1]:=false;
    987         NotifyMessage:=s;
    988         Notify(ntAIError);
    989         j:=bixTerm;
    990         end
    991       else ProcessClientData[p1]:=true;
    992       if j=bixNoTerm then j:=bixSuper_Virtual;
    993         // crashed tournament -- load as supervisor
    994       bixView[p1]:=j;
    995       end;
    996     end;
    997   end
    998 else result:=false;
    999 
    1000 if result then
    1001   begin
    1002   CL:=TCmdList.Create;
    1003   CL.LoadFromFile(LogFile);
    1004   end;
    1005 LogFile.Free;
    1006 if not result then exit;
    1007 
    1008 Notify(ntStartDone);
    1009 if LoadTurn<0 then LoadTurn:=GTurn;
    1010 if MovieMode then Mode:=moMovie
    1011 else if LoadTurn=0 then Mode:=moLoading
    1012 else Mode:=moLoading_Fast;
    1013 {$IFDEF TEXTLOG}AssignFile(TextLog,SavePath+LogFileName+'.txt');Rewrite(TextLog);{$ENDIF}
    1014 LoadOK:=true;
    1015 StartGame;
    1016 if MovieMode then
    1017   begin
    1018   Brain[bix[0]].Client(cShowGame,0,nil^);
    1019   Notify(ntBackOff);
    1020   end
    1021 else Notify(ntLoadBegin);
    1022 
    1023 started:=false;
    1024 StatRequest:=false;
    1025 MovieStopped:=false;
    1026 {$IFDEF LOADPERF}QueryPerformanceCounter(time_total0); time_a:=0; time_b:=0; time_c:=0;{$ENDIF}
    1027 while not MovieStopped and (CL.Progress<1000) do
    1028   begin
    1029   FormerCLState:=CL.State;
    1030   CL.Get(Command, p1, Subject, Data);
    1031   if p1<0 then p1:=pTurn;
    1032   if StatRequest
    1033     and (Command and (sctMask or sExecute)<>sctInternal or sExecute) then
    1034     begin GenerateStat(pTurn); StatRequest:=false end;
    1035       // complete all internal commands following an sTurn before generating statistics
    1036   if (Command=sTurn) and not started then
    1037     begin
    1038     {$IFDEF TEXTLOG}WriteLn(TextLog,'---Turn 0 P0---');{$ENDIF}
    1039     for p1:=0 to nPl-1 do
    1040       if (bix[p1]>=0) and ((Mode<>moMovie) or (p1=0)) then
    1041         CallPlayer(cReplay,p1,nil^);
    1042     BeforeTurn0;
    1043     if MovieMode then
    1044       begin
    1045       Inform(pTurn);
    1046       CallPlayer(cMovieTurn,0,nil^);
    1047       end;
    1048     StatRequest:=true;
    1049     started:=true;
    1050     end
    1051   else if (Command=sTurn) and (pTurn=0) and (GTurn=LoadTurn) then
    1052     begin
    1053     assert(CL.State.LoadPos=FormerCLState.LoadPos+4); // size of sTurn
    1054     CL.State:=FormerCLState;
    1055     CL.Cut;
    1056     Break;
    1057     end
    1058   else if Command=sIntDataChange then
    1059     begin
    1060     {$IFDEF TEXTLOG}LoadPos0:=CL.State.LoadPos;{$ENDIF}
    1061     if ProcessClientData[p1] then
    1062       CL.GetDataChanges(RW[p1].Data, Brain[bix[p1]].DataSize)
    1063     else CL.GetDataChanges(nil, 0);
    1064     {$IFDEF TEXTLOG}WriteLn(TextLog,Format('Data Changes P%d (%d Bytes)', [p1,CL.State.LoadPos-LoadPos0]));{$ENDIF}
    1065     end
    1066   else
    1067     begin
    1068     {$IFDEF TEXTLOG}CmdInfo:=Format('Command %x',[Command]);{$ENDIF}
    1069     if Command and (sctMask or sExecute)=sctInternal or sExecute then
    1070       IntServer(Command, p1, Subject, Data^) // internal command
    1071     else
    1072       begin
    1073       StatRequest:= Command=sTurn;
    1074       Server(Command, p1, Subject, Data^);
    1075       end;
    1076     {$IFDEF TEXTLOG}WriteLn(TextLog,CmdInfo);{$ENDIF}
    1077     end;
    1078   if not MovieMode then Notify(ntLoadState+CL.Progress*128 div 1000);
    1079   end;
    1080 
    1081 if MovieMode then
    1082   begin
    1083   Notify(ntBackOn);
    1084   Brain[bix[0]].Client(cBreakGame,-1,nil^);
    1085   EndGame;
    1086   Notify(ntStartGo);
    1087   result:=false;
    1088   exit;
    1089   end;
    1090 
    1091 if StatRequest then GenerateStat(pTurn);
    1092 assert(started);
    1093 {$IFDEF TEXTLOG}CloseFile(TextLog);{$ENDIF}
    1094 {$IFDEF LOADPERF}QueryPerformanceCounter(time_total);{time in s is: (time_total-time_total0)/PerfFreq}{$ENDIF}
    1095 NoLogChanges;
    1096 NoLogCityTileChanges;
    1097 if LogFileName[1]='~' then
    1098   begin Delete(LogFileName,1,1); nLogOpened:=-1 end
    1099 else nLogOpened:=CL.State.nLog;
    1100 
    1101 Mode:=moPlaying;
    1102 LastEndClientCommand:=-1;
    1103 if (GTestFlags and tfUncover<>0) or (Difficulty[pTurn]=0) then
    1104   DiscoverAll(pTurn,lObserveSuper) {supervisor - all tiles visible}
    1105 else DiscoverViewAreas(pTurn);
    1106 
    1107 for p1:=0 to nPl-1 do if 1 shl p1 and (GAlive or GWatching)<>0 then
    1108   begin
    1109   RecalcPeaceMap(p1);
    1110   for ix:=0 to RW[p1].nEnemyUn-1 do with RW[p1].EnemyUn[ix] do
    1111     emix:=RWemix[p1,Owner,mix];
    1112   Inform(p1);
    1113   end;
    1114 {$IFOPT O-}CheckBorders(-2);{$ENDIF} // for testing only
    1115 Notify(ntEndInfo);
    1116 if not LoadOK then
    1117   begin NotifyMessage:=SavePath+LogFileName; Notify(ntLoadError); end;
    1118 Brain[bix[0]].Client(cShowGame,0,nil^);
    1119 Notify(ntBackOff);
    1120 Inform(pTurn);
    1121 ChangeClientWhenDone(cResume,0,nil^,0);
    1122 end; //LoadGame
    1123 
    1124 procedure InsertTerritoryUpdateCommands;
    1125 var
    1126 p1,Command,Subject: integer;
    1127 Data: pointer;
    1128 FormerCLState: TCmdListState;
    1129 begin
    1130 while CL.Progress<1000 do
    1131   begin
    1132   FormerCLState:=CL.State;
    1133   CL.Get(Command, p1, Subject, Data);
    1134   if (Command=sIntExpandTerritory) and (p1=pTurn) then
    1135     begin
    1136     IntServer(Command, p1, Subject, Data^);
    1137     {$IFDEF TEXTLOG}WriteLn(TextLog,'AfterTurn - ExpandTerritory');{$ENDIF}
    1138     end
    1139   else
    1140     begin
    1141     CL.State:=FormerCLState;
    1142     break
     1876      DiscoverViewAreas(pTurn);
     1877      if MirBuilt then
     1878        DiscoverAll(pTurn, lObserveUnhidden)
    11431879    end
    11441880  end;
    1145 {$IFOPT O-}InvalidTreatyMap:=0;{$ENDIF}
     1881  // CheckContact;
     1882end; { BeforeTurn }
     1883
     1884procedure AfterTurn;
     1885var
     1886  cix, uix, p1, Loc1, Job0: integer;
     1887  JobDone: boolean;
     1888begin
     1889  with RW[pTurn] do
     1890  begin
     1891    for cix := 0 to nCity - 1 do
     1892      if City[cix].Loc >= 0 then
     1893      begin
     1894        // City[cix].Flags:=City[cix].Flags and not chProductionSabotaged;
     1895        City[cix].Flags := City[cix].Flags and (chCaptured or chDisorder);
     1896        CollectCityResources(pTurn, cix); // new style
     1897      end;
     1898
     1899    inc(Money, OracleIncome);
     1900    OracleIncome := 0;
     1901    if GWonder[woOracle].EffectiveOwner = pTurn then
     1902    begin
     1903      for p1 := 0 to nPl - 1 do
     1904        if (1 shl p1 and GAlive <> 0) and
     1905          ((p1 = pTurn) or (RW[pTurn].Treaty[p1] > trNoContact)) then
     1906          for cix := 0 to RW[p1].nCity - 1 do
     1907            if (RW[p1].City[cix].Loc >= 0) and
     1908              (RW[p1].City[cix].built[imTemple] > 0) then
     1909              inc(OracleIncome);
     1910    end;
     1911
     1912    if (GTestFlags and tfImmImprove = 0) and (Government <> gAnarchy) then
     1913      for cix := 0 to nCity - 1 do
     1914        if (City[cix].Loc >= 0) and (City[cix].Flags and chCaptured = 0) then
     1915          PayCityMaintenance(pTurn, cix);
     1916
     1917    if ServerVersion[pTurn] >= $000EF0 then
     1918    begin // let settlers work
     1919      for cix := 0 to nCity - 1 do
     1920        City[cix].Flags := City[cix].Flags and not chFounded;
     1921      for uix := 0 to nUn - 1 do
     1922        with Un[uix] do
     1923          if Loc >= 0 then
     1924          begin
     1925            Loc1 := Loc;
     1926            Job0 := Job;
     1927            if Job <> jNone then
     1928              JobDone := Work(pTurn, uix);
     1929            { settlers do terrain improvement jobs }
     1930            if Health <= 0 then
     1931              RemoveUnit_UpdateMap(pTurn, uix);
     1932
     1933            if (Job0 = jCity) and JobDone then // new city
     1934            begin
     1935              AddBestCityTile(pTurn, RW[pTurn].nCity - 1);
     1936              UpdateUnitMap(Loc1, true);
     1937              if Mode >= moMovie then // tell enemies
     1938                for p1 := 0 to nPl - 1 do
     1939                  if (1 shl p1 and GWatching <> 0) and (p1 <> pTurn) and
     1940                    (ObserveLevel[Loc1] and (3 shl (2 * p1)) > 0) then
     1941                    CallPlayer(cShowCityChanged, p1, Loc1);
     1942            end
     1943          end;
     1944    end;
     1945
     1946    for uix := 0 to nUn - 1 do
     1947      with Un[uix] do
     1948        if Loc >= 0 then
     1949        begin { next turn for all units }
     1950          if Model[mix].Domain = dAir then
     1951            if (Master >= 0) or (RealMap[Loc] and fCity <> 0) or
     1952              (RealMap[Loc] and fTerImp = tiBase) then
     1953            begin
     1954              Fuel := Model[mix].Cap[mcFuel];
     1955              Flags := Flags or unBombsLoaded
     1956            end
     1957            else if Model[mix].Kind = mkSpecial_Glider then { glider }
     1958            begin
     1959              if RealMap[Loc] and fTerrain < fGrass then
     1960              begin
     1961                RemoveUnit_UpdateMap(pTurn, uix); // unit lost
     1962                Happened := Happened or phGliderLost
     1963              end
     1964            end
     1965            else
     1966            begin
     1967              dec(Fuel);
     1968              if Fuel < 0 then
     1969              begin
     1970                RemoveUnit_UpdateMap(pTurn, uix); // unit lost
     1971                Happened := Happened or phPlaneLost
     1972              end
     1973            end
     1974          else if (Master < 0) and (Movement > 0) then // check HostileDamage
     1975          begin
     1976            Health := Health - HostileDamage(pTurn, mix, Loc, Movement);
     1977            if Health < 0 then
     1978              RemoveUnit_UpdateMap(pTurn, uix);
     1979          end
     1980        end; { unit loop 1 }
     1981
     1982    for uix := 0 to nUn - 1 do
     1983      with Un[uix] do
     1984      begin
     1985        Flags := Flags and not unWithdrawn;
     1986        if (Loc >= 0) and (Model[mix].Domain = dGround) and (Master < 0) and
     1987          ((integer(Movement) = Model[mix].Speed) or
     1988          (Model[mix].Cap[mcAcademy] > 0) and (Movement * 2 >= Model[mix].Speed))
     1989        then
     1990          Flags := Flags or unFortified; // fortify unmoved units
     1991      end;
     1992
     1993    if (GTestFlags and tfUncover = 0) and (Difficulty[pTurn] > 0) then
     1994    begin // restrict view area to current positions
     1995      MaskD(ObserveLevel, MapSize, not Cardinal(3 shl (2 * pTurn)));
     1996      if Mode > moLoading_Fast then
     1997        MaskD(RW[pTurn].Map^, MapSize, not Cardinal(fUnit or fHiddenUnit or
     1998          fStealthUnit or fObserved or fSpiedOut or fOwned or fOwnZoCUnit or
     1999          fInEnemyZoC));
     2000      RW[pTurn].nEnemyUn := 0;
     2001      DiscoverViewAreas(pTurn);
     2002    end;
     2003
     2004    if GWinner = 0 then
     2005      for p1 := 0 to nPl - 1 do
     2006        if 1 shl p1 and GAlive <> 0 then
     2007          CheckWin(p1);
     2008  end;
     2009end; // Afterturn
     2010
     2011procedure NextPlayer;
     2012begin
     2013  if GTurn = 0 then
     2014    BeforeTurn0
     2015  else
     2016    BeforeTurn;
     2017  NoLogCityTileChanges;
     2018  GenerateStat(pTurn);
     2019  Inform(pTurn);
     2020  ChangeClient;
    11462021end;
    11472022
    1148 procedure StartNewGame(const Path, FileName, Map: string; Newlx, Newly,
    1149   NewLandMass, NewMaxTurn: integer);
     2023function ExecuteMove(p, uix, ToLoc: integer; var MoveInfo: TMoveInfo;
     2024  ShowMove: TShowMove): integer;
    11502025var
    1151 p: integer;
     2026  i, p1, FromLoc, uix1, nUpdateLoc: integer;
     2027  MinLevel, MissionResult: Cardinal;
     2028  PModel: ^TModel;
     2029  UpdateLoc: array [0 .. numax - 1] of integer;
     2030  SeeFrom, SeeTo, ExtDiscover: boolean;
    11522031begin
    1153 Notify(ntStartDone);
    1154 SavePath:=Path;
    1155 LogFileName:=FileName;
    1156 MapFileName:=Map;
    1157 if FastContact then begin lx:=24; ly:=42; end
    1158 else begin lx:=Newlx; ly:=Newly end;
    1159 MapSize:=lx*ly;
    1160 if MapFileName<>'' then LandMass:=0
    1161 else LandMass:=NewLandMass;
    1162 MaxTurn:=NewMaxTurn;
    1163 Randomize;
    1164 RND:=RandSeed;
    1165 Mode:=moPlaying;
    1166 CL:=TCmdList.Create;
    1167 StartGame;
    1168 NoLogChanges;
    1169 for p:=0 to nPl-1 do if bix[p]>=0 then
    1170   CallPlayer(cGetReady,p,nil^);
    1171 LogChanges;
    1172 CL.Put(sTurn, 0, 0, nil);
    1173 BeforeTurn0;
    1174 NoLogCityTileChanges;
    1175 GenerateStat(pTurn);
    1176 nLogOpened:=-1;
    1177 LastEndClientCommand:=-1;
    1178 Brain[bix[0]].Client(cShowGame,0,nil^);
    1179 Notify(ntBackOff);
    1180 Inform(pTurn);
    1181 ChangeClientWhenDone(cTurn,0,nil^,0)
    1182 end;
    1183 
    1184 procedure DirectHelp(Command: integer);
    1185 begin
    1186 InitBrain(bixTerm);
    1187 Brain[bixTerm].Client(Command,-1,nil^);
    1188 AICredits:=#0;
    1189 end;
    1190 
    1191 procedure EditMap(const Map: string; Newlx, Newly, NewLandMass: integer);
    1192 var
    1193 p1,Loc1: integer;
    1194 Game: TNewGameData;
    1195 begin
    1196 Notify(ntStartDone);
    1197 Notify(ntInitLocalHuman);
    1198 MapFileName:=Map;
    1199 lx:=Newlx;
    1200 ly:=Newly;
    1201 MapSize:=lx*ly;
    1202 LandMass:=NewLandMass;
    1203 bix[0]:=bixTerm;
    1204 Difficulty[0]:=0;
    1205 InitBrain(bixTerm);
    1206 
    1207 Randomize;
    1208 GAlive:=0;
    1209 GWatching:=1;
    1210 if not LoadMap(MapFileName) then
    1211   for Loc1:=0 to MapSize-1 do RealMap[Loc1]:=fOcean or ($F shl 27);
    1212 CL:=nil;
    1213 InitMapEditor;
    1214 RW[0].Data:=nil;
    1215 RW[0].BorderHelper:=nil;
    1216 RW[0].Alive:=0;
    1217 Game.lx:=lx; Game.ly:=ly;
    1218 Game.RO[0]:=@RW[0];
    1219 Game.Difficulty[0]:=0;
    1220 for p1:=1 to nPl-1 do begin Game.RO[p1]:=nil; Game.Difficulty[p1]:=-1 end;
    1221 Brain[bixTerm].Client(cNewMap,-1,Game);
    1222 
    1223 DiscoverAll(0,lObserveSuper);
    1224 Notify(ntEndInfo);
    1225 Brain[bix[0]].Client(cShowGame,0,nil^);
    1226 Notify(ntBackOff);
    1227 ChangeClientWhenDone(cEditMap,0,nil^,0)
    1228 end;
    1229 
    1230 procedure DestroySpacePort_TellPlayers(p,pCapturer: integer);
    1231 var
    1232 cix,i,p1: integer;
    1233 ShowShipChange: TShowShipChange;
    1234 begin
    1235 // stop ship part production
    1236 for cix:=0 to RW[p].nCity-1 do with RW[p].City[cix] do
    1237   if (Loc>=0) and (Project and cpImp<>0)
    1238     and ((Project and cpIndex=woMIR)
    1239       or (Imp[Project and cpIndex].Kind=ikShipPart)) then
     2032  result := 0;
     2033  with RW[p], Un[uix] do
     2034  begin
     2035    PModel := @Model[mix];
     2036    FromLoc := Loc;
     2037
     2038    if Master < 0 then
     2039      FreeUnit(p, uix);
     2040    if (MoveInfo.MoveType in [mtMove, mtCapture]) and MoveInfo.MountainDelay
     2041    then
    12402042    begin
    1241     inc(RW[p].Money,Prod0);
    1242     Prod:=0;
    1243     Prod0:=0;
    1244     Project:=cpImp+imTrGoods;
    1245     Project0:=cpImp+imTrGoods
     2043      Flags := Flags or unMountainDelay;
    12462044    end;
    1247 
    1248 // destroy ship
    1249 with GShip[p] do if Parts[0]+Parts[1]+Parts[2]>0 then
    1250   begin
    1251   for i:=0 to nShipPart-1 do
     2045    Loc := -2;
     2046    if TroopLoad + AirLoad > 0 then
     2047      for i := 0 to nUn - 1 do
     2048        if (Un[i].Loc >= 0) and (Un[i].Master = uix) then
     2049          Un[i].Loc := -2;
     2050    UpdateUnitMap(FromLoc);
     2051
     2052    if Mode >= moMovie then { show move in interface modules }
    12522053    begin
    1253     ShowShipChange.Ship1Change[i]:=-Parts[i];
    1254     if pCapturer>=0 then
    1255       begin
    1256       ShowShipChange.Ship2Change[i]:=Parts[i];
    1257       inc(GShip[pCapturer].Parts[i], Parts[i]);
    1258       end;
    1259     Parts[i]:=0;
     2054      ShowMove.EndHealth := MoveInfo.EndHealth;
     2055      ShowMove.EndHealthDef := -1;
     2056      if Master >= 0 then
     2057        if Model[Un[Master].mix].Domain = dAir then
     2058          ShowMove.Flags := ShowMove.Flags or umPlaneUnloading
     2059        else
     2060          ShowMove.Flags := ShowMove.Flags or umShipUnloading;
     2061      if MoveInfo.ToMaster >= 0 then
     2062        if Model[Un[MoveInfo.ToMaster].mix].Domain = dAir then
     2063          ShowMove.Flags := ShowMove.Flags or umPlaneLoading
     2064        else
     2065          ShowMove.Flags := ShowMove.Flags or umShipLoading;
     2066      for p1 := 0 to nPl - 1 do
     2067        if (1 shl p1 and GWatching <> 0) and ((p1 <> p) or (bix[p1] = bixTerm))
     2068        then
     2069        begin
     2070          if PModel.Cap[mcStealth] > 0 then
     2071            MinLevel := lObserveSuper
     2072          else if PModel.Cap[mcSub] > 0 then
     2073            MinLevel := lObserveAll
     2074          else
     2075            MinLevel := lObserveUnhidden;
     2076          SeeFrom := (p1 = p) or (ObserveLevel[FromLoc] shr (2 * p1) and
     2077            3 >= MinLevel);
     2078          SeeTo := (p1 = p) or (ObserveLevel[ToLoc] shr (2 * p1) and
     2079            3 >= MinLevel);
     2080          if SeeFrom and SeeTo then
     2081          begin
     2082            TellAboutModel(p1, p, mix);
     2083            if p1 = p then
     2084              ShowMove.emix := -1
     2085            else
     2086              ShowMove.emix := emixSafe(p1, p, mix);
     2087            if MoveInfo.MoveType = mtCapture then
     2088              CallPlayer(cShowCapturing, p1, ShowMove)
     2089            else
     2090              CallPlayer(cShowMoving, p1, ShowMove);
     2091          end
     2092          else if SeeFrom then
     2093            CallPlayer(cShowUnitChanged, p1, FromLoc);
     2094        end;
    12602095    end;
    1261   if Mode>=moMovie then
     2096
     2097    if MoveInfo.MoveType <> mtSpyMission then
     2098      Loc := ToLoc;
     2099    if TroopLoad + AirLoad > 0 then
     2100      for i := 0 to nUn - 1 do
     2101        if Un[i].Loc = -2 then
     2102          Un[i].Loc := ToLoc;
     2103
     2104    ExtDiscover := false;
     2105    nUpdateLoc := 0;
     2106    if MoveInfo.MoveType = mtCapture then
    12622107    begin
    1263     if pCapturer>=0 then ShowShipChange.Reason:=scrCapture
    1264     else ShowShipChange.Reason:=scrDestruction;
    1265     ShowShipChange.Ship1Owner:=p;
    1266     ShowShipChange.Ship2Owner:=pCapturer;
    1267     for p1:=0 to nPl-1 do if 1 shl p1 and (GAlive or GWatching)<>0 then
    1268       begin
    1269       move(GShip,RW[p1].Ship,SizeOf(GShip));
    1270       if 1 shl p1 and GWatching<>0 then
    1271         CallPlayer(cShowShipChange,p1,ShowShipChange);
    1272       end;
    1273     end
    1274   end
    1275 end;
    1276 
    1277 procedure DestroyCity_TellPlayers(p,cix: integer; SaveUnits: boolean);
    1278 begin
    1279 if RW[p].City[cix].built[imSpacePort]>0 then
    1280   DestroySpacePort_TellPlayers(p,-1);
    1281 DestroyCity(p,cix,SaveUnits);
    1282 end;
    1283 
    1284 procedure ChangeCityOwner_TellPlayers(pOld,cixOld,pNew: integer);
    1285 begin
    1286 if RW[pOld].City[cixOld].built[imSpacePort]>0 then
    1287   if RW[pNew].NatBuilt[imSpacePort]>0 then
    1288     DestroySpacePort_TellPlayers(pOld,pNew)
    1289   else DestroySpacePort_TellPlayers(pOld,-1);
    1290 ChangeCityOwner(pOld,cixOld,pNew);
    1291 end;
    1292 
    1293 procedure CheckWin(p: integer);
    1294 var
    1295 i: integer;
    1296 ShipComplete: boolean;
    1297 begin
    1298 ShipComplete:=true;
    1299 for i:=0 to nShipPart-1 do
    1300   if GShip[p].Parts[i]<ShipNeed[i] then ShipComplete:=false;
    1301 if ShipComplete then GWinner:=GWinner or 1 shl p; // game won!
    1302 end;
    1303 
    1304 procedure BeforeTurn;
    1305 var
    1306 i,p1,uix,cix,V21,Loc1,Cost,Job0,nAlive,nAppliers,ad,
    1307   OldLoc,SiegedTiles,nUpdateLoc: integer;
    1308 UpdateLoc: array[0..numax-1] of integer;
    1309 Radius: TVicinity21Loc;
    1310 ShowShipChange: TShowShipChange;
    1311 TribeExtinct, JobDone, MirBuilt: boolean;
    1312 begin
    1313 {$IFOPT O-}assert(1 shl pTurn and InvalidTreatyMap=0);{$ENDIF}
    1314 assert(1 shl pTurn and (GAlive or GWatching)<>0);
    1315 if (1 shl pTurn and GAlive=0) and (Difficulty[pTurn]>0) then
    1316   exit;
    1317 
    1318 if (GWonder[woGrLibrary].EffectiveOwner=pTurn) and (GWinner=0) then
    1319   begin // check great library effect
    1320   nAlive:=0;
    1321   for p1:=0 to nPl-1 do if 1 shl p1 and GAlive<>0 then inc(nAlive);
    1322   for ad:=0 to nAdv-5 do if RW[pTurn].Tech[ad]<tsSeen then
    1323     begin
    1324     nAppliers:=0;
    1325     for p1:=0 to nPl-1 do
    1326       if (p1<>pTurn) and (1 shl p1 and GAlive<>0)
    1327         and (RW[p1].Tech[ad]>=tsApplicable) then
    1328         inc(nAppliers);
    1329     if nAppliers*2>nAlive then
    1330       begin
    1331       SeeTech(pTurn,ad);
    1332       inc(nTech[pTurn]);
    1333       if Mode>=moMovie then
    1334         CallPlayer(cShowGreatLibTech,pTurn,ad);
    1335         // do not call CallPlayer(pTurn) while map is invalid
    1336       end;
    1337     end;
    1338   end;
    1339 
    1340 MaskD(ObserveLevel,MapSize,not Cardinal(3 shl (2*pTurn)));
    1341 if Mode>moLoading_Fast then
    1342   MaskD(RW[pTurn].Map^,MapSize,not Cardinal(fUnit or fHiddenUnit or fStealthUnit
    1343     or fObserved or fSpiedOut or fOwned or fOwnZoCUnit or fInEnemyZoC));
    1344 RW[pTurn].nEnemyUn:=0;
    1345 
    1346 MirBuilt:=false;
    1347 if (Difficulty[pTurn]>0) and (GWinner=0) then with RW[pTurn] do
    1348   begin
    1349   if nCity>0 then for p1:=0 to nPl-1 do
    1350     if GTurn=EvaStart[p1]+PeaceEvaTurns then
    1351       begin // peace contract -- remove all units from p1's territory
    1352       Loc1:=City[0].Loc; // search destination for homeless units
    1353       for cix:=1 to nCity-1 do
    1354         if (City[cix].Loc>=0) and ((Loc1<0) or (City[cix].Built[imPalace]>0)) then
    1355           Loc1:=City[cix].Loc;
    1356       for uix:=0 to nUn-1 do with Un[uix] do
    1357         if (Loc>=0) and (Model[mix].Kind<>mkDiplomat)
    1358           and ((Home>=0) or (Loc1>=0))
    1359           and (RealMap[Loc] shr 27=Cardinal(p1)) then
     2108      assert(Occupant[ToLoc] < 0);
     2109      for uix1 := 0 to RW[MoveInfo.Defender].nUn - 1 do
     2110        with RW[MoveInfo.Defender].Un[uix1] do
     2111          if (Loc >= 0) and (Home = MoveInfo.Dcix) then
    13602112          begin
    1361           OldLoc:=Loc;
    1362           if Master>=0 then
    1363             begin // transport unload
    1364             if Model[mix].Domain=dAir then dec(Un[Master].AirLoad)
    1365             else dec(Un[Master].TroopLoad);
    1366             Master:=-1;
    1367             end
    1368           else FreeUnit(pTurn,uix);
    1369 
    1370           if Home>=0 then Loc:=City[Home].Loc
    1371           else Loc:=Loc1;
    1372           PlaceUnit(pTurn,uix);
    1373           UpdateUnitMap(OldLoc);
    1374           UpdateUnitMap(Loc);
    1375           Flags:=Flags or unWithdrawn;
    1376           Happened:=Happened or phPeaceEvacuation;
    1377           end
    1378       end;
    1379 
    1380   if Mode>=moMovie then
    1381     fillchar(ShowShipChange,sizeof(ShowShipChange),0);
    1382   TribeExtinct:=true;
    1383   nUpdateLoc:=0;
    1384   for cix:=0 to nCity-1 do with City[cix] do if Loc>=0 then
    1385     begin {next turn for all cities - city loop 1}
    1386 //    if ServerVersion[pTurn]>=$000EF0 then
    1387 //      Flags:=Flags and (chFounded or chCaptured or chProductionSabotaged or chDisorder)
    1388 //    else Flags:=Flags and (chCaptured or chProductionSabotaged or chDisorder);
    1389     // check for siege
    1390     SiegedTiles:=0;
    1391     V21_to_Loc(Loc,Radius);
    1392     for V21:=1 to 26 do if Tiles and (1 shl V21) and not (1 shl CityOwnTile)<>0 then
    1393       begin
    1394       Loc1:=Radius[V21];
    1395       assert((Loc1>=0) and (Loc1<MapSize) and (UsedByCity[Loc1]=Loc));
    1396       p1:=RealMap[Loc1] shr 27;
    1397       if (RealMap[Loc1] and fCity<>0)
    1398         or (p1<nPl) and (p1<>pTurn) and (RW[pTurn].Treaty[p1]>=trPeace)
    1399         or (ZoCMap[Loc1]>0) and (Occupant[Loc1]<>pTurn)
    1400         and (Treaty[Occupant[Loc1]]<trPeace) then
     2113            UpdateLoc[nUpdateLoc] := Loc;
     2114            inc(nUpdateLoc)
     2115          end;
     2116      // unit will be removed -- remember position and update for all players
     2117
     2118      if (RW[MoveInfo.Defender].City[MoveInfo.Dcix].Size > 2) and (nCity < ncmax)
     2119      then
     2120      begin // city captured
     2121        ChangeCityOwner_TellPlayers(MoveInfo.Defender, MoveInfo.Dcix, p);
     2122        City[nCity - 1].Flags := CaptureTurns shl 16;
     2123        CityShrink(p, nCity - 1);
     2124        if Mode = moPlaying then
     2125          with RW[p].City[nCity - 1] do
     2126          begin
     2127            // SavedResourceWeights[nCity-1]:=ResourceWeights;
     2128            SavedTiles[nCity - 1] := Tiles;
     2129          end;
     2130        ExtDiscover := true;
     2131
     2132        // Temple of Zeus effect
     2133        if GWonder[woZeus].EffectiveOwner = p then
    14012134        begin
    1402         Tiles:=Tiles and not (1 shl V21);
    1403         UsedByCity[Loc1]:=-1;
    1404         Flags:=Flags or chSiege;
    1405         inc(SiegedTiles);
    1406         end;
    1407       end;
    1408     while SiegedTiles>0 do // replace sieged tiles
    1409       begin
    1410       if not AddBestCityTile(pTurn,cix) then break;
    1411       dec(SiegedTiles);
    1412       end;
    1413 
    1414     if Flags and chFounded=0 then
    1415       begin
    1416 //      CollectCityResources(pTurn,cix); // old style
    1417 
    1418       if CityTurn(pTurn,cix) then
    1419         TribeExtinct:=false
    1420       else
    1421         begin // city is erased
    1422         RemoveDomainUnits(dSea,pTurn,Loc);
    1423         RemoveDomainUnits(dAir,pTurn,Loc);
    1424         Map[Loc]:=Map[Loc] and not fCity; // !!! do this in inner core
    1425         UpdateLoc[nUpdateLoc]:=Loc;
    1426         inc(nUpdateLoc);
    1427         DestroyCity_TellPlayers(pTurn,cix,true);
    1428         end;
    1429 
    1430       if (Flags and chProduction<>0) and (Project0 and cpImp<>0) then
    1431         begin
    1432         if Project0 and cpIndex=woMir then // MIR completed
    1433           MirBuilt:=true
    1434         else if Project0 and cpIndex=woManhattan then
    1435           GColdWarStart:=GTurn
    1436         else if Imp[Project0 and cpIndex].Kind=ikShipPart then {ship parts produced}
    1437           inc(ShowShipChange.Ship1Change[Project0 and cpIndex-imShipComp]);
    1438         end
    1439       end
    1440     end;{city loop 1}
    1441   if nUpdateLoc>0 then
    1442     begin
    1443     CheckBorders(-1,pTurn);
    1444     for i:=0 to nUpdateLoc-1 do UpdateUnitMap(UpdateLoc[i],true);
    1445     if Mode>=moMovie then
    1446       for p1:=0 to nPl-1 do
    1447         if (1 shl p1 and GWatching<>0) and (p1<>pTurn) then
    1448           for i:=0 to nUpdateLoc-1 do
    1449             if ObserveLevel[UpdateLoc[i]] shr (2*p1) and 3>=lObserveUnhidden then
    1450               CallPlayer(cShowCityChanged,p1,UpdateLoc[i]);
    1451     end;
    1452 
    1453   for uix:=0 to nUn-1 do with Un[uix] do if Loc>=0 then
    1454     begin // unit loop 2
    1455     if Health<100 then Recover(pTurn,uix);
    1456 
    1457     if Flags and unMountainDelay<>0 then
    1458       begin
    1459       Movement:=0;
    1460       Flags:=Flags and not unMountainDelay
    1461       end
    1462     else Movement:=UnitSpeed(pTurn,mix,Health); {refresh movement}
    1463 
    1464     assert(Loc>=0);
    1465     if Model[mix].Kind<>mkDiplomat then
    1466       begin // check treaty violation
    1467       p1:=RealMap[Loc] shr 27;
    1468       if (p1<nPl) and (p1<>pTurn) and (Treaty[p1]>=trPeace) then
    1469         begin
    1470         if (Job in [jCity,jPillage,jClear,jAfforest,jTrans])
    1471           or (Job in [jIrr,jMine,jFort,jBase]) and (RealMap[Loc] and fTerImp<>0) then
    1472           Job:=jNone;
    1473         if (GTurn>EvaStart[p1]+PeaceEvaTurns) and (Treaty[p1]<>trAlliance) then
    1474           begin
    1475           EvaStart[p1]:=GTurn;
    1476           Happened:=Happened or phPeaceViolation;
    1477           if Mode>=moMovie then
    1478             CallPlayer(cShowPeaceViolation,p1,pTurn);
    1479           end;
    1480         end;
    1481       end;
    1482 
    1483     if ServerVersion[pTurn]>=$000EF0 then
    1484       begin
    1485       if (Health<=0) or TribeExtinct then RemoveUnit_UpdateMap(pTurn,uix);
    1486       end
    1487     end;
    1488 
    1489   if ServerVersion[pTurn]<$000EF0 then
    1490     for uix:=0 to nUn-1 do with Un[uix] do if Loc>=0 then
    1491       begin // unit loop 3
    1492       Loc1:=Loc;
    1493       Job0:=Job;
    1494       if Job<>jNone then JobDone:=Work(pTurn,uix);
    1495         {settlers do terrain improvement jobs}
    1496       if (Health<=0) or TribeExtinct then RemoveUnit_UpdateMap(pTurn,uix);
    1497 
    1498       if (Job0=jCity) and JobDone then // new city
    1499         begin
    1500         AddBestCityTile(pTurn,RW[pTurn].nCity-1);
    1501         UpdateUnitMap(Loc1,true);
    1502         if Mode>=moMovie then // tell enemies
    1503           for p1:=0 to nPl-1 do
    1504             if (1 shl p1 and GWatching<>0) and (p1<>pTurn)
    1505               and (ObserveLevel[Loc1] and (3 shl (2*p1))>0) then
    1506                 CallPlayer(cShowCityChanged,p1,Loc1);
    1507         end
    1508       end;
    1509 
    1510   {pollution - city loop 3}
    1511   for cix:=0 to nCity-1 do with City[cix] do
    1512     if (Loc>=0) and (Pollution>=MaxPollution) then
    1513       Pollute(pTurn,cix);
    1514 
    1515   CompactLists(pTurn);
    1516   if (nUn=0) and (nCity=0) then
    1517     begin // nation made extinct
    1518     Happened:=Happened or phExtinct;
    1519     GAlive:=GAlive and not (1 shl pTurn);
    1520     Stat[stPop,pTurn,GTurn]:=0;
    1521     Stat[stMil,pTurn,GTurn]:=0;
    1522     Stat[stScience,pTurn,GTurn]:=0;
    1523     Stat[stExplore,pTurn,GTurn]:=0;
    1524     Stat[stTerritory,pTurn,GTurn]:=0;
    1525     Stat[stWork,pTurn,GTurn]:=0;
    1526     for p1:=0 to nPl-1 do if 1 shl p1 and (GAlive or GWatching)<>0 then
    1527       begin
    1528       if p1<>pTurn then
    1529         begin
    1530         GiveCivilReport(p1, pTurn);
    1531         if (GTestFlags and tfUncover<>0) or (Difficulty[p1]=0)
    1532           or (RW[p1].Treaty[pTurn]=trAlliance) then
    1533           GiveMilReport(p1, pTurn);
    1534         end;
    1535       with RW[p1] do
    1536         begin
    1537         Alive:=GAlive;
    1538         for Loc1:=0 to MapSize-1 do
    1539           if Territory[Loc1]=pTurn then // remove territory of extinct nation from player maps
     2135          GiveCivilReport(p, MoveInfo.Defender);
     2136          for i := 0 to nAdv - 1 do
     2137            if not(i in FutureTech) and (RW[p].Tech[i] < tsSeen) and
     2138              (RW[MoveInfo.Defender].Tech[i] >= tsApplicable) then
    15402139            begin
    1541             Territory[Loc1]:=-1;
    1542             Map[Loc1]:=Map[Loc1] and not fPeace
     2140              Happened := Happened or phStealTech;
     2141              GStealFrom := MoveInfo.Defender;
     2142              Break
    15432143            end
    15442144        end;
    1545       end;
    1546     exit
    1547     end;
    1548 
    1549   // check research
    1550   Cost:=TechCost(pTurn);
    1551   if GTestFlags and tfImmAdvance<>0 then Research:=Cost;
    1552   if (Happened and phTech=0) and (Research>=Cost) then
    1553     begin
    1554     if ResearchTech=adMilitary then EnableDevModel(pTurn) {new Unit class initiated}
    1555     else if ResearchTech>=0 then
    1556       DiscoverTech(pTurn,ResearchTech);
    1557 
    1558     dec(Research,Cost);
    1559     Happened:=Happened or phTech;
    1560     ResearchTech:=-1
    1561     end
    1562   else if (ResearchTech=-2) and (nCity>0) then
    1563     begin
    1564     Happened:=Happened or phTech;
    1565     ResearchTech:=-1
    1566     end;
    1567 
    1568   if Credibility<MaxCredibility then
    1569     for p1:=0 to nPl-1 do
    1570       if (p1<>pTurn) and (1 shl p1 and GAlive<>0)
    1571         and (Treaty[p1]>=trPeace) then
    1572         begin inc(Credibility); Break end;
    1573 
    1574   if GWinner=0 then CheckWin(pTurn);
    1575   if (Mode>=moMovie) and (GWinner=0) and ((ShowShipChange.Ship1Change[0]>0)
    1576     or (ShowShipChange.Ship1Change[1]>0) or (ShowShipChange.Ship1Change[2]>0)) then
    1577     begin
    1578     ShowShipChange.Reason:=scrProduction;
    1579     ShowShipChange.Ship1Owner:=pTurn;
    1580     ShowShipChange.Ship2Owner:=-1;
    1581     for p1:=0 to nPl-1 do
    1582       if (p1<>pTurn) and (1 shl p1 and (GAlive or GWatching)<>0) then
    1583         begin
    1584         move(GShip,RW[p1].Ship,SizeOf(GShip));
    1585         if 1 shl p1 and GWatching<>0 then
    1586           CallPlayer(cShowShipChange,p1,ShowShipChange);
    1587         end
    1588     end;
    1589   if WinOnAlone and (GAlive and not (1 shl pTurn or 1)=0) then
    1590     GWinner:=1 shl pTurn; // break if only one nation left
    1591 
    1592   if GTurn=AnarchyStart+AnarchyTurns then
    1593     begin
    1594     AnarchyStart:=-AnarchyTurns-1;
    1595     Government:=gDespotism;
    1596     for p1:=0 to nPl-1 do if (p1<>pTurn) and ((GAlive or GWatching) and (1 shl p1)<>0) then
    1597       RW[p1].EnemyReport[pTurn].Government:=gDespotism;
    1598     inc(Happened,phChangeGov)
    1599     end;
    1600   end; // if Difficulty[pTurn]>0
    1601 
    1602 if (pTurn=0) and (GWinner>0) then
    1603   begin // game over, give world map and all reports to player 0
    1604   DiscoverAll(pTurn,lObserveSuper);
    1605   for p1:=1 to nPl-1 do if 1 shl p1 and GAlive<>0 then
    1606     begin
    1607     if RW[pTurn].Treaty[p1]<trNone then
    1608       begin
    1609       RW[pTurn].Treaty[p1]:=trNone;
    1610       RW[p1].Treaty[pTurn]:=trNone;
    1611       end;
    1612     GiveCivilReport(pTurn,p1);
    1613     GiveMilReport(pTurn,p1);
    1614     end;
    1615   end
    1616 else
    1617   begin
    1618   // show observed areas
    1619   if (GTestFlags and tfUncover<>0) or (Difficulty[pTurn]=0) then {supervisor - all tiles visible}
    1620     begin
    1621     if (bix[pTurn]<>bixNoTerm)
    1622       and ((Difficulty[pTurn]>0) or (Mode>moLoading_Fast)) then
    1623       DiscoverAll(pTurn,lObserveSuper)
    1624     end
    1625   else
    1626     begin
    1627     DiscoverViewAreas(pTurn);
    1628     if MirBuilt then
    1629       DiscoverAll(pTurn,lObserveUnhidden)
    1630     end
    1631   end;
    1632 //CheckContact;
    1633 end; {BeforeTurn}
    1634 
    1635 procedure AfterTurn;
    1636 var
    1637 cix,uix,p1,Loc1,Job0: integer;
    1638 JobDone: boolean;
    1639 begin
    1640 with RW[pTurn] do
    1641   begin
    1642   for cix:=0 to nCity-1 do if City[cix].Loc>=0 then
    1643     begin
    1644 //    City[cix].Flags:=City[cix].Flags and not chProductionSabotaged;
    1645     City[cix].Flags:=City[cix].Flags and (chCaptured or chDisorder);
    1646     CollectCityResources(pTurn,cix); // new style
    1647     end;
    1648 
    1649   inc(Money,OracleIncome);
    1650   OracleIncome:=0;
    1651   if GWonder[woOracle].EffectiveOwner=pTurn then
    1652     begin
    1653     for p1:=0 to nPl-1 do
    1654       if (1 shl p1 and GAlive<>0)
    1655         and ((p1=pTurn) or (RW[pTurn].Treaty[p1]>trNoContact)) then
    1656         for cix:=0 to RW[p1].nCity-1 do
    1657           if (RW[p1].City[cix].Loc>=0) and (RW[p1].City[cix].Built[imTemple]>0) then
    1658             inc(OracleIncome);
    1659     end;
    1660 
    1661   if (GTestFlags and tfImmImprove=0) and (Government<>gAnarchy) then
    1662     for cix:=0 to nCity-1 do
    1663       if (City[cix].Loc>=0) and (City[cix].Flags and chCaptured=0) then
    1664         PayCityMaintenance(pTurn,cix);
    1665 
    1666   if ServerVersion[pTurn]>=$000EF0 then
    1667     begin // let settlers work
    1668     for cix:=0 to nCity-1 do
    1669       City[cix].Flags:=City[cix].Flags and not chFounded;
    1670     for uix:=0 to nUn-1 do with Un[uix] do if Loc>=0 then
    1671       begin
    1672       Loc1:=Loc;
    1673       Job0:=Job;
    1674       if Job<>jNone then JobDone:=Work(pTurn,uix);
    1675         {settlers do terrain improvement jobs}
    1676       if Health<=0 then RemoveUnit_UpdateMap(pTurn,uix);
    1677 
    1678       if (Job0=jCity) and JobDone then // new city
    1679         begin
    1680         AddBestCityTile(pTurn,RW[pTurn].nCity-1);
    1681         UpdateUnitMap(Loc1,true);
    1682         if Mode>=moMovie then // tell enemies
    1683           for p1:=0 to nPl-1 do
    1684             if (1 shl p1 and GWatching<>0) and (p1<>pTurn)
    1685               and (ObserveLevel[Loc1] and (3 shl (2*p1))>0) then
    1686                 CallPlayer(cShowCityChanged,p1,Loc1);
    1687         end
    1688       end;
    1689     end;
    1690 
    1691   for uix:=0 to nUn-1 do with Un[uix] do if Loc>=0 then
    1692     begin {next turn for all units}
    1693     if Model[mix].Domain=dAir then
    1694       if (Master>=0) or (RealMap[Loc] and fCity<>0)
    1695         or (RealMap[Loc] and fTerImp=tiBase) then
    1696         begin
    1697         Fuel:=Model[mix].Cap[mcFuel];
    1698         Flags:=Flags or unBombsLoaded
    1699         end
    1700       else if Model[mix].Kind=mkSpecial_Glider then {glider}
    1701         begin
    1702         if RealMap[Loc] and fTerrain<fGrass then
    1703           begin
    1704           RemoveUnit_UpdateMap(pTurn,uix); // unit lost
    1705           Happened:=Happened or phGliderLost
    1706           end
    1707         end
    1708       else
    1709         begin
    1710         dec(Fuel);
    1711         if Fuel<0 then
    1712           begin
    1713           RemoveUnit_UpdateMap(pTurn,uix); // unit lost
    1714           Happened:=Happened or phPlaneLost
    1715           end
    1716         end
    1717     else if (Master<0) and (Movement>0) then // check HostileDamage
    1718       begin
    1719       Health:=Health-HostileDamage(pTurn,mix,Loc,Movement);
    1720       if Health<0 then RemoveUnit_UpdateMap(pTurn,uix);
    1721       end
    1722     end; {unit loop 1}
    1723 
    1724   for uix:=0 to nUn-1 do with Un[uix] do
    1725     begin
    1726     Flags:=Flags and not unWithdrawn;
    1727     if (Loc>=0) and (Model[mix].Domain=dGround) and (Master<0)
    1728       and ((integer(Movement)=Model[mix].Speed)
    1729       or (Model[mix].Cap[mcAcademy]>0) and (Movement*2>=Model[mix].Speed)) then
    1730       Flags:=Flags or unFortified; // fortify unmoved units
    1731     end;
    1732 
    1733   if (GTestFlags and tfUncover=0) and (Difficulty[pTurn]>0) then
    1734     begin // restrict view area to current positions
    1735     MaskD(ObserveLevel,MapSize,not Cardinal(3 shl (2*pTurn)));
    1736     if Mode>moLoading_Fast then
    1737       MaskD(RW[pTurn].Map^,MapSize,not Cardinal(fUnit or fHiddenUnit or fStealthUnit
    1738         or fObserved or fSpiedOut or fOwned or fOwnZoCUnit or fInEnemyZoC));
    1739     RW[pTurn].nEnemyUn:=0;
    1740     DiscoverViewAreas(pTurn);
    1741     end;
    1742 
    1743   if GWinner=0 then
    1744     for p1:=0 to nPl-1 do if 1 shl p1 and GAlive<>0 then
    1745       CheckWin(p1);
    1746   end;
    1747 end; //Afterturn
    1748 
    1749 procedure NextPlayer;
    1750 begin
    1751 if GTurn=0 then BeforeTurn0
    1752 else BeforeTurn;
    1753 NoLogCityTileChanges;
    1754 GenerateStat(pTurn);
    1755 Inform(pTurn);
    1756 ChangeClient;
    1757 end;
    1758 
    1759 function ExecuteMove(p,uix,ToLoc: integer;
    1760   var MoveInfo: TMoveInfo; ShowMove: TShowMove): integer;
    1761 var
    1762 i,p1,FromLoc,uix1,nUpdateLoc: integer;
    1763 MinLevel, MissionResult: Cardinal;
    1764 PModel: ^TModel;
    1765 UpdateLoc: array[0..numax-1] of integer;
    1766 SeeFrom,SeeTo,ExtDiscover: boolean;
    1767 begin
    1768 result:=0;
    1769 with RW[p],Un[uix] do
    1770   begin
    1771   PModel:=@Model[mix];
    1772   FromLoc:=Loc;
    1773 
    1774   if Master<0 then
    1775     FreeUnit(p,uix);
    1776   if (MoveInfo.MoveType in [mtMove,mtCapture]) and MoveInfo.MountainDelay then
    1777     begin Flags:=Flags or unMountainDelay; end;
    1778   Loc:=-2;
    1779   if TroopLoad+AirLoad>0 then
    1780     for i:=0 to nUn-1 do
    1781       if (Un[i].Loc>=0) and (Un[i].Master=uix) then
    1782         Un[i].Loc:=-2;
    1783   UpdateUnitMap(FromLoc);
    1784 
    1785   if Mode>=moMovie then {show move in interface modules}
    1786     begin
    1787     ShowMove.EndHealth:=MoveInfo.EndHealth;
    1788     ShowMove.EndHealthDef:=-1;
    1789     if Master>=0 then
    1790       if Model[Un[Master].mix].Domain=dAir then
    1791         ShowMove.Flags:=ShowMove.Flags or umPlaneUnloading
    1792       else ShowMove.Flags:=ShowMove.Flags or umShipUnloading;
    1793     if MoveInfo.ToMaster>=0 then
    1794       if Model[Un[MoveInfo.ToMaster].mix].Domain=dAir then
    1795         ShowMove.Flags:=ShowMove.Flags or umPlaneLoading
    1796       else ShowMove.Flags:=ShowMove.Flags or umShipLoading;
    1797     for p1:=0 to nPl-1 do
    1798       if (1 shl p1 and GWatching<>0)
    1799         and ((p1<>p) or (bix[p1]=bixTerm)) then
    1800         begin
    1801         if PModel.Cap[mcStealth]>0 then MinLevel:=lObserveSuper
    1802         else if PModel.Cap[mcSub]>0 then MinLevel:=lObserveAll
    1803         else MinLevel:=lObserveUnhidden;
    1804         SeeFrom:= (p1=p) or (ObserveLevel[FromLoc] shr (2*p1) and 3>=MinLevel);
    1805         SeeTo:= (p1=p) or (ObserveLevel[ToLoc] shr (2*p1) and 3>=MinLevel);
    1806         if SeeFrom and SeeTo then
    1807           begin
    1808           TellAboutModel(p1,p,mix);
    1809           if p1=p then ShowMove.emix:=-1
    1810           else ShowMove.emix:=emixSafe(p1,p,mix);
    1811           if MoveInfo.MoveType=mtCapture then CallPlayer(cShowCapturing,p1,ShowMove)
    1812           else CallPlayer(cShowMoving,p1,ShowMove);
    1813           end
    1814         else if SeeFrom then
    1815           CallPlayer(cShowUnitChanged,p1,FromLoc);
    1816         end;
    1817     end;
    1818 
    1819   if MoveInfo.MoveType<>mtSpyMission then
    1820     Loc:=ToLoc;
    1821   if TroopLoad+AirLoad>0 then
    1822     for i:=0 to nUn-1 do
    1823       if Un[i].Loc=-2 then Un[i].Loc:=ToLoc;
    1824 
    1825   ExtDiscover:=false;
    1826   nUpdateLoc:=0;
    1827   if MoveInfo.MoveType=mtCapture then
    1828     begin
    1829     assert(Occupant[ToLoc]<0);
    1830     for uix1:=0 to RW[MoveInfo.Defender].nUn-1 do with RW[MoveInfo.Defender].Un[uix1] do
    1831       if (Loc>=0) and (Home=MoveInfo.Dcix) then
    1832         begin UpdateLoc[nUpdateLoc]:=Loc; inc(nUpdateLoc) end;
    1833         // unit will be removed -- remember position and update for all players
    1834 
    1835     if (RW[MoveInfo.Defender].City[MoveInfo.Dcix].Size>2) and (nCity<ncmax) then
    1836       begin // city captured
    1837       ChangeCityOwner_TellPlayers(MoveInfo.Defender,MoveInfo.Dcix,p);
    1838       City[nCity-1].Flags:=CaptureTurns shl 16;
    1839       CityShrink(p,nCity-1);
    1840       if Mode=moPlaying then with RW[p].City[nCity-1] do
    1841         begin
    1842 //        SavedResourceWeights[nCity-1]:=ResourceWeights;
    1843         SavedTiles[nCity-1]:=Tiles;
    1844         end;
    1845       ExtDiscover:=true;
    1846 
    1847       // Temple of Zeus effect
    1848       if GWonder[woZeus].EffectiveOwner=p then
    1849         begin
    1850         GiveCivilReport(p,MoveInfo.Defender);
    1851         for i:=0 to nAdv-1 do
    1852           if not (i in FutureTech) and (RW[p].Tech[i]<tsSeen)
    1853             and (RW[MoveInfo.Defender].Tech[i]>=tsApplicable) then
    1854             begin
    1855             Happened:=Happened or phStealTech;
    1856             GStealFrom:=MoveInfo.Defender;
    1857             Break
    1858             end
    1859         end;
    1860       if Mode=moPlaying then LogCheckBorders(p,nCity-1,MoveInfo.Defender);
    1861       {$IFOPT O-}if Mode<moPlaying then InvalidTreatyMap:=not(1 shl p);{$ENDIF}
     2145        if Mode = moPlaying then
     2146          LogCheckBorders(p, nCity - 1, MoveInfo.Defender);
     2147{$IFOPT O-} if Mode < moPlaying then
     2148          InvalidTreatyMap := not(1 shl p); {$ENDIF}
    18622149        // territory should not be considered for the rest of the command
    18632150        // execution, because during loading a game it's incorrect before
    18642151        // subsequent sIntExpandTerritory is processed
    18652152      end
    1866     else // city destroyed
    1867       begin
    1868       DestroyCity_TellPlayers(MoveInfo.Defender,MoveInfo.Dcix,false);
    1869       CheckBorders(ToLoc,MoveInfo.Defender);
    1870       end;
    1871     RecalcPeaceMap(p);
    1872     if Mode>=moMovie then
    1873       move(GWonder,Wonder,SizeOf(GWonder));
    1874     end; {if MoveInfo.MoveType=mtCapture}
    1875 
    1876   if MoveInfo.MoveType=mtSpyMission then
     2153      else // city destroyed
     2154      begin
     2155        DestroyCity_TellPlayers(MoveInfo.Defender, MoveInfo.Dcix, false);
     2156        CheckBorders(ToLoc, MoveInfo.Defender);
     2157      end;
     2158      RecalcPeaceMap(p);
     2159      if Mode >= moMovie then
     2160        move(GWonder, Wonder, SizeOf(GWonder));
     2161    end; { if MoveInfo.MoveType=mtCapture }
     2162
     2163    if MoveInfo.MoveType = mtSpyMission then
    18772164    begin
    1878     MissionResult:=DoSpyMission(p,MoveInfo.Defender,MoveInfo.Dcix,SpyMission);
    1879     if (Mode=moPlaying) and (SpyMission=smStealForeignReports) then
    1880       CallPlayer(cShowMissionResult,p,MissionResult);
     2165      MissionResult := DoSpyMission(p, MoveInfo.Defender, MoveInfo.Dcix,
     2166        SpyMission);
     2167      if (Mode = moPlaying) and (SpyMission = smStealForeignReports) then
     2168        CallPlayer(cShowMissionResult, p, MissionResult);
    18812169    end;
    18822170
    1883   Health:=MoveInfo.EndHealth;
    1884   dec(Movement,MoveInfo.Cost);
    1885   // transport unload
    1886   if Master>=0 then
     2171    Health := MoveInfo.EndHealth;
     2172    dec(Movement, MoveInfo.Cost);
     2173    // transport unload
     2174    if Master >= 0 then
    18872175    begin
    1888     if PModel.Domain=dAir then dec(Un[Master].AirLoad)
     2176      if PModel.Domain = dAir then
     2177        dec(Un[Master].AirLoad)
     2178      else
     2179      begin
     2180        dec(Un[Master].TroopLoad);
     2181        assert(Movement <= 0);
     2182      end;
     2183      Master := -1;
     2184    end;
     2185
     2186    if (Health <= 0) or (MoveInfo.MoveType = mtSpyMission) then
     2187      RemoveUnit(p, uix) // spy mission or victim of HostileDamage
    18892188    else
    1890       begin
    1891       dec(Un[Master].TroopLoad);
    1892       assert(Movement<=0);
    1893       end;
    1894     Master:=-1;
     2189    begin // transport load
     2190      Master := MoveInfo.ToMaster;
     2191      if MoveInfo.ToMaster >= 0 then
     2192      begin
     2193        if PModel.Domain = dAir then
     2194          inc(Un[MoveInfo.ToMaster].AirLoad)
     2195        else
     2196          inc(Un[MoveInfo.ToMaster].TroopLoad);
     2197      end
     2198      else
     2199        PlaceUnit(p, uix);
    18952200    end;
    18962201
    1897   if (Health<=0) or (MoveInfo.MoveType=mtSpyMission) then
    1898     RemoveUnit(p,uix) // spy mission or victim of HostileDamage
    1899   else
    1900     begin // transport load
    1901     Master:=MoveInfo.ToMaster;
    1902     if MoveInfo.ToMaster>=0 then
    1903       begin
    1904       if PModel.Domain=dAir then inc(Un[MoveInfo.ToMaster].AirLoad)
    1905       else inc(Un[MoveInfo.ToMaster].TroopLoad);
     2202    if (MoveInfo.MoveType = mtCapture) and (nUpdateLoc > 0) then
     2203      RecalcMapZoC(p);
     2204    UpdateUnitMap(ToLoc, MoveInfo.MoveType = mtCapture);
     2205    for i := 0 to nUpdateLoc - 1 do
     2206      UpdateUnitMap(UpdateLoc[i]);
     2207    // tell about lost units of defender
     2208
     2209    if (MoveInfo.MoveType <> mtSpyMission) and (Master < 0) then
     2210    begin
     2211      if (PModel.Kind = mkDiplomat) or (PModel.Domain = dAir) or
     2212        (PModel.Cap[mcRadar] + PModel.Cap[mcCarrier] + PModel.Cap[mcAcademy] >
     2213        0) or (RealMap[ToLoc] and fTerrain = fMountains) or
     2214        (RealMap[ToLoc] and fTerImp = tiFort) or
     2215        (RealMap[ToLoc] and fTerImp = tiBase) then
     2216        ExtDiscover := true;
     2217      if (PModel.Kind = mkDiplomat) or (PModel.Cap[mcSpy] > 0) then
     2218        i := lObserveSuper
     2219      else if (PModel.Domain = dAir) or
     2220        (PModel.Cap[mcRadar] + PModel.Cap[mcCarrier] > 0) then
     2221        i := lObserveAll
     2222      else
     2223        i := lObserveUnhidden;
     2224      if ExtDiscover then
     2225      begin
     2226        if Discover21(ToLoc, p, i, true, PModel.Domain = dGround) then
     2227          result := result or rEnemySpotted;
    19062228      end
    1907     else PlaceUnit(p,uix);
     2229      else
     2230      begin
     2231        if Discover9(ToLoc, p, i, true, PModel.Domain = dGround) then
     2232          result := result or rEnemySpotted;
     2233      end;
    19082234    end;
    19092235
    1910   if (MoveInfo.MoveType=mtCapture) and (nUpdateLoc>0) then
    1911     RecalcMapZoC(p);
    1912   UpdateUnitMap(ToLoc,MoveInfo.MoveType=mtCapture);
    1913   for i:=0 to nUpdateLoc-1 do UpdateUnitMap(UpdateLoc[i]);
    1914     // tell about lost units of defender
    1915 
    1916   if (MoveInfo.MoveType<>mtSpyMission) and (Master<0) then
    1917     begin
    1918     if (PModel.Kind=mkDiplomat) or (PModel.Domain=dAir)
    1919       or (PModel.Cap[mcRadar]+PModel.Cap[mcCarrier]+PModel.Cap[mcAcademy]>0)
    1920       or (RealMap[ToLoc] and fTerrain=fMountains)
    1921       or (RealMap[ToLoc] and fTerImp=tiFort)
    1922       or (RealMap[ToLoc] and fTerImp=tiBase) then
    1923       ExtDiscover:=true;
    1924     if (PModel.Kind=mkDiplomat) or (PModel.Cap[mcSpy]>0) then
    1925       i:=lObserveSuper
    1926     else if (PModel.Domain=dAir)
    1927       or (PModel.Cap[mcRadar]+PModel.Cap[mcCarrier]>0) then
    1928       i:=lObserveAll
    1929     else i:=lObserveUnhidden;
    1930     if ExtDiscover then
    1931       begin
    1932       if Discover21(ToLoc,p,i,true, PModel.Domain=dGround) then
    1933         result:=result or rEnemySpotted;
    1934       end
    1935     else
    1936       begin
    1937       if Discover9(ToLoc,p,i,true, PModel.Domain=dGround) then
    1938         result:=result or rEnemySpotted;
    1939       end;
    1940     end;
    1941 
    1942   if Mode>=moMovie then {show after-move in interface modules}
    1943     for p1:=0 to nPl-1 do
    1944       if (1 shl p1 and GWatching<>0)
    1945         and ((p1<>p) or (bix[p1]=bixTerm)) then
     2236    if Mode >= moMovie then { show after-move in interface modules }
     2237      for p1 := 0 to nPl - 1 do
     2238        if (1 shl p1 and GWatching <> 0) and ((p1 <> p) or (bix[p1] = bixTerm))
     2239        then
    19462240        begin
    1947         if PModel.Cap[mcStealth]>0 then MinLevel:=lObserveSuper
    1948         else if PModel.Cap[mcSub]>0 then MinLevel:=lObserveAll
    1949         else MinLevel:=lObserveUnhidden;
    1950         SeeFrom:= (p1=p) or (ObserveLevel[FromLoc] shr (2*p1) and 3>=MinLevel);
    1951         SeeTo:= (p1=p) or (ObserveLevel[ToLoc] shr (2*p1) and 3>=MinLevel);
    1952         if SeeTo and (MoveInfo.MoveType=mtCapture) then
    1953           CallPlayer(cShowCityChanged,p1,ToLoc);
    1954         if SeeFrom and SeeTo then
    1955           CallPlayer(cShowAfterMove,p1,ToLoc)
    1956         else if (MoveInfo.MoveType<>mtSpyMission) and SeeTo then
    1957           CallPlayer(cShowUnitChanged,p1,ToLoc);
    1958         for i:=0 to nUpdateLoc-1 do
    1959           if ObserveLevel[UpdateLoc[i]] shr (2*p1) and 3>=lObserveUnhidden then
    1960             CallPlayer(cShowUnitChanged,p1,UpdateLoc[i]);
     2241          if PModel.Cap[mcStealth] > 0 then
     2242            MinLevel := lObserveSuper
     2243          else if PModel.Cap[mcSub] > 0 then
     2244            MinLevel := lObserveAll
     2245          else
     2246            MinLevel := lObserveUnhidden;
     2247          SeeFrom := (p1 = p) or (ObserveLevel[FromLoc] shr (2 * p1) and
     2248            3 >= MinLevel);
     2249          SeeTo := (p1 = p) or (ObserveLevel[ToLoc] shr (2 * p1) and
     2250            3 >= MinLevel);
     2251          if SeeTo and (MoveInfo.MoveType = mtCapture) then
     2252            CallPlayer(cShowCityChanged, p1, ToLoc);
     2253          if SeeFrom and SeeTo then
     2254            CallPlayer(cShowAfterMove, p1, ToLoc)
     2255          else if (MoveInfo.MoveType <> mtSpyMission) and SeeTo then
     2256            CallPlayer(cShowUnitChanged, p1, ToLoc);
     2257          for i := 0 to nUpdateLoc - 1 do
     2258            if ObserveLevel[UpdateLoc[i]] shr (2 * p1) and 3 >= lObserveUnhidden
     2259            then
     2260              CallPlayer(cShowUnitChanged, p1, UpdateLoc[i]);
    19612261        end;
    19622262  end;
    19632263end; // ExecuteMove
    19642264
    1965 function ExecuteAttack(p,uix,ToLoc: integer;
    1966   var MoveInfo: TMoveInfo; ShowMove: TShowMove): integer;
    1967 
    1968   procedure WriteBattleHistory(ToLoc, FromLoc, Attacker, Defender,
    1969     mixAttacker, mixDefender: integer; AttackerLost, DefenderLost: boolean);
     2265function ExecuteAttack(p, uix, ToLoc: integer; var MoveInfo: TMoveInfo;
     2266  ShowMove: TShowMove): integer;
     2267
     2268  procedure WriteBattleHistory(ToLoc, FromLoc, Attacker, Defender, mixAttacker,
     2269    mixDefender: integer; AttackerLost, DefenderLost: boolean);
    19702270  var
    1971   AttackerBattle, DefenderBattle: ^TBattle;
     2271    AttackerBattle, DefenderBattle: ^TBattle;
    19722272  begin
    1973   with RW[Attacker] do
     2273    with RW[Attacker] do
    19742274    begin
    1975     if nBattleHistory=0 then
    1976       ReallocMem(BattleHistory, 16*SizeOf(TBattle))
    1977     else if (nBattleHistory>=16)
    1978       and (nBattleHistory and (nBattleHistory-1)=0) then
    1979       ReallocMem(BattleHistory, nBattleHistory*(2*SizeOf(TBattle)));
    1980     AttackerBattle:=@BattleHistory[nBattleHistory];
    1981     inc(nBattleHistory);
     2275      if nBattleHistory = 0 then
     2276        ReallocMem(BattleHistory, 16 * SizeOf(TBattle))
     2277      else if (nBattleHistory >= 16) and
     2278        (nBattleHistory and (nBattleHistory - 1) = 0) then
     2279        ReallocMem(BattleHistory, nBattleHistory * (2 * SizeOf(TBattle)));
     2280      AttackerBattle := @BattleHistory[nBattleHistory];
     2281      inc(nBattleHistory);
    19822282    end;
    1983   with RW[Defender] do
     2283    with RW[Defender] do
    19842284    begin
    1985     if nBattleHistory=0 then
    1986       ReallocMem(BattleHistory, 16*SizeOf(TBattle))
    1987     else if (nBattleHistory>=16)
    1988       and (nBattleHistory and (nBattleHistory-1)=0) then
    1989       ReallocMem(BattleHistory, nBattleHistory*(2*SizeOf(TBattle)));
    1990     DefenderBattle:=@BattleHistory[nBattleHistory];
    1991     inc(nBattleHistory);
     2285      if nBattleHistory = 0 then
     2286        ReallocMem(BattleHistory, 16 * SizeOf(TBattle))
     2287      else if (nBattleHistory >= 16) and
     2288        (nBattleHistory and (nBattleHistory - 1) = 0) then
     2289        ReallocMem(BattleHistory, nBattleHistory * (2 * SizeOf(TBattle)));
     2290      DefenderBattle := @BattleHistory[nBattleHistory];
     2291      inc(nBattleHistory);
    19922292    end;
    1993   AttackerBattle.Enemy:=Defender;
    1994   AttackerBattle.Flags:=0;
    1995   AttackerBattle.Turn:=GTurn;
    1996   AttackerBattle.mix:=mixAttacker;
    1997   AttackerBattle.mixEnemy:=mixDefender;
    1998   AttackerBattle.ToLoc:=ToLoc;
    1999   AttackerBattle.FromLoc:=FromLoc;
    2000   DefenderBattle.Enemy:=Attacker;
    2001   DefenderBattle.Flags:=bhEnemyAttack;
    2002   DefenderBattle.Turn:=GTurn;
    2003   DefenderBattle.mix:=mixDefender;
    2004   DefenderBattle.mixEnemy:=mixAttacker;
    2005   DefenderBattle.ToLoc:=ToLoc;
    2006   DefenderBattle.FromLoc:=FromLoc;
    2007   if AttackerLost then
     2293    AttackerBattle.Enemy := Defender;
     2294    AttackerBattle.Flags := 0;
     2295    AttackerBattle.Turn := GTurn;
     2296    AttackerBattle.mix := mixAttacker;
     2297    AttackerBattle.mixEnemy := mixDefender;
     2298    AttackerBattle.ToLoc := ToLoc;
     2299    AttackerBattle.FromLoc := FromLoc;
     2300    DefenderBattle.Enemy := Attacker;
     2301    DefenderBattle.Flags := bhEnemyAttack;
     2302    DefenderBattle.Turn := GTurn;
     2303    DefenderBattle.mix := mixDefender;
     2304    DefenderBattle.mixEnemy := mixAttacker;
     2305    DefenderBattle.ToLoc := ToLoc;
     2306    DefenderBattle.FromLoc := FromLoc;
     2307    if AttackerLost then
    20082308    begin
    2009     AttackerBattle.Flags:=AttackerBattle.Flags or bhMyUnitLost;
    2010     DefenderBattle.Flags:=DefenderBattle.Flags or bhEnemyUnitLost;
     2309      AttackerBattle.Flags := AttackerBattle.Flags or bhMyUnitLost;
     2310      DefenderBattle.Flags := DefenderBattle.Flags or bhEnemyUnitLost;
    20112311    end;
    2012   if DefenderLost then
     2312    if DefenderLost then
    20132313    begin
    2014     AttackerBattle.Flags:=AttackerBattle.Flags or bhEnemyUnitLost;
    2015     DefenderBattle.Flags:=DefenderBattle.Flags or bhMyUnitLost;
     2314      AttackerBattle.Flags := AttackerBattle.Flags or bhEnemyUnitLost;
     2315      DefenderBattle.Flags := DefenderBattle.Flags or bhMyUnitLost;
    20162316    end;
    20172317  end;
    20182318
    20192319var
    2020 i,p1,FromLoc,uix1,nUpdateLoc,ExpGain, ExpelToLoc,cix1: integer;
    2021 PModel: ^TModel;
    2022 UpdateLoc: array[0..numax-1] of integer;
    2023 LoseCityPop,CityDestroyed,SeeFrom,SeeTo,ZoCDefenderDestroyed: boolean;
     2320  i, p1, FromLoc, uix1, nUpdateLoc, ExpGain, ExpelToLoc, cix1: integer;
     2321  PModel: ^TModel;
     2322  UpdateLoc: array [0 .. numax - 1] of integer;
     2323  LoseCityPop, CityDestroyed, SeeFrom, SeeTo, ZoCDefenderDestroyed: boolean;
    20242324begin
    2025 result:=0;
    2026 with RW[p].Un[uix] do
     2325  result := 0;
     2326  with RW[p].Un[uix] do
    20272327  begin
    2028   PModel:=@RW[p].Model[mix];
    2029   FromLoc:=Loc;
    2030 
    2031   ShowMove.EndHealth:=MoveInfo.EndHealth;
    2032   ShowMove.EndHealthDef:=MoveInfo.EndHealthDef;
    2033   if MoveInfo.MoveType=mtAttack then
    2034     WriteBattleHistory(ToLoc, FromLoc, p, MoveInfo.Defender, mix,
    2035       RW[MoveInfo.Defender].Un[MoveInfo.Duix].mix,
    2036       MoveInfo.EndHealth<=0, MoveInfo.EndHealthDef<=0);
    2037 
    2038 { if RW[p].Treaty[MoveInfo.Defender]=trCeaseFire then
    2039     begin
    2040     if Mode>=moMovie then
     2328    PModel := @RW[p].Model[mix];
     2329    FromLoc := Loc;
     2330
     2331    ShowMove.EndHealth := MoveInfo.EndHealth;
     2332    ShowMove.EndHealthDef := MoveInfo.EndHealthDef;
     2333    if MoveInfo.MoveType = mtAttack then
     2334      WriteBattleHistory(ToLoc, FromLoc, p, MoveInfo.Defender, mix,
     2335        RW[MoveInfo.Defender].Un[MoveInfo.Duix].mix, MoveInfo.EndHealth <= 0,
     2336        MoveInfo.EndHealthDef <= 0);
     2337
     2338    { if RW[p].Treaty[MoveInfo.Defender]=trCeaseFire then
     2339      begin
     2340      if Mode>=moMovie then
    20412341      CallPlayer(cShowCancelTreaty,MoveInfo.Defender,p);
    2042     CancelTreaty(p,MoveInfo.Defender)
    2043     end;}
    2044   if Mode>=moMovie then {show attack in interface modules}
    2045     for p1:=0 to nPl-1 do
    2046       if (1 shl p1 and GWatching<>0)
    2047         and ((p1<>p) or (bix[p1]=bixTerm)) then
     2342      CancelTreaty(p,MoveInfo.Defender)
     2343      end; }
     2344    if Mode >= moMovie then { show attack in interface modules }
     2345      for p1 := 0 to nPl - 1 do
     2346        if (1 shl p1 and GWatching <> 0) and ((p1 <> p) or (bix[p1] = bixTerm))
     2347        then
    20482348        begin
    2049         SeeFrom:= ObserveLevel[FromLoc] shr (2*p1) and 3>=lObserveUnhidden;
    2050         SeeTo:= ObserveLevel[ToLoc] shr (2*p1) and 3>=lObserveUnhidden;
    2051         if SeeFrom and SeeTo then
     2349          SeeFrom := ObserveLevel[FromLoc] shr (2 * p1) and
     2350            3 >= lObserveUnhidden;
     2351          SeeTo := ObserveLevel[ToLoc] shr (2 * p1) and 3 >= lObserveUnhidden;
     2352          if SeeFrom and SeeTo then
    20522353          begin
    2053           TellAboutModel(p1,p,mix);
    2054           if p1=p then ShowMove.emix:=-1
    2055           else ShowMove.emix:=emixSafe(p1,p,mix);
    2056           CallPlayer(cShowAttacking,p1,ShowMove);
     2354            TellAboutModel(p1, p, mix);
     2355            if p1 = p then
     2356              ShowMove.emix := -1
     2357            else
     2358              ShowMove.emix := emixSafe(p1, p, mix);
     2359            CallPlayer(cShowAttacking, p1, ShowMove);
    20572360          end;
    20582361        end;
    20592362
    2060   LoseCityPop:=false;
    2061   if (RealMap[ToLoc] and fCity<>0) and
    2062     ((MoveInfo.MoveType=mtAttack) and (MoveInfo.EndHealthDef<=0)
    2063     or (MoveInfo.MoveType=mtBombard) and (BombardmentDestroysCity or (RW[MoveInfo.Defender].City[MoveInfo.Dcix].Size>2))) then
    2064     case PModel.Domain of
    2065       dGround: LoseCityPop:= (PModel.Cap[mcArtillery]>0)
    2066         or (RW[MoveInfo.Defender].City[MoveInfo.Dcix].Built[imWalls]=0)
    2067         and (Continent[ToLoc]<>GrWallContinent[MoveInfo.Defender]);
    2068       dSea: LoseCityPop:= RW[MoveInfo.Defender].City[MoveInfo.Dcix].Built[imCoastalFort]=0;
    2069       dAir: LoseCityPop:= RW[MoveInfo.Defender].City[MoveInfo.Dcix].Built[imMissileBat]=0;
    2070       end;
    2071   CityDestroyed:=LoseCityPop and (RW[MoveInfo.Defender].City[MoveInfo.Dcix].Size<=2);
    2072 
    2073   if MoveInfo.MoveType=mtBombard then
     2363    LoseCityPop := false;
     2364    if (RealMap[ToLoc] and fCity <> 0) and
     2365      ((MoveInfo.MoveType = mtAttack) and (MoveInfo.EndHealthDef <= 0) or
     2366      (MoveInfo.MoveType = mtBombard) and (BombardmentDestroysCity or
     2367      (RW[MoveInfo.Defender].City[MoveInfo.Dcix].Size > 2))) then
     2368      case PModel.Domain of
     2369        dGround:
     2370          LoseCityPop := (PModel.Cap[mcArtillery] > 0) or
     2371            (RW[MoveInfo.Defender].City[MoveInfo.Dcix].built[imWalls] = 0) and
     2372            (Continent[ToLoc] <> GrWallContinent[MoveInfo.Defender]);
     2373        dSea:
     2374          LoseCityPop := RW[MoveInfo.Defender].City[MoveInfo.Dcix].built
     2375            [imCoastalFort] = 0;
     2376        dAir:
     2377          LoseCityPop := RW[MoveInfo.Defender].City[MoveInfo.Dcix].built
     2378            [imMissileBat] = 0;
     2379      end;
     2380    CityDestroyed := LoseCityPop and
     2381      (RW[MoveInfo.Defender].City[MoveInfo.Dcix].Size <= 2);
     2382
     2383    if MoveInfo.MoveType = mtBombard then
    20742384    begin
    2075     assert(Movement>=100);
    2076     if PModel.Attack=0 then Flags:=Flags and not unBombsLoaded;
    2077     dec(Movement,100)
     2385      assert(Movement >= 100);
     2386      if PModel.Attack = 0 then
     2387        Flags := Flags and not unBombsLoaded;
     2388      dec(Movement, 100)
    20782389    end
    2079   else if MoveInfo.MoveType=mtExpel then
     2390    else if MoveInfo.MoveType = mtExpel then
    20802391    begin
    2081     assert(Movement>=100);
    2082     Job:=jNone;
    2083     Flags:=Flags and not unFortified;
    2084     dec(Movement,100)
     2392      assert(Movement >= 100);
     2393      Job := jNone;
     2394      Flags := Flags and not unFortified;
     2395      dec(Movement, 100)
    20852396    end
    2086   else
     2397    else
    20872398    begin
    2088     assert(MoveInfo.MoveType=mtAttack);
    2089     if MoveInfo.EndHealth=0 then
    2090       RemoveUnit(p,uix,MoveInfo.Defender) // destroy attacker
     2399      assert(MoveInfo.MoveType = mtAttack);
     2400      if MoveInfo.EndHealth = 0 then
     2401        RemoveUnit(p, uix, MoveInfo.Defender) // destroy attacker
     2402      else
     2403      begin // update attacker
     2404        ExpGain := (Health - MoveInfo.EndHealth + 1) shr 1;
     2405        if Exp + ExpGain > (nExp - 1) * ExpCost then
     2406          Exp := (nExp - 1) * ExpCost
     2407        else
     2408          inc(Exp, ExpGain);
     2409        Health := MoveInfo.EndHealth;
     2410        Job := jNone;
     2411        if RW[MoveInfo.Defender].Model[RW[MoveInfo.Defender].Un[MoveInfo.Duix]
     2412          .mix].Domain < dAir then
     2413          Flags := Flags and not unBombsLoaded;
     2414        Flags := Flags and not unFortified;
     2415        if Movement > 100 then
     2416          dec(Movement, 100)
     2417        else
     2418          Movement := 0;
     2419      end;
     2420    end;
     2421
     2422    ZoCDefenderDestroyed := false;
     2423    nUpdateLoc := 0;
     2424    if MoveInfo.MoveType = mtExpel then
     2425      with RW[MoveInfo.Defender], Un[MoveInfo.Duix] do
     2426      begin // expel friendly unit
     2427        if Home >= 0 then
     2428          ExpelToLoc := City[Home].Loc
     2429        else
     2430        begin
     2431          ExpelToLoc := City[0].Loc; // search destination for homeless units
     2432          for cix1 := 1 to nCity - 1 do
     2433            if (City[cix1].Loc >= 0) and
     2434              ((ExpelToLoc < 0) or (City[cix1].built[imPalace] > 0)) then
     2435              ExpelToLoc := City[cix1].Loc;
     2436        end;
     2437        if ExpelToLoc >= 0 then
     2438        begin
     2439          FreeUnit(MoveInfo.Defender, MoveInfo.Duix);
     2440          Loc := ExpelToLoc;
     2441          PlaceUnit(MoveInfo.Defender, MoveInfo.Duix);
     2442          UpdateLoc[nUpdateLoc] := Loc;
     2443          inc(nUpdateLoc);
     2444          Flags := Flags or unWithdrawn;
     2445        end
     2446      end
     2447    else if (MoveInfo.MoveType = mtAttack) and (MoveInfo.EndHealthDef > 0) then
     2448      with RW[MoveInfo.Defender].Un[MoveInfo.Duix] do
     2449      begin // update defender
     2450        ExpGain := (Health - MoveInfo.EndHealthDef + 1) shr 1;
     2451        if Exp + ExpGain > (nExp - 1) * ExpCost then
     2452          Exp := (nExp - 1) * ExpCost
     2453        else
     2454          inc(Exp, ExpGain);
     2455        Health := MoveInfo.EndHealthDef;
     2456      end
    20912457    else
    2092       begin // update attacker
    2093       ExpGain:=(Health-MoveInfo.EndHealth+1) shr 1;
    2094       if Exp+ExpGain>(nExp-1)*ExpCost then Exp:=(nExp-1)*ExpCost
    2095       else inc(Exp,ExpGain);
    2096       Health:=MoveInfo.EndHealth;
    2097       Job:=jNone;
    2098       if RW[MoveInfo.Defender].Model[RW[MoveInfo.Defender].Un[MoveInfo.Duix].mix].Domain<dAir then
    2099         Flags:=Flags and not unBombsLoaded;
    2100       Flags:=Flags and not unFortified;
    2101       if Movement>100 then dec(Movement,100)
    2102       else Movement:=0;
    2103       end;
    2104     end;
    2105 
    2106   ZoCDefenderDestroyed:=false;
    2107   nUpdateLoc:=0;
    2108   if MoveInfo.MoveType=mtExpel then with RW[MoveInfo.Defender],Un[MoveInfo.Duix] do
    2109     begin // expel friendly unit
    2110     if Home>=0 then ExpelToLoc:=City[Home].Loc
    2111     else
    2112       begin
    2113       ExpelToLoc:=City[0].Loc; // search destination for homeless units
    2114       for cix1:=1 to nCity-1 do
    2115         if (City[cix1].Loc>=0) and ((ExpelToLoc<0) or (City[cix1].Built[imPalace]>0)) then
    2116           ExpelToLoc:=City[cix1].Loc;
    2117       end;
    2118     if ExpelToLoc>=0 then
    2119       begin
    2120       FreeUnit(MoveInfo.Defender,MoveInfo.Duix);
    2121       Loc:=ExpelToLoc;
    2122       PlaceUnit(MoveInfo.Defender,MoveInfo.Duix);
    2123       UpdateLoc[nUpdateLoc]:=Loc;
    2124       inc(nUpdateLoc);
    2125       Flags:=Flags or unWithdrawn;
    2126       end
    2127     end
    2128   else if (MoveInfo.MoveType=mtAttack) and (MoveInfo.EndHealthDef>0) then
    2129     with RW[MoveInfo.Defender].Un[MoveInfo.Duix] do
    2130       begin // update defender
    2131       ExpGain:=(Health-MoveInfo.EndHealthDef+1) shr 1;
    2132       if Exp+ExpGain>(nExp-1)*ExpCost then Exp:=(nExp-1)*ExpCost
    2133       else inc(Exp,ExpGain);
    2134       Health:=MoveInfo.EndHealthDef;
    2135       end
    2136   else
    21372458    begin // destroy defenders
    2138     if MoveInfo.MoveType<>mtBombard then
    2139       begin
    2140       ZoCDefenderDestroyed:=RW[MoveInfo.Defender].Model[RW[MoveInfo.Defender].Un[MoveInfo.Duix].mix].Flags and mdZOC<>0;
    2141       if ((RealMap[ToLoc] and fCity=0)
    2142         and (RealMap[ToLoc] and fTerImp<>tiBase)
    2143         and (RealMap[ToLoc] and fTerImp<>tiFort))
    2144         or LoseCityPop and (RW[MoveInfo.Defender].City[MoveInfo.Dcix].Size=2) then
    2145         RemoveAllUnits(MoveInfo.Defender,ToLoc,p) {no city, base or fortress}
    2146       else RemoveUnit(MoveInfo.Defender,MoveInfo.Duix,p);
    2147       end;
    2148 
    2149     if LoseCityPop then // city defender defeated -- shrink city
    2150       if not CityDestroyed then
    2151         CityShrink(MoveInfo.Defender,MoveInfo.Dcix)
    2152       else
     2459      if MoveInfo.MoveType <> mtBombard then
     2460      begin
     2461        ZoCDefenderDestroyed := RW[MoveInfo.Defender].Model
     2462          [RW[MoveInfo.Defender].Un[MoveInfo.Duix].mix].Flags and mdZOC <> 0;
     2463        if ((RealMap[ToLoc] and fCity = 0) and
     2464          (RealMap[ToLoc] and fTerImp <> tiBase) and
     2465          (RealMap[ToLoc] and fTerImp <> tiFort)) or LoseCityPop and
     2466          (RW[MoveInfo.Defender].City[MoveInfo.Dcix].Size = 2) then
     2467          RemoveAllUnits(MoveInfo.Defender, ToLoc, p)
     2468          { no city, base or fortress }
     2469        else
     2470          RemoveUnit(MoveInfo.Defender, MoveInfo.Duix, p);
     2471      end;
     2472
     2473      if LoseCityPop then // city defender defeated -- shrink city
     2474        if not CityDestroyed then
     2475          CityShrink(MoveInfo.Defender, MoveInfo.Dcix)
     2476        else
    21532477        begin
    2154         for uix1:=0 to RW[MoveInfo.Defender].nUn-1 do with RW[MoveInfo.Defender].Un[uix1] do
    2155           if (Loc>=0) and (Home=MoveInfo.Dcix) then
    2156             begin UpdateLoc[nUpdateLoc]:=Loc; inc(nUpdateLoc) end;
    2157             // unit will be removed -- remember position and update for all players
    2158         DestroyCity_TellPlayers(MoveInfo.Defender,MoveInfo.Dcix,false);
    2159         CheckBorders(ToLoc,MoveInfo.Defender);
    2160         RecalcPeaceMap(p);
     2478          for uix1 := 0 to RW[MoveInfo.Defender].nUn - 1 do
     2479            with RW[MoveInfo.Defender].Un[uix1] do
     2480              if (Loc >= 0) and (Home = MoveInfo.Dcix) then
     2481              begin
     2482                UpdateLoc[nUpdateLoc] := Loc;
     2483                inc(nUpdateLoc)
     2484              end;
     2485          // unit will be removed -- remember position and update for all players
     2486          DestroyCity_TellPlayers(MoveInfo.Defender, MoveInfo.Dcix, false);
     2487          CheckBorders(ToLoc, MoveInfo.Defender);
     2488          RecalcPeaceMap(p);
    21612489        end;
    21622490    end;
    21632491
    2164   if CityDestroyed and (nUpdateLoc>0) then
    2165     RecalcMapZoC(p)
    2166   else if ZoCDefenderDestroyed then
    2167     RecalcV8ZoC(p,ToLoc);
    2168   UpdateUnitMap(FromLoc);
    2169   UpdateUnitMap(ToLoc,LoseCityPop);
    2170   for i:=0 to nUpdateLoc-1 do UpdateUnitMap(UpdateLoc[i]);
     2492    if CityDestroyed and (nUpdateLoc > 0) then
     2493      RecalcMapZoC(p)
     2494    else if ZoCDefenderDestroyed then
     2495      RecalcV8ZoC(p, ToLoc);
     2496    UpdateUnitMap(FromLoc);
     2497    UpdateUnitMap(ToLoc, LoseCityPop);
     2498    for i := 0 to nUpdateLoc - 1 do
     2499      UpdateUnitMap(UpdateLoc[i]);
    21712500    // tell about lost units of defender
    21722501
    2173   if Mode>=moMovie then
     2502    if Mode >= moMovie then
    21742503    begin
    2175     for i:=0 to RW[p].nEnemyModel-1 do with RW[p].EnemyModel[i] do
    2176       Lost:=Destroyed[p,Owner,mix];
    2177     for p1:=0 to nPl-1 do {show after-attack in interface modules}
    2178       if (1 shl p1 and GWatching<>0)
    2179         and ((p1<>p) or (bix[p1]=bixTerm)) then
     2504      for i := 0 to RW[p].nEnemyModel - 1 do
     2505        with RW[p].EnemyModel[i] do
     2506          Lost := Destroyed[p, Owner, mix];
     2507      for p1 := 0 to nPl - 1 do { show after-attack in interface modules }
     2508        if (1 shl p1 and GWatching <> 0) and ((p1 <> p) or (bix[p1] = bixTerm))
     2509        then
    21802510        begin
    2181         SeeFrom:= ObserveLevel[FromLoc] shr (2*p1) and 3>=lObserveUnhidden;
    2182         SeeTo:= ObserveLevel[ToLoc] shr (2*p1) and 3>=lObserveUnhidden;
    2183         if SeeTo and CityDestroyed then
    2184           CallPlayer(cShowCityChanged,p1,ToLoc); // city was destroyed
    2185         if SeeFrom and SeeTo then
     2511          SeeFrom := ObserveLevel[FromLoc] shr (2 * p1) and
     2512            3 >= lObserveUnhidden;
     2513          SeeTo := ObserveLevel[ToLoc] shr (2 * p1) and 3 >= lObserveUnhidden;
     2514          if SeeTo and CityDestroyed then
     2515            CallPlayer(cShowCityChanged, p1, ToLoc); // city was destroyed
     2516          if SeeFrom and SeeTo then
    21862517          begin
    2187           CallPlayer(cShowAfterAttack,p1,ToLoc);
    2188           CallPlayer(cShowAfterAttack,p1,FromLoc);
     2518            CallPlayer(cShowAfterAttack, p1, ToLoc);
     2519            CallPlayer(cShowAfterAttack, p1, FromLoc);
    21892520          end
    2190         else
     2521          else
    21912522          begin
    2192           if SeeTo then
    2193             CallPlayer(cShowUnitChanged,p1,ToLoc);
    2194           if SeeFrom then
    2195             CallPlayer(cShowUnitChanged,p1,FromLoc);
     2523            if SeeTo then
     2524              CallPlayer(cShowUnitChanged, p1, ToLoc);
     2525            if SeeFrom then
     2526              CallPlayer(cShowUnitChanged, p1, FromLoc);
    21962527          end;
    2197         if SeeTo and (MoveInfo.MoveType=mtExpel) and (ExpelToLoc>=0) then
    2198           CallPlayer(cShowUnitChanged,p1,ExpelToLoc);
     2528          if SeeTo and (MoveInfo.MoveType = mtExpel) and (ExpelToLoc >= 0) then
     2529            CallPlayer(cShowUnitChanged, p1, ExpelToLoc);
    21992530        end;
    22002531    end
     
    22022533end; // ExecuteAttack
    22032534
    2204 function MoveUnit(p,uix,dx,dy: integer; TestOnly: boolean): integer;
     2535function MoveUnit(p, uix, dx, dy: integer; TestOnly: boolean): integer;
    22052536var
    2206 ToLoc: integer;
    2207 MoveInfo: TMoveInfo;
    2208 ShowMove: TShowMove;
     2537  ToLoc: integer;
     2538  MoveInfo: TMoveInfo;
     2539  ShowMove: TShowMove;
    22092540begin
    2210 {$IFOPT O-}assert(1 shl p and InvalidTreatyMap=0);{$ENDIF}
    2211 with RW[p].Un[uix] do
     2541{$IFOPT O-}assert(1 shl p and InvalidTreatyMap = 0); {$ENDIF}
     2542  with RW[p].Un[uix] do
    22122543  begin
    2213   ToLoc:=dLoc(Loc,dx,dy);
    2214   if (ToLoc<0) or (ToLoc>=MapSize) then
    2215     begin result:=eInvalid; exit end;
    2216   result:=CalculateMove(p,uix,ToLoc,3-dy and 1,TestOnly,MoveInfo);
    2217   if result=eZOC_EnemySpotted then
    2218     ZOCTile:=ToLoc;
    2219   if (result>=rExecuted) and not TestOnly then
     2544    ToLoc := dLoc(Loc, dx, dy);
     2545    if (ToLoc < 0) or (ToLoc >= MapSize) then
    22202546    begin
    2221     ShowMove.dx:=dx;
    2222     ShowMove.dy:=dy;
    2223     ShowMove.FromLoc:=Loc;
    2224     ShowMove.mix:=mix;
    2225     ShowMove.Health:=Health;
    2226     ShowMove.Fuel:=Fuel;
    2227     ShowMove.Exp:=Exp;
    2228     ShowMove.Load:=TroopLoad+AirLoad;
    2229     ShowMove.Owner:=p;
    2230     if (TroopLoad>0) or (AirLoad>0) then
    2231       ShowMove.Flags:=unMulti
    2232     else ShowMove.Flags:=0;
    2233     case MoveInfo.MoveType of
    2234       mtCapture: ShowMove.Flags:=ShowMove.Flags or umCapturing;
    2235       mtSpyMission: ShowMove.Flags:=ShowMove.Flags or umSpyMission;
    2236       mtBombard: ShowMove.Flags:=ShowMove.Flags or umBombarding;
    2237       mtExpel: ShowMove.Flags:=ShowMove.Flags or umExpelling;
    2238       end;
    2239     case MoveInfo.MoveType of
    2240       mtMove,mtCapture,mtSpyMission:
    2241         result:=ExecuteMove(p,uix,ToLoc,MoveInfo,ShowMove) or result;
    2242       mtAttack,mtBombard,mtExpel:
    2243         result:=ExecuteAttack(p,uix,ToLoc,MoveInfo,ShowMove) or result
     2547      result := eInvalid;
     2548      exit
     2549    end;
     2550    result := CalculateMove(p, uix, ToLoc, 3 - dy and 1, TestOnly, MoveInfo);
     2551    if result = eZOC_EnemySpotted then
     2552      ZOCTile := ToLoc;
     2553    if (result >= rExecuted) and not TestOnly then
     2554    begin
     2555      ShowMove.dx := dx;
     2556      ShowMove.dy := dy;
     2557      ShowMove.FromLoc := Loc;
     2558      ShowMove.mix := mix;
     2559      ShowMove.Health := Health;
     2560      ShowMove.Fuel := Fuel;
     2561      ShowMove.Exp := Exp;
     2562      ShowMove.Load := TroopLoad + AirLoad;
     2563      ShowMove.Owner := p;
     2564      if (TroopLoad > 0) or (AirLoad > 0) then
     2565        ShowMove.Flags := unMulti
     2566      else
     2567        ShowMove.Flags := 0;
     2568      case MoveInfo.MoveType of
     2569        mtCapture:
     2570          ShowMove.Flags := ShowMove.Flags or umCapturing;
     2571        mtSpyMission:
     2572          ShowMove.Flags := ShowMove.Flags or umSpyMission;
     2573        mtBombard:
     2574          ShowMove.Flags := ShowMove.Flags or umBombarding;
     2575        mtExpel:
     2576          ShowMove.Flags := ShowMove.Flags or umExpelling;
     2577      end;
     2578      case MoveInfo.MoveType of
     2579        mtMove, mtCapture, mtSpyMission:
     2580          result := ExecuteMove(p, uix, ToLoc, MoveInfo, ShowMove) or result;
     2581        mtAttack, mtBombard, mtExpel:
     2582          result := ExecuteAttack(p, uix, ToLoc, MoveInfo, ShowMove) or result
    22442583      end;
    22452584    end
    22462585  end; // with
    2247 end; {MoveUnit}
    2248 
    2249 function Server(Command,Player,Subject:integer;var Data): integer; stdcall;
     2586end; { MoveUnit }
     2587
     2588function Server(Command, Player, Subject: integer; var Data): integer; stdcall;
    22502589
    22512590  function CountPrice(const Offer: TOffer; PriceType: integer): integer;
    22522591  var
    2253   i: integer;
     2592    i: integer;
    22542593  begin
    2255   result:=0;
    2256   for i:=0 to Offer.nDeliver+Offer.nCost-1 do
    2257     if Offer.Price[i] and $FFFF0000=Cardinal(PriceType) then inc(result);
     2594    result := 0;
     2595    for i := 0 to Offer.nDeliver + Offer.nCost - 1 do
     2596      if Offer.Price[i] and $FFFF0000 = Cardinal(PriceType) then
     2597        inc(result);
    22582598  end;
    22592599
    2260 {  procedure UpdateBorderHelper;
     2600{ procedure UpdateBorderHelper;
    22612601  var
    22622602  x, y, Loc, Loc1, dx, dy, ObserveMask: integer;
     
    22642604  ObserveMask:=3 shl (2*pTurn);
    22652605  for x:=0 to lx-1 do for y:=0 to ly shr 1-1 do
    2266     begin
    2267     Loc:=lx*(y*2)+x;
    2268     if ObserveLevel[Loc] and ObserveMask<>0 then
    2269       begin
    2270       for dy:=0 to 1 do for dx:=0 to 1 do
     2606  begin
     2607  Loc:=lx*(y*2)+x;
     2608  if ObserveLevel[Loc] and ObserveMask<>0 then
     2609  begin
     2610  for dy:=0 to 1 do for dx:=0 to 1 do
     2611  begin
     2612  Loc1:=(Loc+dx-1+lx) mod lx +lx*((y+dy)*2-1);
     2613  if (Loc1>=0) and (Loc1<MapSize)
     2614  and (ObserveLevel[Loc1] and ObserveMask<>0) then
     2615  if RealMap[Loc1] and $78000000=RealMap[Loc] and $78000000 then
     2616  begin
     2617  RW[pTurn].BorderHelper[Loc]:=RW[pTurn].BorderHelper[Loc] and not (1 shl (dy*2+dx));
     2618  RW[pTurn].BorderHelper[Loc1]:=RW[pTurn].BorderHelper[Loc1] and not (8 shr (dy*2+dx))
     2619  end
     2620  else
     2621  begin
     2622  RW[pTurn].BorderHelper[Loc]:=RW[pTurn].BorderHelper[Loc] or (1 shl (dy*2+dx));
     2623  RW[pTurn].BorderHelper[Loc1]:=RW[pTurn].BorderHelper[Loc1] or (8 shr (dy*2+dx));
     2624  end
     2625  end
     2626  end
     2627  end
     2628  end; }
     2629
     2630const
     2631  ptSelect = 0;
     2632  ptTrGoods = 1;
     2633  ptUn = 2;
     2634  ptCaravan = 3;
     2635  ptImp = 4;
     2636  ptWonder = 6;
     2637  ptShip = 7;
     2638  ptInvalid = 8;
     2639
     2640  function ProjectType(Project: integer): integer;
     2641  begin
     2642    if Project and cpCompleted <> 0 then
     2643      result := ptSelect
     2644    else if Project and (cpImp + cpIndex) = cpImp + imTrGoods then
     2645      result := ptTrGoods
     2646    else if Project and cpImp = 0 then
     2647      if RW[Player].Model[Project and cpIndex].Kind = mkCaravan then
     2648        result := ptCaravan
     2649      else
     2650        result := ptUn
     2651    else if Project and cpIndex >= nImp then
     2652      result := ptInvalid
     2653    else if Imp[Project and cpIndex].Kind = ikWonder then
     2654      result := ptWonder
     2655    else if Imp[Project and cpIndex].Kind = ikShipPart then
     2656      result := ptShip
     2657    else
     2658      result := ptImp
     2659  end;
     2660
     2661const
     2662  Dirx: array [0 .. 7] of integer = (1, 2, 1, 0, -1, -2, -1, 0);
     2663  Diry: array [0 .. 7] of integer = (-1, 0, 1, 2, 1, 0, -1, -2);
     2664
     2665var
     2666  d, i, j, p1, p2, pt0, pt1, uix1, cix1, Loc0, Loc1, dx, dy, NewCap, MinCap,
     2667    MaxCap, CapWeight, Cost, NextProd, Preq, TotalFood, TotalProd, CheckSum,
     2668    StopTurn, FutureMCost, NewProject, OldImp, mix, V8, V21, AStr, DStr,
     2669    ABaseDamage, DBaseDamage: integer;
     2670  CityReport, AltCityReport: TCityReport;
     2671  FormerCLState: TCmdListState;
     2672  EndTime: int64;
     2673  Adjacent: TVicinity8Loc;
     2674  Radius: TVicinity21Loc;
     2675  ShowShipChange: TShowShipChange;
     2676  ShowNegoData: TShowNegoData;
     2677  logged, ok, HasShipChanged, AllHumansDead, OfferFullySupported: boolean;
     2678
     2679begin { >>>server }
     2680  if Command = sTurn then
     2681  begin
     2682    p2 := -1;
     2683    for p1 := 0 to nPl - 1 do
     2684      if (p1 <> Player) and (1 shl p1 and GWatching <> 0) then
     2685        CallPlayer(cShowTurnChange, p1, p2);
     2686  end;
     2687
     2688  assert(MapSize = lx * ly);
     2689  assert(Command and (sctMask or sExecute) <> sctInternal or sExecute);
     2690  // not for internal commands
     2691  if (Command < 0) or (Command >= $10000) then
     2692  begin
     2693    result := eUnknown;
     2694    exit
     2695  end;
     2696
     2697  if (Player < 0) or (Player >= nPl) or
     2698    ((Command and (sctMask or sExecute) <> sctInfo) and
     2699    ((Subject < 0) or (Subject >= $1000))) then
     2700  begin
     2701    result := eInvalid;
     2702    exit
     2703  end;
     2704
     2705  if (1 shl Player and (GAlive or GWatching) = 0) and
     2706    not((Command = sTurn) or (Command = sBreak) or (Command = sResign) or
     2707    (Command = sGetAIInfo) or (Command = sGetAICredits) or
     2708    (Command = sGetVersion) or (Command and $FF0F = sGetChart)) then
     2709  begin
     2710    PutMessage(1 shl 16 + 1, Format('NOT Alive: %d', [Player]));
     2711    result := eNoTurn;
     2712    exit
     2713  end;
     2714
     2715  result := eOK;
     2716
     2717  // check if command allowed now
     2718  if (Mode = moPlaying) and not((Command >= cClientEx) or (Command = sMessage)
     2719    or (Command = sSetDebugMap) or (Command = sGetDebugMap) or
     2720    (Command = sGetAIInfo) or (Command = sGetAICredits) or
     2721    (Command = sGetVersion) or (Command = sGetTechCost) or
     2722    (Command = sGetDefender) or (Command = sGetUnitReport) or
     2723    (Command = sGetCityReport) or (Command = sGetCityTileInfo) or
     2724    (Command = sGetCity) or (Command = sGetEnemyCityReport) or
     2725    (Command = sGetEnemyCityAreaInfo) or (Command = sGetCityReportNew) or
     2726    (Command and $FF0F = sGetChart) or (Command and $FF0F = sSetAttitude))
     2727  // commands always allowed
     2728    and not((Player = pTurn) and (Command < $1000))
     2729  // info request always allowed for pTurn
     2730    and ((pDipActive < 0) and (Player <> pTurn) // not his turn
     2731    or (pDipActive >= 0) and (Player <> pDipActive)
     2732    // not active in negotiation mode
     2733    or (pDipActive >= 0) and (Command and sctMask <> sctEndClient)) then
     2734  // no nego command
     2735  begin
     2736    PutMessage(1 shl 16 + 1, Format('No Turn: %d calls %x',
     2737      [Player, Command shr 4]));
     2738    result := eNoTurn;
     2739    exit
     2740  end;
     2741
     2742  // do not use EXIT hereafter!
     2743
     2744{$IFOPT O-}
     2745  HandoverStack[nHandoverStack] := Player + $1000;
     2746  HandoverStack[nHandoverStack + 1] := Command;
     2747  inc(nHandoverStack, 2);
     2748
     2749  InvalidTreatyMap := 0;
     2750  // new command, sIntExpandTerritory of previous command was processed
     2751{$ENDIF}
     2752  if (Mode = moPlaying) and (Command >= sExecute) and
     2753    ((Command and sctMask <> sctEndClient) or (Command = sTurn)) and
     2754    (Command and sctMask <> sctModel) and (Command <> sCancelTreaty) and
     2755    (Command <> sSetCityTiles) and (Command <> sBuyCityProject) and
     2756    ((Command < cClientEx) or ProcessClientData[Player]) then
     2757  begin { log command }
     2758    FormerCLState := CL.State;
     2759    CL.Put(Command, Player, Subject, @Data);
     2760    logged := true;
     2761  end
     2762  else
     2763    logged := false;
     2764
     2765  case Command of
     2766
     2767    {
     2768      Info Request Commands
     2769      ____________________________________________________________________
     2770    }
     2771    sMessage:
     2772      Brain[bix[0]].Client(cDebugMessage, Subject, Data);
     2773
     2774    sSetDebugMap:
     2775      DebugMap[Player] := @Data;
     2776
     2777    sGetDebugMap:
     2778      pointer(Data) := DebugMap[Subject];
     2779
     2780    { sChangeSuperView:
     2781      if Difficulty[Player]=0 then
     2782      begin
     2783      for i:=0 to nBrain-1 do if Brain[i].Initialized then
     2784      CallClient(i, cShowSuperView, Subject)
     2785      end
     2786      else result:=eInvalid; }
     2787
     2788    sRefreshDebugMap:
     2789      Brain[bix[0]].Client(cRefreshDebugMap, -1, Player);
     2790
     2791    sGetChart .. sGetChart + (nStat - 1) shl 4:
     2792      if (Subject >= 0) and (Subject < nPl) and (bix[Subject] >= 0) then
     2793      begin
     2794        StopTurn := 0;
     2795        if (Difficulty[Player] = 0) or (GTestFlags and tfUncover <> 0)
     2796        // supervisor
     2797          or (Subject = Player) // own chart
     2798          or (GWinner > 0) // game end chart
     2799          or (1 shl Subject and GAlive = 0) then // chart of extinct nation
     2800          if Subject > Player then
     2801            StopTurn := GTurn
     2802          else
     2803            StopTurn := GTurn + 1
     2804        else if RW[Player].Treaty[Subject] > trNoContact then
     2805          if Command shr 4 and $F = stMil then
     2806            StopTurn := RW[Player].EnemyReport[Subject].TurnOfMilReport + 1
     2807          else
     2808            StopTurn := RW[Player].EnemyReport[Subject].TurnOfCivilReport + 1;
     2809        move(Stat[Command shr 4 and $F, Subject]^, Data,
     2810          StopTurn * SizeOf(integer));
     2811        FillChar(TChart(Data)[StopTurn], (GTurn - StopTurn) *
     2812          SizeOf(integer), 0);
     2813      end
     2814      else
     2815        result := eInvalid;
     2816
     2817    sGetTechCost:
     2818      integer(Data) := TechCost(Player);
     2819
     2820    sGetAIInfo:
     2821      if AIInfo[Subject] = '' then
     2822        pchar(Data) := nil
     2823      else
     2824        pchar(Data) := @AIInfo[Subject][1];
     2825
     2826    sGetAICredits:
     2827      if AICredits = '' then
     2828        pchar(Data) := nil
     2829      else
     2830        pchar(Data) := @AICredits[1];
     2831
     2832    sGetVersion:
     2833      integer(Data) := Version;
     2834
     2835    sGetGameChanged:
     2836      if Player <> 0 then
     2837        result := eInvalid
     2838      else if (CL <> nil) and (CL.State.nLog = nLogOpened) and
     2839        (CL.State.MoveCode = 0) and not HasCityTileChanges and
     2840        not HasChanges(Player) then
     2841        result := eNotChanged;
     2842
     2843    sGetTileInfo:
     2844      if (Subject >= 0) and (Subject < MapSize) then
     2845        result := GetTileInfo(Player, -2, Subject, TTileInfo(Data))
     2846      else
     2847        result := eInvalid;
     2848
     2849    sGetCityTileInfo:
     2850      if (Subject >= 0) and (Subject < MapSize) then
     2851        result := GetTileInfo(Player, -1, Subject, TTileInfo(Data))
     2852      else
     2853        result := eInvalid;
     2854
     2855    sGetHypoCityTileInfo:
     2856      if (Subject >= 0) and (Subject < MapSize) then
     2857      begin
     2858        if (TTileInfo(Data).ExplCity < 0) or
     2859          (TTileInfo(Data).ExplCity >= RW[Player].nCity) then
     2860          result := eInvalid
     2861        else if ObserveLevel[Subject] shr (Player * 2) and 3 = 0 then
     2862          result := eNoPreq
     2863        else
     2864          result := GetTileInfo(Player, TTileInfo(Data).ExplCity, Subject,
     2865            TTileInfo(Data))
     2866      end
     2867      else
     2868        result := eInvalid;
     2869
     2870    sGetJobProgress:
     2871      if (Subject >= 0) and (Subject < MapSize) then
     2872      begin
     2873        if ObserveLevel[Subject] shr (Player * 2) and 3 = 0 then
     2874          result := eNoPreq
     2875        else
     2876          result := GetJobProgress(Player, Subject, TJobProgressData(Data))
     2877      end
     2878      else
     2879        result := eInvalid;
     2880
     2881    sGetModels:
     2882      if (GTestFlags and tfUncover <> 0) or (Difficulty[Player] = 0)
     2883      then { supervisor only command }
     2884      begin
     2885        for p1 := 0 to nPl - 1 do
     2886          if (p1 <> Player) and (1 shl p1 and GAlive <> 0) then
     2887            for mix := 0 to RW[p1].nModel - 1 do
     2888              TellAboutModel(Player, p1, mix);
     2889      end
     2890      else
     2891        result := eInvalid;
     2892
     2893    sGetUnits:
     2894      if (Subject >= 0) and (Subject < MapSize) and
     2895        (ObserveLevel[Subject] shr (Player * 2) and 3 = lObserveSuper) then
     2896        integer(Data) := GetUnitStack(Player, Subject)
     2897      else
     2898        result := eNoPreq;
     2899
     2900    sGetDefender:
     2901      if (Subject >= 0) and (Subject < MapSize) and (Occupant[Subject] = Player)
     2902      then
     2903        Strongest(Subject, integer(Data), d, i, j)
     2904      else
     2905        result := eInvalid;
     2906
     2907    sGetBattleForecast, sGetBattleForecastEx:
     2908      if (Subject >= 0) and (Subject < MapSize) and
     2909        (ObserveLevel[Subject] and (3 shl (Player * 2)) > 0) then
     2910        with TBattleForecast(Data) do
     2911          if (1 shl pAtt and GAlive <> 0) and (mixAtt >= 0) and
     2912            (mixAtt < RW[pAtt].nModel) and
     2913            ((pAtt = Player) or (RWemix[Player, pAtt, mixAtt] >= 0)) then
     2914          begin
     2915            result := GetBattleForecast(Subject, TBattleForecast(Data), uix1,
     2916              cix1, AStr, DStr, ABaseDamage, DBaseDamage);
     2917            if Command = sGetBattleForecastEx then
     2918            begin
     2919              TBattleForecastEx(Data).AStr := (AStr + 200) div 400;
     2920              TBattleForecastEx(Data).DStr := (DStr + 200) div 400;
     2921              TBattleForecastEx(Data).ABaseDamage := ABaseDamage;
     2922              TBattleForecastEx(Data).DBaseDamage := DBaseDamage;
     2923            end;
     2924            if result = eOK then
     2925              result := eInvalid // no enemy unit there!
     2926          end
     2927          else
     2928            result := eInvalid
     2929      else
     2930        result := eInvalid;
     2931
     2932    sGetUnitReport:
     2933      if (Subject < 0) or (Subject >= RW[Player].nUn) or
     2934        (RW[Player].Un[Subject].Loc < 0) then
     2935        result := eInvalid
     2936      else
     2937        GetUnitReport(Player, Subject, TUnitReport(Data));
     2938
     2939    sGetMoveAdvice:
     2940      if (Subject < 0) or (Subject >= RW[Player].nUn) or
     2941        (RW[Player].Un[Subject].Loc < 0) then
     2942        result := eInvalid
     2943      else
     2944        result := GetMoveAdvice(Player, Subject, TMoveAdviceData(Data));
     2945
     2946    sGetPlaneReturn:
     2947      if (Subject < 0) or (Subject >= RW[Player].nUn) or
     2948        (RW[Player].Un[Subject].Loc < 0) or
     2949        (RW[Player].Model[RW[Player].Un[Subject].mix].Domain <> dAir) then
     2950        result := eInvalid
     2951      else
     2952      begin
     2953        if CanPlaneReturn(Player, Subject, TPlaneReturnData(Data)) then
     2954          result := eOK
     2955        else
     2956          result := eNoWay
     2957      end;
     2958
     2959    sGetCity:
     2960      if (Subject >= 0) and (Subject < MapSize) and
     2961        (ObserveLevel[Subject] shr (Player * 2) and 3 = lObserveSuper) and
     2962        (RealMap[Subject] and fCity <> 0) then
     2963        with TGetCityData(Data) do
    22712964        begin
    2272         Loc1:=(Loc+dx-1+lx) mod lx +lx*((y+dy)*2-1);
    2273         if (Loc1>=0) and (Loc1<MapSize)
    2274           and (ObserveLevel[Loc1] and ObserveMask<>0) then
    2275           if RealMap[Loc1] and $78000000=RealMap[Loc] and $78000000 then
     2965          Owner := Player;
     2966          SearchCity(Subject, Owner, cix1);
     2967          c := RW[Owner].City[cix1];
     2968          if (Owner <> Player) and (c.Project and cpImp = 0) then
     2969            TellAboutModel(Player, Owner, c.Project and cpIndex);
     2970        end
     2971      else
     2972        result := eInvalid;
     2973
     2974    sGetCityReport:
     2975      if (Subject < 0) or (Subject >= RW[Player].nCity) or
     2976        (RW[Player].City[Subject].Loc < 0) then
     2977        result := eInvalid
     2978      else
     2979        result := GetCityReport(Player, Subject, TCityReport(Data));
     2980
     2981    sGetCityReportNew:
     2982      if (Subject < 0) or (Subject >= RW[Player].nCity) or
     2983        (RW[Player].City[Subject].Loc < 0) then
     2984        result := eInvalid
     2985      else
     2986        GetCityReportNew(Player, Subject, TCityReportNew(Data));
     2987
     2988    sGetCityAreaInfo:
     2989      if (Subject < 0) or (Subject >= RW[Player].nCity) or
     2990        (RW[Player].City[Subject].Loc < 0) then
     2991        result := eInvalid
     2992      else
     2993        GetCityAreaInfo(Player, RW[Player].City[Subject].Loc,
     2994          TCityAreaInfo(Data));
     2995
     2996    sGetEnemyCityReport:
     2997      if (Subject >= 0) and (Subject < MapSize) and
     2998        (ObserveLevel[Subject] shr (Player * 2) and 3 = lObserveSuper) and
     2999        (RealMap[Subject] and fCity <> 0) then
     3000      begin
     3001        p1 := Occupant[Subject];
     3002        if p1 < 0 then
     3003          p1 := 1;
     3004        SearchCity(Subject, p1, cix1);
     3005        TCityReport(Data).HypoTiles := -1;
     3006        TCityReport(Data).HypoTax := -1;
     3007        TCityReport(Data).HypoLux := -1;
     3008        GetCityReport(p1, cix1, TCityReport(Data))
     3009      end
     3010      else
     3011        result := eInvalid;
     3012
     3013    sGetEnemyCityReportNew:
     3014      if (Subject >= 0) and (Subject < MapSize) and
     3015        (ObserveLevel[Subject] shr (Player * 2) and 3 = lObserveSuper) and
     3016        (RealMap[Subject] and fCity <> 0) then
     3017      begin
     3018        p1 := Occupant[Subject];
     3019        if p1 < 0 then
     3020          p1 := 1;
     3021        SearchCity(Subject, p1, cix1);
     3022        TCityReport(Data).HypoTiles := -1;
     3023        TCityReport(Data).HypoTax := -1;
     3024        TCityReport(Data).HypoLux := -1;
     3025        GetCityReportNew(p1, cix1, TCityReportNew(Data));
     3026      end
     3027      else
     3028        result := eInvalid;
     3029
     3030    sGetEnemyCityAreaInfo:
     3031      if (Subject >= 0) and (Subject < MapSize) and
     3032        (ObserveLevel[Subject] shr (Player * 2) and 3 = lObserveSuper) and
     3033        (RealMap[Subject] and fCity <> 0) then
     3034      begin
     3035        p1 := Occupant[Subject];
     3036        if p1 < 0 then
     3037          p1 := 1;
     3038        SearchCity(Subject, p1, cix1);
     3039        GetCityAreaInfo(p1, Subject, TCityAreaInfo(Data))
     3040      end
     3041      else
     3042        result := eInvalid;
     3043
     3044    sGetCityTileAdvice:
     3045      if (Subject < 0) or (Subject >= RW[Player].nCity) or
     3046        (RW[Player].City[Subject].Loc < 0) then
     3047        result := eInvalid
     3048      else
     3049        GetCityTileAdvice(Player, Subject, TCityTileAdviceData(Data));
     3050
     3051    {
     3052      Map Editor Commands
     3053      ____________________________________________________________________
     3054    }
     3055    sEditTile:
     3056      if Player = 0 then
     3057        with TEditTileData(Data) do
     3058          EditTile(Loc, NewTile)
     3059      else
     3060        result := eInvalid;
     3061
     3062    sRandomMap:
     3063      if (Player = 0) and MapGeneratorAvailable then
     3064      begin
     3065        CreateElevation;
     3066        PreviewElevation := false;
     3067        CreateMap(false);
     3068        FillChar(ObserveLevel, MapSize * 4, 0);
     3069        DiscoverAll(Player, lObserveSuper);
     3070      end
     3071      else
     3072        result := eInvalid;
     3073
     3074    sMapGeneratorRequest:
     3075      if not MapGeneratorAvailable then
     3076        result := eInvalid;
     3077
     3078    {
     3079      Client Deactivation Commands
     3080      ____________________________________________________________________
     3081    }
     3082    sTurn, sTurn - sExecute:
     3083      begin
     3084        AllHumansDead := true;
     3085        for p1 := 0 to nPl - 1 do
     3086          if (1 shl p1 and GAlive <> 0) and (bix[p1] = bixTerm) then
     3087            AllHumansDead := false;
     3088        if (pDipActive >= 0) // still in negotiation mode
     3089          or (pTurn = 0) and ((GWinner > 0) or (GTurn = MaxTurn) or
     3090          (Difficulty[0] > 0) and AllHumansDead) then // game end reached
     3091          result := eViolation
     3092        else if Command >= sExecute then
     3093        begin
     3094          if Mode = moPlaying then
     3095          begin
     3096            CL.State := FormerCLState;
     3097            LogCityTileChanges;
     3098{$IFNDEF SCR}
     3099            if pTurn = 0 then
    22763100            begin
    2277             RW[pTurn].BorderHelper[Loc]:=RW[pTurn].BorderHelper[Loc] and not (1 shl (dy*2+dx));
    2278             RW[pTurn].BorderHelper[Loc1]:=RW[pTurn].BorderHelper[Loc1] and not (8 shr (dy*2+dx))
     3101              LogChanges;
     3102              SaveGame('~' + LogFileName, true);
    22793103            end
     3104{$ENDIF}
     3105          end
     3106          else if (Mode = moMovie) and (pTurn = 0) then
     3107            CallPlayer(cMovieEndTurn, 0, nil^);
     3108          GWatching := GWatching and GAlive or 1;
     3109          RW[pTurn].Happened := 0;
     3110          uixSelectedTransport := -1;
     3111          SpyMission := smSabotageProd;
     3112          if 1 shl pTurn and GAlive <> 0 then
     3113          begin
     3114            // calculate checksum
     3115            TotalFood := 0;
     3116            TotalProd := 0;
     3117            for i := 0 to RW[pTurn].nCity - 1 do
     3118              if RW[pTurn].City[i].Loc >= 0 then
     3119              begin
     3120                inc(TotalFood, RW[pTurn].City[i].Food);
     3121                inc(TotalProd, RW[pTurn].City[i].Prod);
     3122              end;
     3123            CheckSum := TotalFood and 7 + TotalProd and 7 shl 3 +
     3124              RW[pTurn].Money and 7 shl 6 + Worked[pTurn] div 100 and 7 shl 9;
     3125          end
    22803126          else
     3127            CheckSum := 0;
     3128
     3129          if Mode < moPlaying then // check checksum
     3130          begin
     3131            if CheckSum <> Subject then
     3132              LoadOK := false
     3133          end
     3134          else // save checksum
     3135            CL.Put(Command, Player, CheckSum, @Data);
     3136{$IFDEF TEXTLOG}
     3137          CmdInfo := '';
     3138          if CheckSum and 7 <> Subject and 7 then
     3139            CmdInfo := Format('***ERROR (Food %d) ',
     3140              [(CheckSum and 7 - Subject and 7 + 12) mod 8 - 4]) + CmdInfo;
     3141          if CheckSum shr 3 and 7 <> Subject shr 3 and 7 then
     3142            CmdInfo := '***ERROR (Prod) ' + CmdInfo;
     3143          if CheckSum shr 6 and 7 <> Subject shr 6 and 7 then
     3144            CmdInfo := '***ERROR (Research) ' + CmdInfo;
     3145          if CheckSum shr 9 and 7 <> Subject shr 9 and 7 then
     3146            CmdInfo := '***ERROR (Work) ' + CmdInfo;
     3147{$ENDIF}
     3148          if 1 shl pTurn and GAlive <> 0 then
     3149          begin
     3150            AfterTurn;
     3151            if Mode < moPlaying then
     3152              InsertTerritoryUpdateCommands;
     3153            // if bix[pTurn]=bixTerm then UpdateBorderHelper;
     3154          end;
     3155
     3156          repeat
     3157            pTurn := (pTurn + 1) mod nPl;
     3158            if pTurn = 0 then
     3159              inc(GTurn);
     3160            if (bix[pTurn] >= 0) and ((1 shl pTurn) and GAlive = 0) then
     3161            begin // already made extinct -- continue statistics
     3162              Stat[stExplore, pTurn, GTurn] := 0;
     3163              Stat[stPop, pTurn, GTurn] := 0;
     3164              Stat[stTerritory, pTurn, GTurn] := 0;
     3165              Stat[stScience, pTurn, GTurn] := 0;
     3166              Stat[stWork, pTurn, GTurn] := 0;
     3167              Stat[stMil, pTurn, GTurn] := 0;
     3168            end;
     3169          until (pTurn = 0) or ((1 shl pTurn and (GAlive or GWatching) <> 0) and
     3170            (GWinner = 0));
     3171          if (Mode = moLoading_Fast) and
     3172            ((GTurn = LoadTurn) or (GTurn = LoadTurn - 1) and (pTurn > 0)) then
     3173            Mode := moLoading;
     3174          if Mode = moPlaying then
     3175          begin
     3176            CCCommand := cTurn;
     3177            CCPlayer := pTurn;
     3178            Notify(ntNextPlayer)
     3179          end
     3180          else
     3181          begin
     3182            if GTurn = 0 then
     3183              BeforeTurn0
     3184            else
     3185              BeforeTurn;
     3186            if (Mode = moMovie) and (pTurn = 0) then
    22813187            begin
    2282             RW[pTurn].BorderHelper[Loc]:=RW[pTurn].BorderHelper[Loc] or (1 shl (dy*2+dx));
    2283             RW[pTurn].BorderHelper[Loc1]:=RW[pTurn].BorderHelper[Loc1] or (8 shr (dy*2+dx));
    2284             end
     3188              Inform(pTurn);
     3189              CallPlayer(cMovieTurn, 0, nil^);
     3190            end;
     3191          end;
     3192{$IFDEF TEXTLOG}CmdInfo := CmdInfo + Format('---Turn %d P%d---', [GTurn, pTurn]); {$ENDIF}
     3193        end;
     3194      end; // sTurn
     3195
     3196    sBreak, sResign, sNextRound, sReload:
     3197      if Mode = moMovie then
     3198        MovieStopped := true
     3199      else
     3200      begin
     3201        if Command = sReload then
     3202        begin
     3203          ok := (Difficulty[0] = 0) and (bix[0] <> bixNoTerm) and
     3204            (integer(Data) >= 0) and (integer(Data) < GTurn);
     3205          for p1 := 1 to nPl - 1 do
     3206            if bix[p1] = bixTerm then
     3207              ok := false;
     3208          // allow reload in AI-only games only
     3209        end
     3210        else
     3211          ok := Player = 0;
     3212        if ok then
     3213        begin
     3214          if (Command = sBreak) or (Command = sResign) then
     3215            Notify(ntBackOn);
     3216          for i := 0 to nBrain - 1 do
     3217            if Brain[i].Initialized then
     3218            begin
     3219              if i >= bixFirstAI then
     3220                Notify(ntDeinitModule + i);
     3221              CallClient(i, cBreakGame, nil^);
     3222            end;
     3223          Notify(ntEndInfo);
     3224          if (Command = sBreak) or (Command = sReload) then
     3225          begin
     3226            LogCityTileChanges;
     3227            LogChanges;
     3228            SaveGame(LogFileName, false);
     3229          end;
     3230          DeleteFile(SavePath + '~' + LogFileName);
     3231          EndGame;
     3232          case Command of
     3233            sBreak:
     3234              Notify(ntStartGoRefresh);
     3235            sResign:
     3236              Notify(ntStartGo);
     3237            sNextRound:
     3238              StartNewGame(SavePath, LogFileName, MapFileName, lx, ly,
     3239                LandMass, MaxTurn);
     3240            sReload:
     3241              LoadGame(SavePath, LogFileName, integer(Data), false);
     3242          end
     3243        end
     3244        else
     3245          result := eInvalid;
     3246      end;
     3247
     3248    sAbandonMap, sSaveMap:
     3249      if Player = 0 then
     3250      begin
     3251        if Command = sSaveMap then
     3252          SaveMap(MapFileName);
     3253        Notify(ntBackOn);
     3254        Brain[bixTerm].Client(cBreakGame, -1, nil^);
     3255        ReleaseMapEditor;
     3256        if Command = sSaveMap then
     3257          Notify(ntStartGoRefreshMaps)
     3258        else
     3259          Notify(ntStartGo)
     3260      end
     3261      else
     3262        result := eInvalid;
     3263
     3264    scContact .. scContact + (nPl - 1) shl 4, scContact - sExecute .. scContact
     3265      - sExecute + (nPl - 1) shl 4:
     3266      if (pDipActive >= 0) or (1 shl (Command shr 4 and $F) and GAlive = 0) then
     3267        result := eInvalid
     3268      else if GWinner > 0 then
     3269        result := eViolation // game end reached
     3270      else if RW[Player].Treaty[Command shr 4 and $F] = trNoContact then
     3271        result := eNoPreq
     3272      else if GTurn < GColdWarStart + ColdWarTurns then
     3273        result := eColdWar
     3274      else if RW[Player].Government = gAnarchy then
     3275        result := eAnarchy
     3276      else if RW[Command shr 4 and $F].Government = gAnarchy then
     3277      begin
     3278        result := eAnarchy;
     3279        LastEndClientCommand := scReject; // enable cancel treaty
     3280        pContacted := Command shr 4 and $F;
     3281      end
     3282      else if Command >= sExecute then
     3283      begin // contact request
     3284        pContacted := Command shr 4 and $F;
     3285        pDipActive := pContacted;
     3286        assert(Mode = moPlaying);
     3287        Inform(pDipActive);
     3288        ChangeClientWhenDone(scContact, pDipActive, pTurn, 4);
     3289      end;
     3290
     3291    scReject, scReject - sExecute:
     3292      if LastEndClientCommand and $FF0F = scContact then
     3293      begin
     3294        if Command >= sExecute then
     3295        begin // contact requested and not accepted yet
     3296          pDipActive := -1;
     3297          assert(Mode = moPlaying);
     3298          ChangeClientWhenDone(cContinue, pTurn, nil^, 0);
    22853299        end
    22863300      end
    2287     end
    2288   end;}
    2289 
    2290 const
    2291 ptSelect=0; ptTrGoods=1; ptUn=2; ptCaravan=3; ptImp=4; ptWonder=6;
    2292 ptShip=7; ptInvalid=8;
    2293 
    2294   function ProjectType(Project: integer): integer;
    2295   begin
    2296   if Project and cpCompleted<>0 then result:=ptSelect
    2297   else if Project and (cpImp+cpIndex)=cpImp+imTrGoods then result:=ptTrGoods
    2298   else if Project and cpImp=0 then
    2299     if RW[Player].Model[Project and cpIndex].Kind=mkCaravan then result:=ptCaravan
    2300     else result:=ptUn
    2301   else if Project and cpIndex>=nImp then result:=ptInvalid
    2302   else if Imp[Project and cpIndex].Kind=ikWonder then result:=ptWonder
    2303   else if Imp[Project and cpIndex].Kind=ikShipPart then result:=ptShip
    2304   else result:=ptImp
    2305   end;
    2306 
    2307 const
    2308 Dirx: array[0..7] of integer=(1,2,1,0,-1,-2,-1,0);
    2309 Diry: array[0..7] of integer=(-1,0,1,2,1,0,-1,-2);
    2310 
    2311 var
    2312 d,i,j,p1,p2,pt0,pt1,uix1,cix1,Loc0,Loc1,dx,dy,NewCap,MinCap,MaxCap,
    2313   CapWeight,Cost,NextProd,Preq,TotalFood,TotalProd,CheckSum,StopTurn,
    2314   FutureMCost,NewProject,OldImp,mix,V8,V21,AStr,DStr,ABaseDamage,DBaseDamage: integer;
    2315 CityReport,AltCityReport:TCityReport;
    2316 FormerCLState: TCmdListState;
    2317 EndTime: int64;
    2318 Adjacent: TVicinity8Loc;
    2319 Radius: TVicinity21Loc;
    2320 ShowShipChange: TShowShipChange;
    2321 ShowNegoData: TShowNegoData;
    2322 logged,ok,HasShipChanged,AllHumansDead,OfferFullySupported:boolean;
    2323 
    2324 begin {>>>server}
    2325 if Command=sTurn then
    2326   begin
    2327   p2:=-1;
    2328   for p1:=0 to nPl-1 do if (p1<>Player) and (1 shl p1 and GWatching<>0) then
    2329     CallPlayer(cShowTurnChange,p1,p2);
    2330   end;
    2331 
    2332 assert(MapSize=lx*ly);
    2333 assert(Command and (sctMask or sExecute)<>sctInternal or sExecute); // not for internal commands
    2334 if (Command<0) or (Command>=$10000) then
    2335   begin result:=eUnknown; exit end;
    2336 
    2337 if (Player<0) or (Player>=nPl)
    2338   or ((Command and (sctMask or sExecute)<>sctInfo)
    2339     and ((Subject<0) or (Subject>=$1000))) then
    2340   begin result:=eInvalid; exit end;
    2341 
    2342 if (1 shl Player and (GAlive or GWatching)=0) and
    2343   not ((Command=sTurn) or (Command=sBreak) or (Command=sResign)
    2344   or (Command=sGetAIInfo) or (Command=sGetAICredits) or (Command=sGetVersion)
    2345   or (Command and $FF0F=sGetChart)) then
    2346   begin
    2347   PutMessage(1 shl 16+1, Format('NOT Alive: %d',[Player]));
    2348   result:=eNoTurn;
    2349   exit
    2350   end;
    2351 
    2352 result:=eOK;
    2353 
    2354 // check if command allowed now
    2355 if (Mode=moPlaying)
    2356   and not ((Command>=cClientEx) or (Command=sMessage) or (Command=sSetDebugMap)
    2357   or (Command=sGetDebugMap)
    2358   or (Command=sGetAIInfo) or (Command=sGetAICredits) or (Command=sGetVersion)
    2359   or (Command=sGetTechCost) or (Command=sGetDefender)
    2360   or (Command=sGetUnitReport)
    2361   or (Command=sGetCityReport) or (Command=sGetCityTileInfo)
    2362   or (Command=sGetCity) or (Command=sGetEnemyCityReport)
    2363   or (Command=sGetEnemyCityAreaInfo) or (Command=sGetCityReportNew)
    2364   or (Command and $FF0F=sGetChart) or (Command and $FF0F=sSetAttitude))
    2365     // commands always allowed
    2366   and not ((Player=pTurn) and (Command<$1000))
    2367     // info request always allowed for pTurn
    2368   and ((pDipActive<0) and (Player<>pTurn) // not his turn
    2369   or (pDipActive>=0) and (Player<>pDipActive) // not active in negotiation mode
    2370   or (pDipActive>=0) and (Command and sctMask<>sctEndClient)) then // no nego command
    2371   begin
    2372   PutMessage(1 shl 16+1, Format('No Turn: %d calls %x',
    2373     [Player,Command shr 4]));
    2374   result:=eNoTurn;
    2375   exit
    2376   end;
    2377 
    2378 // do not use EXIT hereafter!
    2379 
    2380 {$IFOPT O-}
    2381 HandoverStack[nHandoverStack]:=Player+$1000;
    2382 HandoverStack[nHandoverStack+1]:=Command;
    2383 inc(nHandoverStack,2);
    2384 
    2385 InvalidTreatyMap:=0; // new command, sIntExpandTerritory of previous command was processed
    2386 {$ENDIF}
    2387 
    2388 if (Mode=moPlaying) and (Command>=sExecute)
    2389   and ((Command and sctMask<>sctEndClient) or (Command=sTurn))
    2390   and (Command and sctMask<>sctModel) and (Command<>sCancelTreaty)
    2391   and (Command<>sSetCityTiles) and (Command<>sBuyCityProject)
    2392   and ((Command<cClientEx) or ProcessClientData[Player]) then
    2393   begin {log command}
    2394   FormerCLState:=CL.State;
    2395   CL.Put(Command, Player, Subject, @Data);
    2396   logged:=true;
    2397   end
    2398 else logged:=false;
    2399 
    2400 case Command of
    2401 
    2402 {
    2403                         Info Request Commands
    2404  ____________________________________________________________________
    2405 }
    2406   sMessage:
    2407     Brain[bix[0]].Client(cDebugMessage,Subject,Data);
    2408 
    2409   sSetDebugMap:
    2410     DebugMap[Player]:=@Data;
    2411 
    2412   sGetDebugMap:
    2413     pointer(Data):=DebugMap[Subject];
    2414 
    2415   {sChangeSuperView:
    2416     if Difficulty[Player]=0 then
    2417       begin
    2418       for i:=0 to nBrain-1 do if Brain[i].Initialized then
    2419         CallClient(i, cShowSuperView, Subject)
    2420       end
    2421     else result:=eInvalid;}
    2422 
    2423   sRefreshDebugMap:
    2424     Brain[bix[0]].Client(cRefreshDebugMap,-1,Player);
    2425 
    2426   sGetChart..sGetChart+(nStat-1) shl 4:
    2427     if (Subject>=0) and (Subject<nPl) and (bix[Subject]>=0) then
    2428       begin
    2429       StopTurn:=0;
    2430       if (Difficulty[Player]=0) or (GTestFlags and tfUncover<>0) // supervisor
    2431         or (Subject=Player)  // own chart
    2432         or (GWinner>0) // game end chart
    2433         or (1 shl Subject and GAlive=0) then // chart of extinct nation
    2434         if Subject>Player then StopTurn:=GTurn
    2435         else StopTurn:=GTurn+1
    2436       else if RW[Player].Treaty[Subject]>trNoContact then
    2437         if Command shr 4 and $f=stMil then
    2438           StopTurn:=RW[Player].EnemyReport[Subject].TurnOfMilReport+1
    2439         else StopTurn:=RW[Player].EnemyReport[Subject].TurnOfCivilReport+1;
    2440       move(Stat[Command shr 4 and $f, Subject]^, Data, StopTurn*SizeOf(integer));
    2441       FillChar(TChart(Data)[StopTurn],(GTurn-StopTurn)*SizeOf(integer),0);
    2442       end
    2443     else result:=eInvalid;
    2444 
    2445   sGetTechCost:
    2446     integer(Data):=TechCost(Player);
    2447 
    2448   sGetAIInfo:
    2449     if AIInfo[Subject]='' then pchar(Data):=nil
    2450     else pchar(Data):=@AIInfo[Subject][1];
    2451 
    2452   sGetAICredits:
    2453     if AICredits='' then pchar(Data):=nil
    2454     else pchar(Data):=@AICredits[1];
    2455 
    2456   sGetVersion:
    2457     integer(Data):=Version;
    2458 
    2459   sGetGameChanged:
    2460     if Player<>0 then result:=eInvalid
    2461     else if (CL<>nil) and (CL.state.nLog=nLogOpened) and (CL.state.MoveCode=0)
    2462       and not HasCityTileChanges and not HasChanges(Player) then
    2463       result:=eNotChanged;
    2464 
    2465   sGetTileInfo:
    2466     if (Subject>=0) and (Subject<MapSize) then
    2467       result:=GetTileInfo(Player,-2,Subject,TTileInfo(Data))
    2468     else result:=eInvalid;
    2469 
    2470   sGetCityTileInfo:
    2471     if (Subject>=0) and (Subject<MapSize) then
    2472       result:=GetTileInfo(Player,-1,Subject,TTileInfo(Data))
    2473     else result:=eInvalid;
    2474 
    2475   sGetHypoCityTileInfo:
    2476     if (Subject>=0) and (Subject<MapSize) then
    2477       begin
    2478       if (TTileInfo(Data).ExplCity<0) or (TTileInfo(Data).ExplCity>=RW[Player].nCity) then
    2479         result:=eInvalid
    2480       else if ObserveLevel[Subject] shr (Player*2) and 3=0 then
    2481         result:=eNoPreq
    2482       else result:=GetTileInfo(Player,TTileInfo(Data).ExplCity,Subject,TTileInfo(Data))
    2483       end
    2484     else result:=eInvalid;
    2485 
    2486   sGetJobProgress:
    2487     if (Subject>=0) and (Subject<MapSize) then
    2488       begin
    2489       if ObserveLevel[Subject] shr (Player*2) and 3=0 then
    2490         result:=eNoPreq
    2491       else result:=GetJobProgress(Player,Subject,TJobProgressData(Data))
    2492       end
    2493     else result:=eInvalid;
    2494 
    2495   sGetModels:
    2496     if (GTestFlags and tfUncover<>0) or (Difficulty[Player]=0) then {supervisor only command}
    2497       begin
    2498       for p1:=0 to nPl-1 do if (p1<>Player) and (1 shl p1 and GAlive<>0) then
    2499         for mix:=0 to RW[p1].nModel-1 do
    2500           TellAboutModel(Player,p1,mix);
    2501       end
    2502     else result:=eInvalid;
    2503 
    2504   sGetUnits:
    2505     if (Subject>=0) and (Subject<MapSize)
    2506       and (ObserveLevel[Subject] shr (Player*2) and 3=lObserveSuper) then
    2507       integer(Data):=GetUnitStack(Player,Subject)
    2508     else result:=eNoPreq;
    2509 
    2510   sGetDefender:
    2511     if (Subject>=0) and (Subject<MapSize) and (Occupant[Subject]=Player) then
    2512       Strongest(Subject,integer(Data),d,i,j)
    2513     else result:=eInvalid;
    2514 
    2515   sGetBattleForecast,sGetBattleForecastEx:
    2516     if (Subject>=0) and (Subject<MapSize)
    2517       and (ObserveLevel[Subject] and (3 shl (Player*2))>0) then
    2518       with TBattleForecast(Data) do
    2519         if (1 shl pAtt and GAlive<>0)
    2520           and (mixAtt>=0) and (mixAtt<RW[pAtt].nModel)
    2521           and ((pAtt=Player) or (RWemix[Player,pAtt,mixAtt]>=0)) then
    2522           begin
    2523           result:=GetBattleForecast(Subject,TBattleForecast(Data),uix1,cix1,
    2524             AStr,DStr,ABaseDamage,DBaseDamage);
    2525           if Command=sGetBattleForecastEx then
    2526             begin
    2527             TBattleForecastEx(Data).AStr:=(AStr+200) div 400;
    2528             TBattleForecastEx(Data).DStr:=(DStr+200) div 400;
    2529             TBattleForecastEx(Data).ABaseDamage:=ABaseDamage;
    2530             TBattleForecastEx(Data).DBaseDamage:=DBaseDamage;
    2531             end;
    2532           if result=eOk then
    2533             result:=eInvalid // no enemy unit there!
    2534           end
    2535         else result:=eInvalid
    2536     else result:=eInvalid;
    2537 
    2538   sGetUnitReport:
    2539     if (Subject<0) or (Subject>=RW[Player].nUn)
    2540       or (RW[Player].Un[Subject].Loc<0) then
    2541       result:=eInvalid
    2542     else GetUnitReport(Player, Subject, TUnitReport(Data));
    2543 
    2544   sGetMoveAdvice:
    2545     if (Subject<0) or (Subject>=RW[Player].nUn)
    2546       or (RW[Player].Un[Subject].Loc<0) then
    2547       result:=eInvalid
    2548     else result:=GetMoveAdvice(Player,Subject, TMoveAdviceData(Data));
    2549 
    2550   sGetPlaneReturn:
    2551     if (Subject<0) or (Subject>=RW[Player].nUn)
    2552       or (RW[Player].Un[Subject].Loc<0)
    2553       or (RW[Player].Model[RW[Player].Un[Subject].mix].Domain<>dAir) then
    2554       result:=eInvalid
    2555     else
    2556       begin
    2557       if CanPlaneReturn(Player,Subject, TPlaneReturnData(Data)) then result:=eOK
    2558       else result:=eNoWay
    2559       end;
    2560 
    2561   sGetCity:
    2562     if (Subject>=0) and (Subject<MapSize)
    2563       and (ObserveLevel[Subject] shr (Player*2) and 3=lObserveSuper)
    2564       and (RealMap[Subject] and fCity<>0) then
    2565       with TGetCityData(Data) do
    2566         begin
    2567         Owner:=Player;
    2568         SearchCity(Subject,Owner,cix1);
    2569         c:=RW[Owner].City[cix1];
    2570         if (Owner<>Player) and (c.Project and cpImp=0) then
    2571           TellAboutModel(Player,Owner,c.Project and cpIndex);
    2572         end
    2573     else result:=eInvalid;
    2574 
    2575   sGetCityReport:
    2576     if (Subject<0) or (Subject>=RW[Player].nCity) or (RW[Player].City[Subject].Loc<0) then
    2577       result:=eInvalid
    2578     else result:=GetCityReport(Player,Subject,TCityReport(Data));
    2579 
    2580   sGetCityReportNew:
    2581     if (Subject<0) or (Subject>=RW[Player].nCity) or (RW[Player].City[Subject].Loc<0) then
    2582       result:=eInvalid
    2583     else GetCityReportNew(Player,Subject,TCityReportNew(Data));
    2584 
    2585   sGetCityAreaInfo:
    2586     if (Subject<0) or (Subject>=RW[Player].nCity) or (RW[Player].City[Subject].Loc<0) then
    2587       result:=eInvalid
    2588     else GetCityAreaInfo(Player, RW[Player].City[Subject].Loc,
    2589       TCityAreaInfo(Data));
    2590 
    2591   sGetEnemyCityReport:
    2592     if (Subject>=0) and (Subject<MapSize)
    2593       and (ObserveLevel[Subject] shr (Player*2) and 3=lObserveSuper)
    2594       and (RealMap[Subject] and fCity<>0) then
    2595       begin
    2596       p1:=Occupant[Subject];
    2597       if p1<0 then p1:=1;
    2598       SearchCity(Subject,p1,cix1);
    2599       TCityReport(Data).HypoTiles:=-1;
    2600       TCityReport(Data).HypoTax:=-1;
    2601       TCityReport(Data).HypoLux:=-1;
    2602       GetCityReport(p1,cix1,TCityReport(Data))
    2603       end
    2604     else result:=eInvalid;
    2605 
    2606   sGetEnemyCityReportNew:
    2607     if (Subject>=0) and (Subject<MapSize)
    2608       and (ObserveLevel[Subject] shr (Player*2) and 3=lObserveSuper)
    2609       and (RealMap[Subject] and fCity<>0) then
    2610       begin
    2611       p1:=Occupant[Subject];
    2612       if p1<0 then p1:=1;
    2613       SearchCity(Subject,p1,cix1);
    2614       TCityReport(Data).HypoTiles:=-1;
    2615       TCityReport(Data).HypoTax:=-1;
    2616       TCityReport(Data).HypoLux:=-1;
    2617       GetCityReportNew(p1,cix1,TCityReportNew(Data));
    2618       end
    2619     else result:=eInvalid;
    2620 
    2621   sGetEnemyCityAreaInfo:
    2622     if (Subject>=0) and (Subject<MapSize)
    2623       and (ObserveLevel[Subject] shr (Player*2) and 3=lObserveSuper)
    2624       and (RealMap[Subject] and fCity<>0) then
    2625       begin
    2626       p1:=Occupant[Subject];
    2627       if p1<0 then p1:=1;
    2628       SearchCity(Subject,p1,cix1);
    2629       GetCityAreaInfo(p1,Subject,TCityAreaInfo(Data))
    2630       end
    2631     else result:=eInvalid;
    2632 
    2633   sGetCityTileAdvice:
    2634     if (Subject<0) or (Subject>=RW[Player].nCity) or (RW[Player].City[Subject].Loc<0) then
    2635       result:=eInvalid
    2636     else GetCityTileAdvice(Player, Subject, TCityTileAdviceData(Data));
    2637 
    2638 {
    2639                       Map Editor Commands
    2640  ____________________________________________________________________
    2641 }
    2642   sEditTile:
    2643     if Player=0 then with TEditTileData(Data) do EditTile(Loc, NewTile)
    2644     else result:=eInvalid;
    2645 
    2646   sRandomMap:
    2647     if (Player=0) and MapGeneratorAvailable then
    2648       begin
    2649       CreateElevation;
    2650       PreviewElevation:=false;
    2651       CreateMap(false);
    2652       FillChar(ObserveLevel,MapSize*4,0);
    2653       DiscoverAll(Player,lObserveSuper);
    2654       end
    2655     else result:=eInvalid;
    2656 
    2657   sMapGeneratorRequest:
    2658     if not MapGeneratorAvailable then result:=eInvalid;
    2659 
    2660 {
    2661                     Client Deactivation Commands
    2662  ____________________________________________________________________
    2663 }
    2664   sTurn, sTurn-sExecute:
    2665     begin
    2666     AllHumansDead:=true;
    2667     for p1:=0 to nPl-1 do
    2668       if (1 shl p1 and GAlive<>0) and (bix[p1]=bixTerm) then
    2669         AllHumansDead:=false;
    2670     if (pDipActive>=0) // still in negotiation mode
    2671       or (pTurn=0) and ((GWinner>0) or (GTurn=MaxTurn)
    2672         or (Difficulty[0]>0) and AllHumansDead) then // game end reached
    2673       result:=eViolation
    2674     else if Command>=sExecute then
    2675       begin
    2676       if Mode=moPlaying then
    2677         begin
    2678         CL.State:=FormerCLState;
    2679         LogCityTileChanges;
    2680         {$IFNDEF SCR}
    2681         if pTurn=0 then
    2682           begin LogChanges; SaveGame('~'+LogFileName,true); end
    2683         {$ENDIF} 
    2684         end
    2685       else if (Mode=moMovie) and (pTurn=0) then
    2686         CallPlayer(cMovieEndTurn,0,nil^);
    2687       GWatching:=GWatching and GAlive or 1;
    2688       RW[pTurn].Happened:=0;
    2689       uixSelectedTransport:=-1;
    2690       SpyMission:=smSabotageProd;
    2691       if 1 shl pTurn and GAlive<>0 then
    2692         begin
    2693         // calculate checksum
    2694         TotalFood:=0;
    2695         TotalProd:=0;
    2696         for i:=0 to RW[pTurn].nCity-1 do if RW[pTurn].City[i].Loc>=0 then
    2697           begin
    2698           inc(TotalFood,RW[pTurn].City[i].Food);
    2699           inc(TotalProd,RW[pTurn].City[i].Prod);
    2700           end;
    2701         CheckSum:=TotalFood and 7 + TotalProd and 7 shl 3
    2702           + RW[pTurn].Money and 7 shl 6
    2703           + Worked[pTurn] div 100 and 7 shl 9;
    2704         end
    2705       else CheckSum:=0;
    2706 
    2707       if Mode<moPlaying then // check checksum
    2708         begin
    2709         if CheckSum<>Subject then
    2710           LoadOK:=false
    2711         end
    2712       else // save checksum
    2713         CL.Put(Command, Player, CheckSum, @Data);
    2714       {$IFDEF TEXTLOG}
    2715         CmdInfo:='';
    2716         if CheckSum and 7<>Subject and 7 then CmdInfo:=Format('***ERROR (Food %d) ',[(CheckSum and 7-Subject and 7+12) mod 8 -4])+CmdInfo;
    2717         if CheckSum shr 3 and 7<>Subject shr 3 and 7 then CmdInfo:='***ERROR (Prod) '+CmdInfo;
    2718         if CheckSum shr 6 and 7<>Subject shr 6 and 7 then CmdInfo:='***ERROR (Research) '+CmdInfo;
    2719         if CheckSum shr 9 and 7<>Subject shr 9 and 7 then CmdInfo:='***ERROR (Work) '+CmdInfo;
    2720       {$ENDIF}
    2721 
    2722       if 1 shl pTurn and GAlive<>0 then
    2723         begin
    2724         AfterTurn;
    2725         if Mode<moPlaying then
    2726           InsertTerritoryUpdateCommands;
    2727         //if bix[pTurn]=bixTerm then UpdateBorderHelper;
    2728         end;
    2729 
    2730       repeat
    2731         pTurn:=(pTurn+1) mod nPl;
    2732         if pTurn=0 then inc(GTurn);
    2733         if (bix[pTurn]>=0) and ((1 shl pTurn) and GAlive=0) then
    2734           begin // already made extinct -- continue statistics
    2735           Stat[stExplore,pTurn,GTurn]:=0;
    2736           Stat[stPop,pTurn,GTurn]:=0;
    2737           Stat[stTerritory,pTurn,GTurn]:=0;
    2738           Stat[stScience,pTurn,GTurn]:=0;
    2739           Stat[stWork,pTurn,GTurn]:=0;
    2740           Stat[stMil,pTurn,GTurn]:=0;
    2741           end;
    2742       until (pTurn=0) or ((1 shl pTurn and (GAlive or GWatching)<>0) and (GWinner=0));
    2743       if (Mode=moLoading_Fast) and ((GTurn=LoadTurn) or (GTurn=LoadTurn-1) and (pTurn>0)) then
    2744         Mode:=moLoading;
    2745       if Mode=moPlaying then
    2746         begin
    2747         CCCommand:=cTurn; CCPlayer:=pTurn;
    2748         Notify(ntNextPlayer)
    2749         end
    2750       else
    2751         begin
    2752         if GTurn=0 then BeforeTurn0
    2753         else BeforeTurn;
    2754         if (Mode=moMovie) and (pTurn=0) then
    2755           begin
    2756           Inform(pTurn);
    2757           CallPlayer(cMovieTurn,0,nil^);
    2758           end;
    2759         end;
    2760       {$IFDEF TEXTLOG}CmdInfo:=CmdInfo+Format('---Turn %d P%d---', [GTurn,pTurn]);{$ENDIF}
    2761       end;
    2762     end; // sTurn
    2763 
    2764   sBreak, sResign, sNextRound, sReload:
    2765     if Mode=moMovie then
    2766       MovieStopped:=true
    2767     else
    2768       begin
    2769       if Command=sReload then
    2770         begin
    2771         ok:= (Difficulty[0]=0) and (bix[0]<>bixNoTerm)
    2772           and (integer(Data)>=0) and (integer(Data)<GTurn);
    2773         for p1:=1 to nPl-1 do if bix[p1]=bixTerm then ok:=false;
    2774           // allow reload in AI-only games only
    2775         end
    2776       else ok:= Player=0;
    2777       if ok then
    2778         begin
    2779         if (Command=sBreak) or (Command=sResign) then Notify(ntBackOn);
    2780         for i:=0 to nBrain-1 do if Brain[i].Initialized then
    2781           begin
    2782           if i>=bixFirstAI then
    2783             Notify(ntDeinitModule+i);
    2784           CallClient(i, cBreakGame, nil^);
    2785           end;
    2786         Notify(ntEndInfo);
    2787         if (Command=sBreak) or (Command=sReload) then
    2788           begin
    2789           LogCityTileChanges;
    2790           LogChanges;
    2791           SaveGame(LogFileName,false);
    2792           end;
    2793         DeleteFile(SavePath+'~'+LogFileName);
    2794         EndGame;
    2795         case Command of
    2796           sBreak: Notify(ntStartGoRefresh);
    2797           sResign: Notify(ntStartGo);
    2798           sNextRound: StartNewGame(SavePath, LogFileName, MapFileName, lx, ly, LandMass,
    2799             MaxTurn);
    2800           sReload: LoadGame(SavePath,LogFileName,integer(Data),false);
    2801           end
    2802         end
    2803       else result:=eInvalid;
    2804       end;
    2805 
    2806   sAbandonMap, sSaveMap:
    2807     if Player=0 then
    2808       begin
    2809       if Command=sSaveMap then SaveMap(MapFileName);
    2810       Notify(ntBackOn);
    2811       Brain[bixTerm].Client(cBreakGame,-1,nil^);
    2812       ReleaseMapEditor;
    2813       if Command=sSaveMap then Notify(ntStartGoRefreshMaps)
    2814       else Notify(ntStartGo)
    2815       end
    2816     else result:=eInvalid;
    2817 
    2818   scContact..scContact+(nPl-1) shl 4,
    2819   scContact-sExecute..scContact-sExecute+(nPl-1) shl 4:
    2820     if (pDipActive>=0) or (1 shl (Command shr 4 and $f) and GAlive=0) then
    2821       result:=eInvalid
    2822     else if GWinner>0 then result:=eViolation // game end reached
    2823     else if RW[Player].Treaty[Command shr 4 and $f]=trNoContact then
    2824       result:=eNoPreq
    2825     else if GTurn<GColdWarStart+ColdWarTurns then result:=eColdWar
    2826     else if RW[Player].Government=gAnarchy then
    2827       result:=eAnarchy
    2828     else if RW[Command shr 4 and $f].Government=gAnarchy then
    2829       begin
    2830       result:=eAnarchy;
    2831       LastEndClientCommand:=scReject; //enable cancel treaty
    2832       pContacted:=Command shr 4 and $f;
    2833       end
    2834     else if Command>=sExecute then
    2835       begin // contact request
    2836       pContacted:=Command shr 4 and $f;
    2837       pDipActive:=pContacted;
    2838       assert(Mode=moPlaying);
    2839       Inform(pDipActive);
    2840       ChangeClientWhenDone(scContact,pDipActive,pTurn,4);
    2841       end;
    2842 
    2843   scReject, scReject-sExecute:
    2844     if LastEndClientCommand and $FF0F=scContact then
    2845       begin
    2846       if Command>=sExecute then
    2847         begin // contact requested and not accepted yet
    2848         pDipActive:=-1;
    2849         assert(Mode=moPlaying);
    2850         ChangeClientWhenDone(cContinue,pTurn,nil^,0);
     3301      else
     3302        result := eInvalid;
     3303
     3304    scDipStart, scDipStart - sExecute:
     3305      if LastEndClientCommand and $FF0F = scContact then
     3306      begin
     3307        if Command >= sExecute then
     3308        begin // accept contact
     3309          pContacted := pDipActive;
     3310          RW[pContacted].EnemyReport[pTurn].Credibility :=
     3311            RW[pTurn].Credibility;
     3312          pDipActive := pTurn;
     3313          assert(Mode = moPlaying);
     3314          IntServer(sIntHaveContact, pTurn, pContacted, nil^);
     3315          ChangeClientWhenDone(scDipStart, pDipActive, nil^, 0);
    28513316        end
    28523317      end
    2853     else result:=eInvalid;
    2854 
    2855   scDipStart, scDipStart-sExecute:
    2856     if LastEndClientCommand and $FF0F=scContact then
    2857       begin
    2858       if Command>=sExecute then
    2859         begin // accept contact
    2860         pContacted:=pDipActive;
    2861         RW[pContacted].EnemyReport[pTurn].Credibility:=RW[pTurn].Credibility;
    2862         pDipActive:=pTurn;
    2863         assert(Mode=moPlaying);
    2864         IntServer(sIntHaveContact,pTurn,pContacted,nil^);
    2865         ChangeClientWhenDone(scDipStart,pDipActive,nil^,0);
    2866         end
    2867       end
    2868     else result:=eInvalid;
    2869 
    2870   scDipNotice, scDipAccept, scDipCancelTreaty, scDipBreak,
    2871   scDipNotice-sExecute, scDipAccept-sExecute, scDipCancelTreaty-sExecute,
    2872   scDipBreak-sExecute:
    2873     if pDipActive>=0 then
    2874       begin
    2875       assert(Mode=moPlaying);
    2876       if pDipActive=pTurn then p1:=pContacted
    2877       else p1:=pTurn;
    2878       if (Command and not sExecute=scDipBreak and not sExecute)
    2879         and (LastEndClientCommand<>scDipBreak) then // ok
    2880       else if (Command and not sExecute=scDipNotice and not sExecute)
    2881         and ((LastEndClientCommand=scDipCancelTreaty)
    2882         or (LastEndClientCommand=scDipBreak)) then // ok
    2883       else if (Command and not sExecute=scDipAccept and not sExecute)
    2884         and (LastEndClientCommand=scDipOffer) then with LastOffer do
     3318      else
     3319        result := eInvalid;
     3320
     3321    scDipNotice, scDipAccept, scDipCancelTreaty, scDipBreak,
     3322      scDipNotice - sExecute, scDipAccept - sExecute,
     3323      scDipCancelTreaty - sExecute, scDipBreak - sExecute:
     3324      if pDipActive >= 0 then
     3325      begin
     3326        assert(Mode = moPlaying);
     3327        if pDipActive = pTurn then
     3328          p1 := pContacted
     3329        else
     3330          p1 := pTurn;
     3331        if (Command and not sExecute = scDipBreak and not sExecute) and
     3332          (LastEndClientCommand <> scDipBreak) then // ok
     3333        else if (Command and not sExecute = scDipNotice and not sExecute) and
     3334          ((LastEndClientCommand = scDipCancelTreaty) or
     3335          (LastEndClientCommand = scDipBreak)) then // ok
     3336        else if (Command and not sExecute = scDipAccept and not sExecute) and
     3337          (LastEndClientCommand = scDipOffer) then
     3338          with LastOffer do
     3339          begin
     3340            // check if offer can be accepted
     3341            if nDeliver + nCost = 0 then
     3342              result := eOfferNotAcceptable;
     3343            for i := 0 to nDeliver + nCost - 1 do
     3344              if Price[i] = opChoose then
     3345                result := eOfferNotAcceptable;
     3346            for i := 0 to nCost - 1 do
     3347              if not PayPrice(pDipActive, p1, Price[nDeliver + i], false) then
     3348                result := eOfferNotAcceptable;
     3349            if (Command >= sExecute) and (result >= rExecuted) then
     3350            begin
     3351              IntServer(sIntPayPrices + nDeliver + nCost, p1, pDipActive,
     3352                LastOffer);
     3353              // CheckContact;
     3354
     3355              // tell other players about ship part trades
     3356              HasShipChanged := false;
     3357              FillChar(ShowShipChange, SizeOf(ShowShipChange), 0);
     3358              for i := 0 to nDeliver + nCost - 1 do
     3359                if Price[i] and opMask = opShipParts then
     3360                begin
     3361                  HasShipChanged := true;
     3362                  if i >= nDeliver then
     3363                  begin // p1 has demanded from pDipActive
     3364                    ShowShipChange.Ship1Change[Price[i] shr 16 and 3] :=
     3365                      +integer(Price[i] and $FFFF);
     3366                    ShowShipChange.Ship2Change[Price[i] shr 16 and 3] :=
     3367                      -integer(Price[i] and $FFFF);
     3368                  end
     3369                  else
     3370                  begin // p1 has delivered to pDipActive
     3371                    ShowShipChange.Ship1Change[Price[i] shr 16 and 3] :=
     3372                      -integer(Price[i] and $FFFF);
     3373                    ShowShipChange.Ship2Change[Price[i] shr 16 and 3] :=
     3374                      +integer(Price[i] and $FFFF);
     3375                  end
     3376                end;
     3377              if HasShipChanged then
     3378              begin
     3379                ShowShipChange.Reason := scrTrade;
     3380                ShowShipChange.Ship1Owner := p1;
     3381                ShowShipChange.Ship2Owner := pDipActive;
     3382                for p2 := 0 to nPl - 1 do
     3383                  if (p2 <> p1) and (p2 <> pDipActive) and
     3384                    (1 shl p2 and (GAlive or GWatching) <> 0) then
     3385                  begin
     3386                    move(GShip, RW[p2].Ship, SizeOf(GShip));
     3387                    if 1 shl p2 and GWatching <> 0 then
     3388                      CallPlayer(cShowShipChange, p2, ShowShipChange);
     3389                  end
     3390              end
     3391            end;
     3392          end
     3393        else if (Command and not sExecute = scDipCancelTreaty and not sExecute)
     3394          and (RW[pDipActive].Treaty[p1] >= trPeace) then
    28853395        begin
    2886         // check if offer can be accepted
    2887         if nDeliver+nCost=0 then result:=eOfferNotAcceptable;
    2888         for i:=0 to nDeliver+nCost-1 do
    2889           if Price[i]=opChoose then result:=eOfferNotAcceptable;
    2890         for i:=0 to nCost-1 do
    2891           if not PayPrice(pDipActive,p1,Price[nDeliver+i],false) then
    2892             result:=eOfferNotAcceptable;
    2893         if (Command>=sExecute) and (result>=rExecuted) then
     3396          if (ServerVersion[pDipActive] >= $010100) and
     3397            (GTurn < RW[pDipActive].LastCancelTreaty[p1] + CancelTreatyTurns)
     3398          then
     3399            result := eCancelTreatyRush
     3400          else if Command >= sExecute then
    28943401          begin
    2895           IntServer(sIntPayPrices+nDeliver+nCost,p1,pDipActive,LastOffer);
    2896 //          CheckContact;
    2897 
    2898           // tell other players about ship part trades
    2899           HasShipChanged:=false;
    2900           fillchar(ShowShipChange,sizeof(ShowShipChange),0);
    2901           for i:=0 to nDeliver+nCost-1 do
    2902             if Price[i] and opMask=opShipParts then
     3402            IntServer(sIntCancelTreaty, pDipActive, p1, nil^);
     3403            for p2 := 0 to nPl - 1 do
     3404              if (p2 <> p1) and (1 shl p2 and PeaceEnded <> 0) then
    29033405              begin
    2904               HasShipChanged:=true;
    2905               if i>=nDeliver then
    2906                 begin // p1 has demanded from pDipActive
    2907                 ShowShipChange.Ship1Change[Price[i] shr 16 and 3]:=+integer(Price[i] and $FFFF);
    2908                 ShowShipChange.Ship2Change[Price[i] shr 16 and 3]:=-integer(Price[i] and $FFFF);
    2909                 end
    2910               else
    2911                 begin // p1 has delivered to pDipActive
    2912                 ShowShipChange.Ship1Change[Price[i] shr 16 and 3]:=-integer(Price[i] and $FFFF);
    2913                 ShowShipChange.Ship2Change[Price[i] shr 16 and 3]:=+integer(Price[i] and $FFFF);
    2914                 end
     3406                i := p1 shl 4 + pDipActive;
     3407                CallPlayer(cShowSupportAllianceAgainst, p2, i);
    29153408              end;
    2916           if HasShipChanged then
    2917             begin
    2918             ShowShipChange.Reason:=scrTrade;
    2919             ShowShipChange.Ship1Owner:=p1;
    2920             ShowShipChange.Ship2Owner:=pDipActive;
    2921             for p2:=0 to nPl-1 do
    2922               if (p2<>p1) and (p2<>pDipActive) and (1 shl p2 and (GAlive or GWatching)<>0) then
    2923                 begin
    2924                 move(GShip,RW[p2].Ship,SizeOf(GShip));
    2925                 if 1 shl p2 and GWatching<>0 then
    2926                   CallPlayer(cShowShipChange,p2,ShowShipChange);
    2927                 end
    2928             end
    2929           end;
    2930         end
    2931       else if (Command and not sExecute=scDipCancelTreaty and not sExecute)
    2932         and (RW[pDipActive].Treaty[p1]>=trPeace) then
    2933         begin
    2934         if (ServerVersion[pDipActive]>=$010100)
    2935           and (GTurn<RW[pDipActive].LastCancelTreaty[p1]+CancelTreatyTurns) then
    2936           result:=eCancelTreatyRush
    2937         else if Command>=sExecute then
    2938           begin
    2939           IntServer(sIntCancelTreaty,pDipActive,p1,nil^);
    2940           for p2:=0 to nPl-1 do
    2941             if (p2<>p1) and (1 shl p2 and PeaceEnded<>0) then
     3409            for p2 := 0 to nPl - 1 do
     3410              if (p2 <> p1) and (1 shl p2 and PeaceEnded <> 0) then
    29423411              begin
    2943               i:=p1 shl 4+pDipActive;
    2944               CallPlayer(cShowSupportAllianceAgainst,p2,i);
    2945               end;
    2946           for p2:=0 to nPl-1 do
    2947             if (p2<>p1) and (1 shl p2 and PeaceEnded<>0) then
    2948               begin
    2949               i:=p2;
    2950               CallPlayer(cShowCancelTreatyByAlliance,pDipActive,i);
     3412                i := p2;
     3413                CallPlayer(cShowCancelTreatyByAlliance, pDipActive, i);
    29513414              end;
    29523415          end
    29533416        end
    2954       else result:=eInvalid;
    2955       if (Command>=sExecute) and (result>=rExecuted) then
    2956         if LastEndClientCommand=scDipBreak then
     3417        else
     3418          result := eInvalid;
     3419        if (Command >= sExecute) and (result >= rExecuted) then
     3420          if LastEndClientCommand = scDipBreak then
    29573421          begin // break negotiation
    2958           pDipActive:=-1;
    2959           CallPlayer(cShowEndContact,pContacted,nil^);
    2960           ChangeClientWhenDone(cContinue,pTurn,nil^,0);
     3422            pDipActive := -1;
     3423            CallPlayer(cShowEndContact, pContacted, nil^);
     3424            ChangeClientWhenDone(cContinue, pTurn, nil^, 0);
    29613425          end
    2962         else
     3426          else
    29633427          begin
    2964           if (GTestFlags and tfUncover<>0) or (Difficulty[0]=0) then
    2965             with ShowNegoData do
     3428            if (GTestFlags and tfUncover <> 0) or (Difficulty[0] = 0) then
     3429              with ShowNegoData do
    29663430              begin // display negotiation in log window
    2967               pSender:=pDipActive;
    2968               pTarget:=p1;
    2969               Action:=Command;
    2970               Brain[bix[0]].Client(cShowNego,1 shl 16+3,ShowNegoData);
     3431                pSender := pDipActive;
     3432                pTarget := p1;
     3433                Action := Command;
     3434                Brain[bix[0]].Client(cShowNego, 1 shl 16 + 3, ShowNegoData);
    29713435              end;
    2972           pDipActive:=p1;
    2973           ChangeClientWhenDone(Command,pDipActive,nil^,0);
     3436            pDipActive := p1;
     3437            ChangeClientWhenDone(Command, pDipActive, nil^, 0);
    29743438          end
    29753439      end
    2976     else result:=eInvalid;
    2977 
    2978   scDipOffer, scDipOffer-sExecute:
    2979     if (pDipActive>=0) and (LastEndClientCommand<>scDipCancelTreaty)
    2980       and (LastEndClientCommand<>scDipBreak) then
    2981       if (LastEndClientCommand=scDipOffer) and (LastOffer.nDeliver+LastOffer.nCost
    2982         +TOffer(Data).nDeliver+TOffer(Data).nCost=0) then
     3440      else
     3441        result := eInvalid;
     3442
     3443    scDipOffer, scDipOffer - sExecute:
     3444      if (pDipActive >= 0) and (LastEndClientCommand <> scDipCancelTreaty) and
     3445        (LastEndClientCommand <> scDipBreak) then
     3446        if (LastEndClientCommand = scDipOffer) and
     3447          (LastOffer.nDeliver + LastOffer.nCost + TOffer(Data).nDeliver +
     3448          TOffer(Data).nCost = 0) then
    29833449        begin
    2984         if Command>=sExecute then
     3450          if Command >= sExecute then
    29853451          begin // agreed discussion end
    2986           pDipActive:=-1;
    2987           CallPlayer(cShowEndContact,pContacted,nil^);
    2988           assert(Mode=moPlaying);
    2989           ChangeClientWhenDone(cContinue,pTurn,nil^,0);
     3452            pDipActive := -1;
     3453            CallPlayer(cShowEndContact, pContacted, nil^);
     3454            assert(Mode = moPlaying);
     3455            ChangeClientWhenDone(cContinue, pTurn, nil^, 0);
    29903456          end
    29913457        end
    2992       else
     3458        else
    29933459        begin
    2994         // check if offer can be made
    2995         if pDipActive=pTurn then p1:=pContacted
    2996         else p1:=pTurn;
    2997         if RW[pDipActive].Treaty[p1]<trPeace then
     3460          // check if offer can be made
     3461          if pDipActive = pTurn then
     3462            p1 := pContacted
     3463          else
     3464            p1 := pTurn;
     3465          if RW[pDipActive].Treaty[p1] < trPeace then
    29983466          begin // no tribute allowed!
    2999           for i:=0 to TOffer(Data).nDeliver+TOffer(Data).nCost-1 do
    3000             if (TOffer(Data).Price[i] and opMask=opTribute) then result:=eInvalidOffer;
    3001           for i:=0 to TOffer(Data).nDeliver+TOffer(Data).nCost-1 do
    3002             if (TOffer(Data).Price[i]=opTreaty+trPeace) then result:=eOK;
     3467            for i := 0 to TOffer(Data).nDeliver + TOffer(Data).nCost - 1 do
     3468              if (TOffer(Data).Price[i] and opMask = opTribute) then
     3469                result := eInvalidOffer;
     3470            for i := 0 to TOffer(Data).nDeliver + TOffer(Data).nCost - 1 do
     3471              if (TOffer(Data).Price[i] = opTreaty + trPeace) then
     3472                result := eOK;
    30033473          end;
    3004         for i:=0 to TOffer(Data).nDeliver-1 do
    3005           if (TOffer(Data).Price[i]<>opChoose)
    3006             and not PayPrice(pDipActive,p1,TOffer(Data).Price[i],false) then
    3007             result:=eInvalidOffer;
    3008         if CountPrice(TOffer(Data),opTreaty)>1 then
    3009           result:=eInvalidOffer;
    3010         for i:=0 to nShipPart-1 do
    3011           if CountPrice(TOffer(Data),opShipParts+i shl 16)>1 then
    3012             result:=eInvalidOffer;
    3013         if CountPrice(TOffer(Data),opMoney)>1 then
    3014           result:=eInvalidOffer;
    3015         if CountPrice(TOffer(Data),opTribute)>1 then
    3016           result:=eInvalidOffer;
    3017         case CountPrice(TOffer(Data),opChoose) of
    3018           0:;
    3019           1:
    3020             if (TOffer(Data).nCost=0) or (TOffer(Data).nDeliver=0) then
    3021               result:=eInvalidOffer;
    3022           else result:=eInvalidOffer;
     3474          for i := 0 to TOffer(Data).nDeliver - 1 do
     3475            if (TOffer(Data).Price[i] <> opChoose) and
     3476              not PayPrice(pDipActive, p1, TOffer(Data).Price[i], false) then
     3477              result := eInvalidOffer;
     3478          if CountPrice(TOffer(Data), opTreaty) > 1 then
     3479            result := eInvalidOffer;
     3480          for i := 0 to nShipPart - 1 do
     3481            if CountPrice(TOffer(Data), opShipParts + i shl 16) > 1 then
     3482              result := eInvalidOffer;
     3483          if CountPrice(TOffer(Data), opMoney) > 1 then
     3484            result := eInvalidOffer;
     3485          if CountPrice(TOffer(Data), opTribute) > 1 then
     3486            result := eInvalidOffer;
     3487          case CountPrice(TOffer(Data), opChoose) of
     3488            0:
     3489              ;
     3490            1:
     3491              if (TOffer(Data).nCost = 0) or (TOffer(Data).nDeliver = 0) then
     3492                result := eInvalidOffer;
     3493          else
     3494            result := eInvalidOffer;
    30233495          end;
    30243496
    3025         // !!! check here if cost can be demanded
    3026 
    3027         if (Command>=sExecute) and (result>=rExecuted) then
     3497          // !!! check here if cost can be demanded
     3498
     3499          if (Command >= sExecute) and (result >= rExecuted) then
    30283500          begin
    3029           OfferFullySupported:= (TOffer(Data).nDeliver<=2)
    3030             and (TOffer(Data).nCost<=2); // >2 no more allowed
    3031           for i:=0 to TOffer(Data).nDeliver+TOffer(Data).nCost-1 do
     3501            OfferFullySupported := (TOffer(Data).nDeliver <= 2) and
     3502              (TOffer(Data).nCost <= 2); // >2 no more allowed
     3503            for i := 0 to TOffer(Data).nDeliver + TOffer(Data).nCost - 1 do
    30323504            begin
    3033             if TOffer(Data).Price[i] and opMask=opTribute then
    3034               OfferFullySupported:=false; // tribute no more part of the game
    3035             if (TOffer(Data).Price[i] and opMask=opTreaty)
    3036               and (TOffer(Data).Price[i]-opTreaty<=RW[pDipActive].Treaty[p1]) then
    3037               OfferFullySupported:=false; // agreed treaty end no more part of the game
    3038             if TOffer(Data).Price[i]=opTreaty+trCeaseFire then
    3039               OfferFullySupported:=false; // ceasefire no more part of the game
     3505              if TOffer(Data).Price[i] and opMask = opTribute then
     3506                OfferFullySupported := false;
     3507              // tribute no more part of the game
     3508              if (TOffer(Data).Price[i] and opMask = opTreaty) and
     3509                (TOffer(Data).Price[i] - opTreaty <= RW[pDipActive].Treaty[p1])
     3510              then
     3511                OfferFullySupported := false;
     3512              // agreed treaty end no more part of the game
     3513              if TOffer(Data).Price[i] = opTreaty + trCeaseFire then
     3514                OfferFullySupported := false;
     3515              // ceasefire no more part of the game
    30403516            end;
    3041           if not OfferFullySupported then
     3517            if not OfferFullySupported then
    30423518            begin
    3043             // some elements have been removed from the game -
    3044             // automatically respond will null-offer
    3045             LastOffer.nDeliver:=0;
    3046             LastOffer.nCost:=0;
    3047             ChangeClientWhenDone(scDipOffer,pDipActive,LastOffer,SizeOf(LastOffer));
     3519              // some elements have been removed from the game -
     3520              // automatically respond will null-offer
     3521              LastOffer.nDeliver := 0;
     3522              LastOffer.nCost := 0;
     3523              ChangeClientWhenDone(scDipOffer, pDipActive, LastOffer,
     3524                SizeOf(LastOffer));
    30483525            end
    3049           else
     3526            else
    30503527            begin
    3051             if (GTestFlags and tfUncover<>0) or (Difficulty[0]=0) then
    3052               with ShowNegoData do
     3528              if (GTestFlags and tfUncover <> 0) or (Difficulty[0] = 0) then
     3529                with ShowNegoData do
    30533530                begin // display negotiation in log window
    3054                 pSender:=pDipActive;
    3055                 pTarget:=p1;
    3056                 Action:=Command;
    3057                 Offer:=TOffer(Data);
    3058                 Brain[bix[0]].Client(cShowNego,1 shl 16+3,ShowNegoData);
     3531                  pSender := pDipActive;
     3532                  pTarget := p1;
     3533                  Action := Command;
     3534                  Offer := TOffer(Data);
     3535                  Brain[bix[0]].Client(cShowNego, 1 shl 16 + 3, ShowNegoData);
    30593536                end;
    3060             LastOffer:=TOffer(Data);
    3061             // show offered things to receiver
    3062             for i:=0 to LastOffer.nDeliver-1 do
    3063               ShowPrice(pDipActive,p1,LastOffer.Price[i]);
    3064             pDipActive:=p1;
    3065             assert(Mode=moPlaying);
    3066             ChangeClientWhenDone(scDipOffer,pDipActive,LastOffer,SizeOf(LastOffer));
     3537              LastOffer := TOffer(Data);
     3538              // show offered things to receiver
     3539              for i := 0 to LastOffer.nDeliver - 1 do
     3540                ShowPrice(pDipActive, p1, LastOffer.Price[i]);
     3541              pDipActive := p1;
     3542              assert(Mode = moPlaying);
     3543              ChangeClientWhenDone(scDipOffer, pDipActive, LastOffer,
     3544                SizeOf(LastOffer));
    30673545            end
    30683546          end
    30693547        end
    3070     else result:=eInvalid;
    3071 
    3072 {
    3073                           General Commands
    3074  ____________________________________________________________________
    3075 }
    3076   sClearTestFlag:
    3077     if Player=0 then
    3078       begin
    3079       {$IFDEF TEXTLOG}CmdInfo:=Format('ClearTestFlag %x', [Subject]);{$ENDIF}
    3080       ClearTestFlags(Subject);
     3548      else
     3549        result := eInvalid;
     3550
     3551    {
     3552      General Commands
     3553      ____________________________________________________________________
     3554    }
     3555    sClearTestFlag:
     3556      if Player = 0 then
     3557      begin
     3558{$IFDEF TEXTLOG}CmdInfo := Format('ClearTestFlag %x', [Subject]); {$ENDIF}
     3559        ClearTestFlags(Subject);
    30813560      end
    3082     else result:=eInvalid;
    3083 
    3084   sSetTestFlag:
    3085     if Player=0 then
    3086       begin
    3087       {$IFDEF TEXTLOG}CmdInfo:=Format('SetTestFlag %x', [Subject]);{$ENDIF}
    3088       SetTestFlags(Player,Subject);
    3089 //      CheckContact;
     3561      else
     3562        result := eInvalid;
     3563
     3564    sSetTestFlag:
     3565      if Player = 0 then
     3566      begin
     3567{$IFDEF TEXTLOG}CmdInfo := Format('SetTestFlag %x', [Subject]); {$ENDIF}
     3568        SetTestFlags(Player, Subject);
     3569        // CheckContact;
    30903570      end
    3091     else result:=eInvalid;
    3092 
    3093   sSetGovernment, sSetGovernment-sExecute:
    3094     begin
    3095     {$IFDEF TEXTLOG}CmdInfo:=Format('SetGovernment P%d: %d', [Player,Subject]);{$ENDIF}
    3096     if RW[Player].Happened and phChangeGov=0 then result:=eViolation
    3097     else if RW[Player].Government=Subject then result:=eNotChanged
    3098     else if (Subject>=nGov) then result:=eInvalid
    3099     else if (Subject>=gMonarchy)
    3100       and (RW[Player].Tech[GovPreq[Subject]]<tsApplicable) then
    3101       result:=eNoPreq
    3102     else if Command>=sExecute then
    3103       begin
    3104       RW[Player].Government:=Subject;
    3105       for p1:=0 to nPl-1 do if (p1<>Player) and ((GAlive or GWatching) and (1 shl p1)<>0) then
    3106         RW[p1].EnemyReport[Player].Government:=Subject;
    3107       end
    3108     end;
    3109 
    3110   sSetRates, sSetRates-sExecute:
    3111     begin
    3112     {$IFDEF TEXTLOG}CmdInfo:=Format('SetRates P%d: %d/%d', [Player,Subject and $f *10,Subject shr 4 *10]);{$ENDIF}
    3113     if Subject and $f+Subject shr 4>10 then result:=eInvalid
    3114     else if (RW[Player].TaxRate=Subject and $f *10)
    3115       and (RW[Player].LuxRate=Subject shr 4 *10) then
    3116       result:=eNotChanged
    3117     else if Command>=sExecute then
    3118       begin
    3119       RW[Player].TaxRate:=Subject and $f *10;
    3120       RW[Player].LuxRate:=Subject shr 4 *10;
    3121       end
    3122     end;
    3123 
    3124   sRevolution:
    3125     begin
    3126     {$IFDEF TEXTLOG}CmdInfo:=Format('Revolution P%d', [Player]);{$ENDIF}
    3127     if RW[Player].Government=gAnarchy then result:=eInvalid
    3128     else
    3129       begin
    3130       RW[Player].Government:=gAnarchy;
    3131       for p1:=0 to nPl-1 do if (p1<>Player) and ((GAlive or GWatching) and (1 shl p1)<>0) then
    3132         RW[p1].EnemyReport[Player].Government:=gAnarchy;
    3133       RW[Player].AnarchyStart:=GTurn;
    3134       end;
    3135     end;
    3136 
    3137   sSetResearch, sSetResearch-sExecute: with RW[Player] do
    3138     begin
    3139     {$IFDEF TEXTLOG}CmdInfo:=Format('SetResearch P%d: %d', [Player,Subject]);{$ENDIF}
    3140     if (Happened and phTech<>0)
    3141       and ((Subject<nAdv) or (Subject=adMilitary)) then
    3142       begin
    3143       if (Mode=moPlaying) and (Subject=adMilitary)
    3144         and (DevModelTurn[Player]<>GTurn) then
    3145         result:=eNoModel
    3146       else if Subject<>adMilitary then
     3571      else
     3572        result := eInvalid;
     3573
     3574    sSetGovernment, sSetGovernment - sExecute:
     3575      begin
     3576{$IFDEF TEXTLOG}CmdInfo := Format('SetGovernment P%d: %d', [Player, Subject]); {$ENDIF}
     3577        if RW[Player].Happened and phChangeGov = 0 then
     3578          result := eViolation
     3579        else if RW[Player].Government = Subject then
     3580          result := eNotChanged
     3581        else if (Subject >= nGov) then
     3582          result := eInvalid
     3583        else if (Subject >= gMonarchy) and
     3584          (RW[Player].Tech[GovPreq[Subject]] < tsApplicable) then
     3585          result := eNoPreq
     3586        else if Command >= sExecute then
    31473587        begin
    3148         if Subject=futComputingTechnology then
     3588          RW[Player].Government := Subject;
     3589          for p1 := 0 to nPl - 1 do
     3590            if (p1 <> Player) and ((GAlive or GWatching) and (1 shl p1) <> 0)
     3591            then
     3592              RW[p1].EnemyReport[Player].Government := Subject;
     3593        end
     3594      end;
     3595
     3596    sSetRates, sSetRates - sExecute:
     3597      begin
     3598{$IFDEF TEXTLOG}CmdInfo := Format('SetRates P%d: %d/%d', [Player, Subject and $F * 10, Subject shr 4 * 10]); {$ENDIF}
     3599        if Subject and $F + Subject shr 4 > 10 then
     3600          result := eInvalid
     3601        else if (RW[Player].TaxRate = Subject and $F * 10) and
     3602          (RW[Player].LuxRate = Subject shr 4 * 10) then
     3603          result := eNotChanged
     3604        else if Command >= sExecute then
     3605        begin
     3606          RW[Player].TaxRate := Subject and $F * 10;
     3607          RW[Player].LuxRate := Subject shr 4 * 10;
     3608        end
     3609      end;
     3610
     3611    sRevolution:
     3612      begin
     3613{$IFDEF TEXTLOG}CmdInfo := Format('Revolution P%d', [Player]); {$ENDIF}
     3614        if RW[Player].Government = gAnarchy then
     3615          result := eInvalid
     3616        else
     3617        begin
     3618          RW[Player].Government := gAnarchy;
     3619          for p1 := 0 to nPl - 1 do
     3620            if (p1 <> Player) and ((GAlive or GWatching) and (1 shl p1) <> 0)
     3621            then
     3622              RW[p1].EnemyReport[Player].Government := gAnarchy;
     3623          RW[Player].AnarchyStart := GTurn;
     3624        end;
     3625      end;
     3626
     3627    sSetResearch, sSetResearch - sExecute:
     3628      with RW[Player] do
     3629      begin
     3630{$IFDEF TEXTLOG}CmdInfo := Format('SetResearch P%d: %d', [Player, Subject]);
     3631        {$ENDIF}
     3632        if (Happened and phTech <> 0) and
     3633          ((Subject < nAdv) or (Subject = adMilitary)) then
     3634        begin
     3635          if (Mode = moPlaying) and (Subject = adMilitary) and
     3636            (DevModelTurn[Player] <> GTurn) then
     3637            result := eNoModel
     3638          else if Subject <> adMilitary then
    31493639          begin
    3150           if Tech[Subject]>=MaxFutureTech_Computing then result:=eInvalid
     3640            if Subject = futComputingTechnology then
     3641            begin
     3642              if Tech[Subject] >= MaxFutureTech_Computing then
     3643                result := eInvalid
     3644            end
     3645            else if Subject in FutureTech then
     3646            begin
     3647              if Tech[Subject] >= MaxFutureTech then
     3648                result := eInvalid
     3649            end
     3650            else if Tech[Subject] >= tsApplicable then
     3651              result := eInvalid; // already discovered
     3652            if Tech[Subject] <> tsSeen then // look if preqs met
     3653              if AdvPreq[Subject, 2] <> preNone then
     3654              begin // 2 of 3 required
     3655                i := 0;
     3656                for j := 0 to 2 do
     3657                  if Tech[AdvPreq[Subject, j]] >= tsApplicable then
     3658                    inc(i);
     3659                if i < 2 then
     3660                  result := eNoPreq
     3661              end
     3662              else if (AdvPreq[Subject, 0] <> preNone) and
     3663                (Tech[AdvPreq[Subject, 0]] < tsApplicable) or
     3664                (AdvPreq[Subject, 1] <> preNone) and
     3665                (Tech[AdvPreq[Subject, 1]] < tsApplicable) then
     3666                result := eNoPreq
     3667          end;
     3668          if (result = eOK) and (Command >= sExecute) then
     3669          begin
     3670            if (Mode = moPlaying) and (Subject = adMilitary) then
     3671              IntServer(sIntSetDevModel, Player, 0, DevModel.Kind);
     3672            // save DevModel, because sctModel commands are not logged
     3673            ResearchTech := Subject;
    31513674          end
    3152         else if Subject in FutureTech then
     3675        end
     3676        else
     3677          result := eViolation;
     3678      end;
     3679
     3680    sStealTech, sStealTech - sExecute:
     3681      begin
     3682{$IFDEF TEXTLOG}CmdInfo := Format('StealTech P%d: %d', [Player, Subject]);
     3683        {$ENDIF}
     3684        if RW[Player].Happened and phStealTech = 0 then
     3685          result := eInvalid
     3686        else if (Subject >= nAdv) or (Subject in FutureTech) or
     3687          (RW[Player].Tech[Subject] >= tsSeen) or
     3688          (RW[GStealFrom].Tech[Subject] < tsApplicable) then
     3689          result := eInvalid
     3690        else if Command >= sExecute then
     3691        begin
     3692          SeeTech(Player, Subject);
     3693          dec(RW[Player].Happened, phStealTech);
     3694        end
     3695      end;
     3696
     3697    sSetAttitude .. sSetAttitude + (nPl - 1) shl 4,
     3698      sSetAttitude - sExecute .. sSetAttitude - sExecute + (nPl - 1) shl 4:
     3699      begin
     3700        p1 := Command shr 4 and $F;
     3701{$IFDEF TEXTLOG}CmdInfo := Format('SetAttitude P%d to P%d: %d', [Player, p1, Subject]); {$ENDIF}
     3702        if (Subject >= nAttitude) or (p1 >= nPl) or
     3703          (RW[Player].EnemyReport[p1] = nil) then
     3704          result := eInvalid
     3705        else if RW[Player].Treaty[p1] = trNoContact then
     3706          result := eNoPreq
     3707        else if RW[Player].Attitude[p1] = Subject then
     3708          result := eNotChanged
     3709        else if Command >= sExecute then
     3710        begin
     3711          RW[Player].Attitude[p1] := Subject;
     3712          RW[p1].EnemyReport[Player].Attitude := Subject;
     3713        end
     3714      end;
     3715
     3716    sCancelTreaty, sCancelTreaty - sExecute:
     3717      if (LastEndClientCommand <> scReject) or
     3718        (RW[Player].Treaty[pContacted] < trPeace) then
     3719        result := eInvalid
     3720      else if (ServerVersion[Player] >= $010100) and
     3721        (GTurn < RW[Player].LastCancelTreaty[pContacted] + CancelTreatyTurns)
     3722      then
     3723        result := eCancelTreatyRush
     3724      else if Command >= sExecute then
     3725      begin
     3726        CallPlayer(cShowCancelTreaty, pContacted, Player);
     3727        IntServer(sIntCancelTreaty, Player, pContacted, nil^);
     3728        for p2 := 0 to nPl - 1 do
     3729          if (p2 <> pContacted) and (1 shl p2 and PeaceEnded <> 0) then
    31533730          begin
    3154           if Tech[Subject]>=MaxFutureTech then result:=eInvalid
     3731            i := pContacted shl 4 + Player;
     3732            CallPlayer(cShowSupportAllianceAgainst, p2, i);
     3733          end;
     3734        for p2 := 0 to nPl - 1 do
     3735          if (p2 <> pContacted) and (1 shl p2 and PeaceEnded <> 0) then
     3736          begin
     3737            i := p2;
     3738            CallPlayer(cShowCancelTreatyByAlliance, Player, i);
     3739          end;
     3740        LastEndClientCommand := sTurn;
     3741      end;
     3742
     3743    {
     3744      Model Related Commands
     3745      ____________________________________________________________________
     3746    }
     3747    sCreateDevModel, sCreateDevModel - sExecute:
     3748      begin
     3749{$IFDEF TEXTLOG}CmdInfo := Format('CreateDevModel P%d', [Player]); {$ENDIF}
     3750        if Subject >= 4 then
     3751          result := eInvalid
     3752        else if (upgrade[Subject, 0].Preq <> preNone) and
     3753          (RW[Player].Tech[upgrade[Subject, 0].Preq] < tsApplicable) then
     3754          result := eNoPreq
     3755        else if Command >= sExecute then
     3756        begin
     3757          with RW[Player].DevModel do
     3758          begin
     3759            Domain := Subject;
     3760            MStrength := 0;
     3761            MTrans := 0;
     3762            MCost := 0;
     3763            Upgrades := 0;
     3764            FutureMCost := 0;
     3765            for i := 0 to nUpgrade - 1 do
     3766              with upgrade[Domain, i] do
     3767                if (Preq = preNone) or (Preq >= 0) and
     3768                  ((RW[Player].Tech[Preq] >= tsApplicable) or
     3769                  (Preq in FutureTech) and (RW[Player].Tech[Preq] >= 0)) then
     3770                begin
     3771                  if Preq in FutureTech then
     3772                  begin
     3773                    j := RW[Player].Tech[Preq];
     3774                    inc(FutureMCost, j * Cost);
     3775                  end
     3776                  else
     3777                  begin
     3778                    j := 1;
     3779                    if Cost > MCost then
     3780                      MCost := Cost;
     3781                  end;
     3782                  inc(Upgrades, 1 shl i);
     3783                  inc(MStrength, j * Strength);
     3784                  inc(MTrans, j * Trans);
     3785                end;
     3786            inc(MCost, FutureMCost);
     3787            FillChar(Cap, SizeOf(Cap), 0);
     3788            Cap[mcOffense] := 2;
     3789            Cap[mcDefense] := 1;
     3790            for i := 0 to nFeature - 1 do
     3791              with Feature[i] do
     3792                if (1 shl Domain and Domains <> 0) and
     3793                  ((Preq = preNone) or (Preq = preSun) and
     3794                  (GWonder[woSun].EffectiveOwner = Player) or (Preq >= 0) and
     3795                  (RW[Player].Tech[Preq] >= tsApplicable)) and (i in AutoFeature)
     3796                then
     3797                  Cap[i] := 1;
     3798            MaxWeight := 5;
     3799            if (WeightPreq7[Domain] <> preNA) and
     3800              (RW[Player].Tech[WeightPreq7[Domain]] >= tsApplicable) then
     3801              MaxWeight := 7;
     3802            if (WeightPreq10[Domain] <> preNA) and
     3803              (RW[Player].Tech[WeightPreq10[Domain]] >= tsApplicable) then
     3804              if Domain = dSea then
     3805                MaxWeight := 9
     3806              else
     3807                MaxWeight := 10;
     3808          end;
     3809          CalculateModel(RW[Player].DevModel);
     3810          DevModelTurn[Player] := GTurn;
     3811        end
     3812      end;
     3813
     3814    sSetDevModelCap .. sSetDevModelCap + $3F0,
     3815      sSetDevModelCap - sExecute .. sSetDevModelCap - sExecute + $3F0:
     3816      begin
     3817{$IFDEF TEXTLOG}CmdInfo := Format('SetDevModelCap P%d', [Player]); {$ENDIF}
     3818        if Subject >= nFeature then
     3819          result := eInvalid
     3820        else if DevModelTurn[Player] = GTurn then
     3821        begin
     3822          NewCap := Command shr 4 and $3F; { new value }
     3823          with RW[Player].DevModel do
     3824            if 1 shl Domain and Feature[Subject].Domains = 0 then
     3825              result := eDomainMismatch
     3826            else if not((Feature[Subject].Preq = preNone) or
     3827              (Feature[Subject].Preq = preSun) and
     3828              (GWonder[woSun].EffectiveOwner = Player) or
     3829              (Feature[Subject].Preq >= 0) and
     3830              (RW[Player].Tech[Feature[Subject].Preq] >= tsApplicable)) then
     3831              result := eNoPreq
     3832            else
     3833            begin
     3834              if (Subject in AutoFeature) or (Subject = mcDefense) then
     3835                MinCap := 1
     3836              else
     3837                MinCap := 0; { MinCap - minimum use of feature }
     3838              if Subject >= mcFirstNonCap then
     3839                MaxCap := 1
     3840              else if Subject = mcDefense then
     3841              begin
     3842                if Domain = dGround then
     3843                  MaxCap := 2
     3844                else
     3845                  MaxCap := 3;
     3846                if RW[Player].Tech[adSteel] >= tsApplicable then
     3847                  inc(MaxCap)
     3848              end
     3849              else
     3850                MaxCap := 8; { MaxCap - maximum use of this feature }
     3851              if (Domain = dGround) and (Subject = mcDefense) then
     3852                CapWeight := 2
     3853              else
     3854                CapWeight := Feature[Subject].Weight;
     3855              if (NewCap < MinCap) or (NewCap > MaxCap) or
     3856                (Weight + (NewCap - Cap[Subject]) * CapWeight > MaxWeight) then
     3857                result := eViolation
     3858              else if Command >= sExecute then
     3859              begin
     3860                Cap[Subject] := NewCap;
     3861
     3862                // mutual feature exclusion
     3863                case Subject of
     3864                  mcSub:
     3865                    begin
     3866                      if ServerVersion[Player] >= $010103 then
     3867                        Cap[mcSeaTrans] := 0;
     3868                      Cap[mcArtillery] := 0;
     3869                      Cap[mcCarrier] := 0;
     3870                      if Cap[mcDefense] > 2 then
     3871                        Cap[mcDefense] := 2
     3872                    end;
     3873                  mcSeaTrans:
     3874                    begin
     3875                      if ServerVersion[Player] >= $010103 then
     3876                        Cap[mcSub] := 0;
     3877                    end;
     3878                  mcCarrier:
     3879                    Cap[mcSub] := 0;
     3880                  mcArtillery:
     3881                    Cap[mcSub] := 0;
     3882                  mcAlpine:
     3883                    begin
     3884                      Cap[mcOver] := 0;
     3885                      Cap[mcMob] := 0;
     3886                    end;
     3887                  mcOver:
     3888                    Cap[mcAlpine] := 0;
     3889                  mcMob:
     3890                    begin
     3891                      Cap[mcAlpine] := 0;
     3892                    end;
     3893                end;
     3894
     3895                CalculateModel(RW[Player].DevModel);
     3896              end
     3897            end;
     3898        end
     3899        else
     3900          result := eNoModel;
     3901      end;
     3902
     3903    {
     3904      Unit Related Commands
     3905      ____________________________________________________________________
     3906    }
     3907    sRemoveUnit, sRemoveUnit - sExecute:
     3908      begin
     3909{$IFDEF TEXTLOG}CmdInfo := Format('RemoveUnit P%d Mod%d Loc%d', [Player, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc]); {$ENDIF}
     3910        if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
     3911          result := eInvalid
     3912        else
     3913        begin
     3914          result := eRemoved;
     3915          Loc0 := RW[Player].Un[Subject].Loc;
     3916          if RealMap[Loc0] and fCity <> 0 then { check utilize }
     3917          begin
     3918            SearchCity(Loc0, Player, cix1);
     3919            with RW[Player].City[cix1] do
     3920            begin
     3921              if (RW[Player].Model[RW[Player].Un[Subject].mix].Kind = mkCaravan)
     3922                and ((Project and cpImp = 0) or
     3923                (Imp[Project and cpIndex].Kind <> ikShipPart)) or
     3924                (Project and cpImp = 0) and
     3925                (RW[Player].Model[Project and cpIndex].Kind <> mkCaravan) then
     3926                result := eUtilized;
     3927              if Command >= sExecute then
     3928              begin
     3929                if result = eUtilized then
     3930                begin
     3931                  with RW[Player].Un[Subject] do
     3932                  begin
     3933                    Cost := integer(RW[Player].Model[mix].Cost) * Health *
     3934                      BuildCostMod[Difficulty[Player]] div 1200;
     3935                    if RW[Player].Model[mix].Cap[mcLine] > 0 then
     3936                      Cost := Cost div 2;
     3937                  end;
     3938                  if Project and (cpImp + cpIndex) = cpImp + imTrGoods then
     3939                    inc(RW[Player].Money, Cost)
     3940                  else
     3941                  begin
     3942                    inc(Prod, Cost * 2 div 3);
     3943                    Project0 := Project0 and not cpCompleted;
     3944                    if Project0 and not cpAuto <> Project and not cpAuto then
     3945                      Project0 := Project;
     3946                    Prod0 := Prod;
     3947                  end
     3948                end;
     3949                RemoveUnit_UpdateMap(Player, Subject);
     3950              end;
     3951            end;
    31553952          end
    3156         else if Tech[Subject]>=tsApplicable then
    3157           result:=eInvalid; // already discovered
    3158         if Tech[Subject]<>tsSeen then // look if preqs met
    3159           if AdvPreq[Subject,2]<>preNone then
    3160             begin // 2 of 3 required
    3161             i:=0;
    3162             for j:=0 to 2 do
    3163               if Tech[AdvPreq[Subject,j]]>=tsApplicable then inc(i);
    3164             if i<2 then result:=eNoPreq
    3165             end
    3166           else if (AdvPreq[Subject,0]<>preNone)
    3167             and (Tech[AdvPreq[Subject,0]]<tsApplicable)
    3168             or (AdvPreq[Subject,1]<>preNone)
    3169             and (Tech[AdvPreq[Subject,1]]<tsApplicable) then
    3170             result:=eNoPreq
     3953          else if Command >= sExecute then
     3954            RemoveUnit_UpdateMap(Player, Subject);
     3955        end
     3956      end;
     3957
     3958    sSetUnitHome, sSetUnitHome - sExecute:
     3959      begin
     3960{$IFDEF TEXTLOG}CmdInfo := Format('SetUnitHome P%d Mod%d Loc%d', [Player, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc]); {$ENDIF}
     3961        if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
     3962          result := eInvalid
     3963        else
     3964        begin
     3965          Loc0 := RW[Player].Un[Subject].Loc;
     3966          if RealMap[Loc0] and fCity = 0 then
     3967            result := eInvalid
     3968          else
     3969          begin
     3970            SearchCity(Loc0, Player, cix1);
     3971            if RW[Player].City[cix1].Flags and chCaptured <> 0 then
     3972              result := eViolation
     3973            else if Command >= sExecute then
     3974              RW[Player].Un[Subject].Home := cix1
     3975          end
     3976        end
     3977      end;
     3978
     3979    sSetSpyMission .. sSetSpyMission + (nSpyMission - 1) shl 4,
     3980      sSetSpyMission - sExecute .. sSetSpyMission - sExecute +
     3981      (nSpyMission - 1) shl 4:
     3982      if Command >= sExecute then
     3983        SpyMission := Command shr 4 and $F;
     3984
     3985    sLoadUnit, sLoadUnit - sExecute:
     3986      begin
     3987{$IFDEF TEXTLOG}CmdInfo := Format('LoadUnit P%d Mod%d Loc%d', [Player, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc]); {$ENDIF}
     3988        if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
     3989          result := eInvalid
     3990        else
     3991          result := LoadUnit(Player, Subject, Command < sExecute);
     3992      end;
     3993
     3994    sUnloadUnit, sUnloadUnit - sExecute:
     3995      begin
     3996{$IFDEF TEXTLOG}CmdInfo := Format('UnloadUnit P%d Mod%d Loc%d', [Player, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc]); {$ENDIF}
     3997        if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
     3998          result := eInvalid
     3999        else
     4000          result := UnloadUnit(Player, Subject, Command < sExecute)
     4001      end;
     4002
     4003    sSelectTransport, sSelectTransport - sExecute:
     4004      if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
     4005        result := eInvalid
     4006      else
     4007        with RW[Player].Model[RW[Player].Un[Subject].mix] do
     4008        begin
     4009          if Cap[mcSeaTrans] + Cap[mcAirTrans] + Cap[mcCarrier] = 0 then
     4010            result := eInvalid
     4011          else if Command >= sExecute then
     4012            uixSelectedTransport := Subject;
    31714013        end;
    3172       if (result=eOK) and (Command>=sExecute) then
     4014
     4015    sCreateUnit .. sCreateUnit + (nPl - 1) shl 4,
     4016      sCreateUnit - sExecute .. sCreateUnit - sExecute + (nPl - 1) shl 4:
     4017      if (GTestFlags and tfUncover <> 0) or (Difficulty[Player] = 0)
     4018      then { supervisor only command }
     4019      begin
     4020        p1 := Command shr 4 and $F;
     4021        Loc1 := integer(Data);
     4022        if (Occupant[Loc1] >= 0) and (p1 <> Occupant[Loc1]) or
     4023          (RealMap[Loc1] and fCity <> 0) and
     4024          (RealMap[Loc1] shr 27 <> Cardinal(p1)) or
     4025          (RW[p1].Model[Subject].Domain < dAir) and
     4026          ((RW[p1].Model[Subject].Domain = dSea) <> (RealMap[integer(Data)] and
     4027          fTerrain < fGrass)) then
     4028          result := eViolation
     4029        else if Command >= sExecute then
    31734030        begin
    3174         if (Mode=moPlaying) and (Subject=adMilitary) then
    3175           IntServer(sIntSetDevModel,Player,0,DevModel.Kind);
    3176           // save DevModel, because sctModel commands are not logged
    3177         ResearchTech:=Subject;
     4031          CreateUnit(p1, Subject);
     4032          RW[p1].Un[RW[p1].nUn - 1].Loc := integer(Data);
     4033          PlaceUnit(p1, RW[p1].nUn - 1);
     4034          UpdateUnitMap(integer(Data));
    31784035        end
    31794036      end
    3180     else result:=eViolation;
    3181     end;
    3182 
    3183   sStealTech, sStealTech-sExecute:
    3184     begin
    3185     {$IFDEF TEXTLOG}CmdInfo:=Format('StealTech P%d: %d', [Player,Subject]);{$ENDIF}
    3186     if RW[Player].Happened and phStealTech=0 then result:=eInvalid
    3187     else if (Subject>=nAdv) or (Subject in FutureTech)
    3188       or (RW[Player].Tech[Subject]>=tsSeen)
    3189       or (RW[GStealFrom].Tech[Subject]<tsApplicable) then
    3190       result:=eInvalid
    3191     else if Command>=sExecute then
    3192       begin
    3193       SeeTech(Player,Subject);
    3194       dec(RW[Player].Happened,phStealTech);
    3195       end
    3196     end;
    3197 
    3198   sSetAttitude..sSetAttitude+(nPl-1) shl 4,
    3199   sSetAttitude-sExecute..sSetAttitude-sExecute+(nPl-1) shl 4:
    3200     begin
    3201     p1:=Command shr 4 and $f;
    3202     {$IFDEF TEXTLOG}CmdInfo:=Format('SetAttitude P%d to P%d: %d', [Player,p1,Subject]);{$ENDIF}
    3203     if (Subject>=nAttitude) or (p1>=nPl)
    3204       or (RW[Player].EnemyReport[p1]=nil) then
    3205       result:=eInvalid
    3206     else if RW[Player].Treaty[p1]=trNoContact then
    3207       result:=eNoPreq
    3208     else if RW[Player].Attitude[p1]=Subject then
    3209       result:=eNotChanged
    3210     else if Command>=sExecute then
    3211       begin
    3212       RW[Player].Attitude[p1]:=Subject;
    3213       RW[p1].EnemyReport[Player].Attitude:=Subject;
    3214       end
    3215     end;
    3216 
    3217   sCancelTreaty, sCancelTreaty-sExecute:
    3218     if (LastEndClientCommand<>scReject)
    3219       or (RW[Player].Treaty[pContacted]<trPeace) then
    3220       result:=eInvalid
    3221     else if (ServerVersion[Player]>=$010100)
    3222       and (GTurn<RW[Player].LastCancelTreaty[pContacted]+CancelTreatyTurns) then
    3223       result:=eCancelTreatyRush
    3224     else if Command>=sExecute then
    3225       begin
    3226       CallPlayer(cShowCancelTreaty,pContacted,Player);
    3227       IntServer(sIntCancelTreaty,Player,pContacted,nil^);
    3228       for p2:=0 to nPl-1 do
    3229         if (p2<>pContacted) and (1 shl p2 and PeaceEnded<>0) then
     4037      else
     4038        result := eInvalid;
     4039
     4040    sMoveUnit + (0 + 6 * 8) * 16, sMoveUnit + (1 + 7 * 8) * 16,
     4041      sMoveUnit + (2 + 0 * 8) * 16, sMoveUnit + (1 + 1 * 8) * 16,
     4042      sMoveUnit + (0 + 2 * 8) * 16, sMoveUnit + (7 + 1 * 8) * 16,
     4043      sMoveUnit + (6 + 0 * 8) * 16, sMoveUnit + (7 + 7 * 8) * 16,
     4044      sMoveUnit - sExecute + (0 + 6 * 8) * 16, sMoveUnit - sExecute +
     4045      (1 + 7 * 8) * 16, sMoveUnit - sExecute + (2 + 0 * 8) * 16,
     4046      sMoveUnit - sExecute + (1 + 1 * 8) * 16, sMoveUnit - sExecute +
     4047      (0 + 2 * 8) * 16, sMoveUnit - sExecute + (7 + 1 * 8) * 16,
     4048      sMoveUnit - sExecute + (6 + 0 * 8) * 16, sMoveUnit - sExecute +
     4049      (7 + 7 * 8) * 16:
     4050      begin
     4051        dx := (Command shr 4 + 4) and 7 - 4;
     4052        dy := (Command shr 7 + 4) and 7 - 4;
     4053{$IFDEF TEXTLOG}CmdInfo := Format('MoveUnit P%d I%d Mod%d Loc%d (%d,%d)', [Player, Subject, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc, dx, dy]); {$ENDIF}
     4054        if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
     4055          result := eInvalid
     4056        else
     4057          result := MoveUnit(Player, Subject, dx, dy, Command < sExecute);
     4058      end;
     4059
     4060    {
     4061      Settlers Related Commands
     4062      ____________________________________________________________________
     4063    }
     4064    sAddToCity, sAddToCity - sExecute:
     4065      begin
     4066{$IFDEF TEXTLOG}CmdInfo := Format('AddToCity P%d Mod%d Loc%d', [Player, RW[Player].Un[Subject].mix, RW[Player].Un[Subject].Loc]); {$ENDIF}
     4067        if (Subject >= RW[Player].nUn) or (RW[Player].Un[Subject].Loc < 0) then
     4068          result := eInvalid
     4069        else if not(RW[Player].Model[RW[Player].Un[Subject].mix].Kind
     4070          in [mkSettler, mkSlaves]) and
     4071          (RW[Player].Un[Subject].Flags and unConscripts = 0) then
     4072          result := eViolation
     4073        else
     4074        begin
     4075          Loc0 := RW[Player].Un[Subject].Loc;
     4076          if RealMap[Loc0] and fCity = 0 then
     4077            result := eInvalid
     4078          else
    32304079          begin
    3231           i:=pContacted shl 4+Player;
    3232           CallPlayer(cShowSupportAllianceAgainst,p2,i);
     4080            SearchCity(Loc0, Player, cix1);
     4081            with RW[Player].City[cix1] do
     4082              if not CanCityGrow(Player, cix1) then
     4083                result := eMaxSize
     4084              else if Command >= sExecute then
     4085              begin { add to city }
     4086                if Mode = moPlaying then
     4087                  SavedTiles[cix1] := 0; // save in every case
     4088                if CanCityGrow(Player, cix1) then
     4089                  CityGrowth(Player, cix1);
     4090                if (RW[Player].Model[RW[Player].Un[Subject].mix]
     4091                  .Kind = mkSettler) and CanCityGrow(Player, cix1) then
     4092                  CityGrowth(Player, cix1);
     4093                RemoveUnit_UpdateMap(Player, Subject);
     4094              end
     4095          end
     4096        end
     4097      end;
     4098
     4099    sStartJob .. sStartJob + $3F0, sStartJob - sExecute .. sStartJob + $3F0
     4100      - sExecute:
     4101      begin
     4102        Loc0 := RW[Player].Un[Subject].Loc;
     4103        i := Command shr 4 and $3F; // new job
     4104{$IFDEF TEXTLOG}CmdInfo := Format('StartJob P%d Mod%d Loc%d: %d', [Player, RW[Player].Un[Subject].mix, Loc0, i]); {$ENDIF}
     4105        if (Subject >= RW[Player].nUn) or (Loc0 < 0) then
     4106          result := eInvalid
     4107        else if i >= nJob then
     4108          result := eInvalid
     4109        else
     4110        begin
     4111          result := StartJob(Player, Subject, i, Command < sExecute);
     4112          if result = eCity then
     4113          begin // new city
     4114            cix1 := RW[Player].nCity - 1;
     4115            AddBestCityTile(Player, cix1);
     4116            if Mode = moPlaying then
     4117              with RW[Player].City[cix1] do
     4118              begin
     4119                // SavedResourceWeights[cix1]:=ResourceWeights;
     4120                SavedTiles[cix1] := 0; // save in every case
     4121              end;
     4122            if Mode >= moMovie then { show new city in interface modules }
     4123              for p1 := 0 to nPl - 1 do
     4124                if (1 shl p1 and GWatching <> 0) and (p1 <> Player) and
     4125                  (ObserveLevel[Loc0] and (3 shl (2 * p1)) > 0) then
     4126                  CallPlayer(cShowCityChanged, p1, Loc0);
     4127          end
     4128        end;
     4129      end;
     4130
     4131    {
     4132      City Related Commands
     4133      ____________________________________________________________________
     4134    }
     4135    sSetCityProject, sSetCityProject - sExecute:
     4136      begin
     4137        NewProject := integer(Data) and not cpAuto;
     4138{$IFDEF TEXTLOG}CmdInfo := Format('SetCityProject P%d Loc%d: %d', [Player, RW[Player].City[Subject].Loc, NewProject]); {$ENDIF}
     4139        if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
     4140        then
     4141          result := eInvalid
     4142        else
     4143          with RW[Player].City[Subject] do
     4144          begin
     4145            if NewProject = Project then
     4146              result := eNotChanged
     4147            else
     4148            begin
     4149              pt0 := ProjectType(Project0);
     4150              pt1 := ProjectType(NewProject);
     4151              if NewProject and cpImp = 0 then
     4152              begin
     4153                if NewProject and cpIndex >= RW[Player].nModel then
     4154                  result := eInvalid
     4155                else if (NewProject and cpConscripts <> 0) and
     4156                  not((RW[Player].Tech[adConscription] >= tsApplicable) and
     4157                  (RW[Player].Model[NewProject and cpIndex].Domain = dGround)
     4158                  and (RW[Player].Model[NewProject and cpIndex].Kind < mkScout))
     4159                then
     4160                  result := eViolation
     4161                  // else if (RW[Player].Model[NewProject and cpIndex].Kind=mkSlaves)
     4162                  // and (GWonder[woPyramids].EffectiveOwner<>Player) then
     4163                  // result:=eNoPreq
     4164              end
     4165              else if NewProject and cpIndex >= nImp then
     4166                result := eInvalid
     4167              else
     4168              begin
     4169                Preq := Imp[NewProject and cpIndex].Preq;
     4170                for i := 0 to nImpReplacement - 1 do
     4171                  if (ImpReplacement[i].OldImp = NewProject and cpIndex) and
     4172                    (built[ImpReplacement[i].NewImp] > 0) then
     4173                    result := eObsolete;
     4174                if result = eObsolete then
     4175                else if Preq = preNA then
     4176                  result := eInvalid
     4177                else if (Preq >= 0) and (RW[Player].Tech[Preq] < tsApplicable)
     4178                then
     4179                  result := eNoPreq
     4180                else if built[NewProject and cpIndex] > 0 then
     4181                  result := eInvalid
     4182                else if (NewProject and cpIndex < 28) and
     4183                  (GWonder[NewProject and cpIndex].CityID <> -1) then
     4184                  result := eViolation // wonder already exists
     4185                else if (NewProject and cpIndex = imSpacePort) and
     4186                  (RW[Player].NatBuilt[imSpacePort] > 0) then
     4187                  result := eViolation // space port already exists
     4188                else if (NewProject = cpImp + imBank) and (built[imMarket] = 0)
     4189                  or (NewProject = cpImp + imUniversity) and
     4190                  (built[imLibrary] = 0) or (NewProject = cpImp + imResLab) and
     4191                  (built[imUniversity] = 0) or (NewProject = cpImp + imMfgPlant)
     4192                  and (built[imFactory] = 0) then
     4193                  result := eNoPreq;
     4194                case NewProject - cpImp of
     4195                  woLighthouse, woMagellan, imCoastalFort, imHarbor, imPlatform:
     4196                    begin { city at ocean? }
     4197                      Preq := 0;
     4198                      V8_to_Loc(Loc, Adjacent);
     4199                      for V8 := 0 to 7 do
     4200                      begin
     4201                        Loc1 := Adjacent[V8];
     4202                        if (Loc1 >= 0) and (Loc1 < MapSize) and
     4203                          (RealMap[Loc1] and fTerrain = fShore) then
     4204                          inc(Preq);
     4205                      end;
     4206                      if Preq = 0 then
     4207                        result := eNoPreq;
     4208                    end;
     4209                  woHoover, imHydro:
     4210                    begin { city at river or mountains? }
     4211                      Preq := 0;
     4212                      V8_to_Loc(Loc, Adjacent);
     4213                      for V8 := 0 to 7 do
     4214                      begin
     4215                        Loc1 := Adjacent[V8];
     4216                        if (Loc1 >= 0) and (Loc1 < MapSize) and
     4217                          ((RealMap[Loc1] and fTerrain = fMountains) or
     4218                          (RealMap[Loc1] and fRiver <> 0)) then
     4219                          inc(Preq);
     4220                      end;
     4221                      if Preq = 0 then
     4222                        result := eNoPreq;
     4223                    end;
     4224                  woMIR, imShipComp, imShipPow, imShipHab:
     4225                    if RW[Player].NatBuilt[imSpacePort] = 0 then
     4226                      result := eNoPreq;
     4227                end;
     4228                if (GTestFlags and tfNoRareNeed = 0) and
     4229                  (Imp[NewProject and cpIndex].Kind = ikShipPart) then
     4230                  if RW[Player].Tech[adMassProduction] < tsApplicable then
     4231                    result := eNoPreq
     4232                  else
     4233                  begin // check for rare resources
     4234                    if NewProject and cpIndex = imShipComp then
     4235                      j := 1
     4236                    else if NewProject and cpIndex = imShipPow then
     4237                      j := 2
     4238                    else { if NewProject and cpIndex=imShipHab then }
     4239                      j := 3;
     4240                    // j = rare resource required
     4241                    Preq := 0;
     4242                    V21_to_Loc(Loc, Radius);
     4243                    for V21 := 1 to 26 do
     4244                    begin
     4245                      Loc1 := Radius[V21];
     4246                      if (Loc1 >= 0) and (Loc1 < MapSize) and
     4247                        (RealMap[Loc1] shr 25 and 3 = Cardinal(j)) then
     4248                        inc(Preq);
     4249                    end;
     4250                    if Preq = 0 then
     4251                      result := eNoPreq;
     4252                  end
     4253              end;
     4254
     4255              if (Command >= sExecute) and (result >= rExecuted) then
     4256              begin
     4257                if pt0 <> ptSelect then
     4258                  if NewProject and (cpImp or cpIndex) = Project0 and
     4259                    (cpImp or cpIndex) then
     4260                    Prod := Prod0
     4261                  else if (pt1 = ptTrGoods) or (pt1 = ptShip) or (pt1 <> pt0)
     4262                    and (pt0 <> ptCaravan) then
     4263                  begin
     4264                    inc(RW[Player].Money, Prod0);
     4265                    Prod := 0;
     4266                    Prod0 := 0;
     4267                    Project0 := cpImp + imTrGoods
     4268                  end
     4269                  else
     4270                    Prod := Prod0 * 2 div 3;
     4271                Project := NewProject
     4272              end
     4273            end
     4274          end
     4275      end;
     4276
     4277    sBuyCityProject, sBuyCityProject - sExecute:
     4278      begin
     4279{$IFDEF TEXTLOG}CmdInfo := Format('BuyCityProject P%d Loc%d', [Player, RW[Player].City[Subject].Loc]); {$ENDIF}
     4280        if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
     4281        then
     4282          result := eInvalid
     4283        else
     4284          with RW[Player].City[Subject] do
     4285            if (RW[Player].Government = gAnarchy) or (Flags and chCaptured <> 0)
     4286            then
     4287              result := eOutOfControl
     4288            else if (Project and cpImp <> 0) and
     4289              ((Project and cpIndex = imTrGoods) or
     4290              (Imp[Project and cpIndex].Kind = ikShipPart)) then
     4291              result := eInvalid // don't buy colony ship
     4292            else
     4293            begin
     4294              CityReport.HypoTiles := -1;
     4295              CityReport.HypoTax := -1;
     4296              CityReport.HypoLux := -1;
     4297              GetCityReport(Player, Subject, CityReport);
     4298              Cost := CityReport.ProdCost;
     4299              NextProd := CityReport.ProdRep - CityReport.Support;
     4300              if (CityReport.Working - CityReport.Happy > Size shr 1) or
     4301                (NextProd < 0) then // !!! change to new style disorder
     4302                NextProd := 0;
     4303              Cost := Cost - Prod - NextProd;
     4304              if (GWonder[woMich].EffectiveOwner = Player) and
     4305                (Project and cpImp <> 0) then
     4306                Cost := Cost * 2
     4307              else
     4308                Cost := Cost * 4;
     4309              if Cost <= 0 then
     4310                result := eNotChanged
     4311              else if Cost > RW[Player].Money then
     4312                result := eViolation
     4313              else if Command >= sExecute then
     4314                IntServer(sIntBuyMaterial, Player, Subject, Cost);
     4315              // need to save material/cost because city tiles are not correct
     4316              // when loading
     4317            end;
     4318      end;
     4319
     4320    sSellCityProject, sSellCityProject - sExecute:
     4321      begin
     4322{$IFDEF TEXTLOG}CmdInfo := Format('SellCityProject P%d Loc%d', [Player, RW[Player].City[Subject].Loc]); {$ENDIF}
     4323        if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
     4324        then
     4325          result := eInvalid
     4326        else if Command >= sExecute then
     4327          with RW[Player].City[Subject] do
     4328          begin
     4329            inc(RW[Player].Money, Prod0);
     4330            Prod := 0;
     4331            Prod0 := 0;
    32334332          end;
    3234       for p2:=0 to nPl-1 do
    3235         if (p2<>pContacted) and (1 shl p2 and PeaceEnded<>0) then
    3236           begin
    3237           i:=p2;
    3238           CallPlayer(cShowCancelTreatyByAlliance,Player,i);
    3239           end;
    3240       LastEndClientCommand:=sTurn;
    3241       end;
    3242 
    3243 {
    3244                        Model Related Commands
    3245  ____________________________________________________________________
    3246 }
    3247   sCreateDevModel, sCreateDevModel-sExecute:
    3248     begin
    3249     {$IFDEF TEXTLOG}CmdInfo:=Format('CreateDevModel P%d', [Player]);{$ENDIF}
    3250     if Subject>=4 then result:=eInvalid
    3251     else if (upgrade[Subject,0].Preq<>preNone)
    3252       and (RW[Player].Tech[upgrade[Subject,0].Preq]<tsApplicable) then
    3253       result:=eNoPreq
    3254     else if Command>=sExecute then
    3255       begin
    3256       with RW[Player].DevModel do
    3257         begin
    3258         Domain:=Subject;
    3259         MStrength:=0; MTrans:=0; MCost:=0; Upgrades:=0;
    3260         FutureMCost:=0;
    3261         for i:=0 to nUpgrade-1 do with upgrade[Domain,i] do
    3262           if (Preq=preNone)
    3263             or (Preq>=0) and ((RW[Player].Tech[Preq]>=tsApplicable)
    3264             or (Preq in FutureTech) and (RW[Player].Tech[Preq]>=0)) then
     4333      end;
     4334
     4335    sSellCityImprovement, sSellCityImprovement - sExecute:
     4336      begin
     4337{$IFDEF TEXTLOG}CmdInfo := Format('SellCityImprovement P%d Loc%d: %d', [Player, RW[Player].City[Subject].Loc, integer(Data)]); {$ENDIF}
     4338        if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
     4339        then
     4340          result := eInvalid
     4341        else
     4342          with RW[Player].City[Subject] do
     4343            if built[integer(Data)] = 0 then
     4344              result := eInvalid
     4345            else if (RW[Player].Government = gAnarchy) or
     4346              (Flags and chCaptured <> 0) then
     4347              result := eOutOfControl
     4348            else if Flags and chImprovementSold <> 0 then
     4349              result := eOnlyOnce
     4350            else if Command >= sExecute then
    32654351            begin
    3266             if Preq in FutureTech then
     4352              inc(RW[Player].Money, Imp[integer(Data)].Cost * BuildCostMod
     4353                [Difficulty[Player]] div 12);
     4354              built[integer(Data)] := 0;
     4355              if Imp[integer(Data)].Kind in [ikNatLocal, ikNatGlobal] then
    32674356              begin
    3268               j:=RW[Player].Tech[Preq];
    3269               inc(FutureMCost,j*Cost);
    3270               end
    3271             else
    3272               begin
    3273               j:=1;
    3274               if Cost>MCost then MCost:=Cost;
    3275               end;
    3276             inc(Upgrades,1 shl i);
    3277             inc(MStrength,j*Strength);
    3278             inc(MTrans,j*Trans);
    3279             end;
    3280         inc(MCost,FutureMCost);
    3281         FillChar(Cap,SizeOf(Cap),0);
    3282         Cap[mcOffense]:=2;
    3283         Cap[mcDefense]:=1;
    3284         for i:=0 to nFeature-1 do with Feature[i] do
    3285           if (1 shl Domain and Domains<>0) and ((Preq=preNone)
    3286             or (Preq=preSun) and (GWonder[woSun].EffectiveOwner=Player)
    3287             or (Preq>=0) and (RW[Player].Tech[Preq]>=tsApplicable))
    3288             and (i in AutoFeature) then Cap[i]:=1;
    3289         MaxWeight:=5;
    3290         if (WeightPreq7[Domain]<>preNA)
    3291           and (RW[Player].Tech[WeightPreq7[Domain]]>=tsApplicable) then
    3292           MaxWeight:=7;
    3293         if (WeightPreq10[Domain]<>preNA)
    3294           and (RW[Player].Tech[WeightPreq10[Domain]]>=tsApplicable) then
    3295           if Domain=dSea then MaxWeight:=9
    3296           else MaxWeight:=10;
    3297         end;
    3298       CalculateModel(RW[Player].DevModel);
    3299       DevModelTurn[Player]:=GTurn;
    3300       end
    3301     end;
    3302 
    3303   sSetDevModelCap..sSetDevModelCap+$3F0,
    3304   sSetDevModelCap-sExecute..sSetDevModelCap-sExecute+$3F0:
    3305     begin
    3306     {$IFDEF TEXTLOG}CmdInfo:=Format('SetDevModelCap P%d', [Player]);{$ENDIF}
    3307     if Subject>=nFeature then result:=eInvalid
    3308     else if DevModelTurn[Player]=GTurn then
    3309       begin
    3310       NewCap:=Command shr 4 and $3F; {new value}
    3311       with RW[Player].DevModel do
    3312         if 1 shl Domain and Feature[Subject].Domains=0 then
    3313           result:=eDomainMismatch
    3314         else if not ((Feature[Subject].Preq=preNone)
    3315           or (Feature[Subject].Preq=preSun)
    3316           and (GWonder[woSun].EffectiveOwner=Player)
    3317           or (Feature[Subject].Preq>=0)
    3318           and (RW[Player].Tech[Feature[Subject].Preq]>=tsApplicable)) then
    3319           result:=eNoPreq
    3320         else
    3321           begin
    3322           if (Subject in AutoFeature) or (Subject=mcDefense) then MinCap:=1
    3323           else MinCap:=0; {MinCap - minimum use of feature}
    3324           if Subject>=mcFirstNonCap then MaxCap:=1
    3325           else if Subject=mcDefense then
    3326             begin
    3327             if Domain=dGround then MaxCap:=2
    3328             else MaxCap:=3;
    3329             if RW[Player].Tech[adSteel]>=tsApplicable then inc(MaxCap)
    3330             end
    3331           else MaxCap:=8; {MaxCap - maximum use of this feature}
    3332           if (Domain=dGround) and (Subject=mcDefense) then CapWeight:=2
    3333           else CapWeight:=Feature[Subject].Weight;
    3334           if (NewCap<MinCap) or (NewCap>MaxCap)
    3335             or (Weight+(NewCap-Cap[Subject])*CapWeight>MaxWeight) then
    3336             result:=eViolation
    3337           else if Command>=sExecute then
    3338             begin
    3339             Cap[Subject]:=NewCap;
    3340 
    3341             // mutual feature exclusion
    3342             case Subject of
    3343               mcSub:
    3344                 begin
    3345                 if ServerVersion[Player]>=$010103 then
    3346                   Cap[mcSeaTrans]:=0;
    3347                 Cap[mcArtillery]:=0;
    3348                 Cap[mcCarrier]:=0;
    3349                 if Cap[mcDefense]>2 then Cap[mcDefense]:=2
    3350                 end;
    3351               mcSeaTrans:
    3352                 begin
    3353                 if ServerVersion[Player]>=$010103 then
    3354                   Cap[mcSub]:=0;
    3355                 end;
    3356               mcCarrier: Cap[mcSub]:=0;
    3357               mcArtillery: Cap[mcSub]:=0;
    3358               mcAlpine:
    3359                 begin Cap[mcOver]:=0; Cap[mcMob]:=0; end;
    3360               mcOver: Cap[mcAlpine]:=0;
    3361               mcMob: begin Cap[mcAlpine]:=0; end;
    3362               end;
    3363 
    3364             CalculateModel(RW[Player].DevModel);
    3365             end
    3366           end;
    3367       end
    3368     else result:=eNoModel;
    3369     end;
    3370 
    3371 {
    3372                         Unit Related Commands
    3373  ____________________________________________________________________
    3374 }
    3375   sRemoveUnit,sRemoveUnit-sExecute:
    3376     begin
    3377     {$IFDEF TEXTLOG}CmdInfo:=Format('RemoveUnit P%d Mod%d Loc%d', [Player,RW[Player].Un[Subject].mix,RW[Player].Un[Subject].Loc]);{$ENDIF}
    3378     if (Subject>=RW[Player].nUn) or (RW[Player].Un[Subject].Loc<0) then
    3379       result:=eInvalid
    3380     else
    3381       begin
    3382       result:=eRemoved;
    3383       Loc0:=RW[Player].Un[Subject].Loc;
    3384       if RealMap[Loc0] and fCity<>0 then {check utilize}
    3385         begin
    3386         SearchCity(Loc0,Player,cix1);
    3387         with RW[Player].City[cix1] do
    3388           begin
    3389           if (RW[Player].Model[RW[Player].Un[Subject].mix].Kind=mkCaravan)
    3390             and ((Project and cpImp=0) or (Imp[Project and cpIndex].Kind<>ikShipPart))
    3391             or (Project and cpImp=0) and (RW[Player].Model[Project
    3392             and cpIndex].Kind<>mkCaravan) then
    3393             result:=eUtilized;
    3394           if Command>=sExecute then
    3395             begin
    3396             if result=eUtilized then
    3397               begin
    3398               with RW[Player].Un[Subject] do
    3399                 begin
    3400                 Cost:=integer(RW[Player].Model[mix].Cost)*Health
    3401                   *BuildCostMod[Difficulty[Player]] div 1200;
    3402                 if RW[Player].Model[mix].Cap[mcLine]>0 then
    3403                   Cost:=Cost div 2;
    3404                 end;
    3405               if Project and (cpImp+cpIndex)=cpImp+imTrGoods then
    3406                 inc(RW[Player].Money,Cost)
    3407               else
    3408                 begin
    3409                 inc(Prod,Cost*2 div 3);
    3410                 Project0:=Project0 and not cpCompleted;
    3411                 if Project0 and not cpAuto<>Project and not cpAuto then
    3412                   Project0:=Project;
    3413                 Prod0:=Prod;
     4357                RW[Player].NatBuilt[integer(Data)] := 0;
     4358                case integer(Data) of
     4359                  imGrWall:
     4360                    GrWallContinent[Player] := -1;
     4361                  imSpacePort:
     4362                    DestroySpacePort_TellPlayers(Player, -1);
    34144363                end
    34154364              end;
    3416             RemoveUnit_UpdateMap(Player,Subject);
    3417             end;
    3418           end;
     4365              inc(Flags, chImprovementSold);
     4366            end
     4367      end;
     4368
     4369    sRebuildCityImprovement, sRebuildCityImprovement - sExecute:
     4370      begin
     4371        OldImp := integer(Data);
     4372{$IFDEF TEXTLOG}CmdInfo := Format('RebuildCityImprovement P%d Loc%d: %d', [Player, RW[Player].City[Subject].Loc, OldImp]); {$ENDIF}
     4373        if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
     4374        then
     4375          result := eInvalid
     4376        else
     4377        begin
     4378          if (OldImp < 0) or (OldImp >= nImp) or
     4379            not(Imp[OldImp].Kind in [ikCommon, ikNatLocal, ikNatGlobal]) then
     4380            result := eInvalid
     4381          else
     4382            with RW[Player].City[Subject] do
     4383              if (built[OldImp] = 0) or (Project and cpImp = 0) or
     4384                not(Imp[Project and cpIndex].Kind in [ikCommon, ikNatLocal,
     4385                ikNatGlobal]) then
     4386                result := eInvalid
     4387              else if (RW[Player].Government = gAnarchy) or
     4388                (Flags and chCaptured <> 0) then
     4389                result := eOutOfControl
     4390              else if Flags and chImprovementSold <> 0 then
     4391                result := eOnlyOnce
     4392              else if Command >= sExecute then
     4393              begin
     4394                inc(Prod, Imp[OldImp].Cost * BuildCostMod[Difficulty[Player]]
     4395                  div 12 * 2 div 3);
     4396                Project0 := Project0 and not cpCompleted;
     4397                if Project0 and not cpAuto <> Project and not cpAuto then
     4398                  Project0 := Project;
     4399                Prod0 := Prod;
     4400                built[OldImp] := 0;
     4401                if Imp[OldImp].Kind in [ikNatLocal, ikNatGlobal] then
     4402                begin // nat. project lost
     4403                  RW[Player].NatBuilt[OldImp] := 0;
     4404                  case OldImp of
     4405                    imGrWall:
     4406                      GrWallContinent[Player] := -1;
     4407                    imSpacePort:
     4408                      DestroySpacePort_TellPlayers(Player, -1);
     4409                  end
     4410                end;
     4411                inc(Flags, chImprovementSold);
     4412              end
    34194413        end
    3420       else if Command>=sExecute then
    3421         RemoveUnit_UpdateMap(Player,Subject);
    3422       end
     4414      end;
     4415
     4416    sSetCityTiles, sSetCityTiles - sExecute:
     4417      begin
     4418{$IFDEF TEXTLOG}CmdInfo := Format('SetCityTiles P%d Loc%d: %x', [Player, RW[Player].City[Subject].Loc, integer(Data)]); {$ENDIF}
     4419        if (Subject >= RW[Player].nCity) or (RW[Player].City[Subject].Loc < 0)
     4420        then
     4421          result := eInvalid
     4422        else
     4423          result := SetCityTiles(Player, Subject, integer(Data),
     4424            Command < sExecute);
     4425      end;
     4426
     4427    {
     4428      Client Exclusive Commands
     4429      ____________________________________________________________________
     4430    }
     4431  else
     4432    if Command >= cClientEx then
     4433    begin
     4434{$IFDEF TEXTLOG}CmdInfo := Format('ClientEx%x P%d', [Command, Player]);
     4435      {$ENDIF}
     4436      if ProcessClientData[Player] or (Mode = moPlaying) then
     4437        CallPlayer(Command, Player, Data)
     4438    end
     4439    else
     4440      result := eUnknown;
     4441  end; { case command }
     4442
     4443  // do not log invalid and non-relevant commands
     4444  if result = eZOC_EnemySpotted then
     4445  begin
     4446    assert(Mode = moPlaying);
     4447    CL.State := FormerCLState;
     4448    IntServer(sIntDiscoverZOC, Player, 0, ZOCTile);
     4449  end
     4450  else if result and rEffective = 0 then
     4451    if Mode < moPlaying then
     4452    begin
     4453{$IFDEF TEXTLOG}CmdInfo := Format('***ERROR (%x) ', [result]) + CmdInfo;
     4454      {$ENDIF}
     4455      LoadOK := false;
     4456    end
     4457    else
     4458    begin
     4459      if logged then
     4460        CL.State := FormerCLState;
     4461      if (result < rExecuted) and (Command >= sExecute) then
     4462        PutMessage(1 shl 16 + 1, Format('INVALID: %d calls %x (%d)',
     4463          [Player, Command, Subject]));
    34234464    end;
    34244465
    3425   sSetUnitHome,sSetUnitHome-sExecute:
    3426     begin
    3427     {$IFDEF TEXTLOG}CmdInfo:=Format('SetUnitHome P%d Mod%d Loc%d', [Player,RW[Player].Un[Subject].mix,RW[Player].Un[Subject].Loc]);{$ENDIF}
    3428     if (Subject>=RW[Player].nUn) or (RW[Player].Un[Subject].Loc<0) then
    3429       result:=eInvalid
    3430     else
    3431       begin
    3432       Loc0:=RW[Player].Un[Subject].Loc;
    3433       if RealMap[Loc0] and fCity=0 then result:=eInvalid
    3434       else
    3435         begin
    3436         SearchCity(Loc0,Player,cix1);
    3437         if RW[Player].City[cix1].Flags and chCaptured<>0 then
    3438           result:=eViolation
    3439         else if Command>=sExecute then
    3440           RW[Player].Un[Subject].Home:=cix1
    3441         end
    3442       end
    3443     end;
    3444 
    3445   sSetSpyMission..sSetSpyMission+(nSpyMission-1) shl 4,
    3446     sSetSpyMission-sExecute..sSetSpyMission-sExecute+(nSpyMission-1) shl 4:
    3447     if Command>=sExecute then
    3448       SpyMission:=Command shr 4 and $F;
    3449 
    3450   sLoadUnit,sLoadUnit-sExecute:
    3451     begin
    3452     {$IFDEF TEXTLOG}CmdInfo:=Format('LoadUnit P%d Mod%d Loc%d', [Player,RW[Player].Un[Subject].mix,RW[Player].Un[Subject].Loc]);{$ENDIF}
    3453     if (Subject>=RW[Player].nUn) or (RW[Player].Un[Subject].Loc<0) then
    3454       result:=eInvalid
    3455     else result:=LoadUnit(Player,Subject,Command<sExecute);
    3456     end;
    3457 
    3458   sUnloadUnit,sUnloadUnit-sExecute:
    3459     begin
    3460     {$IFDEF TEXTLOG}CmdInfo:=Format('UnloadUnit P%d Mod%d Loc%d', [Player,RW[Player].Un[Subject].mix,RW[Player].Un[Subject].Loc]);{$ENDIF}
    3461     if (Subject>=RW[Player].nUn) or (RW[Player].Un[Subject].Loc<0) then
    3462       result:=eInvalid
    3463     else result:=UnloadUnit(Player,Subject,Command<sExecute)
    3464     end;
    3465 
    3466   sSelectTransport,sSelectTransport-sExecute:
    3467     if (Subject>=RW[Player].nUn) or (RW[Player].Un[Subject].Loc<0) then
    3468       result:=eInvalid
    3469     else with RW[Player].Model[RW[Player].Un[Subject].mix] do
    3470       begin
    3471       if Cap[mcSeaTrans]+Cap[mcAirTrans]+Cap[mcCarrier]=0 then
    3472         result:=eInvalid
    3473       else if Command>=sExecute then
    3474         uixSelectedTransport:=Subject;
    3475       end;
    3476 
    3477   sCreateUnit..sCreateUnit+(nPl-1) shl 4,
    3478   sCreateUnit-sExecute..sCreateUnit-sExecute+(nPl-1) shl 4:
    3479     if (GTestFlags and tfUncover<>0) or (Difficulty[Player]=0) then {supervisor only command}
    3480       begin
    3481       p1:=Command shr 4 and $f;
    3482       Loc1:=integer(Data);
    3483       if (Occupant[Loc1]>=0) and (p1<>Occupant[Loc1])
    3484         or (RealMap[Loc1] and fCity<>0) and (RealMap[Loc1] shr 27<>Cardinal(p1))
    3485         or (RW[p1].Model[Subject].Domain<dAir)
    3486         and ((RW[p1].Model[Subject].Domain=dSea)
    3487         <>(RealMap[integer(Data)] and fTerrain<fGrass)) then
    3488         result:=eViolation
    3489       else if Command>=sExecute then
    3490         begin
    3491         CreateUnit(p1,Subject);
    3492         RW[p1].Un[RW[p1].nUn-1].Loc:=integer(Data);
    3493         PlaceUnit(p1,RW[p1].nUn-1);
    3494         UpdateUnitMap(integer(Data));
    3495         end
    3496       end
    3497     else result:=eInvalid;
    3498 
    3499   sMoveUnit+(0+6*8)*16,sMoveUnit+(1+7*8)*16,
    3500   sMoveUnit+(2+0*8)*16,sMoveUnit+(1+1*8)*16,
    3501   sMoveUnit+(0+2*8)*16,sMoveUnit+(7+1*8)*16,
    3502   sMoveUnit+(6+0*8)*16,sMoveUnit+(7+7*8)*16,
    3503   sMoveUnit-sExecute+(0+6*8)*16,sMoveUnit-sExecute+(1+7*8)*16,
    3504   sMoveUnit-sExecute+(2+0*8)*16,sMoveUnit-sExecute+(1+1*8)*16,
    3505   sMoveUnit-sExecute+(0+2*8)*16,sMoveUnit-sExecute+(7+1*8)*16,
    3506   sMoveUnit-sExecute+(6+0*8)*16,sMoveUnit-sExecute+(7+7*8)*16:
    3507     begin
    3508     dx:=(Command shr 4 +4) and 7-4; dy:=(Command shr 7 +4) and 7-4;
    3509     {$IFDEF TEXTLOG}CmdInfo:=Format('MoveUnit P%d I%d Mod%d Loc%d (%d,%d)', [Player,Subject,RW[Player].Un[Subject].mix,RW[Player].Un[Subject].Loc,dx,dy]);{$ENDIF}
    3510     if (Subject>=RW[Player].nUn) or (RW[Player].Un[Subject].Loc<0) then
    3511       result:=eInvalid
    3512     else result:=MoveUnit(Player,Subject,dx,dy,Command<sExecute);
    3513     end;
    3514 
    3515 {
    3516                       Settlers Related Commands
    3517  ____________________________________________________________________
    3518 }
    3519   sAddToCity, sAddToCity-sExecute:
    3520     begin
    3521     {$IFDEF TEXTLOG}CmdInfo:=Format('AddToCity P%d Mod%d Loc%d', [Player,RW[Player].Un[Subject].mix,RW[Player].Un[Subject].Loc]);{$ENDIF}
    3522     if (Subject>=RW[Player].nUn) or (RW[Player].Un[Subject].Loc<0) then
    3523       result:=eInvalid
    3524     else if not (RW[Player].Model[RW[Player].Un[Subject].mix].Kind in [mkSettler,mkSlaves])
    3525       and (RW[Player].Un[Subject].Flags and unConscripts=0) then
    3526       result:=eViolation
    3527     else
    3528       begin
    3529       Loc0:=RW[Player].Un[Subject].Loc;
    3530       if RealMap[Loc0] and fCity=0 then result:=eInvalid
    3531       else
    3532         begin
    3533         SearchCity(Loc0,Player,cix1);
    3534         with RW[Player].City[cix1] do
    3535           if not CanCityGrow(Player,cix1) then
    3536             result:=eMaxSize
    3537           else if Command>=sExecute then
    3538             begin {add to city}
    3539             if Mode=moPlaying then
    3540               SavedTiles[cix1]:=0; // save in every case
    3541             if CanCityGrow(Player,cix1) then
    3542               CityGrowth(Player,cix1);
    3543             if (RW[Player].Model[RW[Player].Un[Subject].mix].Kind=mkSettler)
    3544               and CanCityGrow(Player,cix1) then
    3545               CityGrowth(Player,cix1);
    3546             RemoveUnit_UpdateMap(Player,Subject);
    3547             end
    3548         end
    3549       end
    3550     end;
    3551 
    3552   sStartJob..sStartJob+$3F0, sStartJob-sExecute..sStartJob+$3F0-sExecute:
    3553     begin
    3554     Loc0:=RW[Player].Un[Subject].Loc;
    3555     i:=Command shr 4 and $3F; // new job
    3556     {$IFDEF TEXTLOG}CmdInfo:=Format('StartJob P%d Mod%d Loc%d: %d', [Player,RW[Player].Un[Subject].mix,Loc0,i]);{$ENDIF}
    3557     if (Subject>=RW[Player].nUn) or (Loc0<0) then
    3558       result:=eInvalid
    3559     else if i>=nJob then result:=eInvalid
    3560     else
    3561       begin
    3562       result:=StartJob(Player,Subject,i,Command<sExecute);
    3563       if result=eCity then
    3564         begin // new city
    3565         cix1:=RW[Player].nCity-1;
    3566         AddBestCityTile(Player,cix1);
    3567         if Mode=moPlaying then with RW[Player].City[cix1] do
    3568           begin
    3569 //          SavedResourceWeights[cix1]:=ResourceWeights;
    3570           SavedTiles[cix1]:=0; // save in every case
    3571           end;
    3572         if Mode>=moMovie then {show new city in interface modules}
    3573           for p1:=0 to nPl-1 do
    3574             if (1 shl p1 and GWatching<>0) and (p1<>Player)
    3575               and (ObserveLevel[Loc0] and (3 shl (2*p1))>0) then
    3576                 CallPlayer(cShowCityChanged,p1,Loc0);
    3577         end
    3578       end;
    3579     end;
    3580 
    3581 {
    3582                         City Related Commands
    3583  ____________________________________________________________________
    3584 }
    3585   sSetCityProject,sSetCityProject-sExecute:
    3586     begin
    3587     NewProject:=integer(Data) and not cpAuto;
    3588     {$IFDEF TEXTLOG}CmdInfo:=Format('SetCityProject P%d Loc%d: %d', [Player,RW[Player].City[Subject].Loc,NewProject]);{$ENDIF}
    3589     if (Subject>=RW[Player].nCity) or (RW[Player].City[Subject].Loc<0) then
    3590       result:=eInvalid
    3591     else with RW[Player].City[Subject] do
    3592       begin
    3593       if NewProject=Project then result:=eNotChanged
    3594       else
    3595         begin
    3596         pt0:=ProjectType(Project0);
    3597         pt1:=ProjectType(NewProject);
    3598         if NewProject and cpImp=0 then
    3599           begin
    3600           if NewProject and cpIndex>=RW[Player].nModel then
    3601             result:=eInvalid
    3602           else if (NewProject and cpConscripts<>0)
    3603             and not ((RW[Player].Tech[adConscription]>=tsApplicable)
    3604             and (RW[Player].Model[NewProject and cpIndex].Domain=dGround)
    3605             and (RW[Player].Model[NewProject and cpIndex].Kind<mkScout)) then
    3606             result:=eViolation
    3607 //          else if (RW[Player].Model[NewProject and cpIndex].Kind=mkSlaves)
    3608 //            and (GWonder[woPyramids].EffectiveOwner<>Player) then
    3609 //            result:=eNoPreq
    3610           end
    3611         else if NewProject and cpIndex>=nImp then
    3612           result:=eInvalid
    3613         else
    3614           begin
    3615           Preq:=Imp[NewProject and cpIndex].Preq;
    3616           for i:=0 to nImpReplacement-1 do
    3617             if (ImpReplacement[i].OldImp=NewProject and cpIndex)
    3618               and (Built[ImpReplacement[i].NewImp]>0) then
    3619               result:=eObsolete;
    3620           if result=eObsolete then
    3621           else if Preq=preNA then result:=eInvalid
    3622           else if (Preq>=0) and (RW[Player].Tech[Preq]<tsApplicable) then
    3623             result:=eNoPreq
    3624           else if Built[NewProject and cpIndex]>0 then result:=eInvalid
    3625           else if (NewProject and cpIndex<28)
    3626             and (GWonder[NewProject and cpIndex].CityID<>-1) then
    3627             result:=eViolation // wonder already exists
    3628           else if (NewProject and cpIndex=imSpacePort)
    3629             and (RW[Player].NatBuilt[imSpacePort]>0) then
    3630             result:=eViolation // space port already exists
    3631           else if (NewProject=cpImp+imBank) and (Built[imMarket]=0)
    3632             or (NewProject=cpImp+imUniversity) and (Built[imLibrary]=0)
    3633             or (NewProject=cpImp+imResLab) and (Built[imUniversity]=0)
    3634             or (NewProject=cpImp+imMfgPlant) and (Built[imFactory]=0) then
    3635             result:=eNoPreq;
    3636           case NewProject-cpImp of
    3637             woLighthouse,woMagellan,imCoastalFort,imHarbor,imPlatform:
    3638               begin {city at ocean?}
    3639               Preq:=0;
    3640               V8_to_Loc(Loc,Adjacent);
    3641               for V8:=0 to 7 do
    3642                 begin
    3643                 Loc1:=Adjacent[V8];
    3644                 if (Loc1>=0) and (Loc1<MapSize)
    3645                   and (RealMap[Loc1] and fTerrain=fShore) then
    3646                   inc(Preq);
    3647                 end;
    3648               if Preq=0 then result:=eNoPreq;
    3649               end;
    3650             woHoover,imHydro:
    3651               begin {city at river or mountains?}
    3652               Preq:=0;
    3653               V8_to_Loc(Loc,Adjacent);
    3654               for V8:=0 to 7 do
    3655                 begin
    3656                 Loc1:=Adjacent[V8];
    3657                 if (Loc1>=0) and (Loc1<MapSize)
    3658                   and ((RealMap[Loc1] and fTerrain=fMountains)
    3659                   or (RealMap[Loc1] and fRiver<>0)) then inc(Preq);
    3660                 end;
    3661               if Preq=0 then result:=eNoPreq;
    3662               end;
    3663             woMIR,imShipComp,imShipPow,imShipHab:
    3664               if RW[Player].NatBuilt[imSpacePort]=0 then result:=eNoPreq;
    3665             end;
    3666           if (GTestFlags and tfNoRareNeed=0)
    3667             and (Imp[NewProject and cpIndex].Kind=ikShipPart) then
    3668             if RW[Player].Tech[adMassProduction]<tsApplicable then result:=eNoPreq
    3669             else
    3670               begin // check for rare resources
    3671               if NewProject and cpIndex=imShipComp then j:=1
    3672               else if NewProject and cpIndex=imShipPow then j:=2
    3673               else {if NewProject and cpIndex=imShipHab then} j:=3;
    3674                 // j = rare resource required
    3675               Preq:=0;
    3676               V21_to_Loc(Loc,Radius);
    3677               for V21:=1 to 26 do
    3678                 begin
    3679                 Loc1:=Radius[V21];
    3680                 if (Loc1>=0) and (Loc1<MapSize)
    3681                   and (RealMap[Loc1] shr 25 and 3=Cardinal(j)) then
    3682                   inc(Preq);
    3683                 end;
    3684               if Preq=0 then result:=eNoPreq;
    3685               end
    3686           end;
    3687 
    3688         if (Command>=sExecute) and (result>=rExecuted) then
    3689           begin
    3690           if pt0<>ptSelect then
    3691             if NewProject and (cpImp or cpIndex)=Project0 and (cpImp or cpIndex) then
    3692               Prod:=Prod0
    3693             else if (pt1=ptTrGoods) or (pt1=ptShip) or (pt1<>pt0) and (pt0<>ptCaravan) then
    3694               begin
    3695               inc(RW[Player].Money,Prod0);
    3696               Prod:=0;
    3697               Prod0:=0;
    3698               Project0:=cpImp+imTrGoods
    3699               end
    3700             else Prod:=Prod0*2 div 3;
    3701           Project:=NewProject
    3702           end
    3703         end
    3704       end
    3705     end;
    3706 
    3707   sBuyCityProject,sBuyCityProject-sExecute:
    3708     begin
    3709     {$IFDEF TEXTLOG}CmdInfo:=Format('BuyCityProject P%d Loc%d', [Player,RW[Player].City[Subject].Loc]);{$ENDIF}
    3710     if (Subject>=RW[Player].nCity) or (RW[Player].City[Subject].Loc<0) then
    3711       result:=eInvalid
    3712     else with RW[Player].City[Subject] do
    3713       if (RW[Player].Government=gAnarchy) or (Flags and chCaptured<>0) then
    3714         result:=eOutOfControl
    3715       else if (Project and cpImp<>0) and ((Project and cpIndex=imTrGoods)
    3716         or (Imp[Project and cpIndex].Kind=ikShipPart)) then
    3717         result:=eInvalid // don't buy colony ship
    3718       else
    3719         begin
    3720         CityReport.HypoTiles:=-1;
    3721         CityReport.HypoTax:=-1;
    3722         CityReport.HypoLux:=-1;
    3723         GetCityReport(Player,Subject,CityReport);
    3724         Cost:=CityReport.ProdCost;
    3725         NextProd:=CityReport.ProdRep-CityReport.Support;
    3726         if (CityReport.Working-CityReport.Happy>Size shr 1) or (NextProd<0) then // !!! change to new style disorder
    3727           NextProd:=0;
    3728         Cost:=Cost-Prod-NextProd;
    3729         if (GWonder[woMich].EffectiveOwner=Player) and (Project and cpImp<>0) then
    3730           Cost:=Cost*2
    3731         else Cost:=Cost*4;
    3732         if Cost<=0 then result:=eNotChanged
    3733         else if Cost>RW[Player].Money then result:=eViolation
    3734         else if Command>=sExecute then
    3735           IntServer(sIntBuyMaterial, Player, Subject, Cost);
    3736             // need to save material/cost because city tiles are not correct
    3737             // when loading
    3738         end;
    3739     end;
    3740 
    3741   sSellCityProject,sSellCityProject-sExecute:
    3742     begin
    3743     {$IFDEF TEXTLOG}CmdInfo:=Format('SellCityProject P%d Loc%d', [Player,RW[Player].City[Subject].Loc]);{$ENDIF}
    3744     if (Subject>=RW[Player].nCity) or (RW[Player].City[Subject].Loc<0) then
    3745       result:=eInvalid
    3746     else if Command>=sExecute then
    3747       with RW[Player].City[Subject] do
    3748         begin inc(RW[Player].Money,Prod0); Prod:=0; Prod0:=0; end;
    3749     end;
    3750 
    3751   sSellCityImprovement,sSellCityImprovement-sExecute:
    3752     begin
    3753     {$IFDEF TEXTLOG}CmdInfo:=Format('SellCityImprovement P%d Loc%d: %d', [Player,RW[Player].City[Subject].Loc,integer(Data)]);{$ENDIF}
    3754     if (Subject>=RW[Player].nCity) or (RW[Player].City[Subject].Loc<0) then
    3755       result:=eInvalid
    3756     else with RW[Player].City[Subject] do
    3757       if Built[integer(Data)]=0 then result:=eInvalid
    3758       else if (RW[Player].Government=gAnarchy) or (Flags and chCaptured<>0) then
    3759         result:=eOutOfControl
    3760       else if Flags and chImprovementSold<>0 then result:=eOnlyOnce
    3761       else if Command>=sExecute then
    3762         begin
    3763         inc(RW[Player].Money,
    3764           Imp[integer(Data)].Cost*BuildCostMod[Difficulty[Player]] div 12);
    3765         Built[integer(Data)]:=0;
    3766         if Imp[integer(Data)].Kind in [ikNatLocal,ikNatGlobal] then
    3767           begin
    3768           RW[Player].NatBuilt[integer(Data)]:=0;
    3769           case integer(Data) of
    3770             imGrWall: GrWallContinent[Player]:=-1;
    3771             imSpacePort: DestroySpacePort_TellPlayers(Player,-1);
    3772             end
    3773           end;
    3774         inc(Flags,chImprovementSold);
    3775         end
    3776     end;
    3777 
    3778   sRebuildCityImprovement,sRebuildCityImprovement-sExecute:
    3779     begin
    3780     OldImp:=integer(Data);
    3781     {$IFDEF TEXTLOG}CmdInfo:=Format('RebuildCityImprovement P%d Loc%d: %d', [Player,RW[Player].City[Subject].Loc,OldImp]);{$ENDIF}
    3782     if (Subject>=RW[Player].nCity) or (RW[Player].City[Subject].Loc<0) then
    3783       result:=eInvalid
    3784     else
    3785       begin
    3786       if (OldImp<0) or (OldImp>=nImp)
    3787         or not (Imp[OldImp].Kind in [ikCommon,ikNatLocal,ikNatGlobal]) then
    3788         result:=eInvalid
    3789       else with RW[Player].City[Subject] do
    3790         if (Built[OldImp]=0) or (Project and cpImp=0)
    3791           or not (Imp[Project and cpIndex].Kind in [ikCommon,ikNatLocal,ikNatGlobal]) then
    3792           result:=eInvalid
    3793         else if (RW[Player].Government=gAnarchy) or (Flags and chCaptured<>0) then
    3794           result:=eOutOfControl
    3795         else if Flags and chImprovementSold<>0 then result:=eOnlyOnce
    3796         else if Command>=sExecute then
    3797           begin
    3798           inc(Prod,Imp[OldImp].Cost
    3799             *BuildCostMod[Difficulty[Player]] div 12 *2 div 3);
    3800           Project0:=Project0 and not cpCompleted;
    3801           if Project0 and not cpAuto<>Project and not cpAuto then
    3802             Project0:=Project;
    3803           Prod0:=Prod;
    3804           Built[OldImp]:=0;
    3805           if Imp[OldImp].Kind in [ikNatLocal,ikNatGlobal] then
    3806             begin // nat. project lost
    3807             RW[Player].NatBuilt[OldImp]:=0;
    3808             case OldImp of
    3809               imGrWall: GrWallContinent[Player]:=-1;
    3810               imSpacePort: DestroySpacePort_TellPlayers(Player,-1);
    3811               end
    3812             end;
    3813           inc(Flags,chImprovementSold);
    3814           end
    3815       end
    3816     end;
    3817 
    3818   sSetCityTiles, sSetCityTiles-sExecute:
    3819     begin
    3820     {$IFDEF TEXTLOG}CmdInfo:=Format('SetCityTiles P%d Loc%d: %x', [Player,RW[Player].City[Subject].Loc,integer(data)]);{$ENDIF}
    3821     if (Subject>=RW[Player].nCity) or (RW[Player].City[Subject].Loc<0) then
    3822       result:=eInvalid
    3823     else result:=SetCityTiles(Player, Subject, integer(Data), Command<sExecute);
    3824     end;
    3825 
    3826 {
    3827                       Client Exclusive Commands
    3828  ____________________________________________________________________
    3829 }
    3830   else
    3831     if Command>=cClientEx then
    3832       begin
    3833       {$IFDEF TEXTLOG}CmdInfo:=Format('ClientEx%x P%d', [Command,Player]);{$ENDIF}
    3834       if ProcessClientData[Player] or (Mode=moPlaying) then
    3835         CallPlayer(Command,Player,Data)
    3836       end
    3837     else result:=eUnknown;
    3838   end;{case command}
    3839 
    3840 // do not log invalid and non-relevant commands
    3841 if result=eZOC_EnemySpotted then
    3842   begin
    3843   assert(Mode=moPlaying);
    3844   CL.State:=FormerCLState;
    3845   IntServer(sIntDiscoverZOC,Player,0,ZOCTile);
    3846   end
    3847 else if result and rEffective=0 then
    3848   if Mode<moPlaying then
    3849     begin
    3850     {$IFDEF TEXTLOG}CmdInfo:=Format('***ERROR (%x) ',[result])+CmdInfo;{$ENDIF}
    3851     LoadOK:=false;
    3852     end
    3853   else
    3854     begin
    3855     if logged then CL.State:=FormerCLState;
    3856     if (result<rExecuted) and (Command>=sExecute) then
    3857       PutMessage(1 shl 16+1, Format('INVALID: %d calls %x (%d)',
    3858         [Player,Command,Subject]));
    3859     end;
    3860 
    3861 if (Command and (cClientEx or sExecute or sctMask)=sExecute or sctEndClient)
    3862   and (result>=rExecuted) then LastEndClientCommand:=Command;
    3863 {$IFOPT O-}dec(nHandoverStack,2);{$ENDIF}
    3864 end;{<<<server}
    3865 
     4466  if (Command and (cClientEx or sExecute or sctMask) = sExecute or sctEndClient)
     4467    and (result >= rExecuted) then
     4468    LastEndClientCommand := Command;
     4469{$IFOPT O-}dec(nHandoverStack, 2); {$ENDIF}
     4470end; { <<<server }
    38664471
    38674472initialization
     4473
    38684474QueryPerformanceFrequency(PerfFreq);
    3869 FindFirst(ParamStr(0),$21,ExeInfo);
    3870 
    3871 {$IFOPT O-}nHandoverStack:=0;{$ENDIF}
     4475FindFirst(ParamStr(0), $21, ExeInfo);
     4476
     4477{$IFOPT O-}nHandoverStack := 0; {$ENDIF}
    38724478
    38734479end.
    3874 
  • trunk/Inp.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit Inp;
    43
     
    65
    76uses
    8   ScreenTools,Messg,
     7  ScreenTools, Messg,
    98
    109  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
     
    3635procedure TInputDlg.FormCreate(Sender: TObject);
    3736begin
    38 Canvas.Font.Assign(UniFont[ftNormal]);
    39 Canvas.Brush.Style:=bsClear;
    40 TitleHeight:=ClientHeight;
    41 InitButtons();
    42 Center:=true
     37  Canvas.Font.Assign(UniFont[ftNormal]);
     38  Canvas.Brush.Style := bsClear;
     39  TitleHeight := ClientHeight;
     40  InitButtons();
     41  Center := true
    4342end;
    4443
    4544procedure TInputDlg.FormPaint(Sender: TObject);
    4645begin
    47 PaintBackground(self,3,3,ClientWidth-6,ClientHeight-6);
    48 Frame(Canvas,0,0,ClientWidth-1,ClientHeight-1,0,0);
    49 Frame(Canvas,1,1,ClientWidth-2,ClientHeight-2,MainTexture.clBevelLight,
    50   MainTexture.clBevelShade);
    51 Frame(Canvas,2,2,ClientWidth-3,ClientHeight-3,MainTexture.clBevelLight,
    52   MainTexture.clBevelShade);
    53 EditFrame(Canvas,EInput.BoundsRect,MainTexture);
    54 BtnFrame(Canvas,OKBtn.BoundsRect,MainTexture);
    55 RisedTextOut(Canvas,(ClientWidth-BiColorTextWidth(Canvas,Caption)) div 2,9,Caption);
    56 {Corner(canvas,1,1,0,MainTexture);
    57 Corner(canvas,ClientWidth-9,1,1,MainTexture);
    58 Corner(canvas,1,ClientHeight-9,2,MainTexture);
    59 Corner(canvas,ClientWidth-9,ClientHeight-9,3,MainTexture);}
     46  PaintBackground(self, 3, 3, ClientWidth - 6, ClientHeight - 6);
     47  Frame(Canvas, 0, 0, ClientWidth - 1, ClientHeight - 1, 0, 0);
     48  Frame(Canvas, 1, 1, ClientWidth - 2, ClientHeight - 2,
     49    MainTexture.clBevelLight, MainTexture.clBevelShade);
     50  Frame(Canvas, 2, 2, ClientWidth - 3, ClientHeight - 3,
     51    MainTexture.clBevelLight, MainTexture.clBevelShade);
     52  EditFrame(Canvas, EInput.BoundsRect, MainTexture);
     53  BtnFrame(Canvas, OKBtn.BoundsRect, MainTexture);
     54  RisedTextOut(Canvas, (ClientWidth - BiColorTextWidth(Canvas, Caption)) div 2,
     55    9, Caption);
     56  { Corner(canvas,1,1,0,MainTexture);
     57    Corner(canvas,ClientWidth-9,1,1,MainTexture);
     58    Corner(canvas,1,ClientHeight-9,2,MainTexture);
     59    Corner(canvas,ClientWidth-9,ClientHeight-9,3,MainTexture); }
    6060end;
    6161
    6262procedure TInputDlg.OKBtnClick(Sender: TObject);
    6363begin
    64 if EInput.Text='' then ModalResult:=mrCancel
    65 else ModalResult:=mrOK
     64  if EInput.Text = '' then
     65    ModalResult := mrCancel
     66  else
     67    ModalResult := mrOK
    6668end;
    6769
    6870procedure TInputDlg.EInputKeyPress(Sender: TObject; var Key: Char);
    6971begin
    70 if (Key=#13) and (EInput.Text<>'') then begin Key:=#0 ; ModalResult:=mrOK end
    71 else if Key=#27 then begin Key:=#0; ModalResult:=mrCancel end
     72  if (Key = #13) and (EInput.Text <> '') then
     73  begin
     74    Key := #0;
     75    ModalResult := mrOK
     76  end
     77  else if Key = #27 then
     78  begin
     79    Key := #0;
     80    ModalResult := mrCancel
     81  end
    7282end;
    7383
    7484procedure TInputDlg.FormShow(Sender: TObject);
    7585begin
    76 OKBtn.Caption:=Phrases.Lookup('BTN_OK');
    77 EInput.Font.Color:=MainTexture.clMark;
    78 EInput.SelStart:=0;
    79 EInput.SelLength:=Length(EInput.Text);
    80 if Center then CenterToRect(Rect(0,0,Screen.Width,Screen.Height));
     86  OKBtn.Caption := Phrases.Lookup('BTN_OK');
     87  EInput.Font.Color := MainTexture.clMark;
     88  EInput.SelStart := 0;
     89  EInput.SelLength := Length(EInput.Text);
     90  if Center then
     91    CenterToRect(Rect(0, 0, Screen.Width, Screen.Height));
    8192end;
    8293
    8394procedure TInputDlg.FormClose(Sender: TObject; var Action: TCloseAction);
    8495begin
    85 Center:=true
     96  Center := true
    8697end;
    8798
    8899procedure TInputDlg.CenterToRect(Rect: TRect);
    89100begin
    90 Center:=false;
    91 Left:=Rect.Left+(Rect.Right-Rect.Left-Width) div 2;
    92 Top:=Rect.Top+(Rect.Bottom-Rect.Top-Height) div 2;
     101  Center := false;
     102  Left := Rect.Left + (Rect.Right - Rect.Left - Width) div 2;
     103  Top := Rect.Top + (Rect.Bottom - Rect.Top - Height) div 2;
    93104end;
    94105
    95106end.
    96 
  • trunk/Integrated.dpr

    r2 r6  
    11{$INCLUDE switches}
    2 
    32program cevo;
    43
     
    1312  CityProcessing in 'CityProcessing.pas',
    1413  UnitProcessing in 'UnitProcessing.pas',
    15   Direct in 'Direct.pas' {DirectDlg},
     14  Direct in 'Direct.pas' {DirectDlg} ,
    1615  ScreenTools in 'ScreenTools.pas',
    17   Start in 'Start.pas' {StartDlg},
    18   Messg in 'Messg.pas' {MessgDlg},
    19   Inp in 'Inp.pas' {InputDlg},
    20   Back in 'Back.pas' {Background},
    21   Log in 'Log.pas' {LogDlg},
     16  Start in 'Start.pas' {StartDlg} ,
     17  Messg in 'Messg.pas' {MessgDlg} ,
     18  Inp in 'Inp.pas' {InputDlg} ,
     19  Back in 'Back.pas' {Background} ,
     20  Log in 'Log.pas' {LogDlg} ,
    2221  PVSB in 'LocalPlayer\PVSB.pas',
    2322  LocalPlayer in 'LocalPlayer\LocalPlayer.pas',
     
    2625  Tribes in 'LocalPlayer\Tribes.pas',
    2726  IsoEngine in 'LocalPlayer\IsoEngine.pas',
    28   Term in 'LocalPlayer\Term.pas' {MainScreen},
    29   MessgEx in 'LocalPlayer\MessgEx.pas' {MessgExDlg},
     27  Term in 'LocalPlayer\Term.pas' {MainScreen} ,
     28  MessgEx in 'LocalPlayer\MessgEx.pas' {MessgExDlg} ,
    3029  BaseWin in 'LocalPlayer\BaseWin.pas',
    31   Help in 'LocalPlayer\Help.pas' {HelpDlg},
    32   Select in 'LocalPlayer\Select.pas' {ListDlg},
    33   CityScreen in 'LocalPlayer\CityScreen.pas' {CityDlg},
    34   UnitStat in 'LocalPlayer\UnitStat.pas' {UnitStatDlg},
    35   Draft in 'LocalPlayer\Draft.pas' {DraftDlg},
    36   NatStat in 'LocalPlayer\NatStat.pas' {NatStatDlg},
    37   Diagram in 'LocalPlayer\Diagram.pas' {DiaDlg},
    38   Wonders in 'LocalPlayer\Wonders.pas' {WonderDlg},
    39   Nego in 'LocalPlayer\Nego.pas' {NegoDlg},
    40   CityType in 'LocalPlayer\CityType.pas' {CityTypeDlg},
    41   Enhance in 'LocalPlayer\Enhance.pas' {EnhanceDlg},
    42   NoTerm in 'NoTerm.pas' {NoTermDlg},
    43   Sound in 'Sound.pas' {SoundPlayer},
    44   Battle in 'LocalPlayer\Battle.pas' {BattleDlg},
    45   Rates in 'LocalPlayer\Rates.pas' {RatesDlg},
     30  Help in 'LocalPlayer\Help.pas' {HelpDlg} ,
     31  Select in 'LocalPlayer\Select.pas' {ListDlg} ,
     32  CityScreen in 'LocalPlayer\CityScreen.pas' {CityDlg} ,
     33  UnitStat in 'LocalPlayer\UnitStat.pas' {UnitStatDlg} ,
     34  Draft in 'LocalPlayer\Draft.pas' {DraftDlg} ,
     35  NatStat in 'LocalPlayer\NatStat.pas' {NatStatDlg} ,
     36  Diagram in 'LocalPlayer\Diagram.pas' {DiaDlg} ,
     37  Wonders in 'LocalPlayer\Wonders.pas' {WonderDlg} ,
     38  Nego in 'LocalPlayer\Nego.pas' {NegoDlg} ,
     39  CityType in 'LocalPlayer\CityType.pas' {CityTypeDlg} ,
     40  Enhance in 'LocalPlayer\Enhance.pas' {EnhanceDlg} ,
     41  NoTerm in 'NoTerm.pas' {NoTermDlg} ,
     42  Sound in 'Sound.pas' {SoundPlayer} ,
     43  Battle in 'LocalPlayer\Battle.pas' {BattleDlg} ,
     44  Rates in 'LocalPlayer\Rates.pas' {RatesDlg} ,
    4645  TechTree in 'LocalPlayer\TechTree.pas' {TechTreeDlg};
    4746
     
    4948
    5049begin
    51   DotNetClient:=nil;
     50  DotNetClient := nil;
    5251  Application.Initialize;
    5352  Application.Title := 'C-evo';
     
    5958  Application.CreateForm(TLogDlg, LogDlg);
    6059  Application.Run;
     60
    6161end.
    62 
  • trunk/Integrated.dproj

    r3 r6  
    230230                <Platform value="Win32">True</Platform>
    231231            </Platforms>
     232            <Deployment Version="2">
     233                <DeployFile LocalName="Win32\Debug\Integrated.exe" Configuration="Debug" Class="ProjectOutput">
     234                    <Platform Name="Win32">
     235                        <RemoteName>Integrated.exe</RemoteName>
     236                        <Overwrite>true</Overwrite>
     237                    </Platform>
     238                </DeployFile>
     239                <DeployClass Name="DependencyModule">
     240                    <Platform Name="Win32">
     241                        <Operation>0</Operation>
     242                        <Extensions>.dll;.bpl</Extensions>
     243                    </Platform>
     244                    <Platform Name="iOSDevice64">
     245                        <Operation>1</Operation>
     246                        <Extensions>.dylib</Extensions>
     247                    </Platform>
     248                    <Platform Name="OSX32">
     249                        <RemoteDir>Contents\MacOS</RemoteDir>
     250                        <Operation>1</Operation>
     251                        <Extensions>.dylib</Extensions>
     252                    </Platform>
     253                    <Platform Name="iOSDevice32">
     254                        <Operation>1</Operation>
     255                        <Extensions>.dylib</Extensions>
     256                    </Platform>
     257                    <Platform Name="iOSSimulator">
     258                        <Operation>1</Operation>
     259                        <Extensions>.dylib</Extensions>
     260                    </Platform>
     261                </DeployClass>
     262                <DeployClass Name="ProjectOSXResource">
     263                    <Platform Name="OSX32">
     264                        <RemoteDir>Contents\Resources</RemoteDir>
     265                        <Operation>1</Operation>
     266                    </Platform>
     267                </DeployClass>
     268                <DeployClass Name="AndroidClassesDexFile">
     269                    <Platform Name="Android">
     270                        <RemoteDir>classes</RemoteDir>
     271                        <Operation>1</Operation>
     272                    </Platform>
     273                </DeployClass>
     274                <DeployClass Name="AdditionalDebugSymbols">
     275                    <Platform Name="Win32">
     276                        <RemoteDir>Contents\MacOS</RemoteDir>
     277                        <Operation>0</Operation>
     278                    </Platform>
     279                    <Platform Name="iOSSimulator">
     280                        <Operation>1</Operation>
     281                    </Platform>
     282                    <Platform Name="OSX32">
     283                        <RemoteDir>Contents\MacOS</RemoteDir>
     284                        <Operation>1</Operation>
     285                    </Platform>
     286                </DeployClass>
     287                <DeployClass Name="iPad_Launch768">
     288                    <Platform Name="iOSSimulator">
     289                        <Operation>1</Operation>
     290                    </Platform>
     291                    <Platform Name="iOSDevice64">
     292                        <Operation>1</Operation>
     293                    </Platform>
     294                    <Platform Name="iOSDevice32">
     295                        <Operation>1</Operation>
     296                    </Platform>
     297                </DeployClass>
     298                <DeployClass Name="Android_LauncherIcon144">
     299                    <Platform Name="Android">
     300                        <RemoteDir>res\drawable-xxhdpi</RemoteDir>
     301                        <Operation>1</Operation>
     302                    </Platform>
     303                </DeployClass>
     304                <DeployClass Name="AndroidLibnativeMipsFile">
     305                    <Platform Name="Android">
     306                        <RemoteDir>library\lib\mips</RemoteDir>
     307                        <Operation>1</Operation>
     308                    </Platform>
     309                </DeployClass>
     310                <DeployClass Required="true" Name="ProjectOutput">
     311                    <Platform Name="Win32">
     312                        <Operation>0</Operation>
     313                    </Platform>
     314                    <Platform Name="iOSDevice64">
     315                        <Operation>1</Operation>
     316                    </Platform>
     317                    <Platform Name="OSX32">
     318                        <RemoteDir>Contents\MacOS</RemoteDir>
     319                        <Operation>1</Operation>
     320                    </Platform>
     321                    <Platform Name="iOSDevice32">
     322                        <Operation>1</Operation>
     323                    </Platform>
     324                    <Platform Name="Android">
     325                        <RemoteDir>library\lib\armeabi-v7a</RemoteDir>
     326                        <Operation>1</Operation>
     327                    </Platform>
     328                    <Platform Name="iOSSimulator">
     329                        <Operation>1</Operation>
     330                    </Platform>
     331                </DeployClass>
     332                <DeployClass Name="DependencyFramework">
     333                    <Platform Name="Win32">
     334                        <Operation>0</Operation>
     335                    </Platform>
     336                    <Platform Name="OSX32">
     337                        <RemoteDir>Contents\MacOS</RemoteDir>
     338                        <Operation>1</Operation>
     339                        <Extensions>.framework</Extensions>
     340                    </Platform>
     341                </DeployClass>
     342                <DeployClass Name="iPhone_Launch640">
     343                    <Platform Name="iOSSimulator">
     344                        <Operation>1</Operation>
     345                    </Platform>
     346                    <Platform Name="iOSDevice64">
     347                        <Operation>1</Operation>
     348                    </Platform>
     349                    <Platform Name="iOSDevice32">
     350                        <Operation>1</Operation>
     351                    </Platform>
     352                </DeployClass>
     353                <DeployClass Name="iPad_Launch1024">
     354                    <Platform Name="iOSSimulator">
     355                        <Operation>1</Operation>
     356                    </Platform>
     357                    <Platform Name="iOSDevice64">
     358                        <Operation>1</Operation>
     359                    </Platform>
     360                    <Platform Name="iOSDevice32">
     361                        <Operation>1</Operation>
     362                    </Platform>
     363                </DeployClass>
     364                <DeployClass Name="ProjectiOSDeviceDebug">
     365                    <Platform Name="iOSDevice64">
     366                        <RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
     367                        <Operation>1</Operation>
     368                    </Platform>
     369                    <Platform Name="iOSDevice32">
     370                        <RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
     371                        <Operation>1</Operation>
     372                    </Platform>
     373                </DeployClass>
     374                <DeployClass Name="AndroidLibnativeX86File">
     375                    <Platform Name="Android">
     376                        <RemoteDir>library\lib\x86</RemoteDir>
     377                        <Operation>1</Operation>
     378                    </Platform>
     379                </DeployClass>
     380                <DeployClass Name="iPhone_Launch320">
     381                    <Platform Name="iOSSimulator">
     382                        <Operation>1</Operation>
     383                    </Platform>
     384                    <Platform Name="iOSDevice64">
     385                        <Operation>1</Operation>
     386                    </Platform>
     387                    <Platform Name="iOSDevice32">
     388                        <Operation>1</Operation>
     389                    </Platform>
     390                </DeployClass>
     391                <DeployClass Name="ProjectiOSInfoPList">
     392                    <Platform Name="iOSSimulator">
     393                        <Operation>1</Operation>
     394                    </Platform>
     395                    <Platform Name="iOSDevice64">
     396                        <Operation>1</Operation>
     397                    </Platform>
     398                    <Platform Name="iOSDevice32">
     399                        <Operation>1</Operation>
     400                    </Platform>
     401                </DeployClass>
     402                <DeployClass Name="AndroidLibnativeArmeabiFile">
     403                    <Platform Name="Android">
     404                        <RemoteDir>library\lib\armeabi</RemoteDir>
     405                        <Operation>1</Operation>
     406                    </Platform>
     407                </DeployClass>
     408                <DeployClass Name="DebugSymbols">
     409                    <Platform Name="Win32">
     410                        <Operation>0</Operation>
     411                    </Platform>
     412                    <Platform Name="iOSSimulator">
     413                        <Operation>1</Operation>
     414                    </Platform>
     415                    <Platform Name="OSX32">
     416                        <RemoteDir>Contents\MacOS</RemoteDir>
     417                        <Operation>1</Operation>
     418                    </Platform>
     419                </DeployClass>
     420                <DeployClass Name="iPad_Launch1536">
     421                    <Platform Name="iOSSimulator">
     422                        <Operation>1</Operation>
     423                    </Platform>
     424                    <Platform Name="iOSDevice64">
     425                        <Operation>1</Operation>
     426                    </Platform>
     427                    <Platform Name="iOSDevice32">
     428                        <Operation>1</Operation>
     429                    </Platform>
     430                </DeployClass>
     431                <DeployClass Name="Android_SplashImage470">
     432                    <Platform Name="Android">
     433                        <RemoteDir>res\drawable-normal</RemoteDir>
     434                        <Operation>1</Operation>
     435                    </Platform>
     436                </DeployClass>
     437                <DeployClass Name="Android_LauncherIcon96">
     438                    <Platform Name="Android">
     439                        <RemoteDir>res\drawable-xhdpi</RemoteDir>
     440                        <Operation>1</Operation>
     441                    </Platform>
     442                </DeployClass>
     443                <DeployClass Name="Android_SplashImage640">
     444                    <Platform Name="Android">
     445                        <RemoteDir>res\drawable-large</RemoteDir>
     446                        <Operation>1</Operation>
     447                    </Platform>
     448                </DeployClass>
     449                <DeployClass Name="iPhone_Launch640x1136">
     450                    <Platform Name="iOSSimulator">
     451                        <Operation>1</Operation>
     452                    </Platform>
     453                    <Platform Name="iOSDevice64">
     454                        <Operation>1</Operation>
     455                    </Platform>
     456                    <Platform Name="iOSDevice32">
     457                        <Operation>1</Operation>
     458                    </Platform>
     459                </DeployClass>
     460                <DeployClass Name="ProjectiOSEntitlements">
     461                    <Platform Name="iOSDevice64">
     462                        <RemoteDir>../</RemoteDir>
     463                        <Operation>1</Operation>
     464                    </Platform>
     465                    <Platform Name="iOSDevice32">
     466                        <RemoteDir>../</RemoteDir>
     467                        <Operation>1</Operation>
     468                    </Platform>
     469                </DeployClass>
     470                <DeployClass Name="Android_LauncherIcon72">
     471                    <Platform Name="Android">
     472                        <RemoteDir>res\drawable-hdpi</RemoteDir>
     473                        <Operation>1</Operation>
     474                    </Platform>
     475                </DeployClass>
     476                <DeployClass Name="AndroidGDBServer">
     477                    <Platform Name="Android">
     478                        <RemoteDir>library\lib\armeabi-v7a</RemoteDir>
     479                        <Operation>1</Operation>
     480                    </Platform>
     481                </DeployClass>
     482                <DeployClass Name="ProjectOSXInfoPList">
     483                    <Platform Name="OSX32">
     484                        <RemoteDir>Contents</RemoteDir>
     485                        <Operation>1</Operation>
     486                    </Platform>
     487                </DeployClass>
     488                <DeployClass Name="ProjectOSXEntitlements">
     489                    <Platform Name="OSX32">
     490                        <RemoteDir>../</RemoteDir>
     491                        <Operation>1</Operation>
     492                    </Platform>
     493                </DeployClass>
     494                <DeployClass Name="iPad_Launch2048">
     495                    <Platform Name="iOSSimulator">
     496                        <Operation>1</Operation>
     497                    </Platform>
     498                    <Platform Name="iOSDevice64">
     499                        <Operation>1</Operation>
     500                    </Platform>
     501                    <Platform Name="iOSDevice32">
     502                        <Operation>1</Operation>
     503                    </Platform>
     504                </DeployClass>
     505                <DeployClass Name="AndroidSplashStyles">
     506                    <Platform Name="Android">
     507                        <RemoteDir>res\values</RemoteDir>
     508                        <Operation>1</Operation>
     509                    </Platform>
     510                </DeployClass>
     511                <DeployClass Name="Android_SplashImage426">
     512                    <Platform Name="Android">
     513                        <RemoteDir>res\drawable-small</RemoteDir>
     514                        <Operation>1</Operation>
     515                    </Platform>
     516                </DeployClass>
     517                <DeployClass Name="AndroidSplashImageDef">
     518                    <Platform Name="Android">
     519                        <RemoteDir>res\drawable</RemoteDir>
     520                        <Operation>1</Operation>
     521                    </Platform>
     522                </DeployClass>
     523                <DeployClass Name="ProjectiOSResource">
     524                    <Platform Name="iOSSimulator">
     525                        <Operation>1</Operation>
     526                    </Platform>
     527                    <Platform Name="iOSDevice64">
     528                        <Operation>1</Operation>
     529                    </Platform>
     530                    <Platform Name="iOSDevice32">
     531                        <Operation>1</Operation>
     532                    </Platform>
     533                </DeployClass>
     534                <DeployClass Name="ProjectAndroidManifest">
     535                    <Platform Name="Android">
     536                        <Operation>1</Operation>
     537                    </Platform>
     538                </DeployClass>
     539                <DeployClass Name="Android_DefaultAppIcon">
     540                    <Platform Name="Android">
     541                        <RemoteDir>res\drawable</RemoteDir>
     542                        <Operation>1</Operation>
     543                    </Platform>
     544                </DeployClass>
     545                <DeployClass Name="File">
     546                    <Platform Name="Win32">
     547                        <Operation>0</Operation>
     548                    </Platform>
     549                    <Platform Name="iOSDevice64">
     550                        <Operation>0</Operation>
     551                    </Platform>
     552                    <Platform Name="OSX32">
     553                        <RemoteDir>Contents\Resources\StartUp\</RemoteDir>
     554                        <Operation>0</Operation>
     555                    </Platform>
     556                    <Platform Name="iOSDevice32">
     557                        <Operation>0</Operation>
     558                    </Platform>
     559                    <Platform Name="Android">
     560                        <Operation>0</Operation>
     561                    </Platform>
     562                    <Platform Name="iOSSimulator">
     563                        <Operation>0</Operation>
     564                    </Platform>
     565                </DeployClass>
     566                <DeployClass Name="AndroidServiceOutput">
     567                    <Platform Name="Android">
     568                        <RemoteDir>library\lib\armeabi-v7a</RemoteDir>
     569                        <Operation>1</Operation>
     570                    </Platform>
     571                </DeployClass>
     572                <DeployClass Required="true" Name="DependencyPackage">
     573                    <Platform Name="Win32">
     574                        <Operation>0</Operation>
     575                        <Extensions>.bpl</Extensions>
     576                    </Platform>
     577                    <Platform Name="iOSDevice64">
     578                        <Operation>1</Operation>
     579                        <Extensions>.dylib</Extensions>
     580                    </Platform>
     581                    <Platform Name="OSX32">
     582                        <RemoteDir>Contents\MacOS</RemoteDir>
     583                        <Operation>1</Operation>
     584                        <Extensions>.dylib</Extensions>
     585                    </Platform>
     586                    <Platform Name="iOSDevice32">
     587                        <Operation>1</Operation>
     588                        <Extensions>.dylib</Extensions>
     589                    </Platform>
     590                    <Platform Name="iOSSimulator">
     591                        <Operation>1</Operation>
     592                        <Extensions>.dylib</Extensions>
     593                    </Platform>
     594                </DeployClass>
     595                <DeployClass Name="Android_LauncherIcon48">
     596                    <Platform Name="Android">
     597                        <RemoteDir>res\drawable-mdpi</RemoteDir>
     598                        <Operation>1</Operation>
     599                    </Platform>
     600                </DeployClass>
     601                <DeployClass Name="Android_SplashImage960">
     602                    <Platform Name="Android">
     603                        <RemoteDir>res\drawable-xlarge</RemoteDir>
     604                        <Operation>1</Operation>
     605                    </Platform>
     606                </DeployClass>
     607                <DeployClass Name="Android_LauncherIcon36">
     608                    <Platform Name="Android">
     609                        <RemoteDir>res\drawable-ldpi</RemoteDir>
     610                        <Operation>1</Operation>
     611                    </Platform>
     612                </DeployClass>
     613                <DeployClass Name="ProjectiOSDeviceResourceRules">
     614                    <Platform Name="iOSDevice64">
     615                        <Operation>1</Operation>
     616                    </Platform>
     617                    <Platform Name="iOSDevice32">
     618                        <Operation>1</Operation>
     619                    </Platform>
     620                </DeployClass>
     621                <ProjectRoot Platform="iOSDevice64" Name="$(PROJECTNAME).app"/>
     622                <ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
     623                <ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/>
     624                <ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/>
     625                <ProjectRoot Platform="OSX32" Name="$(PROJECTNAME).app"/>
     626                <ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/>
     627                <ProjectRoot Platform="iOSSimulator" Name="$(PROJECTNAME).app"/>
     628            </Deployment>
    232629        </BorlandProject>
    233630        <ProjectFileVersion>12</ProjectFileVersion>
     
    235632    <Import Project="$(BDS)\Bin\CodeGear.Delphi.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')"/>
    236633    <Import Project="$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj" Condition="Exists('$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj')"/>
     634    <Import Project="$(MSBuildProjectName).deployproj" Condition="Exists('$(MSBuildProjectName).deployproj')"/>
    237635</Project>
  • trunk/LocalPlayer/BaseWin.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit BaseWin;
    43
     
    65
    76uses
    8   ScreenTools,Messg,
    9 
    10   Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms;
     7  ScreenTools, Messg,
     8
     9  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms;
    1110
    1211type
     
    1615    constructor Create(AOwner: TComponent); override;
    1716    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    18     procedure FormPaint(Sender:TObject);
    19     procedure FormKeyDown(Sender: TObject; var Key: Word;
    20       Shift: TShiftState);
     17    procedure FormPaint(Sender: TObject);
     18    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    2119    procedure FormDeactivate(Sender: TObject);
    2220    procedure SmartUpdateContent(ImmUpdate: boolean = false);
    2321    procedure StayOnTop_Workaround;
    2422  protected
    25     FWindowMode,ModalFrameIndent: integer;
     23    FWindowMode, ModalFrameIndent: integer;
    2624    HelpContext: string;
    2725    procedure ShowNewContent(NewMode: integer; forceclose: boolean = false);
    28     procedure MarkUsedOffscreen(xMax,yMax: integer);
     26    procedure MarkUsedOffscreen(xMax, yMax: integer);
    2927    procedure OffscreenPaint; virtual;
    3028    procedure VPaint; virtual;
     
    3331  end;
    3432
    35 
    3633  TFramedDlg = class(TBufferedDrawDlg)
    3734  public
    3835    constructor Create(AOwner: TComponent); override;
    39     procedure FormCreate(Sender:TObject);
     36    procedure FormCreate(Sender: TObject);
    4037    procedure SmartInvalidate; override;
    4138  protected
     
    4441    procedure InitWindowRegion;
    4542    procedure VPaint; override;
    46     procedure FillOffscreen(Left,Top,Width,Height: integer);
    47     end;
    48 
     43    procedure FillOffscreen(Left, Top, Width, Height: integer);
     44  end;
    4945
    5046const
    51 // window modes
    52 wmNone=0; wmModal=$1; wmPersistent=$2; wmSubmodal=$3;
    53 
    54 
    55 yUnused=161;
    56 NarrowFrame=11; WideFrame=36; SideFrame=9;
     47  // window modes
     48  wmNone = 0;
     49  wmModal = $1;
     50  wmPersistent = $2;
     51  wmSubmodal = $3;
     52
     53  yUnused = 161;
     54  NarrowFrame = 11;
     55  WideFrame = 36;
     56  SideFrame = 9;
    5757
    5858var
    59 UsedOffscreenWidth, UsedOffscreenHeight: integer;
    60 Offscreen: TBitmap;
    61 OffscreenUser: TForm;
     59  UsedOffscreenWidth, UsedOffscreenHeight: integer;
     60  Offscreen: TBitmap;
     61  OffscreenUser: TForm;
    6262
    6363procedure CreateOffscreen;
    6464
    65 
    6665implementation
    6766
    6867uses
    69 Term, Help, ButtonBase, Area;
    70 
     68  Term, Help, ButtonBase, Area;
    7169
    7270constructor TBufferedDrawDlg.Create;
    7371begin
    74 OnClose:=FormClose;
    75 OnPaint:=FormPaint;
    76 OnKeyDown:=FormKeyDown;
    77 OnDeactivate:=FormDeactivate;
    78 inherited;
    79 FWindowMode:=wmNone;
    80 HelpContext:='CONCEPTS';
    81 TitleHeight:=WideFrame;
    82 ModalFrameIndent:=45;
    83 UserLeft:=(Screen.Width-Width) div 2;
    84 UserTop:=(Screen.Height-Height) div 2;
     72  OnClose := FormClose;
     73  OnPaint := FormPaint;
     74  OnKeyDown := FormKeyDown;
     75  OnDeactivate := FormDeactivate;
     76  inherited;
     77  FWindowMode := wmNone;
     78  HelpContext := 'CONCEPTS';
     79  TitleHeight := WideFrame;
     80  ModalFrameIndent := 45;
     81  UserLeft := (Screen.Width - Width) div 2;
     82  UserTop := (Screen.Height - Height) div 2;
    8583end;
    8684
    8785procedure TBufferedDrawDlg.FormClose(Sender: TObject; var Action: TCloseAction);
    8886begin
    89 if FWindowMode=wmPersistent then
    90   begin UserLeft:=Left; UserTop:=Top end;
    91 if OffscreenUser=self then OffscreenUser:=nil;
    92 end;
    93 
    94 procedure TBufferedDrawDlg.FormPaint(Sender:TObject);
    95 begin
    96 if OffscreenUser<>self then OffscreenPaint;
    97 VPaint
     87  if FWindowMode = wmPersistent then
     88  begin
     89    UserLeft := Left;
     90    UserTop := Top
     91  end;
     92  if OffscreenUser = self then
     93    OffscreenUser := nil;
     94end;
     95
     96procedure TBufferedDrawDlg.FormPaint(Sender: TObject);
     97begin
     98  if OffscreenUser <> self then
     99    OffscreenPaint;
     100  VPaint
    98101end;
    99102
     
    101104  Shift: TShiftState);
    102105begin
    103 if Key=VK_ESCAPE then
    104   begin
    105   if fsModal in FormState then ModalResult:=mrCancel
    106   end
    107 else if Key=VK_RETURN then
    108   begin
    109   if fsModal in FormState then ModalResult:=mrOK
    110   end
    111 else if Key=VK_F1 then
    112   HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkText, HelpDlg.TextIndex(HelpContext))
    113 else if FWindowMode=wmPersistent then
    114   MainScreen.FormKeyDown(Sender, Key, Shift);
     106  if Key = VK_ESCAPE then
     107  begin
     108    if fsModal in FormState then
     109      ModalResult := mrCancel
     110  end
     111  else if Key = VK_RETURN then
     112  begin
     113    if fsModal in FormState then
     114      ModalResult := mrOK
     115  end
     116  else if Key = VK_F1 then
     117    HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkText,
     118      HelpDlg.TextIndex(HelpContext))
     119  else if FWindowMode = wmPersistent then
     120    MainScreen.FormKeyDown(Sender, Key, Shift);
    115121end;
    116122
    117123procedure TBufferedDrawDlg.FormDeactivate(Sender: TObject);
    118124begin
    119 if FWindowMode=wmSubmodal then Close
     125  if FWindowMode = wmSubmodal then
     126    Close
    120127end;
    121128
    122129procedure TBufferedDrawDlg.OffscreenPaint;
    123130begin
    124 if (OffscreenUser<>nil) and (OffscreenUser<>self) then
    125   OffscreenUser.Update; // complete working with old owner to prevent rebound
    126 OffscreenUser:=self;
     131  if (OffscreenUser <> nil) and (OffscreenUser <> self) then
     132    OffscreenUser.Update; // complete working with old owner to prevent rebound
     133  OffscreenUser := self;
    127134end;
    128135
    129136procedure TBufferedDrawDlg.VPaint;
    130137begin
    131 BitBlt(Canvas.Handle, 0, 0, ClientWidth,
    132   ClientHeight, offscreen.Canvas. Handle, 0, 0, SRCCOPY);
    133 end;
    134 
    135 procedure TBufferedDrawDlg.ShowNewContent(NewMode: integer; forceclose: boolean);
    136 begin
    137 if Visible then
    138   begin
    139   assert((NewMode=wmModal) or (FWindowMode<>wmModal)); // don't make modal window non-modal
    140   if (NewMode=wmModal) and (forceclose or (FWindowMode<>wmModal)) then
     138  BitBlt(Canvas.Handle, 0, 0, ClientWidth, ClientHeight,
     139    Offscreen.Canvas.Handle, 0, 0, SRCCOPY);
     140end;
     141
     142procedure TBufferedDrawDlg.ShowNewContent(NewMode: integer;
     143  forceclose: boolean);
     144begin
     145  if Visible then
     146  begin
     147    assert((NewMode = wmModal) or (FWindowMode <> wmModal));
     148    // don't make modal window non-modal
     149    if (NewMode = wmModal) and (forceclose or (FWindowMode <> wmModal)) then
    141150    begin // make modal
    142     UserLeft:=Left;
    143     UserTop:=Top;
    144     Visible:=false;
    145     FWindowMode:=NewMode;
    146     ShowModal;
     151      UserLeft := Left;
     152      UserTop := Top;
     153      Visible := false;
     154      FWindowMode := NewMode;
     155      ShowModal;
    147156    end
    148   else if forceclose then
     157    else if forceclose then
    149158    begin // make modal
    150     Visible:=false;
    151     FWindowMode:=NewMode;
    152     Left:=UserLeft;
    153     Top:=UserTop;
    154     Show;
     159      Visible := false;
     160      FWindowMode := NewMode;
     161      Left := UserLeft;
     162      Top := UserTop;
     163      Show;
    155164    end
    156   else
    157     begin
    158     FWindowMode:=NewMode;
    159     if @OnShow<>nil then
    160       OnShow(nil);
    161     Invalidate;
    162     BringToFront
     165    else
     166    begin
     167      FWindowMode := NewMode;
     168      if @OnShow <> nil then
     169        OnShow(nil);
     170      Invalidate;
     171      BringToFront
    163172    end
    164173  end
    165 else
    166   begin
    167   FWindowMode:=NewMode;
    168   Left:=UserLeft;
    169   Top:=UserTop;
    170   if FWindowMode=wmModal then ShowModal
    171   else Show
     174  else
     175  begin
     176    FWindowMode := NewMode;
     177    Left := UserLeft;
     178    Top := UserTop;
     179    if FWindowMode = wmModal then
     180      ShowModal
     181    else
     182      Show
    172183  end
    173184end;
     
    175186procedure TBufferedDrawDlg.SmartUpdateContent(ImmUpdate: boolean);
    176187begin
    177 if Visible then
    178   begin
    179   OffscreenPaint;
    180   SmartInvalidate;
    181   if ImmUpdate then Update
    182   end
    183 end;
    184 
    185 procedure TBufferedDrawDlg.MarkUsedOffscreen(xMax,yMax: integer);
    186 begin
    187 if xMax>UsedOffscreenWidth then UsedOffscreenWidth:=xMax;
    188 if yMax>UsedOffscreenHeight then UsedOffscreenHeight:=yMax;
     188  if Visible then
     189  begin
     190    OffscreenPaint;
     191    SmartInvalidate;
     192    if ImmUpdate then
     193      Update
     194  end
     195end;
     196
     197procedure TBufferedDrawDlg.MarkUsedOffscreen(xMax, yMax: integer);
     198begin
     199  if xMax > UsedOffscreenWidth then
     200    UsedOffscreenWidth := xMax;
     201  if yMax > UsedOffscreenHeight then
     202    UsedOffscreenHeight := yMax;
    189203end;
    190204
     
    193207// after application lost focus, so show all stayontop-windows in first turn
    194208var
    195 SaveOnShow, SaveOnPaint: TNotifyEvent;
    196 begin
    197 Top:=Screen.Height;
    198 SaveOnShow:=OnShow;
    199 OnShow:=nil;
    200 SaveOnPaint:=OnPaint;
    201 OnPaint:=nil;
    202 FWindowMode:=wmNone;
    203 Show;
    204 Hide;
    205 OnShow:=SaveOnShow;
    206 OnPaint:=SaveOnPaint;
    207 end;
    208 
     209  SaveOnShow, SaveOnPaint: TNotifyEvent;
     210begin
     211  Top := Screen.Height;
     212  SaveOnShow := OnShow;
     213  OnShow := nil;
     214  SaveOnPaint := OnPaint;
     215  OnPaint := nil;
     216  FWindowMode := wmNone;
     217  Show;
     218  Hide;
     219  OnShow := SaveOnShow;
     220  OnPaint := SaveOnPaint;
     221end;
    209222
    210223constructor TFramedDlg.Create;
    211224begin
    212 OnCreate:=FormCreate;
    213 inherited;
    214 end;
    215 
    216 procedure TFramedDlg.FormCreate(Sender:TObject);
    217 begin
    218 CaptionLeft:=0; CaptionRight:=$FFFF;
    219 WideBottom:=false;
    220 FullCaption:=true;
    221 TexOverride:=false;
    222 ModalIndication:=true;
    223 Canvas.Brush.Style:=bsClear;
    224 InnerWidth:=ClientWidth-2*SideFrame;
    225 InnerHeight:=ClientHeight-TitleHeight-NarrowFrame;
     225  OnCreate := FormCreate;
     226  inherited;
     227end;
     228
     229procedure TFramedDlg.FormCreate(Sender: TObject);
     230begin
     231  CaptionLeft := 0;
     232  CaptionRight := $FFFF;
     233  WideBottom := false;
     234  FullCaption := true;
     235  TexOverride := false;
     236  ModalIndication := true;
     237  Canvas.Brush.Style := bsClear;
     238  InnerWidth := ClientWidth - 2 * SideFrame;
     239  InnerHeight := ClientHeight - TitleHeight - NarrowFrame;
    226240end;
    227241
    228242procedure TFramedDlg.SmartInvalidate;
    229243var
    230 i,BottomFrame: integer;
    231 r0,r1: HRgn;
    232 begin
    233 if WideBottom then BottomFrame:=WideFrame else BottomFrame:=NarrowFrame;
    234 r0:=CreateRectRgn(SideFrame,TitleHeight,ClientWidth-SideFrame,
    235   ClientHeight-BottomFrame);
    236 for i:=0 to ControlCount-1 do
    237   if not (Controls[i] is TArea) and Controls[i].Visible then
    238     begin
    239     with Controls[i].BoundsRect do
    240       r1:=CreateRectRgn(Left,Top,Right,Bottom);
    241     CombineRgn(r0,r0,r1,RGN_DIFF);
    242     DeleteObject(r1);
     244  i, BottomFrame: integer;
     245  r0, r1: HRgn;
     246begin
     247  if WideBottom then
     248    BottomFrame := WideFrame
     249  else
     250    BottomFrame := NarrowFrame;
     251  r0 := CreateRectRgn(SideFrame, TitleHeight, ClientWidth - SideFrame,
     252    ClientHeight - BottomFrame);
     253  for i := 0 to ControlCount - 1 do
     254    if not(Controls[i] is TArea) and Controls[i].Visible then
     255    begin
     256      with Controls[i].BoundsRect do
     257        r1 := CreateRectRgn(Left, Top, Right, Bottom);
     258      CombineRgn(r0, r0, r1, RGN_DIFF);
     259      DeleteObject(r1);
    243260    end;
    244 InvalidateRgn(Handle,r0,false);
    245 DeleteObject(r0);
     261  InvalidateRgn(Handle, r0, false);
     262  DeleteObject(r0);
    246263end;
    247264
    248265procedure TFramedDlg.VPaint;
    249266
    250   procedure CornerFrame(x0,y0,x1,y1: integer);
    251   begin
    252   Frame(Canvas,x0+1,y0+1,x1-2,y1-2,MainTexture.clBevelLight,MainTexture.clBevelShade);
    253   Frame(Canvas,x0+2,y0+2,x1-3,y1-3,MainTexture.clBevelLight,MainTexture.clBevelShade);
    254   Corner(Canvas,x0+1,y0+1,0,MainTexture);
    255   Corner(Canvas,x1-9,y0+1,1,MainTexture);
    256   Corner(Canvas,x0+1,y1-9,2,MainTexture);
    257   Corner(Canvas,x1-9,y1-9,3,MainTexture);
     267  procedure CornerFrame(x0, y0, x1, y1: integer);
     268  begin
     269    Frame(Canvas, x0 + 1, y0 + 1, x1 - 2, y1 - 2, MainTexture.clBevelLight,
     270      MainTexture.clBevelShade);
     271    Frame(Canvas, x0 + 2, y0 + 2, x1 - 3, y1 - 3, MainTexture.clBevelLight,
     272      MainTexture.clBevelShade);
     273    Corner(Canvas, x0 + 1, y0 + 1, 0, MainTexture);
     274    Corner(Canvas, x1 - 9, y0 + 1, 1, MainTexture);
     275    Corner(Canvas, x0 + 1, y1 - 9, 2, MainTexture);
     276    Corner(Canvas, x1 - 9, y1 - 9, 3, MainTexture);
    258277  end;
    259278
    260279var
    261 i,l,FrameTop,FrameBottom,InnerBottom,Cut,xTexOffset,yTexOffset: integer;
    262 R: TRect;
    263 begin
    264 if not TexOverride then
    265   begin
    266   if (FWindowMode=wmModal) and ModalIndication then MainTexture:=MainTexture
    267   else MainTexture:=MainTexture;
    268   MainTexture:=MainTexture
    269   end;
    270 Canvas.Font.Assign(UniFont[ftCaption]);
    271 l:=BiColorTextWidth(Canvas,Caption);
    272 Cut:=(ClientWidth-l) div 2;
    273 xTexOffset:=(wMaintexture-ClientWidth) div 2;
    274 yTexOffset:=(hMaintexture-ClientHeight) div 2;
    275 if WideBottom then InnerBottom:=ClientHeight-WideFrame
    276 else InnerBottom:=ClientHeight-NarrowFrame;
    277 if FullCaption then begin FrameTop:=0; FrameBottom:=ClientHeight end
    278 else
    279   begin
    280   FrameTop:=TitleHeight-NarrowFrame;
    281   if WideBottom then FrameBottom:=ClientHeight-(WideFrame-NarrowFrame)
    282   else FrameBottom:=ClientHeight
    283   end;
    284 Fill(Canvas,3,InnerBottom+1,ClientWidth-6,ClientHeight-InnerBottom-4,
    285   xTexOffset,yTexOffset);
    286 Fill(Canvas,3,TitleHeight-2,SideFrame-3,InnerBottom-TitleHeight+4,
    287   xTexOffset,yTexOffset);
    288 Fill(Canvas,ClientWidth-SideFrame,TitleHeight-2,SideFrame-3,
    289   InnerBottom-TitleHeight+4,xTexOffset,yTexOffset);
    290 Frame(Canvas,0,FrameTop,ClientWidth-1,FrameBottom-1,0,0);
    291 Frame(Canvas,SideFrame-1,TitleHeight-1,ClientWidth-SideFrame,
    292   InnerBottom,MainTexture.clBevelShade,MainTexture.clBevelLight);
    293 //RFrame(Canvas,SideFrame-2,TitleHeight-2,ClientWidth-SideFrame+1,
    294 //  InnerBottom+1,MainTexture.clBevelShade,MainTexture.clBevelLight);
    295 if FullCaption then
    296   begin
    297   if (FWindowMode<>wmModal) or not ModalIndication then
    298     begin
    299     Fill(Canvas,3,3+FrameTop,ClientWidth-6,TitleHeight-FrameTop-4,
    300       xTexOffset,yTexOffset);
    301     CornerFrame(0,FrameTop,ClientWidth,FrameBottom);
     280  i, l, FrameTop, FrameBottom, InnerBottom, Cut, xTexOffset,
     281    yTexOffset: integer;
     282  R: TRect;
     283begin
     284  if not TexOverride then
     285  begin
     286    if (FWindowMode = wmModal) and ModalIndication then
     287      MainTexture := MainTexture
     288    else
     289      MainTexture := MainTexture;
     290    MainTexture := MainTexture
     291  end;
     292  Canvas.Font.Assign(UniFont[ftCaption]);
     293  l := BiColorTextWidth(Canvas, Caption);
     294  Cut := (ClientWidth - l) div 2;
     295  xTexOffset := (wMaintexture - ClientWidth) div 2;
     296  yTexOffset := (hMaintexture - ClientHeight) div 2;
     297  if WideBottom then
     298    InnerBottom := ClientHeight - WideFrame
     299  else
     300    InnerBottom := ClientHeight - NarrowFrame;
     301  if FullCaption then
     302  begin
     303    FrameTop := 0;
     304    FrameBottom := ClientHeight
     305  end
     306  else
     307  begin
     308    FrameTop := TitleHeight - NarrowFrame;
     309    if WideBottom then
     310      FrameBottom := ClientHeight - (WideFrame - NarrowFrame)
     311    else
     312      FrameBottom := ClientHeight
     313  end;
     314  Fill(Canvas, 3, InnerBottom + 1, ClientWidth - 6, ClientHeight - InnerBottom -
     315    4, xTexOffset, yTexOffset);
     316  Fill(Canvas, 3, TitleHeight - 2, SideFrame - 3, InnerBottom - TitleHeight + 4,
     317    xTexOffset, yTexOffset);
     318  Fill(Canvas, ClientWidth - SideFrame, TitleHeight - 2, SideFrame - 3,
     319    InnerBottom - TitleHeight + 4, xTexOffset, yTexOffset);
     320  Frame(Canvas, 0, FrameTop, ClientWidth - 1, FrameBottom - 1, 0, 0);
     321  Frame(Canvas, SideFrame - 1, TitleHeight - 1, ClientWidth - SideFrame,
     322    InnerBottom, MainTexture.clBevelShade, MainTexture.clBevelLight);
     323  // RFrame(Canvas,SideFrame-2,TitleHeight-2,ClientWidth-SideFrame+1,
     324  // InnerBottom+1,MainTexture.clBevelShade,MainTexture.clBevelLight);
     325  if FullCaption then
     326  begin
     327    if (FWindowMode <> wmModal) or not ModalIndication then
     328    begin
     329      Fill(Canvas, 3, 3 + FrameTop, ClientWidth - 6, TitleHeight - FrameTop - 4,
     330        xTexOffset, yTexOffset);
     331      CornerFrame(0, FrameTop, ClientWidth, FrameBottom);
    302332    end
    303   else with Canvas do
    304     begin
    305     Fill(Canvas,3+ModalFrameIndent,3+FrameTop,ClientWidth-6-2*ModalFrameIndent,
    306       TitleHeight-FrameTop-4,xTexOffset,yTexOffset);
    307     Fill(Canvas,ClientWidth-3-ModalFrameIndent,3+FrameTop,ModalFrameIndent,
    308       TitleHeight-FrameTop-4,xTexOffset,yTexOffset);
    309     Fill(Canvas,3,3+FrameTop,ModalFrameIndent,TitleHeight-FrameTop-4,
    310       xTexOffset,yTexOffset);
    311     CornerFrame(0,FrameTop,ClientWidth,FrameBottom);
    312     Pen.Color:=MainTexture.clBevelShade;
    313     MoveTo(3+ModalFrameIndent,2); LineTo(3+ModalFrameIndent,TitleHeight);
    314     Pen.Color:=MainTexture.clBevelShade;
    315     MoveTo(4+ModalFrameIndent,TitleHeight-1);
    316     LineTo(ClientWidth-4-ModalFrameIndent,TitleHeight-1);
    317     LineTo(ClientWidth-4-ModalFrameIndent,1);
    318     Pen.Color:=MainTexture.clBevelLight;
    319     MoveTo(ClientWidth-5-ModalFrameIndent,2);
    320     LineTo(4+ModalFrameIndent,2);
    321     LineTo(4+ModalFrameIndent,TitleHeight);
    322     MoveTo(ClientWidth-4-ModalFrameIndent,1);
    323     LineTo(3+ModalFrameIndent,1);
    324     Pen.Color:=MainTexture.clBevelLight;
    325     MoveTo(ClientWidth-3-ModalFrameIndent,3); LineTo(ClientWidth-3-ModalFrameIndent,TitleHeight);
    326     end
    327   end
    328 else
    329   begin
    330   Fill(Canvas,3,3+FrameTop,ClientWidth-6,TitleHeight-FrameTop-4,
    331     xTexOffset,yTexOffset);
    332   CornerFrame(0,FrameTop,ClientWidth,FrameBottom);
    333 
    334   Frame(Canvas,CaptionLeft,0,ClientWidth-CaptionLeft-1,FrameTop,0,0);
    335   Fill(Canvas,CaptionLeft+3,3,ClientWidth-2*(CaptionLeft)-6,TitleHeight-4,
    336     xTexOffset,yTexOffset);
    337 
    338   Frame(Canvas,CaptionLeft+1,0+1,
    339     ClientWidth-CaptionLeft-2,TitleHeight-1,MainTexture.clBevelLight,MainTexture.clBevelShade);
    340   Frame(Canvas,CaptionLeft+2,0+2,
    341     ClientWidth-CaptionLeft-3,TitleHeight-1,MainTexture.clBevelLight,MainTexture.clBevelShade);
    342   Corner(Canvas,CaptionLeft+1,0+1,0,MainTexture);
    343   Corner(Canvas,ClientWidth-CaptionLeft-9,0+1,1,MainTexture);
    344 
    345   with Canvas do
    346     begin
    347     Pen.Color:=MainTexture.clBevelShade;
    348     MoveTo(CaptionLeft+1,FrameTop+2);
    349     LineTo(CaptionLeft+1,TitleHeight);
    350     Pen.Color:=MainTexture.clBevelLight;
    351     MoveTo(ClientWidth-CaptionLeft-2,FrameTop+2);
    352     LineTo(ClientWidth-CaptionLeft-2,TitleHeight);
     333    else
     334      with Canvas do
     335      begin
     336        Fill(Canvas, 3 + ModalFrameIndent, 3 + FrameTop,
     337          ClientWidth - 6 - 2 * ModalFrameIndent, TitleHeight - FrameTop - 4,
     338          xTexOffset, yTexOffset);
     339        Fill(Canvas, ClientWidth - 3 - ModalFrameIndent, 3 + FrameTop,
     340          ModalFrameIndent, TitleHeight - FrameTop - 4, xTexOffset, yTexOffset);
     341        Fill(Canvas, 3, 3 + FrameTop, ModalFrameIndent, TitleHeight - FrameTop -
     342          4, xTexOffset, yTexOffset);
     343        CornerFrame(0, FrameTop, ClientWidth, FrameBottom);
     344        Pen.Color := MainTexture.clBevelShade;
     345        MoveTo(3 + ModalFrameIndent, 2);
     346        LineTo(3 + ModalFrameIndent, TitleHeight);
     347        Pen.Color := MainTexture.clBevelShade;
     348        MoveTo(4 + ModalFrameIndent, TitleHeight - 1);
     349        LineTo(ClientWidth - 4 - ModalFrameIndent, TitleHeight - 1);
     350        LineTo(ClientWidth - 4 - ModalFrameIndent, 1);
     351        Pen.Color := MainTexture.clBevelLight;
     352        MoveTo(ClientWidth - 5 - ModalFrameIndent, 2);
     353        LineTo(4 + ModalFrameIndent, 2);
     354        LineTo(4 + ModalFrameIndent, TitleHeight);
     355        MoveTo(ClientWidth - 4 - ModalFrameIndent, 1);
     356        LineTo(3 + ModalFrameIndent, 1);
     357        Pen.Color := MainTexture.clBevelLight;
     358        MoveTo(ClientWidth - 3 - ModalFrameIndent, 3);
     359        LineTo(ClientWidth - 3 - ModalFrameIndent, TitleHeight);
     360      end
     361  end
     362  else
     363  begin
     364    Fill(Canvas, 3, 3 + FrameTop, ClientWidth - 6, TitleHeight - FrameTop - 4,
     365      xTexOffset, yTexOffset);
     366    CornerFrame(0, FrameTop, ClientWidth, FrameBottom);
     367
     368    Frame(Canvas, CaptionLeft, 0, ClientWidth - CaptionLeft - 1,
     369      FrameTop, 0, 0);
     370    Fill(Canvas, CaptionLeft + 3, 3, ClientWidth - 2 * (CaptionLeft) - 6,
     371      TitleHeight - 4, xTexOffset, yTexOffset);
     372
     373    Frame(Canvas, CaptionLeft + 1, 0 + 1, ClientWidth - CaptionLeft - 2,
     374      TitleHeight - 1, MainTexture.clBevelLight, MainTexture.clBevelShade);
     375    Frame(Canvas, CaptionLeft + 2, 0 + 2, ClientWidth - CaptionLeft - 3,
     376      TitleHeight - 1, MainTexture.clBevelLight, MainTexture.clBevelShade);
     377    Corner(Canvas, CaptionLeft + 1, 0 + 1, 0, MainTexture);
     378    Corner(Canvas, ClientWidth - CaptionLeft - 9, 0 + 1, 1, MainTexture);
     379
     380    with Canvas do
     381    begin
     382      Pen.Color := MainTexture.clBevelShade;
     383      MoveTo(CaptionLeft + 1, FrameTop + 2);
     384      LineTo(CaptionLeft + 1, TitleHeight);
     385      Pen.Color := MainTexture.clBevelLight;
     386      MoveTo(ClientWidth - CaptionLeft - 2, FrameTop + 2);
     387      LineTo(ClientWidth - CaptionLeft - 2, TitleHeight);
    353388    end;
    354   if WideBottom then
    355     begin
    356     Frame(Canvas,CaptionLeft,FrameBottom,ClientWidth-CaptionLeft-1,ClientHeight-1,0,0);
    357     Fill(Canvas,CaptionLeft+3,ClientHeight-3-(WideFrame-5),
    358       ClientWidth-2*(CaptionLeft)-6,WideFrame-5,xTexOffset,yTexOffset);
    359     Frame(Canvas,CaptionLeft+1,ClientHeight-WideFrame-1+1,
    360       ClientWidth-CaptionLeft-2,ClientHeight-2,MainTexture.clBevelLight,MainTexture.clBevelShade);
    361     Frame(Canvas,CaptionLeft+2,ClientHeight-WideFrame-1+1,
    362       ClientWidth-CaptionLeft-3,ClientHeight-3,MainTexture.clBevelLight,MainTexture.clBevelShade);
    363     Corner(Canvas,CaptionLeft+1,ClientHeight-9,2,MainTexture);
    364     Corner(Canvas,ClientWidth-CaptionLeft-9,ClientHeight-9,3,MainTexture);
    365 
    366     with Canvas do
     389    if WideBottom then
     390    begin
     391      Frame(Canvas, CaptionLeft, FrameBottom, ClientWidth - CaptionLeft - 1,
     392        ClientHeight - 1, 0, 0);
     393      Fill(Canvas, CaptionLeft + 3, ClientHeight - 3 - (WideFrame - 5),
     394        ClientWidth - 2 * (CaptionLeft) - 6, WideFrame - 5, xTexOffset,
     395        yTexOffset);
     396      Frame(Canvas, CaptionLeft + 1, ClientHeight - WideFrame - 1 + 1,
     397        ClientWidth - CaptionLeft - 2, ClientHeight - 2,
     398        MainTexture.clBevelLight, MainTexture.clBevelShade);
     399      Frame(Canvas, CaptionLeft + 2, ClientHeight - WideFrame - 1 + 1,
     400        ClientWidth - CaptionLeft - 3, ClientHeight - 3,
     401        MainTexture.clBevelLight, MainTexture.clBevelShade);
     402      Corner(Canvas, CaptionLeft + 1, ClientHeight - 9, 2, MainTexture);
     403      Corner(Canvas, ClientWidth - CaptionLeft - 9, ClientHeight - 9, 3,
     404        MainTexture);
     405
     406      with Canvas do
    367407      begin
    368       Pen.Color:=MainTexture.clBevelShade;
    369       MoveTo(CaptionLeft+1,ClientHeight-WideFrame);
    370       LineTo(CaptionLeft+1,FrameBottom-2);
    371       Pen.Color:=MainTexture.clBevelLight;
    372       MoveTo(ClientWidth-CaptionLeft-2,ClientHeight-WideFrame);
    373       LineTo(ClientWidth-CaptionLeft-2,FrameBottom-2);
     408        Pen.Color := MainTexture.clBevelShade;
     409        MoveTo(CaptionLeft + 1, ClientHeight - WideFrame);
     410        LineTo(CaptionLeft + 1, FrameBottom - 2);
     411        Pen.Color := MainTexture.clBevelLight;
     412        MoveTo(ClientWidth - CaptionLeft - 2, ClientHeight - WideFrame);
     413        LineTo(ClientWidth - CaptionLeft - 2, FrameBottom - 2);
    374414      end;
    375415    end
    376416  end;
    377 RisedTextOut(Canvas,Cut-1,7,Caption);
    378 
    379 for i:=0 to ControlCount-1 do
    380   if Controls[i].Visible and (Controls[i] is TButtonBase) then
    381     begin
    382     R:=Controls[i].BoundsRect;
    383     if (R.Bottom<=TitleHeight) or (R.Top>=InnerBottom) then
    384       BtnFrame(Canvas,R,MainTexture);
     417  RisedTextOut(Canvas, Cut - 1, 7, Caption);
     418
     419  for i := 0 to ControlCount - 1 do
     420    if Controls[i].Visible and (Controls[i] is TButtonBase) then
     421    begin
     422      R := Controls[i].BoundsRect;
     423      if (R.Bottom <= TitleHeight) or (R.Top >= InnerBottom) then
     424        BtnFrame(Canvas, R, MainTexture);
    385425    end;
    386426
    387 BitBlt(Canvas.Handle,SideFrame,TitleHeight,ClientWidth-2*SideFrame,
    388   InnerBottom-TitleHeight,offscreen.Canvas.Handle,0,0,SRCCOPY);
     427  BitBlt(Canvas.Handle, SideFrame, TitleHeight, ClientWidth - 2 * SideFrame,
     428    InnerBottom - TitleHeight, Offscreen.Canvas.Handle, 0, 0, SRCCOPY);
    389429end;
    390430
    391431procedure TFramedDlg.InitWindowRegion;
    392432var
    393 r0,r1: HRgn;
    394 begin
    395 if FullCaption then exit;
    396 r0:=CreateRectRgn(0,0,ClientWidth,ClientHeight);
    397 r1:=CreateRectRgn(0,0,CaptionLeft,TitleHeight-NarrowFrame);
    398 CombineRgn(r0,r0,r1,RGN_DIFF);
    399 //DeleteObject(r1);
    400 r1:=CreateRectRgn(ClientWidth-CaptionLeft,0,ClientWidth,TitleHeight-NarrowFrame);
    401 CombineRgn(r0,r0,r1,RGN_DIFF);
    402 //DeleteObject(r1);
    403 if WideBottom then
    404   begin
    405   r1:=CreateRectRgn(0,ClientHeight-(WideFrame-NarrowFrame),CaptionLeft,
    406     ClientHeight);
    407   CombineRgn(r0,r0,r1,RGN_DIFF);
    408   //DeleteObject(r1);
    409   r1:=CreateRectRgn(ClientWidth-CaptionLeft,
    410     ClientHeight-(WideFrame-NarrowFrame),ClientWidth,ClientHeight);
    411   CombineRgn(r0,r0,r1,RGN_DIFF);
    412   //DeleteObject(r1);
    413   end;
    414 SetWindowRgn(Handle,r0,false);
    415 //DeleteObject(r0); // causes crash with Windows 95
    416 end;
    417 
    418 procedure TFramedDlg.FillOffscreen(Left,Top,Width,Height: integer);
    419 begin
    420 Fill(Offscreen.Canvas,Left,Top,Width,Height,SideFrame+(wMaintexture-ClientWidth) div 2,
    421   TitleHeight+(hMaintexture-ClientHeight) div 2);
    422 end;
    423 
     433  r0, r1: HRgn;
     434begin
     435  if FullCaption then
     436    exit;
     437  r0 := CreateRectRgn(0, 0, ClientWidth, ClientHeight);
     438  r1 := CreateRectRgn(0, 0, CaptionLeft, TitleHeight - NarrowFrame);
     439  CombineRgn(r0, r0, r1, RGN_DIFF);
     440  // DeleteObject(r1);
     441  r1 := CreateRectRgn(ClientWidth - CaptionLeft, 0, ClientWidth,
     442    TitleHeight - NarrowFrame);
     443  CombineRgn(r0, r0, r1, RGN_DIFF);
     444  // DeleteObject(r1);
     445  if WideBottom then
     446  begin
     447    r1 := CreateRectRgn(0, ClientHeight - (WideFrame - NarrowFrame),
     448      CaptionLeft, ClientHeight);
     449    CombineRgn(r0, r0, r1, RGN_DIFF);
     450    // DeleteObject(r1);
     451    r1 := CreateRectRgn(ClientWidth - CaptionLeft,
     452      ClientHeight - (WideFrame - NarrowFrame), ClientWidth, ClientHeight);
     453    CombineRgn(r0, r0, r1, RGN_DIFF);
     454    // DeleteObject(r1);
     455  end;
     456  SetWindowRgn(Handle, r0, false);
     457  // DeleteObject(r0); // causes crash with Windows 95
     458end;
     459
     460procedure TFramedDlg.FillOffscreen(Left, Top, Width, Height: integer);
     461begin
     462  Fill(Offscreen.Canvas, Left, Top, Width, Height,
     463    SideFrame + (wMaintexture - ClientWidth) div 2,
     464    TitleHeight + (hMaintexture - ClientHeight) div 2);
     465end;
    424466
    425467procedure CreateOffscreen;
    426468begin
    427 if OffScreen<>nil then exit;
    428 offscreen:=TBitmap.Create;
    429 Offscreen.PixelFormat:=pf24bit;
    430 offscreen.Width:=Screen.Width;
    431 if Screen.Height-yUnused<480 then offscreen.Height:=480
    432 else offscreen.Height:=Screen.Height-yUnused;
    433 offscreen.Canvas.Brush.Style:=bsClear;
    434 end;
    435 
     469  if Offscreen <> nil then
     470    exit;
     471  Offscreen := TBitmap.Create;
     472  Offscreen.PixelFormat := pf24bit;
     473  Offscreen.Width := Screen.Width;
     474  if Screen.Height - yUnused < 480 then
     475    Offscreen.Height := 480
     476  else
     477    Offscreen.Height := Screen.Height - yUnused;
     478  Offscreen.Canvas.Brush.Style := bsClear;
     479end;
    436480
    437481initialization
    438 offscreen:=nil;
    439 OffscreenUser:=nil;
     482
     483Offscreen := nil;
     484OffscreenUser := nil;
    440485
    441486finalization
    442 offscreen.Free;
     487
     488Offscreen.Free;
    443489
    444490end.
    445 
  • trunk/LocalPlayer/Battle.pas

    r2 r6  
    55
    66uses
    7   ScreenTools,Protocol,Messg,ButtonBase, ButtonA,
     7  ScreenTools, Protocol, Messg, ButtonBase, ButtonA,
    88
    99  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms;
     
    1717      Shift: TShiftState; X, Y: Integer);
    1818    procedure FormDeactivate(Sender: TObject);
    19     procedure FormKeyDown(Sender: TObject; var Key: Word;
    20       Shift: TShiftState);
     19    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    2120    procedure FormCreate(Sender: TObject);
    2221    procedure FormShow(Sender: TObject);
     
    2423    procedure CancelBtnClick(Sender: TObject);
    2524  public
    26     uix,ToLoc: integer;
     25    uix, ToLoc: Integer;
    2726    Forecast: TBattleForecastEx;
    2827    IsSuicideQuery: boolean;
     
    3231  BattleDlg: TBattleDlg;
    3332
    34 procedure PaintBattleOutcome(ca: TCanvas; xm,ym,uix,ToLoc: integer;
     33procedure PaintBattleOutcome(ca: TCanvas; xm, ym, uix, ToLoc: Integer;
    3534  Forecast: TBattleForecastEx);
    3635
    37 
    3836implementation
    3937
    4038uses
    41 Term,ClientTools,IsoEngine;
     39  Term, ClientTools, IsoEngine;
    4240
    4341{$R *.DFM}
    4442
    4543const
    46 Border=3;
    47 MessageLineSpacing=20;
    48 
    49 DamageColor=$0000E0;
    50 FanaticColor=$800080;
    51 FirstStrikeColor=$A0A0A0;
    52 
    53 
    54 procedure PaintBattleOutcome(ca: TCanvas; xm,ym,uix,ToLoc: integer;
     44  Border = 3;
     45  MessageLineSpacing = 20;
     46
     47  DamageColor = $0000E0;
     48  FanaticColor = $800080;
     49  FirstStrikeColor = $A0A0A0;
     50
     51procedure PaintBattleOutcome(ca: TCanvas; xm, ym, uix, ToLoc: Integer;
    5552  Forecast: TBattleForecastEx);
    5653var
    57 euix,ADamage,DDamage,StrMax,DamageMax,MaxBar,LAStr,LDStr,
    58   LADamage,LDDamage,LABaseDamage,LAAvoidedDamage,LDBaseDamage: integer;
    59 //TerrType: Cardinal;
    60 UnitInfo: TUnitInfo;
    61 TextSize: TSize;
    62 LabelText: string;
    63 FirstStrike: boolean;
    64 begin
    65 MaxBar:=65;
    66 
    67 //TerrType:=MyMap[ToLoc] and fTerrain;
    68 GetUnitInfo(ToLoc,euix,UnitInfo);
    69 
    70 FirstStrike:=(MyModel[MyUn[uix].mix].Cap[mcFirst]>0)
    71   and (Forecast.DBaseDamage>=UnitInfo.Health);
    72 ADamage:=MyUn[uix].Health-Forecast.EndHealthAtt;
    73 if FirstStrike then
    74   ADamage:=ADamage+Forecast.ABaseDamage div 2;
    75 DDamage:=UnitInfo.Health-Forecast.EndHealthDef;
    76 if Forecast.AStr>Forecast.DStr then
    77   StrMax:=Forecast.AStr
    78 else StrMax:=Forecast.DStr;
    79 if ADamage>DDamage then
    80   DamageMax:=ADamage
    81 else DamageMax:=DDamage;
    82 if Forecast.ABaseDamage>Forecast.DBaseDamage then
    83   StrMax:=StrMax*DamageMax div Forecast.ABaseDamage
    84 else StrMax:=StrMax*DamageMax div Forecast.DBaseDamage;
    85 
    86 LAStr:=Forecast.AStr*MaxBar div StrMax;
    87 LDStr:=Forecast.DStr*MaxBar div StrMax;
    88 LADamage:=ADamage*MaxBar div DamageMax;
    89 LABaseDamage:=Forecast.ABaseDamage*MaxBar div DamageMax;
    90 if FirstStrike then
    91   LAAvoidedDamage:=LABaseDamage div 2
    92 else LAAvoidedDamage:=0;
    93 LDDamage:=DDamage*MaxBar div DamageMax;
    94 LDBaseDamage:=Forecast.DBaseDamage*MaxBar div DamageMax;
    95 
    96 DarkGradient(ca,xm-8-LAStr,ym-8,LAStr,2);
    97 VDarkGradient(ca,xm-8,ym-8-LDStr,LDStr,2);
    98 LightGradient(ca,xm+8,ym-8,LDBaseDamage,DamageColor);
    99 if LDDamage>LDBaseDamage then
    100   LightGradient(ca,xm+8+LDBaseDamage,ym-8,LDDamage-LDBaseDamage,FanaticColor);
    101 if LAAvoidedDamage>0 then
    102   VLightGradient(ca,xm-8,ym+8,LAAvoidedDamage,FirstStrikeColor);
    103 VLightGradient(ca,xm-8,ym+8+LAAvoidedDamage,LABaseDamage-LAAvoidedDamage,
    104   DamageColor);
    105 if LADamage>LABaseDamage then
    106   VLightGradient(ca,xm-8,ym+8+LABaseDamage,LADamage-LABaseDamage,FanaticColor);
    107 BitBlt(ca.Handle,xm-12,ym-12,24,24,
    108   GrExt[HGrSystem].Mask.Canvas.Handle,26,146,SRCAND);
    109 BitBlt(ca.Handle,xm-12,ym-12,24,24,
    110   GrExt[HGrSystem].Data.Canvas.Handle,26,146,SRCPAINT);
    111 
    112 LabelText:=Format('%d', [Forecast.AStr]);
    113 TextSize:=ca.TextExtent(LabelText);
    114 if TextSize.cx div 2+2>LAStr div 2 then
    115   RisedTextOut(ca,xm-10-TextSize.cx,ym-(TextSize.cy+1) div 2, LabelText)
    116 else RisedTextOut(ca,xm-8-(LAStr+TextSize.cx) div 2,ym-(TextSize.cy+1) div 2, LabelText);
    117 
    118 LabelText:=Format('%d', [Forecast.DStr]);
    119 TextSize:=ca.TextExtent(LabelText);
    120 if TextSize.cy div 2>LDStr div 2 then
    121   RisedTextOut(ca,xm-(TextSize.cx+1) div 2, ym-8-TextSize.cy,LabelText)
    122 else RisedTextOut(ca,xm-(TextSize.cx+1) div 2, ym-8-(LDStr+TextSize.cy) div 2,LabelText);
    123 
    124 if Forecast.EndHealthDef<=0 then
    125   begin
    126   BitBlt(ca.Handle,xm+9+LDDamage-7,ym-6,14,17,
    127     GrExt[HGrSystem].Mask.Canvas.Handle,51,153,SRCAND);
    128   BitBlt(ca.Handle,xm+8+LDDamage-7,ym-7,14,17,
    129     GrExt[HGrSystem].Mask.Canvas.Handle,51,153,SRCAND);
    130   BitBlt(ca.Handle,xm+8+LDDamage-7,ym-7,14,17,
    131     GrExt[HGrSystem].Data.Canvas.Handle,51,153,SRCPAINT);
    132   end;
    133 LabelText:=Format('%d', [DDamage]);
    134 TextSize:=ca.TextExtent(LabelText);
    135 if TextSize.cx div 2+2>LDDamage div 2 then
    136   begin
    137   if Forecast.EndHealthDef>0 then
    138     RisedTextOut(ca,xm+10,ym-(TextSize.cy+1) div 2, LabelText)
    139   end
    140 else RisedTextOut(ca,xm+8+(LDDamage-TextSize.cx) div 2,ym-(TextSize.cy+1) div 2, LabelText);
    141 
    142 if Forecast.EndHealthAtt<=0 then
    143   begin
    144   BitBlt(ca.Handle,xm-6,ym+9+LADamage-7,14,17,
    145     GrExt[HGrSystem].Mask.Canvas.Handle,51,153,SRCAND);
    146   BitBlt(ca.Handle,xm-7,ym+8+LADamage-7,14,17,
    147     GrExt[HGrSystem].Mask.Canvas.Handle,51,153,SRCAND);
    148   BitBlt(ca.Handle,xm-7,ym+8+LADamage-7,14,17,
    149     GrExt[HGrSystem].Data.Canvas.Handle,51,153,SRCPAINT);
    150   end;
    151 LabelText:=Format('%d', [MyUn[uix].Health-Forecast.EndHealthAtt]);
    152 TextSize:=ca.TextExtent(LabelText);
    153 if TextSize.cy div 2>(LADamage-LAAvoidedDamage) div 2+LAAvoidedDamage then
    154   begin
    155   if Forecast.EndHealthAtt>0 then
    156     RisedTextOut(ca,xm-(TextSize.cx+1) div 2, ym+8+LAAvoidedDamage,LabelText)
    157   end
    158 else RisedTextOut(ca,xm-(TextSize.cx+1) div 2, ym+8+LAAvoidedDamage+(LADamage-LAAvoidedDamage-TextSize.cy) div 2,LabelText);
    159 
    160 NoMap.SetOutput(Buffer);
    161 BitBlt(Buffer.Canvas.Handle,0,0,66,48,ca.Handle,xm+8+4,ym-8-12-48,SRCCOPY);
    162 {if TerrType<fForest then
    163   Sprite(Buffer,HGrTerrain,0,16,66,32,1+TerrType*(xxt*2+1),1+yyt)
    164 else
    165   begin
    166   Sprite(Buffer,HGrTerrain,0,16,66,32,1+2*(xxt*2+1),1+yyt+2*(yyt*3+1));
    167   if (TerrType=fForest) and IsJungle(ToLoc div G.lx) then
     54  euix, ADamage, DDamage, StrMax, DamageMax, MaxBar, LAStr, LDStr, LADamage,
     55    LDDamage, LABaseDamage, LAAvoidedDamage, LDBaseDamage: Integer;
     56  // TerrType: Cardinal;
     57  UnitInfo: TUnitInfo;
     58  TextSize: TSize;
     59  LabelText: string;
     60  FirstStrike: boolean;
     61begin
     62  MaxBar := 65;
     63
     64  // TerrType:=MyMap[ToLoc] and fTerrain;
     65  GetUnitInfo(ToLoc, euix, UnitInfo);
     66
     67  FirstStrike := (MyModel[MyUn[uix].mix].Cap[mcFirst] > 0) and
     68    (Forecast.DBaseDamage >= UnitInfo.Health);
     69  ADamage := MyUn[uix].Health - Forecast.EndHealthAtt;
     70  if FirstStrike then
     71    ADamage := ADamage + Forecast.ABaseDamage div 2;
     72  DDamage := UnitInfo.Health - Forecast.EndHealthDef;
     73  if Forecast.AStr > Forecast.DStr then
     74    StrMax := Forecast.AStr
     75  else
     76    StrMax := Forecast.DStr;
     77  if ADamage > DDamage then
     78    DamageMax := ADamage
     79  else
     80    DamageMax := DDamage;
     81  if Forecast.ABaseDamage > Forecast.DBaseDamage then
     82    StrMax := StrMax * DamageMax div Forecast.ABaseDamage
     83  else
     84    StrMax := StrMax * DamageMax div Forecast.DBaseDamage;
     85
     86  LAStr := Forecast.AStr * MaxBar div StrMax;
     87  LDStr := Forecast.DStr * MaxBar div StrMax;
     88  LADamage := ADamage * MaxBar div DamageMax;
     89  LABaseDamage := Forecast.ABaseDamage * MaxBar div DamageMax;
     90  if FirstStrike then
     91    LAAvoidedDamage := LABaseDamage div 2
     92  else
     93    LAAvoidedDamage := 0;
     94  LDDamage := DDamage * MaxBar div DamageMax;
     95  LDBaseDamage := Forecast.DBaseDamage * MaxBar div DamageMax;
     96
     97  DarkGradient(ca, xm - 8 - LAStr, ym - 8, LAStr, 2);
     98  VDarkGradient(ca, xm - 8, ym - 8 - LDStr, LDStr, 2);
     99  LightGradient(ca, xm + 8, ym - 8, LDBaseDamage, DamageColor);
     100  if LDDamage > LDBaseDamage then
     101    LightGradient(ca, xm + 8 + LDBaseDamage, ym - 8, LDDamage - LDBaseDamage,
     102      FanaticColor);
     103  if LAAvoidedDamage > 0 then
     104    VLightGradient(ca, xm - 8, ym + 8, LAAvoidedDamage, FirstStrikeColor);
     105  VLightGradient(ca, xm - 8, ym + 8 + LAAvoidedDamage,
     106    LABaseDamage - LAAvoidedDamage, DamageColor);
     107  if LADamage > LABaseDamage then
     108    VLightGradient(ca, xm - 8, ym + 8 + LABaseDamage, LADamage - LABaseDamage,
     109      FanaticColor);
     110  BitBlt(ca.Handle, xm - 12, ym - 12, 24, 24,
     111    GrExt[HGrSystem].Mask.Canvas.Handle, 26, 146, SRCAND);
     112  BitBlt(ca.Handle, xm - 12, ym - 12, 24, 24,
     113    GrExt[HGrSystem].Data.Canvas.Handle, 26, 146, SRCPAINT);
     114
     115  LabelText := Format('%d', [Forecast.AStr]);
     116  TextSize := ca.TextExtent(LabelText);
     117  if TextSize.cx div 2 + 2 > LAStr div 2 then
     118    RisedTextOut(ca, xm - 10 - TextSize.cx, ym - (TextSize.cy + 1) div 2,
     119      LabelText)
     120  else
     121    RisedTextOut(ca, xm - 8 - (LAStr + TextSize.cx) div 2,
     122      ym - (TextSize.cy + 1) div 2, LabelText);
     123
     124  LabelText := Format('%d', [Forecast.DStr]);
     125  TextSize := ca.TextExtent(LabelText);
     126  if TextSize.cy div 2 > LDStr div 2 then
     127    RisedTextOut(ca, xm - (TextSize.cx + 1) div 2, ym - 8 - TextSize.cy,
     128      LabelText)
     129  else
     130    RisedTextOut(ca, xm - (TextSize.cx + 1) div 2,
     131      ym - 8 - (LDStr + TextSize.cy) div 2, LabelText);
     132
     133  if Forecast.EndHealthDef <= 0 then
     134  begin
     135    BitBlt(ca.Handle, xm + 9 + LDDamage - 7, ym - 6, 14, 17,
     136      GrExt[HGrSystem].Mask.Canvas.Handle, 51, 153, SRCAND);
     137    BitBlt(ca.Handle, xm + 8 + LDDamage - 7, ym - 7, 14, 17,
     138      GrExt[HGrSystem].Mask.Canvas.Handle, 51, 153, SRCAND);
     139    BitBlt(ca.Handle, xm + 8 + LDDamage - 7, ym - 7, 14, 17,
     140      GrExt[HGrSystem].Data.Canvas.Handle, 51, 153, SRCPAINT);
     141  end;
     142  LabelText := Format('%d', [DDamage]);
     143  TextSize := ca.TextExtent(LabelText);
     144  if TextSize.cx div 2 + 2 > LDDamage div 2 then
     145  begin
     146    if Forecast.EndHealthDef > 0 then
     147      RisedTextOut(ca, xm + 10, ym - (TextSize.cy + 1) div 2, LabelText)
     148  end
     149  else
     150    RisedTextOut(ca, xm + 8 + (LDDamage - TextSize.cx) div 2,
     151      ym - (TextSize.cy + 1) div 2, LabelText);
     152
     153  if Forecast.EndHealthAtt <= 0 then
     154  begin
     155    BitBlt(ca.Handle, xm - 6, ym + 9 + LADamage - 7, 14, 17,
     156      GrExt[HGrSystem].Mask.Canvas.Handle, 51, 153, SRCAND);
     157    BitBlt(ca.Handle, xm - 7, ym + 8 + LADamage - 7, 14, 17,
     158      GrExt[HGrSystem].Mask.Canvas.Handle, 51, 153, SRCAND);
     159    BitBlt(ca.Handle, xm - 7, ym + 8 + LADamage - 7, 14, 17,
     160      GrExt[HGrSystem].Data.Canvas.Handle, 51, 153, SRCPAINT);
     161  end;
     162  LabelText := Format('%d', [MyUn[uix].Health - Forecast.EndHealthAtt]);
     163  TextSize := ca.TextExtent(LabelText);
     164  if TextSize.cy div 2 > (LADamage - LAAvoidedDamage) div 2 + LAAvoidedDamage
     165  then
     166  begin
     167    if Forecast.EndHealthAtt > 0 then
     168      RisedTextOut(ca, xm - (TextSize.cx + 1) div 2, ym + 8 + LAAvoidedDamage,
     169        LabelText)
     170  end
     171  else
     172    RisedTextOut(ca, xm - (TextSize.cx + 1) div 2, ym + 8 + LAAvoidedDamage +
     173      (LADamage - LAAvoidedDamage - TextSize.cy) div 2, LabelText);
     174
     175  NoMap.SetOutput(Buffer);
     176  BitBlt(Buffer.Canvas.Handle, 0, 0, 66, 48, ca.Handle, xm + 8 + 4,
     177    ym - 8 - 12 - 48, SRCCOPY);
     178  { if TerrType<fForest then
     179    Sprite(Buffer,HGrTerrain,0,16,66,32,1+TerrType*(xxt*2+1),1+yyt)
     180    else
     181    begin
     182    Sprite(Buffer,HGrTerrain,0,16,66,32,1+2*(xxt*2+1),1+yyt+2*(yyt*3+1));
     183    if (TerrType=fForest) and IsJungle(ToLoc div G.lx) then
    168184    Sprite(Buffer,HGrTerrain,0,16,66,32,1+7*(xxt*2+1),1+yyt+19*(yyt*3+1))
    169   else Sprite(Buffer,HGrTerrain,0,16,66,32,1+7*(xxt*2+1),1+yyt+2*(2+TerrType-fForest)*(yyt*3+1));
    170   end;}
    171 NoMap.PaintUnit(1,0,UnitInfo,0);
    172 BitBlt(ca.Handle,xm+8+4,ym-8-12-48,66,48,Buffer.Canvas.Handle,0,0,SRCCOPY);
    173 
    174 BitBlt(Buffer.Canvas.Handle,0,0,66,48,ca.Handle,xm-8-4-66,ym+8+12,SRCCOPY);
    175 MakeUnitInfo(me,MyUn[uix],UnitInfo);
    176 UnitInfo.Flags:=UnitInfo.Flags and not unFortified;
    177 NoMap.PaintUnit(1,0,UnitInfo,0);
    178 BitBlt(ca.Handle,xm-8-4-66,ym+8+12,66,48,Buffer.Canvas.Handle,0,0,SRCCOPY);
    179 end; {PaintBattleOutcome}
    180 
     185    else Sprite(Buffer,HGrTerrain,0,16,66,32,1+7*(xxt*2+1),1+yyt+2*(2+TerrType-fForest)*(yyt*3+1));
     186    end; }
     187  NoMap.PaintUnit(1, 0, UnitInfo, 0);
     188  BitBlt(ca.Handle, xm + 8 + 4, ym - 8 - 12 - 48, 66, 48, Buffer.Canvas.Handle,
     189    0, 0, SRCCOPY);
     190
     191  BitBlt(Buffer.Canvas.Handle, 0, 0, 66, 48, ca.Handle, xm - 8 - 4 - 66,
     192    ym + 8 + 12, SRCCOPY);
     193  MakeUnitInfo(me, MyUn[uix], UnitInfo);
     194  UnitInfo.Flags := UnitInfo.Flags and not unFortified;
     195  NoMap.PaintUnit(1, 0, UnitInfo, 0);
     196  BitBlt(ca.Handle, xm - 8 - 4 - 66, ym + 8 + 12, 66, 48, Buffer.Canvas.Handle,
     197    0, 0, SRCCOPY);
     198end; { PaintBattleOutcome }
    181199
    182200procedure TBattleDlg.FormCreate(Sender: TObject);
    183201begin
    184 OKBtn.Caption:=Phrases.Lookup('BTN_YES');
    185 CancelBtn.Caption:=Phrases.Lookup('BTN_NO');
    186 InitButtons();
     202  OKBtn.Caption := Phrases.Lookup('BTN_YES');
     203  CancelBtn.Caption := Phrases.Lookup('BTN_NO');
     204  InitButtons();
    187205end;
    188206
    189207procedure TBattleDlg.FormShow(Sender: TObject);
    190208begin
    191 if IsSuicideQuery then
    192   begin
    193   ClientWidth:=300;
    194   ClientHeight:=288;
    195   OKBtn.Visible:=true;
    196   CancelBtn.Visible:=true;
    197   Left:=(Screen.Width-ClientWidth) div 2; // center on screen
    198   Top:=(Screen.Height-ClientHeight) div 2;
    199   end
    200 else
    201   begin
    202   ClientWidth:=178;
    203   ClientHeight:=178;
    204   OKBtn.Visible:=false;
    205   CancelBtn.Visible:=false;
     209  if IsSuicideQuery then
     210  begin
     211    ClientWidth := 300;
     212    ClientHeight := 288;
     213    OKBtn.Visible := true;
     214    CancelBtn.Visible := true;
     215    Left := (Screen.Width - ClientWidth) div 2; // center on screen
     216    Top := (Screen.Height - ClientHeight) div 2;
     217  end
     218  else
     219  begin
     220    ClientWidth := 178;
     221    ClientHeight := 178;
     222    OKBtn.Visible := false;
     223    CancelBtn.Visible := false;
    206224  end;
    207225end;
     
    209227procedure TBattleDlg.FormPaint(Sender: TObject);
    210228var
    211 ym,cix,p: integer;
    212 s,s1: string;
    213 begin
    214 with Canvas do
    215   begin
    216   Brush.Color:=0;
    217   FillRect(Rect(0,0,ClientWidth,ClientHeight));
    218   Brush.Style:=bsClear;
    219   PaintBackground(self,3+Border,3+Border,ClientWidth-(6+2*Border),
    220     ClientHeight-(6+2*Border))
    221   end;
    222 Frame(Canvas,Border+1,Border+1,ClientWidth-(2+Border),ClientHeight-(2+Border),
    223   MainTexture.clBevelLight,MainTexture.clBevelShade);
    224 Frame(Canvas,2+Border,2+Border,ClientWidth-(3+Border),ClientHeight-(3+Border),
    225   MainTexture.clBevelLight,MainTexture.clBevelShade);
    226 
    227 if IsSuicideQuery then
    228   begin
    229   Canvas.Font.Assign(UniFont[ftCaption]);
    230   s:=Phrases.Lookup('TITLE_SUICIDE');
    231   RisedTextout(Canvas,(ClientWidth-BiColorTextWidth(Canvas,s)) div 2, 7+Border, s);
    232   Canvas.Font.Assign(UniFont[ftNormal]);
    233   s:=Phrases.Lookup('SUICIDE');
    234   p:=pos('\',s);
    235   if p=0 then
    236     RisedTextout(Canvas,(ClientWidth-BiColorTextWidth(Canvas,s)) div 2, 205, s)
    237   else
     229  ym, cix, p: Integer;
     230  s, s1: string;
     231begin
     232  with Canvas do
     233  begin
     234    Brush.Color := 0;
     235    FillRect(Rect(0, 0, ClientWidth, ClientHeight));
     236    Brush.Style := bsClear;
     237    PaintBackground(self, 3 + Border, 3 + Border,
     238      ClientWidth - (6 + 2 * Border), ClientHeight - (6 + 2 * Border))
     239  end;
     240  Frame(Canvas, Border + 1, Border + 1, ClientWidth - (2 + Border),
     241    ClientHeight - (2 + Border), MainTexture.clBevelLight,
     242    MainTexture.clBevelShade);
     243  Frame(Canvas, 2 + Border, 2 + Border, ClientWidth - (3 + Border),
     244    ClientHeight - (3 + Border), MainTexture.clBevelLight,
     245    MainTexture.clBevelShade);
     246
     247  if IsSuicideQuery then
     248  begin
     249    Canvas.Font.Assign(UniFont[ftCaption]);
     250    s := Phrases.Lookup('TITLE_SUICIDE');
     251    RisedTextOut(Canvas, (ClientWidth - BiColorTextWidth(Canvas, s)) div 2,
     252      7 + Border, s);
     253    Canvas.Font.Assign(UniFont[ftNormal]);
     254    s := Phrases.Lookup('SUICIDE');
     255    p := pos('\', s);
     256    if p = 0 then
     257      RisedTextOut(Canvas, (ClientWidth - BiColorTextWidth(Canvas, s))
     258        div 2, 205, s)
     259    else
    238260    begin
    239     s1:=copy(s,1,p-1);
    240     RisedTextout(Canvas,(ClientWidth-BiColorTextWidth(Canvas,s1)) div 2,
    241       205-MessageLineSpacing div 2, s1);
    242     s1:=copy(s,p+1,255);
    243     RisedTextout(Canvas,(ClientWidth-BiColorTextWidth(Canvas,s1)) div 2,
    244       205+(MessageLineSpacing-MessageLineSpacing div 2), s1);
     261      s1 := copy(s, 1, p - 1);
     262      RisedTextOut(Canvas, (ClientWidth - BiColorTextWidth(Canvas, s1)) div 2,
     263        205 - MessageLineSpacing div 2, s1);
     264      s1 := copy(s, p + 1, 255);
     265      RisedTextOut(Canvas, (ClientWidth - BiColorTextWidth(Canvas, s1)) div 2,
     266        205 + (MessageLineSpacing - MessageLineSpacing div 2), s1);
    245267    end;
    246   ym:=110
    247   end
    248 else ym:=ClientHeight div 2;
    249 Canvas.Font.Assign(UniFont[ftSmall]);
    250 PaintBattleOutcome(Canvas, ClientWidth div 2, ym, uix, ToLoc, Forecast);
    251 
    252 for cix:=0 to ControlCount-1 do
    253   if (Controls[cix].Visible) and (Controls[cix] is TButtonBase) then
    254     BtnFrame(Canvas,Controls[cix].BoundsRect,MainTexture);
     268    ym := 110
     269  end
     270  else
     271    ym := ClientHeight div 2;
     272  Canvas.Font.Assign(UniFont[ftSmall]);
     273  PaintBattleOutcome(Canvas, ClientWidth div 2, ym, uix, ToLoc, Forecast);
     274
     275  for cix := 0 to ControlCount - 1 do
     276    if (Controls[cix].Visible) and (Controls[cix] is TButtonBase) then
     277      BtnFrame(Canvas, Controls[cix].BoundsRect, MainTexture);
    255278end;
    256279
     
    258281  Shift: TShiftState; X, Y: Integer);
    259282begin
    260 if not IsSuicideQuery then
    261   Close;
     283  if not IsSuicideQuery then
     284    Close;
    262285end;
    263286
    264287procedure TBattleDlg.FormDeactivate(Sender: TObject);
    265288begin
    266 if not IsSuicideQuery then
    267   Close
     289  if not IsSuicideQuery then
     290    Close
    268291end;
    269292
     
    271294  Shift: TShiftState);
    272295begin
    273 if not IsSuicideQuery and (Key<>VK_SHIFT) then
    274   begin
    275   Close;
    276   MainScreen.Update;
    277   if Key<>VK_ESCAPE then
    278     MainScreen.FormKeyDown(Sender, Key, Shift);
     296  if not IsSuicideQuery and (Key <> VK_SHIFT) then
     297  begin
     298    Close;
     299    MainScreen.Update;
     300    if Key <> VK_ESCAPE then
     301      MainScreen.FormKeyDown(Sender, Key, Shift);
    279302  end
    280303end;
     
    282305procedure TBattleDlg.OKBtnClick(Sender: TObject);
    283306begin
    284 ModalResult:=mrOK;
     307  ModalResult := mrOK;
    285308end;
    286309
    287310procedure TBattleDlg.CancelBtnClick(Sender: TObject);
    288311begin
    289 ModalResult:=mrCancel;
     312  ModalResult := mrCancel;
    290313end;
    291314
    292315end.
    293 
  • trunk/LocalPlayer/CityScreen.pas

    r3 r6  
    11{$INCLUDE switches}
    2 
    32unit CityScreen;
    43
     
    65
    76uses
    8   Protocol,ClientTools,Term,ScreenTools,IsoEngine,BaseWin,
    9 
    10   Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,ExtCtrls,ButtonA,
     7  Protocol, ClientTools, Term, ScreenTools, IsoEngine, BaseWin,
     8
     9  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, ExtCtrls,
     10  ButtonA,
    1111  ButtonB, ButtonBase, ButtonC, Area;
    1212
    1313const
    14 WM_PLAYSOUND=WM_USER;
     14  WM_PLAYSOUND = WM_USER;
    1515
    1616type
    17   TCityCloseAction=(None, RestoreFocus, StepFocus);
     17  TCityCloseAction = (None, RestoreFocus, StepFocus);
    1818
    1919  TCityDlg = class(TBufferedDrawDlg)
     
    3636    Pop1Area: TArea;
    3737    SupportArea: TArea;
    38     procedure FormCreate(Sender:TObject);
    39     procedure FormDestroy(Sender:TObject);
    40     procedure FormMouseDown(Sender:TObject;Button:TMouseButton;
    41       Shift:TShiftState;x,y:integer);
    42     procedure BuyClick(Sender:TObject);
    43     procedure CloseBtnClick(Sender:TObject);
     38    procedure FormCreate(Sender: TObject);
     39    procedure FormDestroy(Sender: TObject);
     40    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
     41      Shift: TShiftState; x, y: integer);
     42    procedure BuyClick(Sender: TObject);
     43    procedure CloseBtnClick(Sender: TObject);
    4444    procedure FormShow(Sender: TObject);
    4545    procedure FormClose(Sender: TObject; var Action: TCloseAction);
     
    4848    procedure NextCityBtnClick(Sender: TObject);
    4949    procedure PrevCityBtnClick(Sender: TObject);
    50     procedure FormKeyDown(Sender: TObject; var Key: Word;
    51       Shift: TShiftState);
    52     //procedure AdviceBtnClick(Sender: TObject);
     50    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
     51    // procedure AdviceBtnClick(Sender: TObject);
    5352    procedure PageUpBtnClick(Sender: TObject);
    5453    procedure PageDownBtnClick(Sender: TObject);
     
    5857    CloseAction: TCityCloseAction;
    5958    procedure OffscreenPaint; override;
    60     procedure ShowNewContent(NewMode,Loc: integer; ShowEvent: cardinal);
     59    procedure ShowNewContent(NewMode, Loc: integer; ShowEvent: cardinal);
    6160    procedure Reset;
    6261    procedure CheckAge;
     
    6463  private
    6564    c: TCity;
    66     Report:TCityReportNew;
    67     cOwner,cGov,
    68     emix{enemy model index of produced unit},
    69     cix,cLoc,Mode,ZoomArea,Page,PageCount,BlinkTime,OpenSoundEvent,
    70       SizeClass,AgePrepared: integer;
    71     Optimize_cixTileChange,Optimize_TilesBeforeChange: integer;
     65    Report: TCityReportNew;
     66    cOwner, cGov, emix { enemy model index of produced unit } , cix, cLoc, Mode,
     67      ZoomArea, Page, PageCount, BlinkTime, OpenSoundEvent, SizeClass,
     68      AgePrepared: integer;
     69    Optimize_cixTileChange, Optimize_TilesBeforeChange: integer;
    7270    Happened: cardinal;
    73     imix:array[0..15] of integer;
     71    imix: array [0 .. 15] of integer;
    7472    CityAreaInfo: TCityAreaInfo;
    7573    AreaMap: TIsoMap;
    76     CityMapTemplate, SmallCityMapTemplate, Back, SmallCityMap, ZoomCityMap, Template: TBitmap;
    77     IsPort,ProdHint,AllowChange: boolean;
     74    CityMapTemplate, SmallCityMapTemplate, Back, SmallCityMap, ZoomCityMap,
     75      Template: TBitmap;
     76    IsPort, ProdHint, AllowChange: boolean;
    7877    procedure InitSmallCityMap;
    7978    procedure InitZoomCityMap;
     
    8180    procedure ChangeCity(d: integer);
    8281    procedure ChangeResourceWeights(iResourceWeights: integer);
    83     procedure OnPlaySound(var Msg:TMessage); message WM_PLAYSOUND;
     82    procedure OnPlaySound(var Msg: TMessage); message WM_PLAYSOUND;
    8483  end;
    8584
    8685var
    87   CityDlg:TCityDlg;
     86  CityDlg: TCityDlg;
    8887
    8988implementation
    9089
    9190uses
    92   Select,Messg,MessgEx,Help,Inp,Tribes,Directories,
     91  Select, Messg, MessgEx, Help, Inp, Tribes, Directories,
    9392
    9493  Math;
     
    9796
    9897const
    99 {modes}
    100 mSupp=1; mImp=2;
    101 
    102 wBar=106;
    103 xDiv=400; xService=296;
    104 xmArea=197; ymArea=170;
    105 xView=326; yView=275;
    106 dxBar=wBar+12; dyBar=39;
    107 xHapp=404; yHapp=9;
    108 xFood=404; yFood=yHapp+3*dyBar+6;
    109 xProd=404; yProd=yFood+3*dyBar+6;
    110 xTrade=404; yTrade=yProd+2*dyBar+22;
    111 xPoll=xmArea-186; yPoll=ymArea+64;
    112 xmOpt=40; ymOpt=ymArea+96+34;
    113 xSmallMap=271; ySmallMap=339; wSmallMap=98; hSmallMap=74;
    114 xSupport=xSmallMap; ySupport=ySmallmap+hSmallmap+2; wSupport=64; hSupport=18;
    115 xZoomMap=34; yZoomMap=338; wZoomMap=228; hZoomMap=124; wZoomEnvironment=68;
    116 
    117 ImpPosition: array[28..nImp-1] of integer=
    118 (-1, //imTrGoods
    119 21, //imBarracks
    120 6, //imGranary
    121 1, //imTemple
    122 7, //imMarket
    123 14, //imLibrary
    124 8, //imCourt
    125 18, //imWalls
    126 10, //imAqueduct
    127 11, //imBank
    128 5, //imCathedral
    129 13, //imUniversity
    130 29, //imHarbor
    131 2, //imTheater
    132 24, //imFactory
    133 25, //imMfgPlant
    134 28, //imRecycling
    135 27, //imPower
    136 27, //imHydro
    137 27, //imNuclear
    138 26, //imPlatform
    139 8, //imTownHall
    140 10, //imSewer
    141 3, //imSupermarket
    142 17, //imHighways
    143 15, //imResLab
    144 19, //imMissileBat
    145 23, //imCoastalFort
    146 22, //imAirport
    147 20, //imDockyard
    148 8, //imPalace
    149 -1, //imGrWall
    150 4, //imColosseum
    151 16, //imObservatory
    152 21, //imMilAcademy
    153 -1, //imBunker
    154 -1, //imAlgae
    155 9, //imStockEx
    156 -1, //imSpacePort
    157 -1, //imShipComp
    158 -1, //imShipPow
    159 -1); //imShipHab
    160 
     98  { modes }
     99  mSupp = 1;
     100  mImp = 2;
     101
     102  wBar = 106;
     103  xDiv = 400;
     104  xService = 296;
     105  xmArea = 197;
     106  ymArea = 170;
     107  xView = 326;
     108  yView = 275;
     109  dxBar = wBar + 12;
     110  dyBar = 39;
     111  xHapp = 404;
     112  yHapp = 9;
     113  xFood = 404;
     114  yFood = yHapp + 3 * dyBar + 6;
     115  xProd = 404;
     116  yProd = yFood + 3 * dyBar + 6;
     117  xTrade = 404;
     118  yTrade = yProd + 2 * dyBar + 22;
     119  xPoll = xmArea - 186;
     120  yPoll = ymArea + 64;
     121  xmOpt = 40;
     122  ymOpt = ymArea + 96 + 34;
     123  xSmallMap = 271;
     124  ySmallMap = 339;
     125  wSmallMap = 98;
     126  hSmallMap = 74;
     127  xSupport = xSmallMap;
     128  ySupport = ySmallMap + hSmallMap + 2;
     129  wSupport = 64;
     130  hSupport = 18;
     131  xZoomMap = 34;
     132  yZoomMap = 338;
     133  wZoomMap = 228;
     134  hZoomMap = 124;
     135  wZoomEnvironment = 68;
     136
     137  ImpPosition: array [28 .. nImp - 1] of integer = (-1, // imTrGoods
     138    21, // imBarracks
     139    6, // imGranary
     140    1, // imTemple
     141    7, // imMarket
     142    14, // imLibrary
     143    8, // imCourt
     144    18, // imWalls
     145    10, // imAqueduct
     146    11, // imBank
     147    5, // imCathedral
     148    13, // imUniversity
     149    29, // imHarbor
     150    2, // imTheater
     151    24, // imFactory
     152    25, // imMfgPlant
     153    28, // imRecycling
     154    27, // imPower
     155    27, // imHydro
     156    27, // imNuclear
     157    26, // imPlatform
     158    8, // imTownHall
     159    10, // imSewer
     160    3, // imSupermarket
     161    17, // imHighways
     162    15, // imResLab
     163    19, // imMissileBat
     164    23, // imCoastalFort
     165    22, // imAirport
     166    20, // imDockyard
     167    8, // imPalace
     168    -1, // imGrWall
     169    4, // imColosseum
     170    16, // imObservatory
     171    21, // imMilAcademy
     172    -1, // imBunker
     173    -1, // imAlgae
     174    9, // imStockEx
     175    -1, // imSpacePort
     176    -1, // imShipComp
     177    -1, // imShipPow
     178    -1); // imShipHab
    161179
    162180var
    163 ImpSorted: array[0..nImp-1] of integer;
    164 
    165 
    166 procedure TCityDlg.FormCreate(Sender:TObject);
    167 begin
    168 inherited;
    169 AreaMap:=TIsoMap.Create;
    170 AreaMap.SetOutput(offscreen);
    171 AreaMap.SetPaintBounds(xmArea-192,ymArea-96-32,xmArea+192,ymArea+96);
    172 Mode:=mImp;
    173 ZoomArea:=1;
    174 ProdHint:=false;
    175 RestoreUnFocus:=-1;
    176 OpenSoundEvent:=-1;
    177 AgePrepared:=-2;
    178 Optimize_cixTileChange:=-1;
    179 InitButtons();
    180 //InitWindowRegion;
    181 CloseBtn.Caption:=Phrases.Lookup('BTN_OK');
    182 BuyBtn.Hint:=Phrases.Lookup('BTN_BUY');
    183 if not Phrases2FallenBackToEnglish then
    184   SupportArea.Hint:=Phrases2.Lookup('TIP_SUPUNITS')
    185 else SupportArea.Hint:=Phrases.Lookup('SUPUNITS');
    186 if not Phrases2FallenBackToEnglish then
    187   begin
    188   Pop0Area.Hint:=Phrases2.Lookup('TIP_WORKING');
    189   Pop1Area.Hint:=Phrases2.Lookup('TIP_CIVIL');
    190   PrimacyArea.Hint:=Phrases2.Lookup('TIP_PRIMACY');
    191   ProjectArea.Hint:=Phrases2.Lookup('TIP_PROJECT');
    192   end;
    193 
    194 Back:=TBitmap.Create;
    195 Back.PixelFormat:=pf24bit;
    196 Back.Width:=ClientWidth; Back.Height:=ClientHeight;
    197 Template:=TBitmap.Create;
    198 LoadGraphicFile(Template, HomeDir+'Graphics\City', gfNoGamma);
    199 Template.PixelFormat:=pf8bit;
    200 CityMapTemplate:=TBitmap.Create;
    201 LoadGraphicFile(CityMapTemplate, HomeDir+'Graphics\BigCityMap', gfNoGamma);
    202 CityMapTemplate.PixelFormat:=pf8bit;
    203 SmallCityMapTemplate:=TBitmap.Create;
    204 LoadGraphicFile(SmallCityMapTemplate, HomeDir+'Graphics\SmallCityMap', gfNoGamma);
    205 SmallCityMapTemplate.PixelFormat:=pf24bit;
    206 SmallCityMap:=TBitmap.Create;
    207 SmallCityMap.PixelFormat:=pf24bit;
    208 SmallCityMap.Width:=98; SmallCityMap.Height:=74;
    209 ZoomCityMap:=TBitmap.Create;
    210 ZoomCityMap.PixelFormat:=pf24bit;
    211 ZoomCityMap.Width:=228; ZoomCityMap.Height:=124;
    212 end;
    213 
    214 procedure TCityDlg.FormDestroy(Sender:TObject);
    215 begin
    216 AreaMap.Free;
    217 SmallCityMap.Free;
    218 ZoomCityMap.Free;
    219 CityMapTemplate.Free;
    220 Template.Free;
    221 Back.Free;
     181  ImpSorted: array [0 .. nImp - 1] of integer;
     182
     183procedure TCityDlg.FormCreate(Sender: TObject);
     184begin
     185  inherited;
     186  AreaMap := TIsoMap.Create;
     187  AreaMap.SetOutput(offscreen);
     188  AreaMap.SetPaintBounds(xmArea - 192, ymArea - 96 - 32, xmArea + 192,
     189    ymArea + 96);
     190  Mode := mImp;
     191  ZoomArea := 1;
     192  ProdHint := false;
     193  RestoreUnFocus := -1;
     194  OpenSoundEvent := -1;
     195  AgePrepared := -2;
     196  Optimize_cixTileChange := -1;
     197  InitButtons();
     198  // InitWindowRegion;
     199  CloseBtn.Caption := Phrases.Lookup('BTN_OK');
     200  BuyBtn.Hint := Phrases.Lookup('BTN_BUY');
     201  if not Phrases2FallenBackToEnglish then
     202    SupportArea.Hint := Phrases2.Lookup('TIP_SUPUNITS')
     203  else
     204    SupportArea.Hint := Phrases.Lookup('SUPUNITS');
     205  if not Phrases2FallenBackToEnglish then
     206  begin
     207    Pop0Area.Hint := Phrases2.Lookup('TIP_WORKING');
     208    Pop1Area.Hint := Phrases2.Lookup('TIP_CIVIL');
     209    PrimacyArea.Hint := Phrases2.Lookup('TIP_PRIMACY');
     210    ProjectArea.Hint := Phrases2.Lookup('TIP_PROJECT');
     211  end;
     212
     213  Back := TBitmap.Create;
     214  Back.PixelFormat := pf24bit;
     215  Back.Width := ClientWidth;
     216  Back.Height := ClientHeight;
     217  Template := TBitmap.Create;
     218  LoadGraphicFile(Template, HomeDir + 'Graphics\City', gfNoGamma);
     219  Template.PixelFormat := pf8bit;
     220  CityMapTemplate := TBitmap.Create;
     221  LoadGraphicFile(CityMapTemplate, HomeDir + 'Graphics\BigCityMap', gfNoGamma);
     222  CityMapTemplate.PixelFormat := pf8bit;
     223  SmallCityMapTemplate := TBitmap.Create;
     224  LoadGraphicFile(SmallCityMapTemplate, HomeDir + 'Graphics\SmallCityMap',
     225    gfNoGamma);
     226  SmallCityMapTemplate.PixelFormat := pf24bit;
     227  SmallCityMap := TBitmap.Create;
     228  SmallCityMap.PixelFormat := pf24bit;
     229  SmallCityMap.Width := 98;
     230  SmallCityMap.Height := 74;
     231  ZoomCityMap := TBitmap.Create;
     232  ZoomCityMap.PixelFormat := pf24bit;
     233  ZoomCityMap.Width := 228;
     234  ZoomCityMap.Height := 124;
     235end;
     236
     237procedure TCityDlg.FormDestroy(Sender: TObject);
     238begin
     239  AreaMap.Free;
     240  SmallCityMap.Free;
     241  ZoomCityMap.Free;
     242  CityMapTemplate.Free;
     243  Template.Free;
     244  Back.Free;
    222245end;
    223246
    224247procedure TCityDlg.Reset;
    225248begin
    226 Mode:=mImp;
    227 ZoomArea:=1;
     249  Mode := mImp;
     250  ZoomArea := 1;
    228251end;
    229252
    230253procedure TCityDlg.CheckAge;
    231254begin
    232 if MainTextureAge<>AgePrepared then
    233   begin
    234   AgePrepared:=MainTextureAge;
    235   bitblt(Back.Canvas.Handle,0,0,ClientWidth,ClientHeight,
    236     MainTexture.Image.Canvas.Handle,0,0,SRCCOPY);
    237   ImageOp_B(Back,Template,0,0,0,0,ClientWidth,ClientHeight);
     255  if MainTextureAge <> AgePrepared then
     256  begin
     257    AgePrepared := MainTextureAge;
     258    bitblt(Back.Canvas.Handle, 0, 0, ClientWidth, ClientHeight,
     259      MainTexture.Image.Canvas.Handle, 0, 0, SRCCOPY);
     260    ImageOp_B(Back, Template, 0, 0, 0, 0, ClientWidth, ClientHeight);
    238261  end
    239262end;
    240263
    241 procedure TCityDlg.CloseBtnClick(Sender:TObject);
    242 begin
    243 Close
     264procedure TCityDlg.CloseBtnClick(Sender: TObject);
     265begin
     266  Close
    244267end;
    245268
    246269procedure TCityDlg.InitSmallCityMap;
    247270var
    248 i,iix,cli1,Color0,Color1,Color2: integer;
    249 begin
    250 if cix>=0 then c:=MyCity[cix];
    251 case MyMap[cLoc] and fTerrain of
    252   fPrairie: cli1:=cliPrairie;
    253   fHills: cli1:=cliHills;
    254   fTundra: cli1:=cliTundra;
    255   else cli1:=cliPlains;
    256   end;
    257 Color0:=Colors.Canvas.Pixels[clkAge0+Age,cliRoad];
    258 Color1:=Colors.Canvas.Pixels[clkCity,cli1];
    259 Color2:=Colors.Canvas.Pixels[clkAge0+Age,cliHouse];
    260 BitBlt(SmallCityMap.Canvas.Handle,0,0,83,hSmallMap,SmallCityMapTemplate.Canvas.Handle,83*SizeClass,0,SRCCOPY);
    261 if IsPort then
    262   begin
    263   BitBlt(SmallCityMap.Canvas.Handle,83,0,15,hSmallMap,SmallCityMapTemplate.Canvas.Handle,332+15,0,SRCCOPY);
    264   ImageOp_CCC(SmallCityMap,0,0,83,hSmallMap,Color0,Color1,Color2);
    265   Color2:=Colors.Canvas.Pixels[clkCity,cliWater];
    266   ImageOp_CCC(SmallCityMap,83,0,15,hSmallMap,Color0,Color1,Color2);
     271  i, iix, cli1, Color0, Color1, Color2: integer;
     272begin
     273  if cix >= 0 then
     274    c := MyCity[cix];
     275  case MyMap[cLoc] and fTerrain of
     276    fPrairie:
     277      cli1 := cliPrairie;
     278    fHills:
     279      cli1 := cliHills;
     280    fTundra:
     281      cli1 := cliTundra;
     282  else
     283    cli1 := cliPlains;
     284  end;
     285  Color0 := Colors.Canvas.Pixels[clkAge0 + Age, cliRoad];
     286  Color1 := Colors.Canvas.Pixels[clkCity, cli1];
     287  Color2 := Colors.Canvas.Pixels[clkAge0 + Age, cliHouse];
     288  bitblt(SmallCityMap.Canvas.Handle, 0, 0, 83, hSmallMap,
     289    SmallCityMapTemplate.Canvas.Handle, 83 * SizeClass, 0, SRCCOPY);
     290  if IsPort then
     291  begin
     292    bitblt(SmallCityMap.Canvas.Handle, 83, 0, 15, hSmallMap,
     293      SmallCityMapTemplate.Canvas.Handle, 332 + 15, 0, SRCCOPY);
     294    ImageOp_CCC(SmallCityMap, 0, 0, 83, hSmallMap, Color0, Color1, Color2);
     295    Color2 := Colors.Canvas.Pixels[clkCity, cliWater];
     296    ImageOp_CCC(SmallCityMap, 83, 0, 15, hSmallMap, Color0, Color1, Color2);
    267297  end
    268 else
    269   begin
    270   BitBlt(SmallCityMap.Canvas.Handle,83,0,15,hSmallMap,SmallCityMapTemplate.Canvas.Handle,332,0,SRCCOPY);
    271   ImageOp_CCC(SmallCityMap,0,0,wSmallMap,hSmallMap,Color0,Color1,Color2);
    272   end;
    273 
    274 with SmallCityMap.canvas do
    275   begin
    276   brush.Color:=Colors.Canvas.Pixels[clkAge0+Age,cliImp];
    277   for i:=0 to 29 do
    278     begin
    279     for iix:=28 to nImp-1 do
    280       if (ImpPosition[iix]=i) and (c.Built[iix]>0) then
     298  else
     299  begin
     300    bitblt(SmallCityMap.Canvas.Handle, 83, 0, 15, hSmallMap,
     301      SmallCityMapTemplate.Canvas.Handle, 332, 0, SRCCOPY);
     302    ImageOp_CCC(SmallCityMap, 0, 0, wSmallMap, hSmallMap, Color0,
     303      Color1, Color2);
     304  end;
     305
     306  with SmallCityMap.Canvas do
     307  begin
     308    brush.Color := Colors.Canvas.Pixels[clkAge0 + Age, cliImp];
     309    for i := 0 to 29 do
     310    begin
     311      for iix := 28 to nImp - 1 do
     312        if (ImpPosition[iix] = i) and (c.Built[iix] > 0) then
    281313        begin
    282         FillRect(Rect(5+16*(i mod 3)+48*(i div 18), 3+12*(i mod 18 div 3),
    283           13+16*(i mod 3)+48*(i div 18), 11+12*(i mod 18 div 3)));
    284         break;
     314          FillRect(Rect(5 + 16 * (i mod 3) + 48 * (i div 18),
     315            3 + 12 * (i mod 18 div 3), 13 + 16 * (i mod 3) + 48 * (i div 18),
     316            11 + 12 * (i mod 18 div 3)));
     317          break;
    285318        end
    286319    end;
    287   i:=30;
    288   for iix:=0 to nImp do
    289     if (c.Built[iix]>0) and ((iix<28) or (ImpPosition[iix]<0)) then
     320    i := 30;
     321    for iix := 0 to nImp do
     322      if (c.Built[iix] > 0) and ((iix < 28) or (ImpPosition[iix] < 0)) then
    290323      begin
    291       FillRect(Rect(5+16*(i mod 3)+48*(i div 18), 3+12*(i mod 18 div 3),
    292         13+16*(i mod 3)+48*(i div 18), 11+12*(i mod 18 div 3)));
    293       inc(i);
    294       if i=36 then break; // area is full
     324        FillRect(Rect(5 + 16 * (i mod 3) + 48 * (i div 18),
     325          3 + 12 * (i mod 18 div 3), 13 + 16 * (i mod 3) + 48 * (i div 18),
     326          11 + 12 * (i mod 18 div 3)));
     327        inc(i);
     328        if i = 36 then
     329          break; // area is full
    295330      end;
    296   if c.Project and cpImp<>0 then
    297     begin
    298     iix:=c.Project and cpIndex;
    299     if iix<>imTrGoods then
     331    if c.Project and cpImp <> 0 then
     332    begin
     333      iix := c.Project and cpIndex;
     334      if iix <> imTrGoods then
    300335      begin
    301       if (iix>=28) and (ImpPosition[iix]>=0) then
    302         i:=ImpPosition[iix];
    303       if i<36 then
     336        if (iix >= 28) and (ImpPosition[iix] >= 0) then
     337          i := ImpPosition[iix];
     338        if i < 36 then
    304339        begin
    305         brush.Color:=Colors.Canvas.Pixels[clkAge0+Age,cliImpProject];
    306         FillRect(Rect(5+16*(i mod 3)+48*(i div 18), 3+12*(i mod 18 div 3),
    307           13+16*(i mod 3)+48*(i div 18), 11+12*(i mod 18 div 3)));
     340          brush.Color := Colors.Canvas.Pixels[clkAge0 + Age, cliImpProject];
     341          FillRect(Rect(5 + 16 * (i mod 3) + 48 * (i div 18),
     342            3 + 12 * (i mod 18 div 3), 13 + 16 * (i mod 3) + 48 * (i div 18),
     343            11 + 12 * (i mod 18 div 3)));
    308344        end
    309345      end
    310346    end;
    311   brush.style:=bsClear;
     347    brush.style := bsClear;
    312348  end
    313349end;
     
    315351procedure TCityDlg.InitZoomCityMap;
    316352begin
    317 bitblt(ZoomCityMap.canvas.handle,0,0,wZoomMap,hZoomMap,Back.Canvas.handle,
    318   xZoomMap,yZoomMap,SRCCOPY);
    319 if Mode=mImp then
    320   begin
    321   if ZoomArea<3 then
    322     ImageOp_B(ZoomCityMap,CityMapTemplate,0,0,376*SizeClass,
    323       112*ZoomArea,wZoomMap,hZoomMap)
    324   else
    325     begin
    326     ImageOp_B(ZoomCityMap,CityMapTemplate,0,0,376*SizeClass+216,
    327       112*(ZoomArea-3),wZoomMap-wZoomEnvironment,hZoomMap);
    328     ImageOp_B(ZoomCityMap,CityMapTemplate,wZoomMap-wZoomEnvironment,0,
    329       1504+wZoomEnvironment*byte(IsPort),112*(ZoomArea-3),wZoomEnvironment,hZoomMap);
    330     end;
    331   end 
     353  bitblt(ZoomCityMap.Canvas.Handle, 0, 0, wZoomMap, hZoomMap,
     354    Back.Canvas.Handle, xZoomMap, yZoomMap, SRCCOPY);
     355  if Mode = mImp then
     356  begin
     357    if ZoomArea < 3 then
     358      ImageOp_B(ZoomCityMap, CityMapTemplate, 0, 0, 376 * SizeClass,
     359        112 * ZoomArea, wZoomMap, hZoomMap)
     360    else
     361    begin
     362      ImageOp_B(ZoomCityMap, CityMapTemplate, 0, 0, 376 * SizeClass + 216,
     363        112 * (ZoomArea - 3), wZoomMap - wZoomEnvironment, hZoomMap);
     364      ImageOp_B(ZoomCityMap, CityMapTemplate, wZoomMap - wZoomEnvironment, 0,
     365        1504 + wZoomEnvironment * byte(IsPort), 112 * (ZoomArea - 3),
     366        wZoomEnvironment, hZoomMap);
     367    end;
     368  end
    332369end;
    333370
    334371procedure TCityDlg.OffscreenPaint;
    335372
    336   procedure FillBar(x,y,pos,Growth,max,Kind: integer; IndicateComplete: boolean);
     373  procedure FillBar(x, y, pos, Growth, max, Kind: integer;
     374    IndicateComplete: boolean);
    337375  var
    338   Tex: TTexture;
    339   begin
    340   Tex:=MainTexture;
    341   if Kind=3 then
    342     begin
    343     Tex.clBevelLight:=GrExt[HGrSystem].Data.Canvas.Pixels[104,36];
    344     Tex.clBevelShade:=Tex.clBevelLight;
    345     end;
    346   PaintRelativeProgressBar(offscreen.Canvas,Kind,x-3,y,wBar-4,pos,Growth,max,
    347     IndicateComplete,Tex);
    348   end;
    349 
    350   procedure PaintResources(x,y,Loc:integer; Add4Happy: boolean);
     376    Tex: TTexture;
     377  begin
     378    Tex := MainTexture;
     379    if Kind = 3 then
     380    begin
     381      Tex.clBevelLight := GrExt[HGrSystem].Data.Canvas.Pixels[104, 36];
     382      Tex.clBevelShade := Tex.clBevelLight;
     383    end;
     384    PaintRelativeProgressBar(offscreen.Canvas, Kind, x - 3, y, wBar - 4, pos,
     385      Growth, max, IndicateComplete, Tex);
     386  end;
     387
     388  procedure PaintResources(x, y, Loc: integer; Add4Happy: boolean);
    351389  var
    352   d,i,Total,xGr,yGr:integer;
    353   TileInfo:TTileInfo;
    354   rare: boolean;
    355   begin
    356   if Server(sGetCityTileInfo,me,Loc,TileInfo)<>eOk then
    357     begin assert(cix<0); exit end;
    358   Total:=TileInfo.Food+TileInfo.Prod+TileInfo.Trade;
    359   rare:=MyMap[Loc] and $06000000>0;
    360   if rare then
    361     inc(Total);
    362   if Add4Happy then
    363     inc(Total,4);
    364   if Total>1 then d:=(xxt-11) div (Total-1);
    365   if d<1 then d:=1;
    366   if d>4 then d:=4;
    367   for i:=0 to Total-1 do
    368     begin
    369     yGr:=115;
    370     if Add4Happy and (i>=Total-4) then
    371       begin xGr:=132; yGr:=126 end
    372     else if rare and (i=Total-1) then xGr:=66+110
    373     else if i>=TileInfo.Food+TileInfo.Prod then xGr:=66+44
    374     else if i>=TileInfo.Prod then xGr:=66
    375     else xGr:=66+22;
    376     Sprite(offscreen,HGrSystem,x+xxt-5+d*(2*i+1-Total),y+yyt-5,10,10,xGr,yGr);
     390    d, i, Total, xGr, yGr: integer;
     391    TileInfo: TTileInfo;
     392    rare: boolean;
     393  begin
     394    if Server(sGetCityTileInfo, me, Loc, TileInfo) <> eOk then
     395    begin
     396      assert(cix < 0);
     397      exit
     398    end;
     399    Total := TileInfo.Food + TileInfo.Prod + TileInfo.Trade;
     400    rare := MyMap[Loc] and $06000000 > 0;
     401    if rare then
     402      inc(Total);
     403    if Add4Happy then
     404      inc(Total, 4);
     405    if Total > 1 then
     406      d := (xxt - 11) div (Total - 1);
     407    if d < 1 then
     408      d := 1;
     409    if d > 4 then
     410      d := 4;
     411    for i := 0 to Total - 1 do
     412    begin
     413      yGr := 115;
     414      if Add4Happy and (i >= Total - 4) then
     415      begin
     416        xGr := 132;
     417        yGr := 126
     418      end
     419      else if rare and (i = Total - 1) then
     420        xGr := 66 + 110
     421      else if i >= TileInfo.Food + TileInfo.Prod then
     422        xGr := 66 + 44
     423      else if i >= TileInfo.Prod then
     424        xGr := 66
     425      else
     426        xGr := 66 + 22;
     427      Sprite(offscreen, HGrSystem, x + xxt - 5 + d * (2 * i + 1 - Total),
     428        y + yyt - 5, 10, 10, xGr, yGr);
    377429    end
    378430  end;
    379431
    380   procedure MakeRed(x,y,w,h: integer);
     432  procedure MakeRed(x, y, w, h: integer);
    381433  type
    382   TLine=array[0..99999,0..2] of Byte;
    383   PLine=^TLine;
     434    TLine = array [0 .. 99999, 0 .. 2] of byte;
     435    PLine = ^TLine;
    384436
    385437    procedure RedLine(line: PLine; length: integer);
    386438    var
    387     i,gray: integer;
    388     begin
    389     for i:=0 to length-1 do
     439      i, gray: integer;
     440    begin
     441      for i := 0 to length - 1 do
    390442      begin
    391       gray:=(integer(line[i,0])+integer(line[i,1])+integer(line[i,2])) *85 shr 8;
    392       line[i,0]:=0;
    393       line[i,1]:=0;
    394       line[i,2]:=gray; //255-(255-gray) div 2;
     443        gray := (integer(line[i, 0]) + integer(line[i, 1]) + integer(line[i, 2])
     444          ) * 85 shr 8;
     445        line[i, 0] := 0;
     446        line[i, 1] := 0;
     447        line[i, 2] := gray; // 255-(255-gray) div 2;
    395448      end
    396449    end;
    397450
    398451  var
    399   i: integer;             
    400   begin
    401   for i:=0 to h-1 do
    402     RedLine(@(PLine(Offscreen.ScanLine[y+i])[x]),w)
     452    i: integer;
     453  begin
     454    for i := 0 to h - 1 do
     455      RedLine(@(PLine(offscreen.ScanLine[y + i])[x]), w)
    403456  end;
    404457
    405458var
    406 line, MessageCount: integer;
     459  line, MessageCount: integer;
    407460
    408461  procedure CheckMessage(Flag: integer);
    409462  var
    410   i, test: integer;
    411   s: string;
    412   begin
    413   if Happened and Flag<>0 then
    414     begin
    415     i:=0;
    416     test:=1;
    417     while test<Flag do begin inc(i); inc(test,test) end;
    418 
    419     if AllowChange and (Sounds<>nil) and (OpenSoundEvent=-1) then
     463    i, test: integer;
     464    s: string;
     465  begin
     466    if Happened and Flag <> 0 then
     467    begin
     468      i := 0;
     469      test := 1;
     470      while test < Flag do
    420471      begin
    421       s:=CityEventSoundItem[i];
    422       if s<>'' then s:=Sounds.Lookup(s);
    423       if (Flag=chProduction) or (s<>'') and (s[1]<>'*') and (s[1]<>'[') then
    424         OpenSoundEvent:=i
     472        inc(i);
     473        inc(test, test)
    425474      end;
    426475
    427     s:=CityEventName(i);
    428 {    if Flag=chNoGrowthWarning then
    429       if c.Built[imAqueduct]=0 then
     476      if AllowChange and (Sounds <> nil) and (OpenSoundEvent = -1) then
     477      begin
     478        s := CityEventSoundItem[i];
     479        if s <> '' then
     480          s := Sounds.Lookup(s);
     481        if (Flag = chProduction) or (s <> '') and (s[1] <> '*') and (s[1] <> '[')
     482        then
     483          OpenSoundEvent := i
     484      end;
     485
     486      s := CityEventName(i);
     487      { if Flag=chNoGrowthWarning then
     488        if c.Built[imAqueduct]=0 then
    430489        s:=Format(s,[Phrases.Lookup('IMPROVEMENTS',imAqueduct)])
    431       else s:=Format(s,[Phrases.Lookup('IMPROVEMENTS',imSewer)]);}
    432     RisedTextOut(offscreen.Canvas,xmOpt+40,ymOpt-1-8*MessageCount+16*line,s);
    433     inc(line)
     490        else s:=Format(s,[Phrases.Lookup('IMPROVEMENTS',imSewer)]); }
     491      RisedTextOut(offscreen.Canvas, xmOpt + 40, ymOpt - 1 - 8 * MessageCount +
     492        16 * line, s);
     493      inc(line)
    434494    end
    435495  end;
    436496
    437497var
    438 x,y,xGr,i,i1,j,iix,d,dx,dy,PrCost,Cnt,Loc1,FreeSupp,Paintiix,HappyGain,
    439   OptiType,rx,ry,TrueFood,TrueProd,TruePoll: integer;
    440 av: Integer;
    441 PrName,s:string;
    442 UnitInfo: TUnitInfo;
    443 UnitReport: TUnitReport;
    444 RedTex: TTexture;
    445 IsCityAlive,CanGrow: boolean;
    446 begin
    447 inherited;
    448 if cix>=0 then c:=MyCity[cix];
    449 Report.HypoTiles:=-1;
    450 Report.HypoTaxRate:=-1;
    451 Report.HypoLuxuryRate:=-1;
    452 if cix>=0 then Server(sGetCityReportNew,me,cix,Report) // own city
    453 else Server(sGetEnemyCityReportNew,me,cLoc,Report); // enemy city
    454 TrueFood:=c.Food;
    455 TrueProd:=c.Prod;
    456 TruePoll:=c.Pollution;
    457 if supervising or (cix<0) then
     498  x, y, xGr, i, i1, j, iix, d, dx, dy, PrCost, Cnt, Loc1, FreeSupp, Paintiix,
     499    HappyGain, OptiType, rx, ry, TrueFood, TrueProd, TruePoll: integer;
     500  av: integer;
     501  PrName, s: string;
     502  UnitInfo: TUnitInfo;
     503  UnitReport: TUnitReport;
     504  RedTex: TTexture;
     505  IsCityAlive, CanGrow: boolean;
     506begin
     507  inherited;
     508  if cix >= 0 then
     509    c := MyCity[cix];
     510  Report.HypoTiles := -1;
     511  Report.HypoTaxRate := -1;
     512  Report.HypoLuxuryRate := -1;
     513  if cix >= 0 then
     514    Server(sGetCityReportNew, me, cix, Report) // own city
     515  else
     516    Server(sGetEnemyCityReportNew, me, cLoc, Report); // enemy city
     517  TrueFood := c.Food;
     518  TrueProd := c.Prod;
     519  TruePoll := c.Pollution;
     520  if supervising or (cix < 0) then
    458521  begin // normalize city from after-turn state
    459   dec(TrueFood,Report.FoodSurplus);
    460   if TrueFood<0 then
    461     TrueFood:=0; // shouldn't happen
    462   dec(TrueProd,Report.Production);
    463   if TrueProd<0 then
    464     TrueProd:=0; // shouldn't happen
    465   dec(TruePoll,Report.AddPollution);
    466   if TruePoll<0 then
    467     TruePoll:=0; // shouldn't happen
    468   end;
    469 IsCityAlive:= (cGov<>gAnarchy) and (c.Flags and chCaptured=0);
    470 if not IsCityAlive then Report.Working:=c.Size;
    471 
    472 RedTex:=MainTexture;
    473 RedTex.clBevelLight:=$0000FF;
    474 RedTex.clBevelShade:=$000000;
    475 RedTex.clTextLight:=$000000;
    476 RedTex.clTextShade:=$0000FF;
    477 
    478 bitblt(offscreen.canvas.handle,0,0,640,480,Back.Canvas.handle,0,0,SRCCOPY);
    479 
    480 offscreen.Canvas.Font.Assign(UniFont[ftCaption]);
    481 RisedTextout(offscreen.Canvas,42,7,Caption);
    482 with offscreen.canvas do
     522    dec(TrueFood, Report.FoodSurplus);
     523    if TrueFood < 0 then
     524      TrueFood := 0; // shouldn't happen
     525    dec(TrueProd, Report.Production);
     526    if TrueProd < 0 then
     527      TrueProd := 0; // shouldn't happen
     528    dec(TruePoll, Report.AddPollution);
     529    if TruePoll < 0 then
     530      TruePoll := 0; // shouldn't happen
     531  end;
     532  IsCityAlive := (cGov <> gAnarchy) and (c.Flags and chCaptured = 0);
     533  if not IsCityAlive then
     534    Report.Working := c.Size;
     535
     536  RedTex := MainTexture;
     537  RedTex.clBevelLight := $0000FF;
     538  RedTex.clBevelShade := $000000;
     539  RedTex.clTextLight := $000000;
     540  RedTex.clTextShade := $0000FF;
     541
     542  bitblt(offscreen.Canvas.Handle, 0, 0, 640, 480, Back.Canvas.Handle, 0,
     543    0, SRCCOPY);
     544
     545  offscreen.Canvas.Font.Assign(UniFont[ftCaption]);
     546  RisedTextOut(offscreen.Canvas, 42, 7, Caption);
     547  with offscreen.Canvas do
    483548  begin // city size
    484   brush.color:=$000000;
    485   fillrect(rect(8+1,7+1,36+1,32+1));
    486   brush.color:=$FFFFFF;
    487   fillrect(rect(8,7,36,32));
    488   brush.style:=bsClear;
    489   font.color:=$000000;
    490   s:=inttostr(c.Size);
    491   TextOut(8+14-textwidth(s) div 2, 7, s);
    492   end;
    493 offscreen.Canvas.Font.Assign(UniFont[ftSmall]);
    494 
    495 if not IsCityAlive then
    496   begin
    497   MakeRed(18,280,298,40);
    498   if cGov=gAnarchy then s:=Phrases.Lookup('GOVERNMENT',gAnarchy)
    499   else {if c.Flags and chCaptured<>0 then}
    500     s:=Phrases.Lookup('CITYEVENTS',14);
    501   RisedTextout(offscreen.canvas,167-BiColorTextWidth(offscreen.canvas,s) div 2,ymOpt-9, s);
     549    brush.Color := $000000;
     550    FillRect(Rect(8 + 1, 7 + 1, 36 + 1, 32 + 1));
     551    brush.Color := $FFFFFF;
     552    FillRect(Rect(8, 7, 36, 32));
     553    brush.style := bsClear;
     554    Font.Color := $000000;
     555    s := inttostr(c.Size);
     556    TextOut(8 + 14 - textwidth(s) div 2, 7, s);
     557  end;
     558  offscreen.Canvas.Font.Assign(UniFont[ftSmall]);
     559
     560  if not IsCityAlive then
     561  begin
     562    MakeRed(18, 280, 298, 40);
     563    if cGov = gAnarchy then
     564      s := Phrases.Lookup('GOVERNMENT', gAnarchy)
     565    else { if c.Flags and chCaptured<>0 then }
     566      s := Phrases.Lookup('CITYEVENTS', 14);
     567    RisedTextOut(offscreen.Canvas, 167 - BiColorTextWidth(offscreen.Canvas, s)
     568      div 2, ymOpt - 9, s);
    502569  end
    503 else if AllowChange then
    504   begin
    505   OptiType:=c.Status shr 4 and $0F;
    506   Sprite(offscreen,HGrSystem2,xmOpt-32,ymOpt-32,64,64,1+OptiType mod 3*64,217+OptiType div 3*64);
    507 
    508   {display messages now}
    509   MessageCount:=0;
    510   for i:=0 to 31 do
    511     if Happened and ($FFFFFFFF-chCaptured) and (1 shl i)<>0 then
    512       inc(MessageCount);
    513   if MessageCount>3 then
    514     MessageCount:=3;
    515   if MessageCount>0 then
    516     begin
    517     MakeBlue(Offscreen,74,280,242,40);
    518     line:=0;
    519     for i:=0 to nCityEventPriority-1 do
    520       if line<MessageCount then
    521         CheckMessage(CityEventPriority[i]);
     570  else if AllowChange then
     571  begin
     572    OptiType := c.Status shr 4 and $0F;
     573    Sprite(offscreen, HGrSystem2, xmOpt - 32, ymOpt - 32, 64, 64,
     574      1 + OptiType mod 3 * 64, 217 + OptiType div 3 * 64);
     575
     576    { display messages now }
     577    MessageCount := 0;
     578    for i := 0 to 31 do
     579      if Happened and ($FFFFFFFF - chCaptured) and (1 shl i) <> 0 then
     580        inc(MessageCount);
     581    if MessageCount > 3 then
     582      MessageCount := 3;
     583    if MessageCount > 0 then
     584    begin
     585      MakeBlue(offscreen, 74, 280, 242, 40);
     586      line := 0;
     587      for i := 0 to nCityEventPriority - 1 do
     588        if line < MessageCount then
     589          CheckMessage(CityEventPriority[i]);
    522590    end
    523   else
    524     begin
    525     s:=Phrases.Lookup('CITYMANAGETYPE',OptiType);
    526     j:=pos('\',s);
    527     if j=0 then
    528       LoweredTextout(offscreen.canvas, -1, MainTexture, xmOpt+40, ymOpt-9, s)
    529     else
     591    else
     592    begin
     593      s := Phrases.Lookup('CITYMANAGETYPE', OptiType);
     594      j := pos('\', s);
     595      if j = 0 then
     596        LoweredTextout(offscreen.Canvas, -1, MainTexture, xmOpt + 40,
     597          ymOpt - 9, s)
     598      else
    530599      begin
    531       LoweredTextout(offscreen.canvas, -1, MainTexture, xmOpt+40, ymOpt-17,
    532         copy(s,1,j-1));
    533       LoweredTextout(offscreen.canvas, -1, MainTexture, xmOpt+40, ymOpt-1,
    534         copy(s,j+1,255));
     600        LoweredTextout(offscreen.Canvas, -1, MainTexture, xmOpt + 40,
     601          ymOpt - 17, copy(s, 1, j - 1));
     602        LoweredTextout(offscreen.Canvas, -1, MainTexture, xmOpt + 40, ymOpt - 1,
     603          copy(s, j + 1, 255));
    535604      end
    536605    end
    537606  end;
    538607
    539 rx:=(192+xxt*2-1) div (xxt*2);
    540 ry:=(96+yyt*2-1) div (yyt*2);
    541 AreaMap.Paint(xmArea-xxt*2*rx,ymArea-yyt*2*ry-3*yyt,dLoc(cLoc,-2*rx+1,-2*ry-1),4*rx-1,4*ry+1,cLoc,cOwner,
    542   false,AllowChange and IsCityAlive and (c.Status and csResourceWeightsMask=0));
    543 bitblt(offscreen.canvas.handle,xmArea+102,42,90,33,Back.Canvas.handle,xmArea+102,42,SRCCOPY);
    544 
    545 if IsCityAlive then
    546   for dy:=-3 to 3 do for dx:=-3 to 3 do
    547     if ((dx+dy) and 1=0) and (dx*dx*dy*dy<81) then
     608  rx := (192 + xxt * 2 - 1) div (xxt * 2);
     609  ry := (96 + yyt * 2 - 1) div (yyt * 2);
     610  AreaMap.Paint(xmArea - xxt * 2 * rx, ymArea - yyt * 2 * ry - 3 * yyt,
     611    dLoc(cLoc, -2 * rx + 1, -2 * ry - 1), 4 * rx - 1, 4 * ry + 1, cLoc, cOwner,
     612    false, AllowChange and IsCityAlive and
     613    (c.Status and csResourceWeightsMask = 0));
     614  bitblt(offscreen.Canvas.Handle, xmArea + 102, 42, 90, 33, Back.Canvas.Handle,
     615    xmArea + 102, 42, SRCCOPY);
     616
     617  if IsCityAlive then
     618    for dy := -3 to 3 do
     619      for dx := -3 to 3 do
     620        if ((dx + dy) and 1 = 0) and (dx * dx * dy * dy < 81) then
     621        begin
     622          Loc1 := dLoc(cLoc, dx, dy);
     623          av := CityAreaInfo.Available[(dy + 3) shl 2 + (dx + 3) shr 1];
     624          if ((av = faNotAvailable) or (av = faTreaty) or (av = faInvalid)) and
     625            ((Loc1 < 0) or (Loc1 >= G.lx * G.ly) or (MyMap[Loc1] and fCity = 0))
     626          then
     627            Sprite(offscreen, HGrTerrain, xmArea - xxt + xxt * dx,
     628              ymArea - yyt + yyt * dy, xxt * 2, yyt * 2, 1 + 5 * (xxt * 2 + 1),
     629              1 + yyt + 15 * (yyt * 3 + 1));
     630          if (1 shl ((dy + 3) shl 2 + (dx + 3) shr 1) and c.Tiles <> 0) then
     631            PaintResources(xmArea - xxt + xxt * dx, ymArea - yyt + yyt * dy,
     632              Loc1, (dx = 0) and (dy = 0));
     633        end;
     634
     635  if Report.Working > 1 then
     636    d := (xService - (xmArea - 192) - 8 - 32) div (Report.Working - 1);
     637  if d > 28 then
     638    d := 28;
     639  for i := Report.Working - 1 downto 0 do
     640  begin
     641    if IsCityAlive then
     642      xGr := 29
     643    else
     644      xGr := 141;
     645    bitblt(offscreen.Canvas.Handle, xmArea - 192 + 5 + i * d, ymArea - 96 - 29,
     646      27, 30, GrExt[HGrSystem].Mask.Canvas.Handle, xGr, 171, SRCAND); { shadow }
     647    Sprite(offscreen, HGrSystem, xmArea - 192 + 4 + i * d, ymArea - 96 - 30, 27,
     648      30, xGr, 171);
     649  end;
     650  if c.Size - Report.Working > 1 then
     651    d := (xmArea + 192 - xService - 32) div (c.Size - Report.Working - 1);
     652  if d > 28 then
     653    d := 28;
     654  for i := 0 to c.Size - Report.Working - 1 do
     655  begin
     656    xGr := 1 + 112;
     657    bitblt(offscreen.Canvas.Handle, xmArea + 192 - 27 + 1 - i * d, 29 + 1, 27,
     658      30, GrExt[HGrSystem].Mask.Canvas.Handle, xGr, 171, SRCAND); { shadow }
     659    Sprite(offscreen, HGrSystem, xmArea + 192 - 27 - i * d, 29, 27, 30,
     660      xGr, 171);
     661    Sprite(offscreen, HGrSystem, xmArea + 192 - 27 + 4 - i * d, 29 + 32, 10,
     662      10, 121, 126);
     663    Sprite(offscreen, HGrSystem, xmArea + 192 - 27 + 13 - i * d, 29 + 32, 10,
     664      10, 121, 126);
     665    // Sprite(offscreen,HGrSystem,xmArea+192-31+18-i*d,ymArea-96-80+32,10,10,88,115);
     666  end;
     667
     668  if c.Project and cpImp = 0 then
     669    PrName := Tribe[cOwner].ModelName[c.Project and cpIndex]
     670  else
     671    PrName := Phrases.Lookup('IMPROVEMENTS', c.Project and cpIndex);
     672  PrCost := Report.ProjectCost;
     673
     674  // happiness section
     675  if IsCityAlive then
     676  begin
     677    if cGov = gFundamentalism then
     678      CountBar(offscreen, xHapp, yHapp + dyBar, wBar, 17,
     679        Phrases.Lookup('FAITH'), Report.CollectedControl, MainTexture)
     680    else
     681    begin
     682      CountBar(offscreen, xHapp, yHapp + dyBar, wBar, 17,
     683        Phrases.Lookup('HAPPINESS'), Report.Morale, MainTexture);
     684      CountBar(offscreen, xHapp, yHapp + 2 * dyBar, wBar, 16,
     685        Phrases.Lookup('CONTROL'), Report.CollectedControl, MainTexture);
     686    end;
     687    CountBar(offscreen, xHapp, yHapp, wBar, 8, Phrases.Lookup('LUX'),
     688      Report.Luxury, MainTexture);
     689    CountBar(offscreen, xHapp + dxBar, yHapp, wBar, 19,
     690      Phrases.Lookup('UNREST'), 2 * Report.Deployed, MainTexture);
     691    CountBar(offscreen, xHapp + dxBar, yHapp + dyBar, wBar, 17,
     692      Phrases.Lookup('HAPPINESSDEMAND'), c.Size, MainTexture);
     693    if Report.HappinessBalance >= 0 then
     694      CountBar(offscreen, xHapp + dxBar, yHapp + 2 * dyBar, wBar, 17,
     695        Phrases.Lookup('HAPPINESSPLUS'), Report.HappinessBalance, MainTexture)
     696    else
     697    begin
     698      MakeRed(xHapp + dxBar - 6, yHapp + 2 * dyBar, wBar + 10, 38);
     699      CountBar(offscreen, xHapp + dxBar, yHapp + 2 * dyBar, wBar, 18,
     700        Phrases.Lookup('LACK'), -Report.HappinessBalance, RedTex);
     701    end;
     702  end;
     703
     704  // food section
     705  if IsCityAlive then
     706  begin
     707    CountBar(offscreen, xFood, yFood + dyBar div 2, wBar, 0,
     708      Phrases.Lookup('FOOD'), Report.CollectedFood, MainTexture);
     709    CountBar(offscreen, xFood + dxBar, yFood + dyBar, wBar, 0,
     710      Phrases.Lookup('DEMAND'), 2 * c.Size, MainTexture);
     711    CountBar(offscreen, xFood + dxBar, yFood, wBar, 0,
     712      Phrases.Lookup('SUPPORT'), Report.FoodSupport, MainTexture);
     713    if Report.FoodSurplus >= 0 then
     714      if (cGov = gFuture) or (c.Size >= NeedAqueductSize) and
     715        (Report.FoodSurplus < 2) then
     716        CountBar(offscreen, xFood + dxBar, yFood + 2 * dyBar, wBar, 6,
     717          Phrases.Lookup('PROFIT'), Report.FoodSurplus, MainTexture)
     718      else
     719        CountBar(offscreen, xFood + dxBar, yFood + 2 * dyBar, wBar, 0,
     720          Phrases.Lookup('SURPLUS'), Report.FoodSurplus, MainTexture)
     721    else
     722    begin
     723      MakeRed(xFood + dxBar - 6, yFood + 2 * dyBar, wBar + 10, 38);
     724      CountBar(offscreen, xFood + dxBar, yFood + 2 * dyBar, wBar, 1,
     725        Phrases.Lookup('LACK'), -Report.FoodSurplus, RedTex);
     726    end;
     727  end;
     728  CanGrow := (c.Size < MaxCitySize) and (cGov <> gFuture) and
     729    (Report.FoodSurplus > 0) and ((c.Size < NeedAqueductSize) or
     730    (c.Built[imAqueduct] = 1) and (c.Size < NeedSewerSize) or
     731    (c.Built[imSewer] = 1));
     732  FillBar(xFood + 3, yFood + 102, TrueFood,
     733    CutCityFoodSurplus(Report.FoodSurplus, IsCityAlive, cGov, c.Size),
     734    Report.Storage, 1, CanGrow);
     735  LoweredTextout(offscreen.Canvas, -1, MainTexture, xFood + 3 - 5,
     736    yFood + 102 - 20, Format('%d/%d', [TrueFood, Report.Storage]));
     737  LoweredTextout(offscreen.Canvas, -1, MainTexture, xFood - 2, yFood + 66,
     738    Phrases.Lookup('STORAGE'));
     739
     740  // production section
     741  if IsCityAlive then
     742  begin
     743    CountBar(offscreen, xProd, yProd, wBar, 2, Phrases.Lookup('MATERIAL'),
     744      Report.CollectedMaterial, MainTexture);
     745    CountBar(offscreen, xProd + dxBar, yProd, wBar, 2,
     746      Phrases.Lookup('SUPPORT'), Report.MaterialSupport, MainTexture);
     747    if Report.Production >= 0 then
     748      if c.Project and (cpImp + cpIndex) = cpImp + imTrGoods then
     749        CountBar(offscreen, xProd + dxBar, yProd + dyBar + 16, wBar, 6,
     750          Phrases.Lookup('PROFIT'), Report.Production, MainTexture)
     751      else
     752        CountBar(offscreen, xProd + dxBar, yProd + dyBar + 16, wBar, 2,
     753          Phrases.Lookup('PROD'), Report.Production, MainTexture)
     754    else
     755    begin
     756      MakeRed(xProd + dxBar - 6, yProd + dyBar, wBar + 10, 38);
     757      CountBar(offscreen, xProd + dxBar, yProd + dyBar + 16, wBar, 3,
     758        Phrases.Lookup('LACK'), -Report.Production, RedTex);
     759    end;
     760  end;
     761  if c.Project and (cpImp + cpIndex) <> cpImp + imTrGoods then
     762    with offscreen.Canvas do
     763    begin
     764      i := Report.Production;
     765      if (i < 0) or not IsCityAlive then
     766        i := 0;
     767      FillBar(xProd + 3, yProd + 16 + 63, TrueProd, i, PrCost, 4, true);
     768      LoweredTextout(offscreen.Canvas, -1, MainTexture, xProd + 3 - 5,
     769        yProd + 16 + 43, Format('%d/%d', [TrueProd, PrCost]));
     770      if BiColorTextWidth(offscreen.Canvas, PrName) > wBar + dxBar then
    548771      begin
    549       Loc1:=dLoc(cLoc,dx,dy);
    550       av := CityAreaInfo.Available[(dy+3) shl 2+(dx+3) shr 1];
    551       if ((av = faNotAvailable) or (av = faTreaty) or (av =faInvalid))
    552         and ((Loc1<0) or (Loc1>=G.lx*G.ly) or (MyMap[Loc1] and fCity=0)) then
    553         Sprite(offscreen,HGrTerrain,xmArea-xxt+xxt*dx,ymArea-yyt+yyt*dy,xxt*2,
    554           yyt*2,1+5*(xxt*2+1),1+yyt+15*(yyt*3+1));
    555       if (1 shl((dy+3) shl 2+(dx+3) shr 1) and c.Tiles<>0) then
    556         PaintResources(xmArea-xxt+xxt*dx,ymArea-yyt+yyt*dy,Loc1,(dx=0) and (dy=0));
     772        repeat
     773          Delete(PrName, length(PrName), 1)
     774        until BiColorTextWidth(offscreen.Canvas, PrName) <= wBar + dxBar;
     775        PrName := PrName + '.'
    557776      end;
    558 
    559 if Report.Working>1 then d:=(xService-(xmArea-192)-8-32) div(Report.Working-1);
    560 if d>28 then d:=28;
    561 for i:=Report.Working-1 downto 0 do
    562   begin
    563   if IsCityAlive then xGr:=29
    564   else xGr:=141;
    565   BitBlt(offscreen.Canvas.Handle,xmArea-192+5+i*d,ymArea-96-29,
    566     27,30,GrExt[HGrSystem].Mask.Canvas.Handle,xGr,171,SRCAND); {shadow}
    567   Sprite(offscreen,HGrSystem,xmArea-192+4+i*d,ymArea-96-30,27,30,xGr,171);
    568   end;
    569 if c.Size-Report.Working>1 then d:=(xmArea+192-xService-32) div(c.Size-Report.Working-1);
    570 if d>28 then d:=28;
    571 for i:=0 to c.Size-Report.Working-1 do
    572   begin
    573   xGr:=1+112;
    574   BitBlt(offscreen.Canvas.Handle,xmArea+192-27+1-i*d,29+1,
    575     27,30,GrExt[HGrSystem].Mask.Canvas.Handle,xGr,171,SRCAND); {shadow}
    576   Sprite(offscreen,HGrSystem,xmArea+192-27-i*d,29,27,30,xGr,171);
    577   Sprite(offscreen,HGrSystem,xmArea+192-27+4-i*d,29+32,10,10,121,126);
    578   Sprite(offscreen,HGrSystem,xmArea+192-27+13-i*d,29+32,10,10,121,126);
    579 //  Sprite(offscreen,HGrSystem,xmArea+192-31+18-i*d,ymArea-96-80+32,10,10,88,115);
    580   end;
    581 
    582 if c.Project and cpImp=0 then
    583   PrName:=Tribe[cOwner].ModelName[c.Project and cpIndex]
    584 else PrName:=Phrases.Lookup('IMPROVEMENTS',c.Project and cpIndex);
    585 PrCost:=Report.ProjectCost;
    586 
    587 // happiness section
    588 if IsCityAlive then
    589   begin
    590   if cGov=gFundamentalism then
    591     CountBar(offscreen,xHapp,yHapp+dyBar,wBar,17,Phrases.Lookup('FAITH'),
    592       Report.CollectedControl,MainTexture)
     777    end;
     778  RisedTextOut(offscreen.Canvas, xProd - 2, yProd + 36, PrName);
     779
     780  // pollution section
     781  if IsCityAlive and (Report.AddPollution > 0) then
     782  begin
     783    FillBar(xPoll + 3, yPoll + 20, TruePoll, Report.AddPollution,
     784      MaxPollution, 3, true);
     785    RisedTextOut(offscreen.Canvas, xPoll + 3 - 5, yPoll + 20 - 20,
     786      Phrases.Lookup('POLL'));
     787  end;
     788
     789  // trade section
     790  if IsCityAlive and (Report.CollectedTrade > 0) then
     791  begin
     792    CountBar(offscreen, xTrade, yTrade + dyBar div 2, wBar, 4,
     793      Phrases.Lookup('TRADE'), Report.CollectedTrade, MainTexture);
     794    CountBar(offscreen, xTrade + dxBar, yTrade + 2 * dyBar, wBar, 5,
     795      Phrases.Lookup('CORR'), Report.Corruption, MainTexture);
     796    CountBar(offscreen, xTrade + dxBar, yTrade, wBar, 6, Phrases.Lookup('TAX'),
     797      Report.Tax, MainTexture);
     798    CountBar(offscreen, xTrade + dxBar, yTrade + dyBar, wBar, 12,
     799      Phrases.Lookup('SCIENCE'), Report.Science, MainTexture);
     800  end;
     801
     802  // small map
     803  bitblt(offscreen.Canvas.Handle, xSmallMap, ySmallMap, wSmallMap, hSmallMap,
     804    SmallCityMap.Canvas.Handle, 0, 0, SRCCOPY);
     805  if Mode = mImp then
     806    Frame(offscreen.Canvas, xSmallMap + 48 * (ZoomArea div 3),
     807      ySmallMap + 24 * (ZoomArea mod 3), xSmallMap + 48 * (ZoomArea div 3) + 49,
     808      ySmallMap + 24 * (ZoomArea mod 3) + 25, MainTexture.clMark,
     809      MainTexture.clMark);
     810  Frame(offscreen.Canvas, xSmallMap - 1, ySmallMap - 1, xSmallMap + wSmallMap,
     811    ySmallMap + hSmallMap, $B0B0B0, $FFFFFF);
     812  RFrame(offscreen.Canvas, xSmallMap - 2, ySmallMap - 2, xSmallMap + wSmallMap +
     813    1, ySmallMap + hSmallMap + 1, $FFFFFF, $B0B0B0);
     814
     815  Frame(offscreen.Canvas, xSupport - 1, ySupport - 1, xSupport + wSupport,
     816    ySupport + hSupport, $B0B0B0, $FFFFFF);
     817  RFrame(offscreen.Canvas, xSupport - 2, ySupport - 2, xSupport + wSupport + 1,
     818    ySupport + hSupport + 1, $FFFFFF, $B0B0B0);
     819  x := xSupport + wSupport div 2;
     820  y := ySupport + hSupport div 2;
     821  if Mode = mSupp then
     822  begin
     823    offscreen.Canvas.brush.Color := MainTexture.clMark;
     824    offscreen.Canvas.FillRect(Rect(x - 27, y - 6, x + 27, y + 6));
     825    offscreen.Canvas.brush.style := bsClear;
     826  end;
     827  Sprite(offscreen, HGrSystem, x - 16, y - 5, 10, 10, 88, 115);
     828  Sprite(offscreen, HGrSystem, x - 5, y - 5, 10, 10, 66, 115);
     829  Sprite(offscreen, HGrSystem, x + 6, y - 5, 10, 10, 154, 126);
     830
     831  bitblt(offscreen.Canvas.Handle, xZoomMap, yZoomMap, wZoomMap, hZoomMap,
     832    ZoomCityMap.Canvas.Handle, 0, 0, SRCCOPY);
     833
     834  for i := 0 to 5 do
     835    imix[i] := -1;
     836  if Mode = mImp then
     837  begin
     838    if ZoomArea = 5 then
     839    begin
     840      Cnt := 0;
     841      for iix := 0 to nImp - 1 do
     842        if ((iix < 28) or (ImpPosition[iix] < 0)) and (c.Built[iix] > 0) then
     843        begin
     844          i := Cnt - Page * 6;
     845          if (i >= 0) and (i < 6) then
     846            imix[i] := iix;
     847          inc(Cnt);
     848        end;
     849      PageCount := (Cnt + 5) div 6;
     850    end
     851    else
     852    begin
     853      for iix := 28 to nImp - 1 do
     854      begin
     855        i := ImpPosition[iix] - 6 * ZoomArea;
     856        if (i >= 0) and (i < 6) and (c.Built[iix] > 0) then
     857          imix[i] := iix;
     858      end;
     859      PageCount := 0;
     860    end;
     861    for i := 0 to 5 do
     862      if imix[i] >= 0 then
     863      begin
     864        iix := imix[i];
     865        x := xZoomMap + 14 + 72 * (i mod 3);
     866        y := yZoomMap + 14 + 56 * (i div 3);
     867        ImpImage(offscreen.Canvas, x, y, iix, cGov, AllowChange and
     868          (ClientMode < scContact));
     869        if IsCityAlive then
     870        begin
     871          if iix = imColosseum then
     872          begin
     873            Sprite(offscreen, HGrSystem, x + 46, y, 14, 14, 82, 100);
     874          end
     875          else
     876          begin
     877            HappyGain := 0;
     878            case iix of
     879              0 .. 27, imTemple:
     880                HappyGain := 2;
     881              imTheater:
     882                HappyGain := 4;
     883              imCathedral:
     884                if MyRO.Wonder[woBach].EffectiveOwner = cOwner then
     885                  HappyGain := 6
     886                else
     887                  HappyGain := 4;
     888            end;
     889            if HappyGain > 1 then
     890            begin
     891              d := 30 div (HappyGain - 1);
     892              if d > 10 then
     893                d := 10
     894            end;
     895            for j := 0 to HappyGain - 1 do
     896              Sprite(offscreen, HGrSystem, x + 50, y + d * j, 10, 10, 132, 126);
     897          end;
     898          for j := 0 to Imp[iix].Maint - 1 do
     899            Sprite(offscreen, HGrSystem, x - 4, y + 29 - 3 * j, 10, 10,
     900              132, 115);
     901        end
     902      end;
     903    if imix[0] >= 0 then
     904      Imp0Area.Hint := Phrases.Lookup('IMPROVEMENTS', imix[0])
     905    else
     906      Imp0Area.Hint := '';
     907    if imix[1] >= 0 then
     908      Imp1Area.Hint := Phrases.Lookup('IMPROVEMENTS', imix[1])
     909    else
     910      Imp1Area.Hint := '';
     911    if imix[2] >= 0 then
     912      Imp2Area.Hint := Phrases.Lookup('IMPROVEMENTS', imix[2])
     913    else
     914      Imp2Area.Hint := '';
     915    if imix[3] >= 0 then
     916      Imp3Area.Hint := Phrases.Lookup('IMPROVEMENTS', imix[3])
     917    else
     918      Imp3Area.Hint := '';
     919    if imix[4] >= 0 then
     920      Imp4Area.Hint := Phrases.Lookup('IMPROVEMENTS', imix[4])
     921    else
     922      Imp4Area.Hint := '';
     923    if imix[5] >= 0 then
     924      Imp5Area.Hint := Phrases.Lookup('IMPROVEMENTS', imix[5])
     925    else
     926      Imp5Area.Hint := '';
     927  end
     928  else { if mode=mSupp then }
     929  begin
     930    LoweredTextout(offscreen.Canvas, -1, MainTexture, xZoomMap + 6,
     931      yZoomMap + 2, Phrases.Lookup('SUPUNITS'));
     932    FreeSupp := c.Size * SupportFree[cGov] shr 1;
     933    Cnt := 0;
     934    for i := 0 to MyRO.nUn - 1 do
     935      if (MyUn[i].Loc >= 0) and (MyUn[i].Home = cix) then
     936        with MyModel[MyUn[i].mix] do
     937        begin
     938          Server(sGetUnitReport, me, i, UnitReport);
     939          if (Cnt >= 6 * Page) and (Cnt < 6 * (Page + 1)) then
     940          begin // unit visible in display
     941            imix[Cnt - 6 * Page] := i;
     942            x := ((Cnt - 6 * Page) mod 3) * 64 + xZoomMap;
     943            y := ((Cnt - 6 * Page) div 3) * 52 + yZoomMap + 20;
     944            MakeUnitInfo(me, MyUn[i], UnitInfo);
     945            NoMap.SetOutput(offscreen);
     946            NoMap.PaintUnit(x, y, UnitInfo, MyUn[i].Status);
     947
     948            for j := 0 to UnitReport.FoodSupport - 1 do
     949              Sprite(offscreen, HGrSystem, x + 38 + 11 * j, y + 40, 10,
     950                10, 66, 115);
     951            for j := 0 to UnitReport.ProdSupport - 1 do
     952            begin
     953              if (FreeSupp > 0) and
     954                (UnitReport.ReportFlags and urfAlwaysSupport = 0) then
     955              begin
     956                Sprite(offscreen, HGrSystem, x + 16 - 11 * j, y + 40, 10,
     957                  10, 143, 115);
     958                dec(FreeSupp);
     959              end
     960              else
     961                Sprite(offscreen, HGrSystem, x + 16 - 11 * j, y + 40, 10,
     962                  10, 88, 115);
     963            end;
     964            if UnitReport.ReportFlags and urfDeployed <> 0 then
     965              for j := 0 to 1 do
     966                Sprite(offscreen, HGrSystem, x + 27 + 11 * j, y + 40, 10,
     967                  10, 154, 126)
     968          end // unit visible in display
     969          else
     970            dec(FreeSupp, UnitReport.ProdSupport);
     971          inc(Cnt);
     972        end;
     973    PageCount := (Cnt + 5) div 6;
     974    Imp0Area.Hint := '';
     975    Imp1Area.Hint := '';
     976    Imp2Area.Hint := '';
     977    Imp3Area.Hint := '';
     978    Imp4Area.Hint := '';
     979    Imp5Area.Hint := '';
     980  end;
     981  PageUpBtn.Visible := PageCount > 1;
     982  PageDownBtn.Visible := PageCount > 1;
     983
     984  with offscreen.Canvas do
     985  begin
     986    { display project now }
     987    DLine(offscreen.Canvas, xView + 9 + xSizeBig, xProd + 2 * wBar + 10,
     988      yProd + dyBar + 16, $FFFFFF, $B0B0B0);
     989    if ProdHint then
     990    begin
     991      Frame(offscreen.Canvas, xView + 9 - 1, yView + 5 - 1,
     992        xView + 9 + xSizeBig, yView + 5 + ySizeBig, $B0B0B0, $FFFFFF);
     993      RFrame(offscreen.Canvas, xView + 9 - 2, yView + 5 - 2,
     994        xView + 9 + xSizeBig + 1, yView + 5 + ySizeBig + 1, $FFFFFF, $B0B0B0);
     995      with offscreen.Canvas do
     996      begin
     997        brush.Color := $000000;
     998        FillRect(Rect(xView + 9, yView + 5, xView + 1 + 72 - 8,
     999          yView + 5 + 40));
     1000        brush.style := bsClear;
     1001      end
     1002    end
     1003    else if AllowChange and (c.Status and 7 <> 0) then
     1004    begin // city type autobuild
     1005      FrameImage(offscreen.Canvas, bigimp, xView + 9, yView + 5, xSizeBig,
     1006        ySizeBig, (c.Status and 7 - 1 + 3) * xSizeBig, 0, (cix >= 0) and
     1007        (ClientMode < scContact));
     1008    end
     1009    else if c.Project and cpImp = 0 then
     1010    begin // project is unit
     1011      FrameImage(offscreen.Canvas, bigimp, xView + 9, yView + 5, xSizeBig,
     1012        ySizeBig, 0, 0, AllowChange and (ClientMode < scContact));
     1013      with Tribe[cOwner].ModelPicture[c.Project and cpIndex] do
     1014        Sprite(offscreen, HGr, xView + 5, yView + 1, 64, 44,
     1015          pix mod 10 * 65 + 1, pix div 10 * 49 + 1);
     1016    end
     1017    else
     1018    begin // project is building
     1019      if ProdHint then
     1020        Paintiix := c.Project0 and cpIndex
     1021      else
     1022        Paintiix := c.Project and cpIndex;
     1023      ImpImage(offscreen.Canvas, xView + 9, yView + 5, Paintiix, cGov,
     1024        AllowChange and (ClientMode < scContact));
     1025    end;
     1026  end;
     1027
     1028  if AllowChange and (ClientMode < scContact) then
     1029  begin
     1030    i := Server(sBuyCityProject - sExecute, me, cix, nil^);
     1031    BuyBtn.Visible := (i = eOk) or (i = eViolation);
     1032  end
    5931033  else
    594     begin
    595     CountBar(offscreen,xHapp,yHapp+dyBar,wBar,17,Phrases.Lookup('HAPPINESS'),
    596       Report.Morale,MainTexture);
    597     CountBar(offscreen,xHapp,yHapp+2*dyBar,wBar,16,Phrases.Lookup('CONTROL'),
    598       Report.CollectedControl,MainTexture);
    599     end;
    600   CountBar(offscreen,xHapp,yHapp,wBar,8,Phrases.Lookup('LUX'),
    601     Report.Luxury,MainTexture);
    602   CountBar(offscreen,xHapp+dxBar,yHapp,wBar,19,Phrases.Lookup('UNREST'),
    603     2*Report.Deployed,MainTexture);
    604   CountBar(offscreen,xHapp+dxBar,yHapp+dyBar,wBar,17,Phrases.Lookup('HAPPINESSDEMAND'),
    605     c.Size,MainTexture);
    606   if Report.HappinessBalance>=0 then
    607     CountBar(offscreen,xHapp+dxBar,yHapp+2*dyBar,wBar,17,Phrases.Lookup('HAPPINESSPLUS'),
    608       Report.HappinessBalance,MainTexture)
     1034    BuyBtn.Visible := false;
     1035
     1036  MarkUsedOffscreen(ClientWidth, ClientHeight);
     1037end; { OffscreenPaint }
     1038
     1039procedure TCityDlg.FormShow(Sender: TObject);
     1040var
     1041  dx, dy, Loc1: integer;
     1042  GetCityData: TGetCityData;
     1043begin
     1044  BlinkTime := 5;
     1045  if cix >= 0 then
     1046  begin { own city }
     1047    c := MyCity[cix];
     1048    cOwner := me;
     1049    cGov := MyRO.Government;
     1050    ProdHint := (cGov <> gAnarchy) and
     1051      (Happened and (chProduction or chFounded or chCaptured or
     1052      chAllImpsMade) <> 0);
     1053    Server(sGetCityAreaInfo, me, cix, CityAreaInfo);
     1054    NextCityBtn.Visible := WindowMode = wmPersistent;
     1055    PrevCityBtn.Visible := WindowMode = wmPersistent;
     1056  end
     1057  else { enemy city }
     1058  begin
     1059    Mode := mImp;
     1060    Server(sGetCity, me, cLoc, GetCityData);
     1061    c := GetCityData.c;
     1062    cOwner := GetCityData.Owner;
     1063    cGov := MyRO.EnemyReport[cOwner].Government;
     1064    Happened := c.Flags and $7FFFFFFF;
     1065    ProdHint := false;
     1066    Server(sGetEnemyCityAreaInfo, me, cLoc, CityAreaInfo);
     1067
     1068    if c.Project and cpImp = 0 then
     1069    begin
     1070      emix := MyRO.nEnemyModel - 1;
     1071      while (emix > 0) and ((MyRO.EnemyModel[emix].Owner <> cOwner) or
     1072        (integer(MyRO.EnemyModel[emix].mix) <> c.Project and cpIndex)) do
     1073        dec(emix);
     1074      if Tribe[cOwner].ModelPicture[c.Project and cpIndex].HGr = 0 then
     1075        InitEnemyModel(emix);
     1076    end;
     1077
     1078    NextCityBtn.Visible := false;
     1079    PrevCityBtn.Visible := false;
     1080  end;
     1081  Page := 0;
     1082
     1083  if c.Size < 5 then
     1084    SizeClass := 0
     1085  else if c.Size < 9 then
     1086    SizeClass := 1
     1087  else if c.Size < 13 then
     1088    SizeClass := 2
    6091089  else
    610     begin
    611     MakeRed(xHapp+dxBar-6,yHapp+2*dyBar,wBar+10,38);
    612     CountBar(offscreen,xHapp+dxBar,yHapp+2*dyBar,wBar,18,Phrases.Lookup('LACK'),
    613       -Report.HappinessBalance,RedTex);
    614     end;
    615   end;
    616 
    617 // food section
    618 if IsCityAlive then
    619   begin
    620   CountBar(offscreen,xFood,yFood+dyBar div 2,wBar,0,Phrases.Lookup('FOOD'),Report.CollectedFood,MainTexture);
    621   CountBar(offscreen,xFood+dxBar,yFood+dyBar,wBar,0,Phrases.Lookup('DEMAND'),2*c.Size,MainTexture);
    622   CountBar(offscreen,xFood+dxBar,yFood,wBar,0,Phrases.Lookup('SUPPORT'),Report.FoodSupport,MainTexture);
    623   if Report.FoodSurplus>=0 then
    624     if (cGov=gFuture)
    625       or (c.Size>=NeedAqueductSize) and (Report.FoodSurplus<2) then
    626       CountBar(offscreen,xFood+dxBar,yFood+2*dyBar,wBar,6,Phrases.Lookup('PROFIT'),
    627         Report.FoodSurplus,MainTexture)
    628     else CountBar(offscreen,xFood+dxBar,yFood+2*dyBar,wBar,0,Phrases.Lookup('SURPLUS'),
    629       Report.FoodSurplus,MainTexture)
     1090    SizeClass := 3;
     1091
     1092  // check if port
     1093  IsPort := false;
     1094  for dx := -2 to 2 do
     1095    for dy := -2 to 2 do
     1096      if abs(dx) + abs(dy) = 2 then
     1097      begin
     1098        Loc1 := dLoc(cLoc, dx, dy);
     1099        if (Loc1 >= 0) and (Loc1 < G.lx * G.ly) and
     1100          (MyMap[Loc1] and fTerrain < fGrass) then
     1101          IsPort := true;
     1102      end;
     1103
     1104  if WindowMode = wmModal then
     1105  begin { center on screen }
     1106    Left := (Screen.Width - Width) div 2;
     1107    Top := (Screen.Height - Height) div 2;
     1108  end;
     1109
     1110  Caption := CityName(c.ID);
     1111
     1112  InitSmallCityMap;
     1113  InitZoomCityMap;
     1114  OpenSoundEvent := -1;
     1115  OffscreenPaint;
     1116  Timer1.Enabled := true;
     1117end;
     1118
     1119procedure TCityDlg.ShowNewContent(NewMode, Loc: integer; ShowEvent: cardinal);
     1120begin
     1121  if MyMap[Loc] and fOwned <> 0 then
     1122  begin // own city
     1123    cix := MyRO.nCity - 1;
     1124    while (cix >= 0) and (MyCity[cix].Loc <> Loc) do
     1125      dec(cix);
     1126    assert(cix >= 0);
     1127    if (Optimize_cixTileChange >= 0) and
     1128      (Optimize_TilesBeforeChange and not MyCity[Optimize_cixTileChange].Tiles
     1129      <> 0) then
     1130    begin
     1131      CityOptimizer_ReleaseCityTiles(Optimize_cixTileChange,
     1132        Optimize_TilesBeforeChange and
     1133        not MyCity[Optimize_cixTileChange].Tiles);
     1134      if WindowMode <> wmModal then
     1135        MainScreen.UpdateViews;
     1136    end;
     1137    Optimize_cixTileChange := cix;
     1138    Optimize_TilesBeforeChange := MyCity[cix].Tiles;
     1139  end
    6301140  else
    631     begin
    632     MakeRed(xFood+dxBar-6,yFood+2*dyBar,wBar+10,38);
    633     CountBar(offscreen,xFood+dxBar,yFood+2*dyBar,wBar,1,Phrases.Lookup('LACK'),
    634       -Report.FoodSurplus,RedTex);
    635     end;
    636   end;
    637 CanGrow:= (c.Size<MaxCitySize) and (cGov<>gFuture)
    638   and (Report.FoodSurplus>0)
    639   and ((c.Size<NeedAqueductSize)
    640     or (c.Built[imAqueduct]=1) and (c.Size<NeedSewerSize)
    641     or (c.Built[imSewer]=1));
    642 FillBar(xFood+3,yFood+102,TrueFood,
    643   CutCityFoodSurplus(Report.FoodSurplus,IsCityAlive,cGov,c.size),
    644   Report.Storage,1,CanGrow);
    645 LoweredTextOut(offscreen.Canvas,-1,MainTexture,xFood+3-5,yFood+102-20,Format('%d/%d',[TrueFood,Report.Storage]));
    646 LoweredTextOut(offscreen.Canvas,-1,MainTexture,xFood-2,yFood+66,Phrases.Lookup('STORAGE'));
    647 
    648 // production section
    649 if IsCityAlive then
    650   begin
    651   CountBar(offscreen,xProd,yProd,wBar,2,Phrases.Lookup('MATERIAL'),
    652     Report.CollectedMaterial,MainTexture);
    653   CountBar(offscreen,xProd+dxBar,yProd,wBar,2,Phrases.Lookup('SUPPORT'),
    654     Report.MaterialSupport,MainTexture);
    655   if Report.Production>=0 then
    656     if c.Project and (cpImp+cpIndex)=cpImp+imTrGoods then
    657       CountBar(offscreen,xProd+dxBar,yProd+dyBar+16,wBar,6,Phrases.Lookup('PROFIT'),
    658         Report.Production,MainTexture)
    659     else CountBar(offscreen,xProd+dxBar,yProd+dyBar+16,wBar,2,Phrases.Lookup('PROD'),
    660       Report.Production,MainTexture)
    661   else
    662     begin
    663     MakeRed(xProd+dxBar-6,yProd+dyBar,wBar+10,38);
    664     CountBar(offscreen,xProd+dxBar,yProd+dyBar+16,wBar,3,Phrases.Lookup('LACK'),
    665       -Report.Production,RedTex);
    666     end;
    667   end;
    668 if c.Project and (cpImp+cpIndex)<>cpImp+imTrGoods then with offscreen.Canvas do
    669   begin
    670   i:=Report.Production;
    671   if (i<0) or not IsCityAlive then i:=0;
    672   FillBar(xProd+3,yProd+16+63,TrueProd,i,PrCost,4,true);
    673   LoweredTextOut(offscreen.Canvas,-1,MainTexture,xProd+3-5,yProd+16+43,
    674     Format('%d/%d',[TrueProd,PrCost]));
    675   if BiColorTextWidth(offscreen.Canvas,PrName)>wBar+dxBar then
    676     begin
    677     repeat Delete(PrName,Length(PrName),1)
    678     until BiColorTextWidth(offscreen.Canvas,PrName)<=wBar+dxBar;
    679     PrName:=PrName+'.'
    680     end;
    681   end;
    682 RisedTextOut(offscreen.Canvas,xProd-2,yProd+36,PrName);
    683 
    684 // pollution section
    685 if IsCityAlive and (Report.AddPollution>0) then
    686   begin
    687   FillBar(xPoll+3,yPoll+20,TruePoll,Report.AddPollution,
    688     MaxPollution,3,true);
    689   RisedTextOut(offscreen.Canvas,xPoll+3-5,yPoll+20-20,Phrases.Lookup('POLL'));
    690   end;
    691 
    692 // trade section
    693 if IsCityAlive and (Report.CollectedTrade>0) then
    694   begin
    695   CountBar(offscreen,xTrade,yTrade+dyBar div 2,wBar,4,Phrases.Lookup('TRADE'),Report.CollectedTrade,MainTexture);
    696   CountBar(offscreen,xTrade+dxBar,yTrade+2*dyBar,wBar,5,Phrases.Lookup('CORR'),Report.Corruption,MainTexture);
    697   CountBar(offscreen,xTrade+dxBar,yTrade,wBar,6,Phrases.Lookup('TAX'),Report.Tax,MainTexture);
    698   CountBar(offscreen,xTrade+dxBar,yTrade+dyBar,wBar,12,Phrases.Lookup('SCIENCE'),Report.Science,MainTexture);
    699   end;
    700 
    701 // small map
    702 BitBlt(Offscreen.Canvas.Handle,xSmallMap,ySmallmap,wSmallMap,hSmallMap,SmallCitymap.Canvas.Handle,0,0,SRCCOPY);
    703 if Mode=mImp then
    704   Frame(Offscreen.Canvas,xSmallMap+48*(ZoomArea div 3),ySmallmap+24*(ZoomArea mod 3),
    705     xSmallMap+48*(ZoomArea div 3)+49,ySmallmap+24*(ZoomArea mod 3)+25,
    706     MainTexture.clMark,MainTexture.clMark);
    707 Frame(Offscreen.Canvas,xSmallMap-1,ySmallmap-1,xSmallMap+wSmallMap,ySmallmap+hSmallMap,$B0B0B0,$FFFFFF);
    708 RFrame(Offscreen.Canvas,xSmallMap-2,ySmallmap-2,xSmallMap+wSmallMap+1,ySmallmap+hSmallMap+1,$FFFFFF,$B0B0B0);
    709 
    710 Frame(Offscreen.Canvas,xSupport-1,ySupport-1,xSupport+wSupport,ySupport+hSupport,$B0B0B0,$FFFFFF);
    711 RFrame(Offscreen.Canvas,xSupport-2,ySupport-2,xSupport+wSupport+1,ySupport+hSupport+1,$FFFFFF,$B0B0B0);
    712 x:=xSupport+wSupport div 2;
    713 y:=ySupport+hSupport div 2;
    714 if Mode=mSupp then
    715   begin
    716   Offscreen.Canvas.brush.Color:=MainTexture.clMark;
    717   Offscreen.Canvas.FillRect(Rect(x-27,y-6,x+27,y+6));
    718   Offscreen.Canvas.brush.style:=bsClear;
    719   end;
    720 Sprite(offscreen,HGrSystem,x-16,y-5,10,10,88,115);
    721 Sprite(offscreen,HGrSystem,x-5,y-5,10,10,66,115);
    722 Sprite(offscreen,HGrSystem,x+6,y-5,10,10,154,126);
    723 
    724 BitBlt(Offscreen.Canvas.Handle,xZoomMap,yZoommap,wZoomMap,hZoomMap,ZoomCitymap.Canvas.Handle,0,0,SRCCOPY);
    725 
    726 for i:=0 to 5 do imix[i]:=-1;
    727 if Mode=mImp then
    728   begin
    729   if ZoomArea=5 then
    730     begin
    731     Cnt:=0;
    732     for iix:=0 to nImp-1 do
    733       if ((iix<28) or (ImpPosition[iix]<0)) and (c.Built[iix]>0) then
     1141    cix := -1;
     1142  AllowChange := not supervising and (cix >= 0);
     1143  cLoc := Loc;
     1144  Happened := ShowEvent;
     1145  inherited ShowNewContent(NewMode);
     1146end;
     1147
     1148procedure TCityDlg.FormMouseDown(Sender: TObject; Button: TMouseButton;
     1149  Shift: TShiftState; x, y: integer);
     1150var
     1151  i, qx, qy, dx, dy, fix, NewTiles, Loc1, iix, SellResult: integer;
     1152  Rebuild: boolean;
     1153begin
     1154  if (ssLeft in Shift) and (x >= xSmallMap) and (x < xSmallMap + wSmallMap) and
     1155    (y >= ySmallMap) and (y < ySmallMap + hSmallMap) then
     1156  begin
     1157    Mode := mImp;
     1158    ZoomArea := (y - ySmallMap) * 3 div hSmallMap + 3 *
     1159      ((x - xSmallMap) * 2 div wSmallMap);
     1160    Page := 0;
     1161    InitZoomCityMap;
     1162    SmartUpdateContent;
     1163    exit;
     1164  end;
     1165  if (ssLeft in Shift) and (x >= xSupport) and (x < xSupport + wSupport) and
     1166    (y >= ySupport) and (y < ySupport + hSupport) then
     1167  begin
     1168    Mode := mSupp;
     1169    Page := 0;
     1170    InitZoomCityMap;
     1171    SmartUpdateContent;
     1172    exit;
     1173  end;
     1174  if not AllowChange then
     1175    exit; // not an own city
     1176
     1177  if (ssLeft in Shift) then
     1178    if (ClientMode < scContact) and (x >= xView) and (y >= yView) and
     1179      (x < xView + 73) and (y < yView + 50) then
     1180      if cGov = gAnarchy then
     1181        with MessgExDlg do
    7341182        begin
    735         i:=Cnt-Page*6;
    736         if (i>=0) and (i<6) then
    737           imix[i]:=iix;
    738         inc(Cnt);
    739         end;
    740     PageCount:=(Cnt+5) div 6;
    741     end
    742   else
    743     begin
    744     for iix:=28 to nImp-1 do
    745       begin
    746       i:=ImpPosition[iix]-6*ZoomArea;
    747       if (i>=0) and (i<6) and (c.Built[iix]>0) then
    748         imix[i]:=iix;
    749       end;
    750     PageCount:=0;
    751     end;
    752   for i:=0 to 5 do if imix[i]>=0 then
    753     begin
    754     iix:=imix[i];
    755     x:=xZoomMap+14+72*(i mod 3);
    756     y:=yZoomMap+14+56*(i div 3);
    757     ImpImage(offscreen.Canvas,x,y,iix,cGov,AllowChange and (ClientMode<scContact));
    758     if IsCityAlive then
    759       begin
    760       if iix=imColosseum then
    761         begin
    762         Sprite(offscreen,HGrSystem,x+46,y,14,14,82,100);
     1183          { MessgText:=Phrases.Lookup('OUTOFCONTROL');
     1184            if c.Project and cpImp=0 then
     1185            MessgText:=Format(MessgText,[Tribe[cOwner].ModelName[c.Project and cpIndex]])
     1186            else MessgText:=Format(MessgText,[Phrases.Lookup('IMPROVEMENTS',c.Project and cpIndex)]); }
     1187          MessgText := Phrases.Lookup('NOCHANGEINANARCHY');
     1188          Kind := mkOk;
     1189          ShowModal;
    7631190        end
    7641191      else
     1192      begin
     1193        if ProdHint then
    7651194        begin
    766         HappyGain:=0;
    767         case iix of
    768           0..27,imTemple: HappyGain:=2;
    769           imTheater: HappyGain:=4;
    770           imCathedral:
    771             if MyRO.Wonder[woBach].EffectiveOwner=cOwner then HappyGain:=6
    772             else HappyGain:=4;
    773           end;
    774         if HappyGain>1 then
    775           begin d:=30 div(HappyGain-1);if d>10 then d:=10 end;
    776         for j:=0 to HappyGain-1 do
    777           Sprite(offscreen,HGrSystem,x+50,y+d*j,10,10,132,126);
     1195          ProdHint := false;
     1196          SmartUpdateContent
    7781197        end;
    779       for j:=0 to Imp[iix].Maint-1 do
    780         Sprite(offscreen,HGrSystem,x-4,y+29-3*j,10,10,132,115);
     1198        ChooseProject;
    7811199      end
    782     end;
    783   if imix[0]>=0 then
    784     Imp0Area.Hint:=Phrases.Lookup('IMPROVEMENTS',imix[0])
    785   else Imp0Area.Hint:='';
    786   if imix[1]>=0 then
    787     Imp1Area.Hint:=Phrases.Lookup('IMPROVEMENTS',imix[1])
    788   else Imp1Area.Hint:='';
    789   if imix[2]>=0 then
    790     Imp2Area.Hint:=Phrases.Lookup('IMPROVEMENTS',imix[2])
    791   else Imp2Area.Hint:='';
    792   if imix[3]>=0 then
    793     Imp3Area.Hint:=Phrases.Lookup('IMPROVEMENTS',imix[3])
    794   else Imp3Area.Hint:='';
    795   if imix[4]>=0 then
    796     Imp4Area.Hint:=Phrases.Lookup('IMPROVEMENTS',imix[4])
    797   else Imp4Area.Hint:='';
    798   if imix[5]>=0 then
    799     Imp5Area.Hint:=Phrases.Lookup('IMPROVEMENTS',imix[5])
    800   else Imp5Area.Hint:='';
    801   end
    802 else {if mode=mSupp then}
    803   begin
    804   LoweredTextOut(offscreen.Canvas,-1,MainTexture,xZoomMap+6,yZoomMap+2,Phrases.Lookup('SUPUNITS'));
    805   FreeSupp:=c.Size*SupportFree[cGov] shr 1;
    806   Cnt:=0;
    807   for i:=0 to MyRO.nUn-1 do if (MyUn[i].Loc>=0) and (MyUn[i].Home=cix) then
    808     with MyModel[MyUn[i].mix] do
     1200    else if (Mode = mImp) and (x >= xZoomMap) and (x < xZoomMap + wZoomMap) and
     1201      (y >= yZoomMap) and (y < yZoomMap + hZoomMap) then
     1202    begin
     1203      i := 5;
     1204      while (i >= 0) and not((x >= xZoomMap + 14 + 72 * (i mod 3)) and
     1205        (x < xZoomMap + 14 + 56 + 72 * (i mod 3)) and
     1206        (y >= yZoomMap + 14 + 56 * (i div 3)) and
     1207        (y < yZoomMap + 14 + 40 + 56 * (i div 3))) do
     1208        dec(i);
     1209      if i >= 0 then
    8091210      begin
    810       Server(sGetUnitReport, me, i, UnitReport);
    811       if (Cnt>=6*Page) and (Cnt<6*(Page+1)) then
    812         begin // unit visible in display
    813         imix[Cnt-6*Page]:=i;
    814         x:=((Cnt-6*Page) mod 3)*64+xZoomMap;
    815         y:=((Cnt-6*Page) div 3)*52+yZoomMap+20;
    816         MakeUnitInfo(me,MyUn[i],UnitInfo);
    817         NoMap.SetOutput(offscreen);
    818         NoMap.PaintUnit(x,y,UnitInfo,MyUn[i].Status);
    819 
    820         for j:=0 to UnitReport.FoodSupport-1 do
    821           Sprite(offscreen,HGrSystem,x+38+11*j,y+40,10,10,66,115);
    822         for j:=0 to UnitReport.ProdSupport-1 do
    823           begin
    824           if (FreeSupp>0) and (UnitReport.ReportFlags and urfAlwaysSupport=0) then
     1211        iix := imix[i];
     1212        if iix >= 0 then
     1213          if ssShift in Shift then
     1214            HelpDlg.ShowNewContent(Mode or wmPersistent, hkImp, iix)
     1215          else if (ClientMode < scContact) then
     1216            with MessgExDlg do
    8251217            begin
    826             Sprite(offscreen,HGrSystem,x+16-11*j,y+40,10,10,143,115);
    827             dec(FreeSupp);
    828             end
    829           else Sprite(offscreen,HGrSystem,x+16-11*j,y+40,10,10,88,115);
    830           end;
    831         if UnitReport.ReportFlags and urfDeployed<>0 then
    832           for j:=0 to 1 do
    833           Sprite(offscreen,HGrSystem,x+27+11*j,y+40,10,10,154,126)
    834         end // unit visible in display
    835       else dec(FreeSupp, UnitReport.ProdSupport);
    836       inc(Cnt);
    837       end;
    838   PageCount:=(Cnt+5) div 6;
    839   Imp0Area.Hint:='';
    840   Imp1Area.Hint:='';
    841   Imp2Area.Hint:='';
    842   Imp3Area.Hint:='';
    843   Imp4Area.Hint:='';
    844   Imp5Area.Hint:='';
    845   end;
    846 PageUpBtn.Visible:= PageCount>1;
    847 PageDownBtn.Visible:= PageCount>1;
    848 
    849 with offscreen.Canvas do
    850   begin
    851   {display project now}
    852   DLine(offscreen.Canvas,xView+9+xSizeBig,xProd+2*wBar+10,yProd+dyBar+16,
    853     $FFFFFF,$B0B0B0);
    854   if prodhint then
    855     begin
    856     Frame(offscreen.canvas,xView+9-1,yView+5-1,xView+9+xSizeBig,yView+5+ySizeBig,$B0B0B0,$FFFFFF);
    857     RFrame(offscreen.canvas,xView+9-2,yView+5-2,xView+9+xSizeBig+1,yView+5+ySizeBig+1,$FFFFFF,$B0B0B0);
    858     with offscreen.canvas do
    859       begin
    860       Brush.Color:=$000000;
    861       FillRect(Rect(xView+9,yView+5,xView+1+72-8,yView+5+40));
    862       Brush.Style:=bsClear;
    863       end
    864     end
    865   else if AllowChange and (c.Status and 7<>0) then
    866     begin // city type autobuild
    867     FrameImage(offscreen.canvas,bigimp,xView+9,yView+5,xSizeBig,ySizeBig,
    868       (c.Status and 7-1+3)*xSizeBig,0,
    869       (cix>=0) and (ClientMode<scContact));
    870     end
    871   else if c.Project and cpImp=0 then
    872     begin // project is unit
    873     FrameImage(offscreen.canvas,bigimp,xView+9,yView+5,xSizeBig,ySizeBig,0,0,
    874       AllowChange and (ClientMode<scContact));
    875     with Tribe[cOwner].ModelPicture[c.Project and cpIndex] do
    876       Sprite(offscreen,HGr,xView+5,yView+1,64,44,
    877         pix mod 10 *65+1,pix div 10*49+1);
    878     end
    879   else
    880     begin // project is building
    881     if ProdHint then Paintiix:=c.Project0 and cpIndex
    882     else Paintiix:=c.Project and cpIndex;
    883     ImpImage(Offscreen.Canvas,xView+9,yView+5,Paintiix,cGov,
    884       AllowChange and (ClientMode<scContact));
    885     end;
    886   end;
    887 
    888 if AllowChange and (ClientMode<scContact) then
    889   begin
    890   i:=Server(sBuyCityProject-sExecute,me,cix,nil^);
    891   BuyBtn.Visible:= (i=eOk) or (i=eViolation);
    892   end
    893 else BuyBtn.Visible:=false;
    894 
    895 MarkUsedOffscreen(ClientWidth,ClientHeight);
    896 end;{OffscreenPaint}
    897 
    898 procedure TCityDlg.FormShow(Sender: TObject);
    899 var
    900 dx,dy,Loc1: integer;
    901 GetCityData: TGetCityData;
    902 begin
    903 BlinkTime:=5;
    904 if cix>=0 then
    905   begin {own city}
    906   c:=MyCity[cix];
    907   cOwner:=me;
    908   cGov:=MyRO.Government;
    909   ProdHint:= (cGov<>gAnarchy)
    910     and (Happened and (chProduction or chFounded or chCaptured or chAllImpsMade)<>0);
    911   Server(sGetCityAreaInfo,me,cix,CityAreaInfo);
    912   NextCityBtn.Visible:= WindowMode=wmPersistent;
    913   PrevCityBtn.Visible:= WindowMode=wmPersistent;
    914   end
    915 else {enemy city}
    916   begin
    917   Mode:=mImp;
    918   Server(sGetCity,me,cLoc,GetCityData);
    919   c:=GetCityData.c;
    920   cOwner:=GetCityData.Owner;
    921   cGov:=MyRO.EnemyReport[cOwner].Government;
    922   Happened:=c.Flags and $7FFFFFFF;
    923   ProdHint:=false;
    924   Server(sGetEnemyCityAreaInfo,me,cLoc,CityAreaInfo);
    925 
    926   if c.Project and cpImp=0 then
    927     begin
    928     emix:=MyRO.nEnemyModel-1;
    929     while (emix>0) and ((MyRO.EnemyModel[emix].Owner<>cOwner)
    930       or (integer(MyRO.EnemyModel[emix].mix)<>c.Project and cpIndex)) do dec(emix);
    931     if Tribe[cOwner].ModelPicture[c.Project and cpIndex].HGr=0 then
    932       InitEnemyModel(emix);
    933     end;
    934 
    935   NextCityBtn.Visible:=false;
    936   PrevCityBtn.Visible:=false;
    937   end;
    938 Page:=0;
    939 
    940 if c.Size<5 then SizeClass:=0
    941 else if c.Size<9 then SizeClass:=1
    942 else if c.Size<13 then SizeClass:=2
    943 else SizeClass:=3;
    944 
    945 // check if port
    946 IsPort:=false;
    947 for dx:=-2 to 2 do for dy:=-2 to 2 do if abs(dx)+abs(dy)=2 then
    948   begin
    949   Loc1:=dLoc(cLoc,dx,dy);
    950   if (Loc1>=0) and (Loc1<G.lx*G.ly) and (MyMap[Loc1] and fTerrain<fGrass) then
    951     IsPort:=true;
    952   end;
    953 
    954 if WindowMode=wmModal then
    955   begin {center on screen}
    956   Left:=(Screen.Width-Width) div 2;
    957   Top:=(Screen.Height-Height) div 2;
    958   end;
    959 
    960 Caption:=CityName(c.ID);
    961 
    962 InitSmallCityMap;
    963 InitZoomCityMap;
    964 OpenSoundEvent:=-1;
    965 OffscreenPaint;
    966 Timer1.Enabled:=true;
    967 end;
    968 
    969 procedure TCityDlg.ShowNewContent(NewMode,Loc: integer; ShowEvent: cardinal);
    970 begin
    971 if MyMap[Loc] and fOwned<>0 then
    972   begin // own city
    973   cix:=MyRO.nCity-1;
    974   while (cix>=0) and (MyCity[cix].Loc<>Loc) do dec(cix);
    975   assert(cix>=0);
    976   if (Optimize_cixTileChange>=0)
    977     and (Optimize_TilesBeforeChange
    978       and not MyCity[Optimize_cixTileChange].Tiles<>0) then
    979     begin
    980     CityOptimizer_ReleaseCityTiles(Optimize_cixTileChange,
    981       Optimize_TilesBeforeChange and not MyCity[Optimize_cixTileChange].Tiles);
    982     if WindowMode<>wmModal then
    983       MainScreen.UpdateViews;
    984     end;
    985   Optimize_cixTileChange:=cix;
    986   Optimize_TilesBeforeChange:=MyCity[cix].Tiles;
    987   end
    988 else cix:=-1;
    989 AllowChange:=not supervising and (cix>=0);
    990 cLoc:=Loc;
    991 Happened:=ShowEvent;
    992 inherited ShowNewContent(NewMode);
    993 end;
    994 
    995 procedure TCityDlg.FormMouseDown(Sender:TObject;
    996   Button:TMouseButton;Shift:TShiftState;x,y:integer);
    997 var
    998 i,qx,qy,dx,dy,fix,NewTiles,Loc1,iix,SellResult: integer;
    999 Rebuild: boolean;
    1000 begin
    1001 if (ssLeft in Shift) and (x>=xSmallMap) and (x<xSmallMap+wSmallMap)
    1002   and (y>=ySmallMap) and (y<ySmallMap+hSmallMap) then
    1003   begin
    1004   Mode:=mImp;
    1005   ZoomArea:=(y-ySmallMap)*3 div hSmallMap+3*((x-xSmallMap)*2 div wSmallMap);
    1006   Page:=0;
    1007   InitZoomCityMap;
    1008   SmartUpdateContent;
    1009   exit;
    1010   end;
    1011 if (ssLeft in Shift) and (x>=xSupport) and (x<xSupport+wSupport)
    1012   and (y>=ySupport) and (y<ySupport+hSupport) then
    1013   begin
    1014   Mode:=mSupp;
    1015   Page:=0;
    1016   InitZoomCityMap;
    1017   SmartUpdateContent;
    1018   exit;
    1019   end;
    1020 if not AllowChange then exit; // not an own city
    1021 
    1022 if (ssLeft in Shift) then
    1023   if (ClientMode<scContact)
    1024     and (x>=xView) and (y>=yView) and (x<xView+73) and (y<yView+50) then
    1025     if cGov=gAnarchy then with MessgExDlg do
    1026       begin
    1027 {      MessgText:=Phrases.Lookup('OUTOFCONTROL');
    1028       if c.Project and cpImp=0 then
    1029         MessgText:=Format(MessgText,[Tribe[cOwner].ModelName[c.Project and cpIndex]])
    1030       else MessgText:=Format(MessgText,[Phrases.Lookup('IMPROVEMENTS',c.Project and cpIndex)]);}
    1031       MessgText:=Phrases.Lookup('NOCHANGEINANARCHY');
    1032       Kind:=mkOk;
    1033       ShowModal;
    1034       end
    1035     else
    1036       begin
    1037       if ProdHint then
    1038         begin
    1039         ProdHint:=false;
    1040         SmartUpdateContent
    1041         end;
    1042       ChooseProject;
    1043       end
    1044   else if (Mode=mImp) and (x>=xZoomMap) and (x<xZoomMap+wZoomMap)
    1045     and (y>=yZoomMap) and (y<yZoomMap+hZoomMap) then
    1046     begin
    1047     i:=5;
    1048     while (i>=0) and
    1049       not ((x>=xZoomMap+14+72*(i mod 3))
    1050         and (x<xZoomMap+14+56+72*(i mod 3))
    1051         and (y>=yZoomMap+14+56*(i div 3))
    1052         and (y<yZoomMap+14+40+56*(i div 3))) do
    1053       dec(i);
    1054     if i>=0 then
    1055       begin
    1056       iix:=imix[i];
    1057       if iix>=0 then
    1058         if ssShift in Shift then
    1059           HelpDlg.ShowNewContent(Mode or wmPersistent, hkImp, iix)
    1060         else if (ClientMode<scContact) then with MessgExDlg do
    1061           begin
    1062           IconKind:=mikImp;
    1063           IconIndex:=iix;
    1064           if (iix=imPalace) or (Imp[iix].Kind=ikWonder) then
    1065             begin
    1066             MessgText:=Phrases.Lookup('IMPROVEMENTS',iix);
    1067             if iix=woOracle then
    1068               MessgText:=MessgText+'\'+Format(Phrases.Lookup('ORACLEINCOME'),
    1069                 [MyRO.OracleIncome]);
    1070             Kind:=mkOk;
    1071             ShowModal;
    1072             end
    1073           else
    1074             begin
    1075             SellResult:=Server(sSellCityImprovement-sExecute,me,cix,iix);
    1076             if SellResult<rExecuted then
     1218              IconKind := mikImp;
     1219              IconIndex := iix;
     1220              if (iix = imPalace) or (Imp[iix].Kind = ikWonder) then
    10771221              begin
    1078               if SellResult=eOnlyOnce then
    1079                 MessgText:=Phrases.Lookup('NOSELLAGAIN')
    1080               else MessgText:=Phrases.Lookup('OUTOFCONTROL');
    1081               MessgText:=Format(MessgText,[Phrases.Lookup('IMPROVEMENTS',iix)]);
    1082               Kind:=mkOk;
    1083               ShowModal;
     1222                MessgText := Phrases.Lookup('IMPROVEMENTS', iix);
     1223                if iix = woOracle then
     1224                  MessgText := MessgText + '\' +
     1225                    Format(Phrases.Lookup('ORACLEINCOME'), [MyRO.OracleIncome]);
     1226                Kind := mkOk;
     1227                ShowModal;
    10841228              end
    1085             else
     1229              else
    10861230              begin
    1087               if Server(sRebuildCityImprovement-sExecute,me,cix,iix)<rExecuted then
    1088                 begin // no rebuild possible, ask for sell only
    1089                 Rebuild:=false;
    1090                 MessgText:=Phrases.Lookup('IMPROVEMENTS',iix);
    1091                 if not Phrases2FallenBackToEnglish then
    1092                   MessgText:=Format(Phrases2.Lookup('SELL2'),[MessgText,
    1093                     Imp[iix].Cost*BuildCostMod[G.Difficulty[me]] div 12])
    1094                 else MessgText:=Format(Phrases.Lookup('SELL'),[MessgText]);
    1095                 if iix=imSpacePort then with MyRO.Ship[me] do
    1096                   if Parts[0]+Parts[1]+Parts[2]>0 then
    1097                     MessgText:=MessgText+' '+Phrases.Lookup('SPDESTRUCTQUERY');
    1098                 Kind:=mkYesNo;
    1099                 ShowModal;
    1100                 if ModalResult<>mrOK then iix:=-1
     1231                SellResult := Server(sSellCityImprovement - sExecute, me,
     1232                  cix, iix);
     1233                if SellResult < rExecuted then
     1234                begin
     1235                  if SellResult = eOnlyOnce then
     1236                    MessgText := Phrases.Lookup('NOSELLAGAIN')
     1237                  else
     1238                    MessgText := Phrases.Lookup('OUTOFCONTROL');
     1239                  MessgText := Format(MessgText,
     1240                    [Phrases.Lookup('IMPROVEMENTS', iix)]);
     1241                  Kind := mkOk;
     1242                  ShowModal;
    11011243                end
    1102               else
     1244                else
    11031245                begin
    1104                 Rebuild:=true;
    1105                 MessgText:=Phrases.Lookup('IMPROVEMENTS',iix);
    1106                 if not Phrases2FallenBackToEnglish then
    1107                   MessgText:=Format(Phrases2.Lookup('DISPOSE2'),[MessgText,
    1108                     Imp[iix].Cost*BuildCostMod[G.Difficulty[me]] div 12 *2 div 3])
    1109                 else MessgText:=Format(Phrases.Lookup('DISPOSE'),[MessgText]);
    1110                 if iix=imSpacePort then with MyRO.Ship[me] do
    1111                   if Parts[0]+Parts[1]+Parts[2]>0 then
    1112                     MessgText:=MessgText+' '+Phrases.Lookup('SPDESTRUCTQUERY');
    1113                 Kind:=mkYesNo;
    1114                 ShowModal;
    1115                 if ModalResult<>mrOK then iix:=-1
    1116                 end;
    1117               if iix>=0 then
    1118                 begin
    1119                 if Rebuild then
     1246                  if Server(sRebuildCityImprovement - sExecute, me, cix, iix) < rExecuted
     1247                  then
     1248                  begin // no rebuild possible, ask for sell only
     1249                    Rebuild := false;
     1250                    MessgText := Phrases.Lookup('IMPROVEMENTS', iix);
     1251                    if not Phrases2FallenBackToEnglish then
     1252                      MessgText := Format(Phrases2.Lookup('SELL2'),
     1253                        [MessgText, Imp[iix].Cost * BuildCostMod
     1254                        [G.Difficulty[me]] div 12])
     1255                    else
     1256                      MessgText := Format(Phrases.Lookup('SELL'), [MessgText]);
     1257                    if iix = imSpacePort then
     1258                      with MyRO.Ship[me] do
     1259                        if Parts[0] + Parts[1] + Parts[2] > 0 then
     1260                          MessgText := MessgText + ' ' +
     1261                            Phrases.Lookup('SPDESTRUCTQUERY');
     1262                    Kind := mkYesNo;
     1263                    ShowModal;
     1264                    if ModalResult <> mrOK then
     1265                      iix := -1
     1266                  end
     1267                  else
    11201268                  begin
    1121                   Play('CITY_REBUILDIMP');
    1122                   Server(sRebuildCityImprovement,me,cix,iix);
     1269                    Rebuild := true;
     1270                    MessgText := Phrases.Lookup('IMPROVEMENTS', iix);
     1271                    if not Phrases2FallenBackToEnglish then
     1272                      MessgText := Format(Phrases2.Lookup('DISPOSE2'),
     1273                        [MessgText, Imp[iix].Cost * BuildCostMod
     1274                        [G.Difficulty[me]] div 12 * 2 div 3])
     1275                    else
     1276                      MessgText := Format(Phrases.Lookup('DISPOSE'),
     1277                        [MessgText]);
     1278                    if iix = imSpacePort then
     1279                      with MyRO.Ship[me] do
     1280                        if Parts[0] + Parts[1] + Parts[2] > 0 then
     1281                          MessgText := MessgText + ' ' +
     1282                            Phrases.Lookup('SPDESTRUCTQUERY');
     1283                    Kind := mkYesNo;
     1284                    ShowModal;
     1285                    if ModalResult <> mrOK then
     1286                      iix := -1
     1287                  end;
     1288                  if iix >= 0 then
     1289                  begin
     1290                    if Rebuild then
     1291                    begin
     1292                      Play('CITY_REBUILDIMP');
     1293                      Server(sRebuildCityImprovement, me, cix, iix);
     1294                    end
     1295                    else
     1296                    begin
     1297                      Play('CITY_SELLIMP');
     1298                      Server(sSellCityImprovement, me, cix, iix);
     1299                    end;
     1300                    CityOptimizer_CityChange(cix);
     1301                    InitSmallCityMap;
     1302                    SmartUpdateContent;
     1303                    if WindowMode <> wmModal then
     1304                      MainScreen.UpdateViews;
    11231305                  end
    1124                 else
    1125                   begin
    1126                   Play('CITY_SELLIMP');
    1127                   Server(sSellCityImprovement,me,cix,iix);
    1128                   end;
    1129                 CityOptimizer_CityChange(cix);
    1130                 InitSmallCityMap;
    1131                 SmartUpdateContent;
    1132                 if WindowMode<>wmModal then
    1133                   MainScreen.UpdateViews;
    11341306                end
    11351307              end
    11361308            end
    1137           end
    11381309      end
    11391310    end
    1140   else if (Mode=mSupp) and (x>=xZoomMap) and (x<xZoomMap+wZoomMap)
    1141     and (y>=yZoomMap) and (y<yZoomMap+hZoomMap) then
    1142     begin
    1143     i:=5;
    1144     while (i>=0) and
    1145       not ((x>=xZoomMap+64*(i mod 3))
    1146         and (x<xZoomMap+64+64*(i mod 3))
    1147         and (y>=yZoomMap+20+48*(i div 3))
    1148         and (y<yZoomMap+20+52+48*(i div 3))) do
    1149       dec(i);
    1150     if (i>=0) and (imix[i]>=0) then
    1151       if ssShift in Shift then
    1152       else if (cix>=0) and (ClientMode<scContact) and (WindowMode<>wmModal) then
     1311    else if (Mode = mSupp) and (x >= xZoomMap) and (x < xZoomMap + wZoomMap) and
     1312      (y >= yZoomMap) and (y < yZoomMap + hZoomMap) then
     1313    begin
     1314      i := 5;
     1315      while (i >= 0) and not((x >= xZoomMap + 64 * (i mod 3)) and
     1316        (x < xZoomMap + 64 + 64 * (i mod 3)) and
     1317        (y >= yZoomMap + 20 + 48 * (i div 3)) and
     1318        (y < yZoomMap + 20 + 52 + 48 * (i div 3))) do
     1319        dec(i);
     1320      if (i >= 0) and (imix[i] >= 0) then
     1321        if ssShift in Shift then
     1322        else if (cix >= 0) and (ClientMode < scContact) and
     1323          (WindowMode <> wmModal) then
    11531324        begin
    1154         CloseAction:=None;
    1155         Close;
    1156         MainScreen.CityClosed(imix[i],false,true);
     1325          CloseAction := None;
     1326          Close;
     1327          MainScreen.CityClosed(imix[i], false, true);
    11571328        end
    11581329    end
    1159   else if (x>=xmArea-192) and (x<xmArea+192) and (y>=ymArea-96) and (y<ymArea+96) then
    1160     begin
    1161     qx:=((4000*xxt*yyt)+(x-xmArea)*(yyt*2)+(y-ymArea+yyt)*(xxt*2)) div (xxt*yyt*4)-1000;
    1162     qy:=((4000*xxt*yyt)+(y-ymArea+yyt)*(xxt*2)-(x-xmArea)*(yyt*2)) div (xxt*yyt*4)-1000;
    1163     dx:=qx-qy;
    1164     dy:=qx+qy;
    1165     if (dx>=-3) and (dx<=3) and (dy>=-3) and (dy<=3) and (dx*dx*dy*dy<81)
    1166       and ((dx<>0) or (dy<>0)) then
    1167       if ssShift in Shift then
     1330    else if (x >= xmArea - 192) and (x < xmArea + 192) and (y >= ymArea - 96)
     1331      and (y < ymArea + 96) then
     1332    begin
     1333      qx := ((4000 * xxt * yyt) + (x - xmArea) * (yyt * 2) + (y - ymArea + yyt)
     1334        * (xxt * 2)) div (xxt * yyt * 4) - 1000;
     1335      qy := ((4000 * xxt * yyt) + (y - ymArea + yyt) * (xxt * 2) - (x - xmArea)
     1336        * (yyt * 2)) div (xxt * yyt * 4) - 1000;
     1337      dx := qx - qy;
     1338      dy := qx + qy;
     1339      if (dx >= -3) and (dx <= 3) and (dy >= -3) and (dy <= 3) and
     1340        (dx * dx * dy * dy < 81) and ((dx <> 0) or (dy <> 0)) then
     1341        if ssShift in Shift then
    11681342        begin // terrain help
    1169         Loc1:=dLoc(cLoc,dx,dy);
    1170         if (Loc1>=0) and (Loc1<G.lx*G.ly) then
    1171           HelpOnTerrain(Loc1, Mode or wmPersistent)
     1343          Loc1 := dLoc(cLoc, dx, dy);
     1344          if (Loc1 >= 0) and (Loc1 < G.lx * G.ly) then
     1345            HelpOnTerrain(Loc1, Mode or wmPersistent)
    11721346        end
    1173       else if (ClientMode<scContact) and (cGov<>gAnarchy)
    1174         and (c.Flags and chCaptured=0) then
     1347        else if (ClientMode < scContact) and (cGov <> gAnarchy) and
     1348          (c.Flags and chCaptured = 0) then
    11751349        begin // toggle exploitation
    1176         assert(not supervising);
    1177         if c.Status and csResourceWeightsMask<>0 then
     1350          assert(not supervising);
     1351          if c.Status and csResourceWeightsMask <> 0 then
    11781352          begin
    1179           with MessgExDlg do
     1353            with MessgExDlg do
    11801354            begin
    1181             MessgText:=Phrases.Lookup('CITYMANAGEOFF');
    1182             OpenSound:='MSG_DEFAULT';
    1183             Kind:=mkOkCancel;
    1184             IconKind:=mikFullControl;
    1185             ShowModal;
     1355              MessgText := Phrases.Lookup('CITYMANAGEOFF');
     1356              OpenSound := 'MSG_DEFAULT';
     1357              Kind := mkOkCancel;
     1358              IconKind := mikFullControl;
     1359              ShowModal;
    11861360            end;
    1187           if MessgExDlg.ModalResult=mrOK then
     1361            if MessgExDlg.ModalResult = mrOK then
    11881362            begin
    1189             MyCity[cix].Status:=MyCity[cix].Status
    1190               and not csResourceWeightsMask; // off
    1191             c.Status:=MyCity[cix].Status;
    1192             SmartUpdateContent
     1363              MyCity[cix].Status := MyCity[cix].Status and
     1364                not csResourceWeightsMask; // off
     1365              c.Status := MyCity[cix].Status;
     1366              SmartUpdateContent
    11931367            end;
    1194           exit;
     1368            exit;
    11951369          end;
    1196         fix:=(dy+3) shl 2+(dx+3) shr 1;
    1197         NewTiles:=MyCity[cix].Tiles xor (1 shl fix);
    1198         if Server(sSetCityTiles,me,cix,NewTiles)>=rExecuted then
     1370          fix := (dy + 3) shl 2 + (dx + 3) shr 1;
     1371          NewTiles := MyCity[cix].Tiles xor (1 shl fix);
     1372          if Server(sSetCityTiles, me, cix, NewTiles) >= rExecuted then
    11991373          begin
    1200           SmartUpdateContent;
    1201           if WindowMode<>wmModal then
    1202             MainScreen.UpdateViews;
     1374            SmartUpdateContent;
     1375            if WindowMode <> wmModal then
     1376              MainScreen.UpdateViews;
    12031377          end
    12041378        end
    12051379    end
    1206   else if (ClientMode<scContact) and (cGov<>gAnarchy) and (c.Flags and chCaptured=0)
    1207     and (x>=xmOpt-32) and (x<xmOpt+32) and (y>=ymOpt-32) and (y<ymOpt+32) then
    1208     begin
    1209     i:=sqr(x-xmOpt)+sqr(y-ymOpt); // click radius
    1210     if i<=32*32 then
     1380    else if (ClientMode < scContact) and (cGov <> gAnarchy) and
     1381      (c.Flags and chCaptured = 0) and (x >= xmOpt - 32) and (x < xmOpt + 32)
     1382      and (y >= ymOpt - 32) and (y < ymOpt + 32) then
     1383    begin
     1384      i := sqr(x - xmOpt) + sqr(y - ymOpt); // click radius
     1385      if i <= 32 * 32 then
    12111386      begin
    1212       if i<16*16 then // inner area clicked
    1213         if c.Status and csResourceWeightsMask<>0 then
    1214           i:=(c.Status shr 4 and $0F) mod 5 +1 // rotate except off
    1215         else i:=3 // rwGrowth
    1216       else case trunc(arctan2(x-xmOpt,ymOpt-y)*180/pi) of
    1217         -25-52*2..-26-52: i:=1;
    1218         -25-52..-26: i:=2;
    1219         -25..25: i:=3;
    1220         26..25+52: i:=4;
    1221         26+52..25+52*2: i:=5;
    1222         180-26..180,-180..-180+26: i:=0;
    1223         else i:=-1;
    1224         end;
    1225       if i>=0 then
     1387        if i < 16 * 16 then // inner area clicked
     1388          if c.Status and csResourceWeightsMask <> 0 then
     1389            i := (c.Status shr 4 and $0F) mod 5 + 1 // rotate except off
     1390          else
     1391            i := 3 // rwGrowth
     1392        else
     1393          case trunc(arctan2(x - xmOpt, ymOpt - y) * 180 / pi) of
     1394            - 25 - 52 * 2 .. -26 - 52:
     1395              i := 1;
     1396            -25 - 52 .. -26:
     1397              i := 2;
     1398            -25 .. 25:
     1399              i := 3;
     1400            26 .. 25 + 52:
     1401              i := 4;
     1402            26 + 52 .. 25 + 52 * 2:
     1403              i := 5;
     1404            180 - 26 .. 180, -180 .. -180 + 26:
     1405              i := 0;
     1406          else
     1407            i := -1;
     1408          end;
     1409        if i >= 0 then
    12261410        begin
    1227         ChangeResourceWeights(i);
    1228         SmartUpdateContent;
    1229         if WindowMode<>wmModal then
    1230           MainScreen.UpdateViews;
     1411          ChangeResourceWeights(i);
     1412          SmartUpdateContent;
     1413          if WindowMode <> wmModal then
     1414            MainScreen.UpdateViews;
    12311415        end
    12321416      end
    12331417    end;
    1234 end;{FormMouseDown}
     1418end; { FormMouseDown }
    12351419
    12361420procedure TCityDlg.ChooseProject;
    12371421const
    1238 ptSelect=0; ptTrGoods=1; ptUn=2; ptCaravan=3; ptImp=4; ptWonder=6;
    1239 ptShip=7; ptInvalid=8;
     1422  ptSelect = 0;
     1423  ptTrGoods = 1;
     1424  ptUn = 2;
     1425  ptCaravan = 3;
     1426  ptImp = 4;
     1427  ptWonder = 6;
     1428  ptShip = 7;
     1429  ptInvalid = 8;
    12401430
    12411431  function ProjectType(Project: integer): integer;
    12421432  begin
    1243   if Project and cpCompleted<>0 then result:=ptSelect
    1244   else if Project and (cpImp+cpIndex)=cpImp+imTrGoods then result:=ptTrGoods
    1245   else if Project and cpImp=0 then
    1246     if MyModel[Project and cpIndex].Kind=mkCaravan then result:=ptCaravan
    1247     else result:=ptUn
    1248   else if Project and cpIndex>=nImp then result:=ptInvalid
    1249   else if Imp[Project and cpIndex].Kind=ikWonder then result:=ptWonder
    1250   else if Imp[Project and cpIndex].Kind=ikShipPart then result:=ptShip
    1251   else result:=ptImp
     1433    if Project and cpCompleted <> 0 then
     1434      result := ptSelect
     1435    else if Project and (cpImp + cpIndex) = cpImp + imTrGoods then
     1436      result := ptTrGoods
     1437    else if Project and cpImp = 0 then
     1438      if MyModel[Project and cpIndex].Kind = mkCaravan then
     1439        result := ptCaravan
     1440      else
     1441        result := ptUn
     1442    else if Project and cpIndex >= nImp then
     1443      result := ptInvalid
     1444    else if Imp[Project and cpIndex].Kind = ikWonder then
     1445      result := ptWonder
     1446    else if Imp[Project and cpIndex].Kind = ikShipPart then
     1447      result := ptShip
     1448    else
     1449      result := ptImp
    12521450  end;
    12531451
    12541452var
    1255 NewProject, OldMoney,pt0,pt1,cix1: integer;
    1256 QueryOk: boolean;
    1257 begin
    1258 assert(not supervising);
    1259 ModalSelectDlg.ShowNewContent_CityProject(wmModal,cix);
    1260 if ModalSelectDlg.result<>-1 then
    1261   begin
    1262   if ModalSelectDlg.result and cpType<>0 then
    1263     begin
    1264     MyCity[cix].Status:=MyCity[cix].Status and not 7
    1265       or (1+ModalSelectDlg.result and cpIndex);
    1266     AutoBuild(cix, MyData.ImpOrder[ModalSelectDlg.result and cpIndex]);
     1453  NewProject, OldMoney, pt0, pt1, cix1: integer;
     1454  QueryOk: boolean;
     1455begin
     1456  assert(not supervising);
     1457  ModalSelectDlg.ShowNewContent_CityProject(wmModal, cix);
     1458  if ModalSelectDlg.result <> -1 then
     1459  begin
     1460    if ModalSelectDlg.result and cpType <> 0 then
     1461    begin
     1462      MyCity[cix].Status := MyCity[cix].Status and not 7 or
     1463        (1 + ModalSelectDlg.result and cpIndex);
     1464      AutoBuild(cix, MyData.ImpOrder[ModalSelectDlg.result and cpIndex]);
    12671465    end
    1268   else
    1269     begin
    1270     NewProject:=ModalSelectDlg.result;
    1271     QueryOk:=true;
    1272     if (NewProject and cpImp<>0) and (NewProject and cpIndex>=28)
    1273       and (MyRO.NatBuilt[NewProject and cpIndex]>0) then
    1274       with MessgExDlg do
     1466    else
     1467    begin
     1468      NewProject := ModalSelectDlg.result;
     1469      QueryOk := true;
     1470      if (NewProject and cpImp <> 0) and (NewProject and cpIndex >= 28) and
     1471        (MyRO.NatBuilt[NewProject and cpIndex] > 0) then
     1472        with MessgExDlg do
    12751473        begin
    1276         cix1:=MyRO.nCity-1;
    1277         while (cix1>=0) and (MyCity[cix1].Built[NewProject and cpIndex]=0) do
    1278           dec(cix1);
    1279         MessgText:=Format(Phrases.Lookup('DOUBLESTATEIMP'),
    1280           [Phrases.Lookup('IMPROVEMENTS', NewProject and cpIndex),
    1281           CityName(MyCity[cix1].ID)]);
    1282         OpenSound:='MSG_DEFAULT';
    1283         Kind:=mkOkCancel;
    1284         IconKind:=mikImp;
    1285         IconIndex:=NewProject and cpIndex;
    1286         ShowModal;
    1287         QueryOk:= ModalResult=mrOK;
     1474          cix1 := MyRO.nCity - 1;
     1475          while (cix1 >= 0) and
     1476            (MyCity[cix1].Built[NewProject and cpIndex] = 0) do
     1477            dec(cix1);
     1478          MessgText := Format(Phrases.Lookup('DOUBLESTATEIMP'),
     1479            [Phrases.Lookup('IMPROVEMENTS', NewProject and cpIndex),
     1480            CityName(MyCity[cix1].ID)]);
     1481          OpenSound := 'MSG_DEFAULT';
     1482          Kind := mkOkCancel;
     1483          IconKind := mikImp;
     1484          IconIndex := NewProject and cpIndex;
     1485          ShowModal;
     1486          QueryOk := ModalResult = mrOK;
    12881487        end;
    1289     if not QueryOk then
    1290       exit;
    1291 
    1292     if (MyCity[cix].Prod>0) then
     1488      if not QueryOk then
     1489        exit;
     1490
     1491      if (MyCity[cix].Prod > 0) then
    12931492      begin
    1294       pt0:=ProjectType(MyCity[cix].Project0);
    1295       pt1:=ProjectType(NewProject);
    1296       if (pt0<>ptSelect) and (pt1<>ptTrGoods) then
     1493        pt0 := ProjectType(MyCity[cix].Project0);
     1494        pt1 := ProjectType(NewProject);
     1495        if (pt0 <> ptSelect) and (pt1 <> ptTrGoods) then
    12971496        begin
    1298         if NewProject and (cpImp or cpIndex)<>MyCity[cix].Project0 and (cpImp or cpIndex) then
     1497          if NewProject and (cpImp or cpIndex) <> MyCity[cix].Project0 and
     1498            (cpImp or cpIndex) then
    12991499          begin // loss of material -- do query
    1300           if (pt1=ptTrGoods) or (pt1=ptShip) or (pt1<>pt0) and (pt0<>ptCaravan) then
    1301             QueryOk:=SimpleQuery(mkOkCancel,Format(Phrases.Lookup('LOSEMAT'),
    1302               [MyCity[cix].Prod0,MyCity[cix].Prod0]),'MSG_DEFAULT')=mrOK
    1303           else if MyCity[cix].Project and (cpImp or cpIndex)=MyCity[cix].Project0 and (cpImp or cpIndex) then
    1304             QueryOk:=SimpleQuery(mkOkCancel,Phrases.Lookup('LOSEMAT3'),'MSG_DEFAULT')=mrOK
     1500            if (pt1 = ptTrGoods) or (pt1 = ptShip) or (pt1 <> pt0) and
     1501              (pt0 <> ptCaravan) then
     1502              QueryOk := SimpleQuery(mkOkCancel,
     1503                Format(Phrases.Lookup('LOSEMAT'), [MyCity[cix].Prod0,
     1504                MyCity[cix].Prod0]), 'MSG_DEFAULT') = mrOK
     1505            else if MyCity[cix].Project and (cpImp or cpIndex) = MyCity[cix]
     1506              .Project0 and (cpImp or cpIndex) then
     1507              QueryOk := SimpleQuery(mkOkCancel, Phrases.Lookup('LOSEMAT3'),
     1508                'MSG_DEFAULT') = mrOK
    13051509          end;
    13061510        end
    13071511      end;
    1308     if not QueryOk then
    1309       exit;
    1310 
    1311     OldMoney:=MyRO.Money;
    1312     MyCity[cix].Status:=MyCity[cix].Status and not 7;
    1313     if (NewProject and cpImp=0)
    1314       and ((MyCity[cix].Size<4) and (MyModel[NewProject and cpIndex].Kind=mkSettler)
    1315         or (MyCity[cix].Size<3) and ((MyModel[NewProject and cpIndex].Kind=mkSlaves)
    1316           or (NewProject and cpConscripts<>0))) then
    1317       if SimpleQuery(mkYesNo,Phrases.Lookup('EMIGRATE'),'MSG_DEFAULT')<>mrOK then
    1318         NewProject:=NewProject or cpDisbandCity;
    1319     Server(sSetCityProject,me,cix,NewProject);
    1320     c.Project:=MyCity[cix].Project;
    1321     if MyRO.Money>OldMoney then
    1322       Play('CITY_SELLIMP');
    1323     end;
    1324   CityOptimizer_CityChange(cix);
    1325 
    1326   if WindowMode<>wmModal then
    1327     MainScreen.UpdateViews;
    1328   InitSmallCityMap;
    1329   SmartUpdateContent;
    1330   end;
    1331 end;
    1332 
    1333 procedure TCityDlg.BuyClick(Sender:TObject);
     1512      if not QueryOk then
     1513        exit;
     1514
     1515      OldMoney := MyRO.Money;
     1516      MyCity[cix].Status := MyCity[cix].Status and not 7;
     1517      if (NewProject and cpImp = 0) and
     1518        ((MyCity[cix].Size < 4) and
     1519        (MyModel[NewProject and cpIndex].Kind = mkSettler) or
     1520        (MyCity[cix].Size < 3) and
     1521        ((MyModel[NewProject and cpIndex].Kind = mkSlaves) or
     1522        (NewProject and cpConscripts <> 0))) then
     1523        if SimpleQuery(mkYesNo, Phrases.Lookup('EMIGRATE'), 'MSG_DEFAULT') <> mrOK
     1524        then
     1525          NewProject := NewProject or cpDisbandCity;
     1526      Server(sSetCityProject, me, cix, NewProject);
     1527      c.Project := MyCity[cix].Project;
     1528      if MyRO.Money > OldMoney then
     1529        Play('CITY_SELLIMP');
     1530    end;
     1531    CityOptimizer_CityChange(cix);
     1532
     1533    if WindowMode <> wmModal then
     1534      MainScreen.UpdateViews;
     1535    InitSmallCityMap;
     1536    SmartUpdateContent;
     1537  end;
     1538end;
     1539
     1540procedure TCityDlg.BuyClick(Sender: TObject);
    13341541var
    1335 NextProd,Cost:integer;
    1336 begin
    1337 if (cix<0) or (ClientMode>=scContact) then exit;
    1338 with MyCity[cix],MessgExDlg do
    1339   begin
    1340   Cost:=Report.ProjectCost;
    1341   NextProd:=Report.Production;
    1342   if NextProd<0 then NextProd:=0;
    1343   Cost:=Cost-Prod-NextProd;
    1344   if (MyRO.Wonder[woMich].EffectiveOwner=me) and (Project and cpImp<>0) then
    1345     Cost:=Cost*2
    1346   else Cost:=Cost*4;
    1347   if (Cost<=0) and (Report.HappinessBalance>=0) {no disorder} then
    1348     begin MessgText:=Phrases.Lookup('READY'); Kind:=mkOK; end
    1349   else if Cost>MyRO.Money then
    1350     begin
    1351     OpenSound:='MSG_DEFAULT';
    1352     MessgText:=Format(Phrases.Lookup('NOMONEY'),[Cost,MyRO.Money]);
    1353     Kind:=mkOK;
     1542  NextProd, Cost: integer;
     1543begin
     1544  if (cix < 0) or (ClientMode >= scContact) then
     1545    exit;
     1546  with MyCity[cix], MessgExDlg do
     1547  begin
     1548    Cost := Report.ProjectCost;
     1549    NextProd := Report.Production;
     1550    if NextProd < 0 then
     1551      NextProd := 0;
     1552    Cost := Cost - Prod - NextProd;
     1553    if (MyRO.Wonder[woMich].EffectiveOwner = me) and (Project and cpImp <> 0)
     1554    then
     1555      Cost := Cost * 2
     1556    else
     1557      Cost := Cost * 4;
     1558    if (Cost <= 0) and (Report.HappinessBalance >= 0) { no disorder } then
     1559    begin
     1560      MessgText := Phrases.Lookup('READY');
     1561      Kind := mkOk;
    13541562    end
    1355   else begin MessgText:=Format(Phrases.Lookup('BUY'),[Cost]); Kind:=mkYesNo; end;
    1356   ShowModal;
    1357   if (Kind=mkYesNo) and (ModalResult=mrOK) then
    1358     begin
    1359     if Server(sBuyCityProject,me,cix,nil^)>=rExecuted then
     1563    else if Cost > MyRO.Money then
     1564    begin
     1565      OpenSound := 'MSG_DEFAULT';
     1566      MessgText := Format(Phrases.Lookup('NOMONEY'), [Cost, MyRO.Money]);
     1567      Kind := mkOk;
     1568    end
     1569    else
     1570    begin
     1571      MessgText := Format(Phrases.Lookup('BUY'), [Cost]);
     1572      Kind := mkYesNo;
     1573    end;
     1574    ShowModal;
     1575    if (Kind = mkYesNo) and (ModalResult = mrOK) then
     1576    begin
     1577      if Server(sBuyCityProject, me, cix, nil^) >= rExecuted then
    13601578      begin
    1361       Play('CITY_BUYPROJECT');
    1362       SmartUpdateContent;
    1363       if WindowMode<>wmModal then
    1364         MainScreen.UpdateViews;
     1579        Play('CITY_BUYPROJECT');
     1580        SmartUpdateContent;
     1581        if WindowMode <> wmModal then
     1582          MainScreen.UpdateViews;
    13651583      end
    13661584    end
     
    13701588procedure TCityDlg.FormClose(Sender: TObject; var Action: TCloseAction);
    13711589begin
    1372 Timer1.Enabled:=false;
    1373 ProdHint:=false;
    1374 MarkCityLoc:=-1;
    1375 if Optimize_cixTileChange>=0 then
    1376   begin
    1377   if Optimize_TilesBeforeChange
    1378     and not MyCity[Optimize_cixTileChange].Tiles<>0 then
    1379     begin
    1380     CityOptimizer_ReleaseCityTiles(Optimize_cixTileChange,
    1381       Optimize_TilesBeforeChange and not MyCity[Optimize_cixTileChange].Tiles);
    1382     if WindowMode<>wmModal then
    1383       MainScreen.UpdateViews;
    1384     end;
    1385   Optimize_cixTileChange:=-1;
    1386   end;
    1387 if CloseAction>None then
    1388   MainScreen.CityClosed(RestoreUnFocus,CloseAction=StepFocus);
    1389 RestoreUnFocus:=-1;
    1390 inherited;
     1590  Timer1.Enabled := false;
     1591  ProdHint := false;
     1592  MarkCityLoc := -1;
     1593  if Optimize_cixTileChange >= 0 then
     1594  begin
     1595    if Optimize_TilesBeforeChange and not MyCity[Optimize_cixTileChange]
     1596      .Tiles <> 0 then
     1597    begin
     1598      CityOptimizer_ReleaseCityTiles(Optimize_cixTileChange,
     1599        Optimize_TilesBeforeChange and
     1600        not MyCity[Optimize_cixTileChange].Tiles);
     1601      if WindowMode <> wmModal then
     1602        MainScreen.UpdateViews;
     1603    end;
     1604    Optimize_cixTileChange := -1;
     1605  end;
     1606  if CloseAction > None then
     1607    MainScreen.CityClosed(RestoreUnFocus, CloseAction = StepFocus);
     1608  RestoreUnFocus := -1;
     1609  inherited;
    13911610end;
    13921611
    13931612procedure TCityDlg.Timer1Timer(Sender: TObject);
    13941613begin
    1395 if ProdHint then
    1396   begin
    1397   BlinkTime:=(BlinkTime+1) mod 12;
    1398   if BlinkTime=0 then with Canvas do
    1399     begin
    1400     BitBlt(canvas.Handle,xView+5,yView+1,64,2,
    1401       back.Canvas.Handle,xView+5,yView+1,SRCCOPY);
    1402     BitBlt(canvas.Handle,xView+5,yView+3,2,42,
    1403       back.Canvas.Handle,xView+5,yView+3,SRCCOPY);
    1404     BitBlt(canvas.Handle,xView+5+62,yView+3,2,42,
    1405       back.Canvas.Handle,xView+5+62,yView+3,SRCCOPY);
    1406     Frame(canvas,xView+9-1,yView+5-1,xView+9+xSizeBig,yView+5+ySizeBig,$B0B0B0,$FFFFFF);
    1407     RFrame(canvas,xView+9-2,yView+5-2,xView+9+xSizeBig+1,yView+5+ySizeBig+1,$FFFFFF,$B0B0B0);
    1408     Brush.Color:=$000000;
    1409     FillRect(Rect(xView+9,yView+5,xView+1+72-8,yView+5+40));
    1410     Brush.Style:=bsClear;
    1411     end
    1412   else if BlinkTime=6 then
    1413     begin
    1414     if AllowChange and (c.Status and 7<>0) then
     1614  if ProdHint then
     1615  begin
     1616    BlinkTime := (BlinkTime + 1) mod 12;
     1617    if BlinkTime = 0 then
     1618      with Canvas do
     1619      begin
     1620        bitblt(Canvas.Handle, xView + 5, yView + 1, 64, 2, Back.Canvas.Handle,
     1621          xView + 5, yView + 1, SRCCOPY);
     1622        bitblt(Canvas.Handle, xView + 5, yView + 3, 2, 42, Back.Canvas.Handle,
     1623          xView + 5, yView + 3, SRCCOPY);
     1624        bitblt(Canvas.Handle, xView + 5 + 62, yView + 3, 2, 42,
     1625          Back.Canvas.Handle, xView + 5 + 62, yView + 3, SRCCOPY);
     1626        Frame(Canvas, xView + 9 - 1, yView + 5 - 1, xView + 9 + xSizeBig,
     1627          yView + 5 + ySizeBig, $B0B0B0, $FFFFFF);
     1628        RFrame(Canvas, xView + 9 - 2, yView + 5 - 2, xView + 9 + xSizeBig + 1,
     1629          yView + 5 + ySizeBig + 1, $FFFFFF, $B0B0B0);
     1630        brush.Color := $000000;
     1631        FillRect(Rect(xView + 9, yView + 5, xView + 1 + 72 - 8,
     1632          yView + 5 + 40));
     1633        brush.style := bsClear;
     1634      end
     1635    else if BlinkTime = 6 then
     1636    begin
     1637      if AllowChange and (c.Status and 7 <> 0) then
    14151638      begin // city type autobuild
    1416       FrameImage(canvas,bigimp,xView+9,yView+5,xSizeBig,ySizeBig,
    1417         (c.Status and 7-1+3)*xSizeBig,0,true);
     1639        FrameImage(Canvas, bigimp, xView + 9, yView + 5, xSizeBig, ySizeBig,
     1640          (c.Status and 7 - 1 + 3) * xSizeBig, 0, true);
    14181641      end
    1419     else if c.Project and cpImp=0 then
     1642      else if c.Project and cpImp = 0 then
    14201643      begin // project is unit
    1421       BitBlt(canvas.Handle,xView+9,yView+5,xSizeBig,ySizeBig,
    1422         bigimp.Canvas.Handle,0,0,SRCCOPY);
    1423       with Tribe[cOwner].ModelPicture[c.Project and cpIndex] do
    1424         Sprite(canvas,HGr,xView+5,yView+1,64,44,
    1425           pix mod 10 *65+1,pix div 10*49+1);
     1644        bitblt(Canvas.Handle, xView + 9, yView + 5, xSizeBig, ySizeBig,
     1645          bigimp.Canvas.Handle, 0, 0, SRCCOPY);
     1646        with Tribe[cOwner].ModelPicture[c.Project and cpIndex] do
     1647          Sprite(Canvas, HGr, xView + 5, yView + 1, 64, 44, pix mod 10 * 65 + 1,
     1648            pix div 10 * 49 + 1);
    14261649      end
    1427     else ImpImage(Canvas,xView+9,yView+5,
    1428       c.Project0 and cpIndex,cGov,true);
     1650      else
     1651        ImpImage(Canvas, xView + 9, yView + 5, c.Project0 and cpIndex,
     1652          cGov, true);
    14291653    end
    14301654  end
     
    14331657procedure TCityDlg.FormPaint(Sender: TObject);
    14341658begin
    1435 inherited;
    1436 if OpenSoundEvent>=0 then PostMessage(Handle, WM_PLAYSOUND, 0, 0);
    1437 end;
    1438 
    1439 procedure TCityDlg.OnPlaySound(var Msg:TMessage);
    1440 begin
    1441 if 1 shl OpenSoundEvent=chProduction then
    1442   begin
    1443   if c.Project0 and cpImp<>0 then
    1444     begin
    1445     if c.Project0 and cpIndex>=28 then // wonders have already extra message with sound
    1446       if Imp[c.Project0 and cpIndex].Kind=ikShipPart then Play('SHIP_BUILT')
    1447       else Play('CITY_IMPCOMPLETE')
     1659  inherited;
     1660  if OpenSoundEvent >= 0 then
     1661    PostMessage(Handle, WM_PLAYSOUND, 0, 0);
     1662end;
     1663
     1664procedure TCityDlg.OnPlaySound(var Msg: TMessage);
     1665begin
     1666  if 1 shl OpenSoundEvent = chProduction then
     1667  begin
     1668    if c.Project0 and cpImp <> 0 then
     1669    begin
     1670      if c.Project0 and cpIndex >= 28 then
     1671      // wonders have already extra message with sound
     1672        if Imp[c.Project0 and cpIndex].Kind = ikShipPart then
     1673          Play('SHIP_BUILT')
     1674        else
     1675          Play('CITY_IMPCOMPLETE')
    14481676    end
    1449   else Play('CITY_UNITCOMPLETE');
     1677    else
     1678      Play('CITY_UNITCOMPLETE');
    14501679  end
    1451 else Play(CityEventSoundItem[OpenSoundEvent]);
    1452 OpenSoundEvent:=-2;
     1680  else
     1681    Play(CityEventSoundItem[OpenSoundEvent]);
     1682  OpenSoundEvent := -2;
    14531683end;
    14541684
    14551685function Prio(iix: integer): integer;
    14561686begin
    1457 case Imp[iix].Kind of
    1458   ikWonder: result:=iix+10000;
    1459   ikNatLocal, ikNatGlobal:
     1687  case Imp[iix].Kind of
     1688    ikWonder:
     1689      result := iix + 10000;
     1690    ikNatLocal, ikNatGlobal:
     1691      case iix of
     1692        imPalace:
     1693          result := 0;
     1694      else
     1695        result := iix + 20000;
     1696      end;
     1697  else
    14601698    case iix of
    1461       imPalace: result:=0;
    1462       else result:=iix+20000;
    1463       end;
    1464   else case iix of
    1465     imTownHall, imCourt: result:=iix+30000;
    1466     imAqueduct, imSewer: result:=iix+40000;
    1467     imTemple, imTheater, imCathedral: result:=iix+50000;
    1468     else result:=iix+90000;
     1699      imTownHall, imCourt:
     1700        result := iix + 30000;
     1701      imAqueduct, imSewer:
     1702        result := iix + 40000;
     1703      imTemple, imTheater, imCathedral:
     1704        result := iix + 50000;
     1705    else
     1706      result := iix + 90000;
    14691707    end;
    14701708  end;
     
    14731711procedure TCityDlg.NextCityBtnClick(Sender: TObject);
    14741712begin
    1475 ChangeCity(+1);
     1713  ChangeCity(+1);
    14761714end;
    14771715
    14781716procedure TCityDlg.PrevCityBtnClick(Sender: TObject);
    14791717begin
    1480 ChangeCity(-1);
     1718  ChangeCity(-1);
    14811719end;
    14821720
    14831721procedure TCityDlg.ChangeCity(d: integer);
    14841722var
    1485 cixNew: integer;
    1486 begin
    1487 cixNew:=cix;
    1488 repeat
    1489   cixNew:=(cixNew+MyRO.nCity+d) mod MyRO.nCity;
    1490 until (MyCity[cixNew].Loc>=0) or (cixNew=cix);
    1491 if cixNew<>cix then
    1492   MainScreen.ZoomToCity(MyCity[cixNew].Loc);
     1723  cixNew: integer;
     1724begin
     1725  cixNew := cix;
     1726  repeat
     1727    cixNew := (cixNew + MyRO.nCity + d) mod MyRO.nCity;
     1728  until (MyCity[cixNew].Loc >= 0) or (cixNew = cix);
     1729  if cixNew <> cix then
     1730    MainScreen.ZoomToCity(MyCity[cixNew].Loc);
    14931731end;
    14941732
     
    14961734  Shift: TShiftState);
    14971735begin
    1498 if ((Key=VK_UP) or (Key=VK_NUMPAD8))
    1499   and (cix>=0) and (WindowMode=wmPersistent) then
    1500   ChangeCity(-1)
    1501 else if ((Key=VK_DOWN) or (Key=VK_NUMPAD2))
    1502   and (cix>=0) and (WindowMode=wmPersistent) then
    1503   ChangeCity(+1)
    1504 else inherited 
    1505 end;
    1506 
    1507 {procedure TCityDlg.AdviceBtnClick(Sender: TObject);
    1508 begin
    1509 AdvisorDlg.GiveCityAdvice(cix);
    1510 end;}
     1736  if ((Key = VK_UP) or (Key = VK_NUMPAD8)) and (cix >= 0) and
     1737    (WindowMode = wmPersistent) then
     1738    ChangeCity(-1)
     1739  else if ((Key = VK_DOWN) or (Key = VK_NUMPAD2)) and (cix >= 0) and
     1740    (WindowMode = wmPersistent) then
     1741    ChangeCity(+1)
     1742  else
     1743    inherited
     1744end;
     1745
     1746{ procedure TCityDlg.AdviceBtnClick(Sender: TObject);
     1747  begin
     1748  AdvisorDlg.GiveCityAdvice(cix);
     1749  end; }
    15111750
    15121751var
    1513 i,j,k: integer;
     1752  i, j, k: integer;
    15141753
    15151754procedure TCityDlg.PageUpBtnClick(Sender: TObject);
    15161755begin
    1517 if Page>0 then
    1518   begin
    1519   dec(Page);
    1520   SmartUpdateContent
     1756  if Page > 0 then
     1757  begin
     1758    dec(Page);
     1759    SmartUpdateContent
    15211760  end
    15221761end;
     
    15241763procedure TCityDlg.PageDownBtnClick(Sender: TObject);
    15251764begin
    1526 if Page<PageCount-1 then
    1527   begin
    1528   inc(Page);
    1529   SmartUpdateContent
     1765  if Page < PageCount - 1 then
     1766  begin
     1767    inc(Page);
     1768    SmartUpdateContent
    15301769  end
    15311770end;
     
    15331772procedure TCityDlg.ChangeResourceWeights(iResourceWeights: integer);
    15341773var
    1535 Advice: TCityTileAdviceData;
    1536 begin
    1537 assert(not supervising);
    1538 assert(cix>=0);
    1539 MyCity[cix].Status:=MyCity[cix].Status
    1540   and not csResourceWeightsMask or (iResourceWeights shl 4);
    1541 c.Status:=MyCity[cix].Status;
    1542 if iResourceWeights>0 then
    1543   begin
    1544   Advice.ResourceWeights:=OfferedResourceWeights[iResourceWeights];
    1545   Server(sGetCityTileAdvice,me,cix,Advice);
    1546   if Advice.Tiles<>MyCity[cix].Tiles then
    1547     Server(sSetCityTiles,me,cix,Advice.Tiles);
    1548   end 
    1549 end;
    1550 
     1774  Advice: TCityTileAdviceData;
     1775begin
     1776  assert(not supervising);
     1777  assert(cix >= 0);
     1778  MyCity[cix].Status := MyCity[cix].Status and not csResourceWeightsMask or
     1779    (iResourceWeights shl 4);
     1780  c.Status := MyCity[cix].Status;
     1781  if iResourceWeights > 0 then
     1782  begin
     1783    Advice.ResourceWeights := OfferedResourceWeights[iResourceWeights];
     1784    Server(sGetCityTileAdvice, me, cix, Advice);
     1785    if Advice.Tiles <> MyCity[cix].Tiles then
     1786      Server(sSetCityTiles, me, cix, Advice.Tiles);
     1787  end
     1788end;
    15511789
    15521790initialization
    1553 for i:=0 to nImp-1 do ImpSorted[i]:=i;
    1554 for i:=0 to nImp-2 do for j:=i+1 to nImp-1 do
    1555   if Prio(ImpSorted[i])>Prio(ImpSorted[j]) then
    1556     begin k:=ImpSorted[i]; ImpSorted[i]:=ImpSorted[j]; ImpSorted[j]:=k end;
     1791
     1792for i := 0 to nImp - 1 do
     1793  ImpSorted[i] := i;
     1794for i := 0 to nImp - 2 do
     1795  for j := i + 1 to nImp - 1 do
     1796    if Prio(ImpSorted[i]) > Prio(ImpSorted[j]) then
     1797    begin
     1798      k := ImpSorted[i];
     1799      ImpSorted[i] := ImpSorted[j];
     1800      ImpSorted[j] := k
     1801    end;
     1802
    15571803end.
    1558 
  • trunk/LocalPlayer/CityType.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit CityType;
    43
     
    65
    76uses
    8   Protocol,ClientTools,Term,ScreenTools,BaseWin,
     7  Protocol, ClientTools, Term, ScreenTools, BaseWin,
    98
    109  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
     
    2625    procedure DeleteBtnClick(Sender: TObject);
    2726  public
    28     procedure ShowNewContent(NewMode: integer); 
     27    procedure ShowNewContent(NewMode: integer);
    2928  protected
    3029    procedure OffscreenPaint; override;
    3130  private
    32     nPool,dragiix,ctype: integer;
    33     Pooliix: array[0..nImp-1] of integer;
    34     listed: Set of 0..nImp;
     31    nPool, dragiix, ctype: integer;
     32    Pooliix: array [0 .. nImp - 1] of integer;
     33    listed: Set of 0 .. nImp;
    3534    Changed: boolean;
    3635    procedure LoadType(NewType: integer);
     
    4847
    4948const
    50 xList=7; yList=0;
    51 nListRow=4; nListCol=10;
    52 xPool=7; yPool=220;
    53 nPoolRow=4; nPoolCol=10;
    54 xSwitch=7; ySwitch=150;
    55 xView=226; yView=130;
    56 
    57 procedure TCityTypeDlg.FormCreate(Sender:TObject);
    58 begin
    59 inherited;
    60 CaptionRight:=CloseBtn.Left;
    61 InitButtons();
    62 HelpContext:='MACRO';
    63 Caption:=Phrases.Lookup('TITLE_CITYTYPES');
    64 DeleteBtn.Hint:=Phrases.Lookup('BTN_DELETE');
    65 end;
    66 
    67 procedure TCityTypeDlg.CloseBtnClick(Sender:TObject);
    68 begin
    69 Close
    70 end;
    71 
    72 procedure TCityTypeDlg.FormPaint(Sender:TObject);
    73 begin
    74 inherited;
    75 BtnFrame(Canvas,DeleteBtn.BoundsRect,MainTexture);
     49  xList = 7;
     50  yList = 0;
     51  nListRow = 4;
     52  nListCol = 10;
     53  xPool = 7;
     54  yPool = 220;
     55  nPoolRow = 4;
     56  nPoolCol = 10;
     57  xSwitch = 7;
     58  ySwitch = 150;
     59  xView = 226;
     60  yView = 130;
     61
     62procedure TCityTypeDlg.FormCreate(Sender: TObject);
     63begin
     64  inherited;
     65  CaptionRight := CloseBtn.Left;
     66  InitButtons();
     67  HelpContext := 'MACRO';
     68  Caption := Phrases.Lookup('TITLE_CITYTYPES');
     69  DeleteBtn.Hint := Phrases.Lookup('BTN_DELETE');
     70end;
     71
     72procedure TCityTypeDlg.CloseBtnClick(Sender: TObject);
     73begin
     74  Close
     75end;
     76
     77procedure TCityTypeDlg.FormPaint(Sender: TObject);
     78begin
     79  inherited;
     80  BtnFrame(Canvas, DeleteBtn.BoundsRect, MainTexture);
    7681end;
    7782
    7883procedure TCityTypeDlg.OffscreenPaint;
    7984var
    80 i,iix: integer;
    81 s: string;
    82 begin
    83 inherited;
    84 offscreen.Canvas.Font.Assign(UniFont[ftSmall]);
    85 FillOffscreen(xList-7,yList,42*nListCol+14,32*nListRow);
    86 FillOffscreen(xPool-7,yPool,42*nPoolCol+14,32*nPoolRow);
    87 FillOffscreen(0,yList+32*nListRow,42*nPoolCol+14,yPool-yList-32*nListRow);
    88 
    89 Frame(offscreen.Canvas,0,yList+32*nListRow,InnerWidth-255,yPool-23,
    90   MainTexture.clBevelLight,MainTexture.clBevelShade);
    91 Frame(offscreen.Canvas,InnerWidth-254,yList+32*nListRow,InnerWidth-89,yPool-23,
    92   MainTexture.clBevelLight,MainTexture.clBevelShade);
    93 Frame(offscreen.Canvas,InnerWidth-88,yList+32*nListRow,InnerWidth-1,yPool-23,
    94   MainTexture.clBevelLight,MainTexture.clBevelShade);
    95 Frame(offscreen.Canvas,0,yPool-22,InnerWidth-1,yPool-1,
    96   MainTexture.clBevelLight,MainTexture.clBevelShade);
    97 for i:=0 to nCityType-1 do
    98   begin
    99   RFrame(offscreen.Canvas,xSwitch+i*42,ySwitch,xSwitch+39+i*42,ySwitch+23,
    100     MainTexture.clBevelShade,MainTexture.clBevelLight);
    101   if i=ctype then
    102     Frame(offscreen.Canvas,xSwitch+1+i*42,ySwitch+1,xSwitch+38+i*42,ySwitch+22,
    103       MainTexture.clBevelShade,MainTexture.clBevelLight)
    104   else Frame(offscreen.Canvas,xSwitch+1+i*42,ySwitch+1,xSwitch+38+i*42,ySwitch+22,
    105     MainTexture.clBevelLight,MainTexture.clBevelShade);
    106   BitBlt(offscreen.Canvas.Handle,xSwitch+2+i*42,ySwitch+2,xSizeSmall,
    107     ySizeSmall,SmallImp.Canvas.Handle,(i+3)*xSizeSmall,0,SRCCOPY)
    108   end;
    109 RisedTextOut(offscreen.Canvas,8,yList+32*nListRow+2,Phrases.Lookup('BUILDORDER'));
    110 RisedTextOut(offscreen.Canvas,8,ySwitch+26,Phrases.Lookup('CITYTYPE',ctype));
    111 s:=Phrases.Lookup('BUILDREST');
    112 RisedTextOut(offscreen.Canvas,(InnerWidth-BiColorTextWidth(Offscreen.Canvas,s)) div 2,
    113   yList+72+32*nListRow,s);
    114 
    115 with offscreen.Canvas do
    116   begin
    117   for i:=1 to nListRow-1 do
    118     DLine(offscreen.Canvas,xList-5,xList+4+42*nListCol,yList-1+32*i,
    119       MainTexture.clBevelLight,MainTexture.clBevelShade);
    120   for i:=0 to nListCol*nListRow-1 do
    121     begin
    122     s:=IntToStr(i+1);
    123     Font.Color:=MainTexture.clTextLight;
    124     Textout(xList+20+i mod nListCol *42-TextWidth(s) div 2,
    125       yList+15+i div nListCol *32-TextHeight(s) div 2,s);
     85  i, iix: integer;
     86  s: string;
     87begin
     88  inherited;
     89  offscreen.Canvas.Font.Assign(UniFont[ftSmall]);
     90  FillOffscreen(xList - 7, yList, 42 * nListCol + 14, 32 * nListRow);
     91  FillOffscreen(xPool - 7, yPool, 42 * nPoolCol + 14, 32 * nPoolRow);
     92  FillOffscreen(0, yList + 32 * nListRow, 42 * nPoolCol + 14,
     93    yPool - yList - 32 * nListRow);
     94
     95  Frame(offscreen.Canvas, 0, yList + 32 * nListRow, InnerWidth - 255,
     96    yPool - 23, MainTexture.clBevelLight, MainTexture.clBevelShade);
     97  Frame(offscreen.Canvas, InnerWidth - 254, yList + 32 * nListRow,
     98    InnerWidth - 89, yPool - 23, MainTexture.clBevelLight,
     99    MainTexture.clBevelShade);
     100  Frame(offscreen.Canvas, InnerWidth - 88, yList + 32 * nListRow,
     101    InnerWidth - 1, yPool - 23, MainTexture.clBevelLight,
     102    MainTexture.clBevelShade);
     103  Frame(offscreen.Canvas, 0, yPool - 22, InnerWidth - 1, yPool - 1,
     104    MainTexture.clBevelLight, MainTexture.clBevelShade);
     105  for i := 0 to nCityType - 1 do
     106  begin
     107    RFrame(offscreen.Canvas, xSwitch + i * 42, ySwitch, xSwitch + 39 + i * 42,
     108      ySwitch + 23, MainTexture.clBevelShade, MainTexture.clBevelLight);
     109    if i = ctype then
     110      Frame(offscreen.Canvas, xSwitch + 1 + i * 42, ySwitch + 1,
     111        xSwitch + 38 + i * 42, ySwitch + 22, MainTexture.clBevelShade,
     112        MainTexture.clBevelLight)
     113    else
     114      Frame(offscreen.Canvas, xSwitch + 1 + i * 42, ySwitch + 1,
     115        xSwitch + 38 + i * 42, ySwitch + 22, MainTexture.clBevelLight,
     116        MainTexture.clBevelShade);
     117    BitBlt(offscreen.Canvas.Handle, xSwitch + 2 + i * 42, ySwitch + 2,
     118      xSizeSmall, ySizeSmall, SmallImp.Canvas.Handle, (i + 3) * xSizeSmall,
     119      0, SRCCOPY)
     120  end;
     121  RisedTextOut(offscreen.Canvas, 8, yList + 32 * nListRow + 2,
     122    Phrases.Lookup('BUILDORDER'));
     123  RisedTextOut(offscreen.Canvas, 8, ySwitch + 26,
     124    Phrases.Lookup('CITYTYPE', ctype));
     125  s := Phrases.Lookup('BUILDREST');
     126  RisedTextOut(offscreen.Canvas,
     127    (InnerWidth - BiColorTextWidth(offscreen.Canvas, s)) div 2,
     128    yList + 72 + 32 * nListRow, s);
     129
     130  with offscreen.Canvas do
     131  begin
     132    for i := 1 to nListRow - 1 do
     133      DLine(offscreen.Canvas, xList - 5, xList + 4 + 42 * nListCol,
     134        yList - 1 + 32 * i, MainTexture.clBevelLight, MainTexture.clBevelShade);
     135    for i := 0 to nListCol * nListRow - 1 do
     136    begin
     137      s := IntToStr(i + 1);
     138      Font.Color := MainTexture.clTextLight;
     139      Textout(xList + 20 + i mod nListCol * 42 - TextWidth(s) div 2,
     140        yList + 15 + i div nListCol * 32 - TextHeight(s) div 2, s);
    126141    end
    127142  end;
    128143
    129 i:=0;
    130 while MyData.ImpOrder[ctype,i]>=0 do
    131   begin
    132   RFrame(offscreen.Canvas,
    133     xList+20-xSizeSmall div 2 + i mod nListCol *42,
    134     yList+15-ySizeSmall div 2 + i div nListCol *32,
    135     xList+21+xSizeSmall div 2 + i mod nListCol *42,
    136     yList+16+ySizeSmall div 2 + i div nListCol *32,
    137     MainTexture.clBevelLight,MainTexture.clBevelShade);
    138   BitBlt(offscreen.Canvas.Handle,
    139     xList+21-xSizeSmall div 2 + i mod nListCol *42,
    140     yList+16-ySizeSmall div 2 + i div nListCol *32,
    141     xSizeSmall,ySizeSmall,SmallImp.Canvas.Handle,
    142     MyData.ImpOrder[ctype,i] mod 7*xSizeSmall,
    143     (MyData.ImpOrder[ctype,i]+SystemIconLines*7) div 7*ySizeSmall,SRCCOPY);
    144   inc(i);
    145   end;
    146 
    147 nPool:=0;
    148 for iix:=28 to nImp-1 do
    149   if not (iix in listed) and (Imp[iix].Kind=ikCommon) and (iix<>imTrGoods)
    150     and (Imp[iix].Preq<>preNA)
    151     and ((Imp[iix].Preq=preNone) or (MyRO.Tech[Imp[iix].Preq]>=tsApplicable)) then
    152     begin
    153     Pooliix[nPool]:=iix;
    154     RFrame(offscreen.Canvas,
    155       xPool+20-xSizeSmall div 2 + nPool mod nPoolCol *42,
    156       yPool+15-ySizeSmall div 2 + nPool div nPoolCol *32,
    157       xPool+21+xSizeSmall div 2 + nPool mod nPoolCol *42,
    158       yPool+16+ySizeSmall div 2 + nPool div nPoolCol *32,
     144  i := 0;
     145  while MyData.ImpOrder[ctype, i] >= 0 do
     146  begin
     147    RFrame(offscreen.Canvas, xList + 20 - xSizeSmall div 2 + i mod nListCol *
     148      42, yList + 15 - ySizeSmall div 2 + i div nListCol * 32,
     149      xList + 21 + xSizeSmall div 2 + i mod nListCol * 42,
     150      yList + 16 + ySizeSmall div 2 + i div nListCol * 32,
    159151      MainTexture.clBevelLight, MainTexture.clBevelShade);
    160     BitBlt(offscreen.Canvas.Handle,
    161       xPool+21-xSizeSmall div 2 + nPool mod nPoolCol *42,
    162       yPool+16-ySizeSmall div 2 + nPool div nPoolCol *32,
    163       xSizeSmall,ySizeSmall,SmallImp.Canvas.Handle,
    164       iix mod 7*xSizeSmall,(iix+SystemIconLines*7) div 7*ySizeSmall,SRCCOPY);
    165     inc(nPool)
     152    BitBlt(offscreen.Canvas.Handle, xList + 21 - xSizeSmall div 2 +
     153      i mod nListCol * 42, yList + 16 - ySizeSmall div 2 + i div nListCol * 32,
     154      xSizeSmall, ySizeSmall, SmallImp.Canvas.Handle,
     155      MyData.ImpOrder[ctype, i] mod 7 * xSizeSmall,
     156      (MyData.ImpOrder[ctype, i] + SystemIconLines * 7) div 7 *
     157      ySizeSmall, SRCCOPY);
     158    inc(i);
     159  end;
     160
     161  nPool := 0;
     162  for iix := 28 to nImp - 1 do
     163    if not(iix in listed) and (Imp[iix].Kind = ikCommon) and (iix <> imTrGoods)
     164      and (Imp[iix].Preq <> preNA) and
     165      ((Imp[iix].Preq = preNone) or (MyRO.Tech[Imp[iix].Preq] >= tsApplicable))
     166    then
     167    begin
     168      Pooliix[nPool] := iix;
     169      RFrame(offscreen.Canvas, xPool + 20 - xSizeSmall div 2 +
     170        nPool mod nPoolCol * 42, yPool + 15 - ySizeSmall div 2 +
     171        nPool div nPoolCol * 32, xPool + 21 + xSizeSmall div 2 +
     172        nPool mod nPoolCol * 42, yPool + 16 + ySizeSmall div 2 +
     173        nPool div nPoolCol * 32, MainTexture.clBevelLight,
     174        MainTexture.clBevelShade);
     175      BitBlt(offscreen.Canvas.Handle, xPool + 21 - xSizeSmall div 2 +
     176        nPool mod nPoolCol * 42, yPool + 16 - ySizeSmall div 2 +
     177        nPool div nPoolCol * 32, xSizeSmall, ySizeSmall, SmallImp.Canvas.Handle,
     178        iix mod 7 * xSizeSmall, (iix + SystemIconLines * 7) div 7 *
     179        ySizeSmall, SRCCOPY);
     180      inc(nPool)
    166181    end;
    167 DeleteBtn.Visible:= MyData.ImpOrder[ctype,0]>=0;
    168 
    169 if dragiix>=0 then
    170   begin
    171   ImpImage(offscreen.Canvas,xView+9,yView+5,dragiix);
    172   s:=Phrases.Lookup('IMPROVEMENTS',dragiix);
    173   RisedTextOut(offscreen.Canvas,xView+36-BiColorTextWidth(Offscreen.Canvas,s) div 2,
    174     ySwitch+26,s);
    175   end;
    176 MarkUsedOffscreen(InnerWidth,InnerHeight);
    177 end; {MainPaint}
     182  DeleteBtn.Visible := MyData.ImpOrder[ctype, 0] >= 0;
     183
     184  if dragiix >= 0 then
     185  begin
     186    ImpImage(offscreen.Canvas, xView + 9, yView + 5, dragiix);
     187    s := Phrases.Lookup('IMPROVEMENTS', dragiix);
     188    RisedTextOut(offscreen.Canvas,
     189      xView + 36 - BiColorTextWidth(offscreen.Canvas, s) div 2,
     190      ySwitch + 26, s);
     191  end;
     192  MarkUsedOffscreen(InnerWidth, InnerHeight);
     193end; { MainPaint }
    178194
    179195procedure TCityTypeDlg.LoadType(NewType: integer);
    180196var
    181 i: integer;
    182 begin
    183 ctype:=NewType;
    184 listed:=[];
    185 i:=0;
    186 while MyData.ImpOrder[ctype,i]>=0 do
    187   begin include(listed,MyData.ImpOrder[ctype,i]); inc(i) end;
    188 Changed:=false
     197  i: integer;
     198begin
     199  ctype := NewType;
     200  listed := [];
     201  i := 0;
     202  while MyData.ImpOrder[ctype, i] >= 0 do
     203  begin
     204    include(listed, MyData.ImpOrder[ctype, i]);
     205    inc(i)
     206  end;
     207  Changed := false
    189208end;
    190209
    191210procedure TCityTypeDlg.SaveType;
    192211var
    193 cix: integer;
    194 begin
    195 if Changed then
    196   begin
    197   for cix:=0 to MyRO.nCity-1 do
    198     if (MyCity[cix].Loc>=0) and (MyCity[cix].Status and 7=ctype+1) then
    199       AutoBuild(cix, MyData.ImpOrder[ctype]);
    200   Changed:=false
     212  cix: integer;
     213begin
     214  if Changed then
     215  begin
     216    for cix := 0 to MyRO.nCity - 1 do
     217      if (MyCity[cix].Loc >= 0) and (MyCity[cix].Status and 7 = ctype + 1) then
     218        AutoBuild(cix, MyData.ImpOrder[ctype]);
     219    Changed := false
    201220  end;
    202221end;
     
    204223procedure TCityTypeDlg.FormShow(Sender: TObject);
    205224begin
    206 LoadType(0);
    207 dragiix:=-1;
    208 OffscreenPaint;
     225  LoadType(0);
     226  dragiix := -1;
     227  OffscreenPaint;
    209228end;
    210229
    211230procedure TCityTypeDlg.ShowNewContent(NewMode: integer);
    212231begin
    213 inherited ShowNewContent(NewMode);
    214 end;
    215 
    216 procedure TCityTypeDlg.PaintBox1MouseDown(Sender: TObject;
    217   Button: TMouseButton; Shift: TShiftState; x, y: integer);
    218 var
    219 i: integer;
    220 begin
    221 x:=x-SideFrame; y:=y-WideFrame;
    222 i:=(x-xList) div 42+(y-yList) div 32 *nListCol;
    223 if (i<nImp) and (MyData.ImpOrder[ctype,i]>=0)
    224   and (x>xList+2+ i mod nListCol *42) and (y>yList+5+ i div nListCol *32)
    225   and (x<xList+3+36+ i mod nListCol *42) and (y<yList+6+20+ i div nListCol *32) then
    226   begin
    227   if ssShift in Shift then
    228     HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkImp, MyData.ImpOrder[ctype,i])
    229   else
    230     begin
    231     dragiix:=MyData.ImpOrder[ctype,i];
    232     Screen.Cursor:=crImpDrag;
     232  inherited ShowNewContent(NewMode);
     233end;
     234
     235procedure TCityTypeDlg.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
     236  Shift: TShiftState; x, y: integer);
     237var
     238  i: integer;
     239begin
     240  x := x - SideFrame;
     241  y := y - WideFrame;
     242  i := (x - xList) div 42 + (y - yList) div 32 * nListCol;
     243  if (i < nImp) and (MyData.ImpOrder[ctype, i] >= 0) and
     244    (x > xList + 2 + i mod nListCol * 42) and
     245    (y > yList + 5 + i div nListCol * 32) and
     246    (x < xList + 3 + 36 + i mod nListCol * 42) and
     247    (y < yList + 6 + 20 + i div nListCol * 32) then
     248  begin
     249    if ssShift in Shift then
     250      HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkImp,
     251        MyData.ImpOrder[ctype, i])
     252    else
     253    begin
     254      dragiix := MyData.ImpOrder[ctype, i];
     255      Screen.Cursor := crImpDrag;
     256      SmartUpdateContent
     257    end;
     258    exit;
     259  end;
     260  i := (x - xPool) div 42 + (y - yPool) div 32 * nPoolCol;
     261  if (i < nPool) and (x > xPool + 2 + i mod nPoolCol * 42) and
     262    (y > yPool + 5 + i div nPoolCol * 32) and
     263    (x < xPool + 3 + 36 + i mod nPoolCol * 42) and
     264    (y < yPool + 6 + 20 + i div nPoolCol * 32) then
     265  begin
     266    if ssShift in Shift then
     267      HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkImp, Pooliix[i])
     268    else
     269    begin
     270      dragiix := Pooliix[i];
     271      Screen.Cursor := crImpDrag;
     272      SmartUpdateContent
     273    end;
     274    exit;
     275  end;
     276  i := (x - xSwitch) div 42;
     277  if (i < nCityType) and (x > xSwitch + 2 + i * 42) and
     278    (x < xSwitch + 3 + 36 + i * 42) and (y >= ySwitch + 2) and (y < ySwitch + 22)
     279  then
     280  begin
     281    SaveType;
     282    LoadType(i);
    233283    SmartUpdateContent
    234     end;
    235   exit;
    236   end;
    237 i:=(x-xPool) div 42+(y-yPool) div 32 *nPoolCol;
    238 if (i<nPool) and (x>xPool+2+ i mod nPoolCol *42)
    239   and (y>yPool+5+ i div nPoolCol *32) and (x<xPool+3+36+ i mod nPoolCol *42)
    240   and (y<yPool+6+20+ i div nPoolCol *32) then
    241   begin
    242   if ssShift in Shift then
    243     HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkImp, Pooliix[i])
    244   else
    245     begin
    246     dragiix:=Pooliix[i];
    247     Screen.Cursor:=crImpDrag;
    248     SmartUpdateContent
    249     end;
    250   exit;
    251   end;
    252 i:=(x-xSwitch) div 42;
    253 if (i<nCityType) and (x>xSwitch+2+ i*42) and (x<xSwitch+3+36+i*42)
    254   and (y>=ySwitch+2) and (y<ySwitch+22) then
    255   begin
    256   SaveType;
    257   LoadType(i);
    258   SmartUpdateContent
    259284  end
    260285end;
     
    265290  procedure UnList(iix: integer);
    266291  var
     292    i: integer;
     293  begin
     294    i := 0;
     295    while (MyData.ImpOrder[ctype, i] >= 0) and
     296      (MyData.ImpOrder[ctype, i] <> iix) do
     297      inc(i);
     298    assert(MyData.ImpOrder[ctype, i] = iix);
     299    move(MyData.ImpOrder[ctype, i + 1], MyData.ImpOrder[ctype, i], nImp - i);
     300    Exclude(listed, iix);
     301  end;
     302
     303var
    267304  i: integer;
    268   begin
    269   i:=0;
    270   while (MyData.ImpOrder[ctype,i]>=0) and (MyData.ImpOrder[ctype,i]<>iix) do
    271     inc(i);
    272   assert(MyData.ImpOrder[ctype,i]=iix);
    273   move(MyData.ImpOrder[ctype,i+1],MyData.ImpOrder[ctype,i],nImp-i);
    274   Exclude(listed,iix);
    275   end;
    276 
    277 var
    278 i: integer;
    279 begin
    280 x:=x-SideFrame; y:=y-WideFrame;
    281 if dragiix>=0 then
    282   begin
    283   if (x>=xList) and (x<xList+nListCol*42)
    284     and (y>=yList) and (y<yList+nListRow*32) then
    285     begin
    286     if dragiix in listed then UnList(dragiix);
    287     i:=(x-xList) div 42+(y-yList) div 32 *nListCol;
    288     while (i>0) and (MyData.ImpOrder[ctype,i-1]<0) do dec(i);
    289     move(MyData.ImpOrder[ctype,i],MyData.ImpOrder[ctype,i+1],nImp-i-1);
    290     MyData.ImpOrder[ctype,i]:=dragiix;
    291     include(listed,dragiix);
    292     Changed:=true
     305begin
     306  x := x - SideFrame;
     307  y := y - WideFrame;
     308  if dragiix >= 0 then
     309  begin
     310    if (x >= xList) and (x < xList + nListCol * 42) and (y >= yList) and
     311      (y < yList + nListRow * 32) then
     312    begin
     313      if dragiix in listed then
     314        UnList(dragiix);
     315      i := (x - xList) div 42 + (y - yList) div 32 * nListCol;
     316      while (i > 0) and (MyData.ImpOrder[ctype, i - 1] < 0) do
     317        dec(i);
     318      move(MyData.ImpOrder[ctype, i], MyData.ImpOrder[ctype, i + 1],
     319        nImp - i - 1);
     320      MyData.ImpOrder[ctype, i] := dragiix;
     321      include(listed, dragiix);
     322      Changed := true
    293323    end
    294   else if (dragiix in listed) and (x>=xPool) and (x<xPool+nPoolCol*42)
    295     and (y>=yPool) and (y<yPool+nPoolRow*32) then
    296     begin
    297     UnList(dragiix);
    298     Changed:=true
     324    else if (dragiix in listed) and (x >= xPool) and (x < xPool + nPoolCol * 42)
     325      and (y >= yPool) and (y < yPool + nPoolRow * 32) then
     326    begin
     327      UnList(dragiix);
     328      Changed := true
    299329    end;
    300   dragiix:=-1;
     330    dragiix := -1;
     331    SmartUpdateContent
     332  end;
     333  Screen.Cursor := crDefault
     334end;
     335
     336procedure TCityTypeDlg.FormClose(Sender: TObject; var Action: TCloseAction);
     337begin
     338  SaveType;
     339  inherited;
     340end;
     341
     342procedure TCityTypeDlg.DeleteBtnClick(Sender: TObject);
     343begin
     344  fillchar(MyData.ImpOrder[ctype], sizeof(MyData.ImpOrder[ctype]), -1);
     345  listed := [];
     346  Changed := true;
    301347  SmartUpdateContent
    302   end;
    303 Screen.Cursor:=crDefault
    304 end;
    305 
    306 procedure TCityTypeDlg.FormClose(Sender: TObject; var Action: TCloseAction);
    307 begin
    308 SaveType;
    309 inherited;
    310 end;
    311 
    312 procedure TCityTypeDlg.DeleteBtnClick(Sender: TObject);
    313 begin
    314 fillchar(MyData.ImpOrder[ctype],sizeof(MyData.ImpOrder[ctype]),-1);
    315 listed:=[];
    316 Changed:=true;
    317 SmartUpdateContent
    318348end;
    319349
    320350end.
    321 
  • trunk/LocalPlayer/ClientTools.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit ClientTools;
    43
     
    98
    109const
    11 nOfferedResourceWeights=6;
    12 OfferedResourceWeights: array[0..nOfferedResourceWeights-1] of cardinal=
    13 (rwOff, rwMaxScience, rwForceScience, rwMaxGrowth, rwForceProd, rwMaxProd);
     10  nOfferedResourceWeights = 6;
     11  OfferedResourceWeights: array [0 .. nOfferedResourceWeights - 1] of cardinal =
     12    (rwOff, rwMaxScience, rwForceScience, rwMaxGrowth, rwForceProd, rwMaxProd);
    1413
    1514type
    16 TImpOrder=array[0..(nImp+4) div 4 *4 -1] of ShortInt;
    17 TEnhancementJobs=array[0..11,0..7] of Byte;
    18 JobResultSet=set of 0..39;
     15  TImpOrder = array [0 .. (nImp + 4) div 4 * 4 - 1] of ShortInt;
     16  TEnhancementJobs = array [0 .. 11, 0 .. 7] of Byte;
     17  JobResultSet = set of 0 .. 39;
    1918
    2019var
    21 Server: TServerCall;
    22 G: TNewGameData;
    23 me: integer;
    24 MyRO: ^TPlayerContext;
    25 MyMap: ^TTileList;
    26 MyUn: ^TUnList;
    27 MyCity: ^TCityList;
    28 MyModel: ^TModelList;
    29 
    30 AdvValue: array[0..nAdv-1] of integer;
    31 
    32 
    33 function dLoc(Loc,dx,dy: integer): integer;
    34 function Distance(Loc0,Loc1: integer): integer;
    35 function UnrestAtLoc(uix,Loc: integer): boolean;
     20  Server: TServerCall;
     21  G: TNewGameData;
     22  me: integer;
     23  MyRO: ^TPlayerContext;
     24  MyMap: ^TTileList;
     25  MyUn: ^TUnList;
     26  MyCity: ^TCityList;
     27  MyModel: ^TModelList;
     28
     29  AdvValue: array [0 .. nAdv - 1] of integer;
     30
     31function dLoc(Loc, dx, dy: integer): integer;
     32function Distance(Loc0, Loc1: integer): integer;
     33function UnrestAtLoc(uix, Loc: integer): boolean;
    3634function GetMoveAdvice(uix, ToLoc: integer;
    3735  var MoveAdviceData: TMoveAdviceData): integer;
     
    4341function IsMilReportNew(Enemy: integer): boolean;
    4442function CutCityFoodSurplus(FoodSurplus: integer; IsCityAlive: boolean;
    45   gov,size: integer): integer;
    46 function CityTaxBalance(cix: integer; const CityReport: TCityReportNew): integer;
     43  gov, size: integer): integer;
     44function CityTaxBalance(cix: integer; const CityReport: TCityReportNew)
     45  : integer;
    4746procedure SumCities(var TaxSum, ScienceSum: integer);
    48 function JobTest(uix,Job: integer; IgnoreResults: JobResultSet = []): boolean;
     47function JobTest(uix, Job: integer; IgnoreResults: JobResultSet = []): boolean;
    4948procedure GetUnitInfo(Loc: integer; var uix: integer; var UnitInfo: TUnitInfo);
    5049procedure GetCityInfo(Loc: integer; var cix: integer; var CityInfo: TCityInfo);
    5150function UnitExhausted(uix: integer): boolean;
    5251function ModelHash(const ModelInfo: TModelInfo): integer;
    53 function ProcessEnhancement(uix: integer; const Jobs: TEnhancementJobs): integer;
     52function ProcessEnhancement(uix: integer; const Jobs: TEnhancementJobs)
     53  : integer;
    5454function AutoBuild(cix: integer; const ImpOrder: TImpOrder): boolean;
    5555procedure DebugMessage(Level: integer; Text: string);
     
    6262procedure CityOptimizer_EndOfTurn;
    6363
    64 
    6564implementation
    6665
    6766var
    68 CityNeedsOptimize: array[0..ncmax-1] of boolean;
    69 
    70 
    71 function dLoc(Loc,dx,dy: integer): integer;
     67  CityNeedsOptimize: array [0 .. ncmax - 1] of boolean;
     68
     69function dLoc(Loc, dx, dy: integer): integer;
    7270var
    73 y0: integer;
     71  y0: integer;
    7472begin
    75 y0:=(Loc+G.lx*1024) div G.lx -1024;
    76 result:=(Loc+(dx+y0 and 1+G.lx*1024) shr 1) mod G.lx +G.lx*(y0+dy)
     73  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)
    7776end;
    7877
    79 function Distance(Loc0,Loc1: integer): integer;
     78function Distance(Loc0, Loc1: integer): integer;
    8079var
    81 dx,dy: integer;
     80  dx, dy: integer;
    8281begin
    83 inc(Loc0,G.lx*1024);
    84 inc(Loc1,G.lx*1024);
    85 dx:=abs(((Loc1 mod G.lx *2 +Loc1 div G.lx and 1)
    86   -(Loc0 mod G.lx *2 +Loc0 div G.lx and 1)+3*G.lx) mod (2*G.lx) -G.lx);
    87 dy:=abs(Loc1 div G.lx-Loc0 div G.lx);
    88 result:=dx+dy+abs(dx-dy) shr 1;
     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);
     86  dy := abs(Loc1 div G.lx - Loc0 div G.lx);
     87  result := dx + dy + abs(dx - dy) shr 1;
    8988end;
    9089
    91 function UnrestAtLoc(uix,Loc: integer): boolean;
     90function UnrestAtLoc(uix, Loc: integer): boolean;
    9291var
    93 uix1: integer;
     92  uix1: integer;
    9493begin
    95 result:=false;
    96 if MyModel[MyUn[uix].mix].Flags and mdCivil=0 then
    97   case MyRO.Government of
    98     gRepublic, gFuture:
    99       result:=(MyRO.Territory[Loc]>=0) and (MyRO.Territory[Loc]<>me)
    100         and (MyRO.Treaty[MyRO.Territory[Loc]]<trAlliance);
    101     gDemocracy:
    102       result:=(MyRO.Territory[Loc]<0) or (MyRO.Territory[Loc]<>me)
    103         and (MyRO.Treaty[MyRO.Territory[Loc]]<trAlliance);
     94  result := false;
     95  if MyModel[MyUn[uix].mix].Flags and mdCivil = 0 then
     96    case MyRO.Government of
     97      gRepublic, gFuture:
     98        result := (MyRO.Territory[Loc] >= 0) and (MyRO.Territory[Loc] <> me) and
     99          (MyRO.Treaty[MyRO.Territory[Loc]] < trAlliance);
     100      gDemocracy:
     101        result := (MyRO.Territory[Loc] < 0) or (MyRO.Territory[Loc] <> me) and
     102          (MyRO.Treaty[MyRO.Territory[Loc]] < trAlliance);
    104103    end;
    105 with MyModel[MyUn[uix].mix] do
    106   if Cap[mcSeaTrans]+Cap[mcAirTrans]+Cap[mcCarrier]>0 then
    107     for uix1:=0 to MyRO.nUn-1 do // check transported units too
    108       if (MyUn[uix1].Loc>=0) and (MyUn[uix1].Master=uix) then
    109         result:=result or UnrestAtLoc(uix1,Loc);
     104  with MyModel[MyUn[uix].mix] do
     105    if Cap[mcSeaTrans] + Cap[mcAirTrans] + Cap[mcCarrier] > 0 then
     106      for uix1 := 0 to MyRO.nUn - 1 do // check transported units too
     107        if (MyUn[uix1].Loc >= 0) and (MyUn[uix1].Master = uix) then
     108          result := result or UnrestAtLoc(uix1, Loc);
    110109end;
    111110
    112 function GetMoveAdvice(uix, ToLoc: integer; var MoveAdviceData: TMoveAdviceData): integer;
     111function GetMoveAdvice(uix, ToLoc: integer;
     112  var MoveAdviceData: TMoveAdviceData): integer;
    113113var
    114 MinEndHealth: integer;
     114  MinEndHealth: integer;
    115115begin
    116 if MyModel[MyUn[uix].mix].Domain=dGround then MinEndHealth:=100
    117 else MinEndHealth:=1; // resistent to hostile terrain -- don't consider
    118 repeat
    119   if MyUn[uix].Health>=MinEndHealth then
    120     begin
    121     MoveAdviceData.ToLoc:=ToLoc;
    122     MoveAdviceData.MoreTurns:=999;
    123     MoveAdviceData.MaxHostile_MovementLeft:=MyUn[uix].Health-MinEndHealth;
    124     result:=Server(sGetMoveAdvice,me,uix,MoveAdviceData);
    125     if (MinEndHealth<=1) or (result<>eNoWay) then exit;
     116  if MyModel[MyUn[uix].mix].Domain = dGround then
     117    MinEndHealth := 100
     118  else
     119    MinEndHealth := 1; // resistent to hostile terrain -- don't consider
     120  repeat
     121    if MyUn[uix].Health >= MinEndHealth then
     122    begin
     123      MoveAdviceData.ToLoc := ToLoc;
     124      MoveAdviceData.MoreTurns := 999;
     125      MoveAdviceData.MaxHostile_MovementLeft := MyUn[uix].Health - MinEndHealth;
     126      result := Server(sGetMoveAdvice, me, uix, MoveAdviceData);
     127      if (MinEndHealth <= 1) or (result <> eNoWay) then
     128        exit;
    126129    end;
    127   case MinEndHealth of
    128     100: MinEndHealth:=50;
    129     50: MinEndHealth:=25;
    130     25: MinEndHealth:=12;
    131     else MinEndHealth:=1
     130    case MinEndHealth of
     131      100:
     132        MinEndHealth := 50;
     133      50:
     134        MinEndHealth := 25;
     135      25:
     136        MinEndHealth := 12;
     137    else
     138      MinEndHealth := 1
    132139    end;
    133 until false
    134 end;
    135 
    136 function ColorOfHealth(Health: integer): integer;
    137 var
    138 red,green: integer;
    139 begin
    140 green:=400*Health div 100; if green>200 then green:=200;
    141 red:=510*(100-Health) div 100; if red>255 then red:=255;
    142 result:=green shl 8 + red
    143 end;
    144 
    145 function IsMultiPlayerGame: boolean;
    146 var
    147 p1: integer;
    148 begin
    149 result:=false;
    150 for p1:=1 to nPl-1 do
    151   if G.RO[p1]<>nil then result:=true;
    152 end;
    153 
    154 procedure ItsMeAgain(p: integer);
    155 begin
    156 if G.RO[p]<>nil then
    157   MyRO:=pointer(G.RO[p])
    158 else if G.SuperVisorRO[p]<>nil then
    159   MyRO:=pointer(G.SuperVisorRO[p])
    160 else exit;
    161 me:=p;
    162 MyMap:=pointer(MyRO.Map);
    163 MyUn:=pointer(MyRO.Un);
    164 MyCity:=pointer(MyRO.City);
    165 MyModel:=pointer(MyRO.Model);
    166 end;
    167 
    168 function GetAge(p: integer): integer;
    169 var
    170 i: integer;
    171 begin
    172 if p=me then
    173   begin
    174   result:=0;
    175   for i:=1 to 3 do
    176     if MyRO.Tech[AgePreq[i]]>=tsApplicable then result:=i;
    177   end
    178 else
    179   begin
    180   result:=0;
    181   for i:=1 to 3 do
    182     if MyRO.EnemyReport[p].Tech[AgePreq[i]]>=tsApplicable then result:=i;
    183   end
    184 end;
    185 
    186 function IsCivilReportNew(Enemy: integer): boolean;
    187 var
    188 i: integer;
    189 begin
    190 assert(Enemy<>me);
    191 i:=MyRO.EnemyReport[Enemy].TurnOfCivilReport;
    192 result:= (i=MyRO.Turn) or (i=MyRO.Turn-1) and (Enemy>me);
    193 end;
    194 
    195 function IsMilReportNew(Enemy: integer): boolean;
    196 var
    197 i: integer;
    198 begin
    199 assert(Enemy<>me);
    200 i:=MyRO.EnemyReport[Enemy].TurnOfMilReport;
    201 result:= (i=MyRO.Turn) or (i=MyRO.Turn-1) and (Enemy>me);
    202 end;
    203 
    204 function CutCityFoodSurplus(FoodSurplus: integer; IsCityAlive: boolean;
    205   gov,size: integer): integer;
    206 begin
    207 result:=FoodSurplus;
    208 if not IsCityAlive
    209   or (result>0)
    210      and ((gov=gFuture)
    211        or (size>=NeedAqueductSize) and (result<2)) then
    212   result:=0; {no growth}
    213 end;
    214 
    215 function CityTaxBalance(cix: integer; const CityReport: TCityReportNew): integer;
    216 var
    217 i: integer;
    218 begin
    219 result:=0;
    220 if (CityReport.HappinessBalance>=0) {no disorder}
    221   and (MyCity[cix].Flags and chCaptured=0) then // not captured
    222   begin
    223   inc(result, CityReport.Tax);
    224   if (MyCity[cix].Project and (cpImp+cpIndex)=cpImp+imTrGoods)
    225     and (CityReport.Production>0) then
    226     inc(result, CityReport.Production);
    227   if ((MyRO.Government=gFuture)
    228       or (MyCity[cix].Size>=NeedAqueductSize)
    229       and (CityReport.FoodSurplus<2))
    230     and (CityReport.FoodSurplus>0) then
    231     inc(result, CityReport.FoodSurplus);
    232   end;
    233 for i:=28 to nImp-1 do if MyCity[cix].Built[i]>0 then
    234   dec(result, Imp[i].Maint);
    235 end;
    236 
    237 procedure SumCities(var TaxSum, ScienceSum: integer);
    238 var
    239 cix: integer;
    240 CityReport: TCityReportNew;
    241 begin
    242 TaxSum:=MyRO.OracleIncome;
    243 ScienceSum:=0;
    244 if MyRO.Government=gAnarchy then exit;
    245 for cix:=0 to MyRO.nCity-1 do if MyCity[cix].Loc>=0 then
    246   begin
    247   CityReport.HypoTiles:=-1;
    248   CityReport.HypoTaxRate:=-1;
    249   CityReport.HypoLuxuryRate:=-1;
    250   Server(sGetCityReportNew,me,cix,CityReport);
    251   if (CityReport.HappinessBalance>=0) {no disorder}
    252     and (MyCity[cix].Flags and chCaptured=0) then // not captured
    253     ScienceSum:=ScienceSum+CityReport.Science;
    254   TaxSum:=TaxSum+CityTaxBalance(cix, CityReport);
    255   end;
    256 end;
    257 
    258 function JobTest(uix,Job: integer; IgnoreResults: JobResultSet): boolean;
    259 var
    260 Test: integer;
    261 begin
    262 Test:=Server(sStartJob+Job shl 4-sExecute,me,uix,nil^);
    263 result:= (Test>=rExecuted) or (Test in IgnoreResults);
    264 end;
    265 
    266 procedure GetUnitInfo(Loc: integer; var uix: integer; var UnitInfo: TUnitInfo);
    267 var
    268 i,Cnt: integer;
    269 begin
    270 if MyMap[Loc] and fOwned<>0 then
    271   begin
    272   Server(sGetDefender,me,Loc,uix);
    273   Cnt:=0;
    274   for i:=0 to MyRO.nUn-1 do
    275     if MyUn[i].Loc=Loc then inc(Cnt);
    276   MakeUnitInfo(me,MyUn[uix],UnitInfo);
    277   if Cnt>1 then UnitInfo.Flags:=UnitInfo.Flags or unMulti;
    278   end
    279 else
    280   begin
    281   uix:=MyRO.nEnemyUn-1;
    282   while (uix>=0) and (MyRO.EnemyUn[uix].Loc<>Loc) do dec(uix);
    283   UnitInfo:=MyRO.EnemyUn[uix];
    284   end
    285 end;{GetUnitInfo}
    286 
    287 procedure GetCityInfo(Loc: integer; var cix: integer; var CityInfo: TCityInfo);
    288 begin
    289 if MyMap[Loc] and fOwned<>0 then
    290   begin
    291   CityInfo.Loc:=Loc;
    292   cix:=MyRO.nCity-1;
    293   while (cix>=0) and (MyCity[cix].Loc<>Loc) do dec(cix);
    294   with CityInfo do
    295     begin
    296     Owner:=me;
    297     ID:=MyCity[cix].ID;
    298     Size:=MyCity[cix].Size;
    299     Flags:=0;
    300     if MyCity[cix].Built[imPalace]>0 then inc(Flags,ciCapital);
    301     if (MyCity[cix].Built[imWalls]>0)
    302       or (MyMap[MyCity[cix].Loc] and fGrWall<>0) then inc(Flags,ciWalled);
    303     if MyCity[cix].Built[imCoastalFort]>0 then inc(Flags,ciCoastalFort);
    304     if MyCity[cix].Built[imMissileBat]>0 then inc(Flags,ciMissileBat);
    305     if MyCity[cix].Built[imBunker]>0 then inc(Flags,ciBunker);
    306     if MyCity[cix].Built[imSpacePort]>0 then inc(Flags,ciSpacePort);
     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;
    307190    end
    308   end
    309 else
    310   begin
    311   cix:=MyRO.nEnemyCity-1;
    312   while (cix>=0) and (MyRO.EnemyCity[cix].Loc<>Loc) do dec(cix);
    313   CityInfo:=MyRO.EnemyCity[cix];
    314   end
    315 end;
    316 
    317 function UnitExhausted(uix: integer): boolean;
    318 // check if another move of this unit is still possible
    319 var
    320 dx, dy: integer;
    321 begin
    322 result:=true;
    323 if (MyUn[uix].Movement>0) or (MyRO.Wonder[woShinkansen].EffectiveOwner=me) then
    324   if (MyUn[uix].Movement>=100) or ((MyModel[MyUn[uix].mix].Kind=mkCaravan)
    325     and (MyMap[MyUn[uix].Loc] and fCity<>0)) then
    326     result:=false
    327   else for dx:=-2 to 2 do for dy:=-2 to 2 do if abs(dx)+abs(dy)=2 then
    328     if Server(sMoveUnit-sExecute+dx and 7 shl 4+dy and 7 shl 7,me,uix,nil^)>=rExecuted then
    329       result:=false;
    330 end;
    331 
    332 function ModelHash(const ModelInfo: TModelInfo): integer;
    333 var
    334 i,FeatureCode,Hash1,Hash2,Hash2r,d: cardinal;
    335 begin
    336 with ModelInfo do
    337   if Kind>mkEnemyDeveloped then
    338     result:=integer($C0000000+Speed div 50+Kind shl 8)
    339   else
    340     begin
    341     FeatureCode:=0;
    342     for i:=mcFirstNonCap to nFeature-1 do
    343       if 1 shl Domain and Feature[i].Domains<>0 then
    344         begin
    345         FeatureCode:=FeatureCode*2;
    346         if 1 shl (i-mcFirstNonCap)<>0 then
    347           inc(FeatureCode);
    348         end;
    349     case Domain of
    350       dGround:
    351         begin
    352         assert(FeatureCode<1 shl 8);
    353         assert(Attack<5113);
    354         assert(Defense<2273);
    355         assert(Cost<1611);
    356         Hash1:=(Attack*2273+Defense)*9+(Speed-150) div 50;
    357         Hash2:=FeatureCode*1611+Cost;
    358         end;
    359       dSea:
    360         begin
    361         assert(FeatureCode<1 shl 9);
    362         assert(Attack<12193);
    363         assert(Defense<6097);
    364         assert(Cost<4381);
    365         Hash1:=((Attack*6097+Defense)*5+(Speed-350) div 100)*2;
    366         if Weight>=6 then inc(Hash1);
    367         Hash2:=((TTrans*17+ATrans_Fuel) shl 9+FeatureCode)*4381+Cost;
    368         end;
    369       dAir:
    370         begin
    371         assert(FeatureCode<1 shl 5);
    372         assert(Attack<2407);
    373         assert(Defense<1605);
    374         assert(Bombs<4813);
    375         assert(Cost<2089);
    376         Hash1:=(Attack*1605+Defense) shl 5+FeatureCode;
    377         Hash2:=((Bombs*7+ATrans_Fuel)*4+TTrans)*2089+Cost;
    378         end;
    379       end;
    380     Hash2r:=0;
    381     for i:=0 to 7 do
    382       begin
    383       Hash2r:=Hash2r*13;
    384       d:=Hash2 div 13;
    385       inc(Hash2r,Hash2-d*13);
    386       Hash2:=d
    387       end;
    388     result:=integer(Domain shl 30+Hash1 xor Hash2r)
     191    else
     192    begin
     193      result := 0;
     194      for i := 1 to 3 do
     195        if MyRO.EnemyReport[p].Tech[AgePreq[i]] >= tsApplicable then
     196          result := i;
    389197    end
    390 end;
    391 
    392 function ProcessEnhancement(uix: integer; const Jobs: TEnhancementJobs): integer;
    393 { return values:
    394 eJobDone - all applicable jobs done
    395 eOK - enhancement not complete
    396 eDied - job done and died (thurst) }
    397 var
    398 stage, NextJob, Tile: integer;
    399 Done: Set of jNone..jTrans;
    400 begin
    401 Done:=[];
    402 Tile:=MyMap[MyUn[uix].Loc];
    403 if Tile and fRoad<>0 then include(Done,jRoad);
    404 if Tile and fRR<>0 then include(Done,jRR);
    405 if (Tile and fTerImp=tiIrrigation) or (Tile and fTerImp=tiFarm) then
    406   include(Done,jIrr);
    407 if Tile and fTerImp=tiFarm then include(Done,jFarm);
    408 if Tile and fTerImp=tiMine then include(Done,jMine);
    409 if Tile and fPoll=0 then include(Done,jPoll);
    410 
    411 if MyUn[uix].Job=jNone then result:=eJobDone
    412 else result:=eOK;
    413 while (result<>eOK) and (result<>eDied) do
    414   begin
    415   stage:=-1;
    416   repeat
    417     if stage=-1 then NextJob:=jPoll
    418     else NextJob:=Jobs[Tile and fTerrain,stage];
    419     if (NextJob=jNone) or not (NextJob in Done) then Break;
    420     inc(stage);
    421   until stage=5;
    422   if (stage=5) or (NextJob=jNone) then
    423     begin result:=eJobDone; Break; end; // tile enhancement complete
    424   result:=Server(sStartJob+NextJob shl 4,me,uix,nil^);
    425   include(Done,NextJob)
    426   end;
    427 end;
    428 
    429 function AutoBuild(cix: integer; const ImpOrder: TImpOrder): boolean;
    430 var
    431 i,NewProject: integer;
    432 begin
    433 result:=false;
    434 if (MyCity[cix].Project and (cpImp+cpIndex)=cpImp+imTrGoods)
    435   or (MyCity[cix].Flags and chProduction<>0) then
    436   begin
    437   i:=0;
    438   repeat
    439     while (ImpOrder[i]>=0) and (MyCity[cix].Built[ImpOrder[i]]>0) do inc(i);
    440     if ImpOrder[i]<0 then Break;
    441     assert(i<nImp);
    442     NewProject:=cpImp+ImpOrder[i];
    443     if Server(sSetCityProject,me,cix,NewProject)>=rExecuted then
    444       begin
    445       result:=true;
    446       CityOptimizer_CityChange(cix);
    447       Break;
    448       end;
    449     inc(i);
    450   until false
    451   end
    452 end;
    453 
    454 procedure CalculateAdvValues;
    455 var
    456 i,j: integer;
    457 known: array[0..nAdv-1] of integer;
    458 
    459   procedure MarkPreqs(i: integer);
    460   begin
    461   if known[i]=0 then
    462     begin
    463     known[i]:=1;
    464     if (i<>adScience) and (i<>adMassProduction) then
    465       begin
    466       if (AdvPreq[i,0]>=0) then MarkPreqs(AdvPreq[i,0]);
    467       if (AdvPreq[i,1]>=0) then MarkPreqs(AdvPreq[i,1]);
     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);
     243    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);
    468333      end
    469334    end
    470   end;
    471 
    472 begin
    473 FillChar(AdvValue,SizeOf(AdvValue),0);
    474 for i:=0 to nAdv-1 do
    475   begin
    476   FillChar(known,SizeOf(known),0);
    477   MarkPreqs(i);
    478   for j:=0 to nAdv-1 do if known[j]>0 then inc(AdvValue[i]);
    479   if i in FutureTech then inc(AdvValue[i],3000)
    480   else if known[adMassProduction]>0 then inc(AdvValue[i],2000)
    481   else if known[adScience]>0 then inc(AdvValue[i],1000)
    482   end;
    483 end;
    484 
    485 procedure DebugMessage(Level: integer; Text: string);
    486 begin
    487 Server(sMessage,me,Level,pchar(Text)^)
    488 end;
    489 
    490 function MarkCitiesAround(Loc,cixExcept: integer): boolean;
    491 // return whether a city was marked
    492 var
    493 cix: integer;
    494 begin
    495 result:=false;
    496 for cix:=0 to MyRO.nCity-1 do
    497   if (cix<>cixExcept) and (MyCity[cix].Loc>=0)
    498     and (MyCity[cix].Flags and chCaptured=0)
    499     and (Distance(MyCity[cix].Loc,Loc)<=5) then
    500     begin
    501     CityNeedsOptimize[cix]:=true;
    502     result:=true;
     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];
    503341    end
    504 end;
    505 
    506 procedure OptimizeCities(CheckOnly: boolean);
    507 var
    508 cix,fix,dx,dy,Loc1,OptiType: integer;
    509 done: boolean;
    510 Advice: TCityTileAdviceData;
    511 begin
    512 repeat
    513   done:=true;
    514   for cix:=0 to MyRO.nCity-1 do if CityNeedsOptimize[cix] then
    515     begin
    516     OptiType:=MyCity[cix].Status shr 4 and $0F;
    517     if OptiType<>0 then
    518       begin
    519       Advice.ResourceWeights:=OfferedResourceWeights[OptiType];
    520       Server(sGetCityTileAdvice,me,cix,Advice);
    521       if Advice.Tiles<>MyCity[cix].Tiles then
    522         if CheckOnly then
    523           assert(false)
     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;
     430  { return values:
     431    eJobDone - all applicable jobs done
     432    eOK - enhancement not complete
     433    eDied - job done and died (thurst) }
     434  var
     435    stage, NextJob, Tile: integer;
     436    Done: Set of jNone .. jTrans;
     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
     458    begin
     459      stage := -1;
     460      repeat
     461        if stage = -1 then
     462          NextJob := jPoll
    524463        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
     470      begin
     471        result := eJobDone;
     472        Break;
     473      end; // tile enhancement complete
     474      result := Server(sStartJob + NextJob shl 4, me, uix, nil^);
     475      include(Done, NextJob)
     476    end;
     477  end;
     478
     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
     486    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
     496        begin
     497          result := true;
     498          CityOptimizer_CityChange(cix);
     499          Break;
     500        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
    525512          begin
    526           for fix:=1 to 26 do
    527             if MyCity[cix].Tiles and not Advice.Tiles and (1 shl fix)<>0 then
    528               begin // tile no longer used by this city -- check using it by another
    529               dy:=fix shr 2-3; dx:=fix and 3 shl 1 -3 + (dy+3) and 1;
    530               Loc1:=dLoc(MyCity[cix].Loc,dx,dy);
    531               if MarkCitiesAround(Loc1,cix) then
    532                 done:=false;
     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
     524      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;
     540      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
     573            begin
     574              OptiType := MyCity[cix].Status shr 4 and $0F;
     575              if OptiType <> 0 then
     576              begin
     577                Advice.ResourceWeights := OfferedResourceWeights[OptiType];
     578                Server(sGetCityTileAdvice, me, cix, Advice);
     579                if Advice.Tiles <> MyCity[cix].Tiles then
     580                  if CheckOnly then
     581                    assert(false)
     582                  else
     583                  begin
     584                    for fix := 1 to 26 do
     585                      if MyCity[cix].Tiles and not Advice.Tiles and
     586                        (1 shl fix) <> 0 then
     587                      begin // tile no longer used by this city -- check using it by another
     588                        dy := fix shr 2 - 3;
     589                        dx := fix and 3 shl 1 - 3 + (dy + 3) and 1;
     590                        Loc1 := dLoc(MyCity[cix].Loc, dx, dy);
     591                        if MarkCitiesAround(Loc1, cix) then
     592                          Done := false;
     593                      end;
     594                    Server(sSetCityTiles, me, cix, Advice.Tiles);
     595                  end;
    533596              end;
    534           Server(sSetCityTiles,me,cix,Advice.Tiles);
    535           end;
    536       end;
    537     CityNeedsOptimize[cix]:=false;
    538     end;
    539 until done;
    540 end;
    541 
    542 procedure CityOptimizer_BeginOfTurn;
    543 var
    544 cix: integer;
    545 begin
    546 fillchar(CityNeedsOptimize,MyRO.nCity-1,0); //false
    547 if MyRO.Government<>gAnarchy then
    548   begin
    549   for cix:=0 to MyRO.nCity-1 do
    550     if (MyCity[cix].Loc>=0) and (MyCity[cix].Flags and chCaptured=0) then
    551       CityNeedsOptimize[cix]:=true;
    552   OptimizeCities(false); // optimize all cities
    553   end
    554 end;
    555 
    556 procedure CityOptimizer_CityChange(cix: integer);
    557 begin
    558 if (MyRO.Government<>gAnarchy) and (MyCity[cix].Flags and chCaptured=0) then
    559   begin
    560   CityNeedsOptimize[cix]:=true;
    561   OptimizeCities(false);
    562   end
    563 end;
    564 
    565 procedure CityOptimizer_TileBecomesAvailable(Loc: integer);
    566 begin
    567 if (MyRO.Government<>gAnarchy) and MarkCitiesAround(Loc,-1) then
    568   OptimizeCities(false);
    569 end;
    570 
    571 procedure CityOptimizer_ReleaseCityTiles(cix, ReleasedTiles: integer);
    572 var
    573 fix,dx,dy,Loc1: integer;
    574 done: boolean;
    575 begin
    576 if (MyRO.Government<>gAnarchy) and (ReleasedTiles<>0) then
    577   begin
    578   done:=true;
    579   for fix:=1 to 26 do if ReleasedTiles and (1 shl fix)<>0 then
    580     begin
    581     dy:=fix shr 2-3; dx:=fix and 3 shl 1 -3 + (dy+3) and 1;
    582     Loc1:=dLoc(MyCity[cix].Loc,dx,dy);
    583     if MarkCitiesAround(Loc1,cix) then
    584       done:=false;
    585     end;
    586   if not done then
    587     OptimizeCities(false);
    588   end
    589 end;
    590 
    591 procedure CityOptimizer_BeforeRemoveUnit(uix: integer);
    592 var
    593 uix1: integer;
    594 begin
    595 if MyRO.Government<>gAnarchy then
    596   begin
    597   if MyUn[uix].Home>=0 then
    598     CityNeedsOptimize[MyUn[uix].Home]:=true;
    599 
    600   // transported units are also removed
    601   for uix1:=0 to MyRO.nUn-1 do
    602     if (MyUn[uix1].Loc>=0) and (MyUn[uix1].Master=uix)
    603       and (MyUn[uix1].Home>=0) then
    604       CityNeedsOptimize[MyUn[uix1].Home]:=true;
    605   end
    606 end;
    607 
    608 procedure CityOptimizer_AfterRemoveUnit;
    609 begin
    610 if MyRO.Government<>gAnarchy then
    611   OptimizeCities(false);
    612 end;
    613 
    614 procedure CityOptimizer_EndOfTurn;
    615 // all cities should already be optimized here -- only check this
    616 var
    617 cix: integer;
    618 begin
     597              CityNeedsOptimize[cix] := false;
     598            end;
     599        until Done;
     600      end;
     601
     602      procedure CityOptimizer_BeginOfTurn;
     603      var
     604        cix: integer;
     605      begin
     606        FillChar(CityNeedsOptimize, MyRO.nCity - 1, 0); // false
     607        if MyRO.Government <> gAnarchy then
     608        begin
     609          for cix := 0 to MyRO.nCity - 1 do
     610            if (MyCity[cix].Loc >= 0) and (MyCity[cix].Flags and chCaptured = 0)
     611            then
     612              CityNeedsOptimize[cix] := true;
     613          OptimizeCities(false); // optimize all cities
     614        end
     615      end;
     616
     617      procedure CityOptimizer_CityChange(cix: integer);
     618      begin
     619        if (MyRO.Government <> gAnarchy) and
     620          (MyCity[cix].Flags and chCaptured = 0) then
     621        begin
     622          CityNeedsOptimize[cix] := true;
     623          OptimizeCities(false);
     624        end
     625      end;
     626
     627      procedure CityOptimizer_TileBecomesAvailable(Loc: integer);
     628      begin
     629        if (MyRO.Government <> gAnarchy) and MarkCitiesAround(Loc, -1) then
     630          OptimizeCities(false);
     631      end;
     632
     633      procedure CityOptimizer_ReleaseCityTiles(cix, ReleasedTiles: integer);
     634      var
     635        fix, dx, dy, Loc1: integer;
     636        Done: boolean;
     637      begin
     638        if (MyRO.Government <> gAnarchy) and (ReleasedTiles <> 0) then
     639        begin
     640          Done := true;
     641          for fix := 1 to 26 do
     642            if ReleasedTiles and (1 shl fix) <> 0 then
     643            begin
     644              dy := fix shr 2 - 3;
     645              dx := fix and 3 shl 1 - 3 + (dy + 3) and 1;
     646              Loc1 := dLoc(MyCity[cix].Loc, dx, dy);
     647              if MarkCitiesAround(Loc1, cix) then
     648                Done := false;
     649            end;
     650          if not Done then
     651            OptimizeCities(false);
     652        end
     653      end;
     654
     655      procedure CityOptimizer_BeforeRemoveUnit(uix: integer);
     656      var
     657        uix1: integer;
     658      begin
     659        if MyRO.Government <> gAnarchy then
     660        begin
     661          if MyUn[uix].Home >= 0 then
     662            CityNeedsOptimize[MyUn[uix].Home] := true;
     663
     664          // transported units are also removed
     665          for uix1 := 0 to MyRO.nUn - 1 do
     666            if (MyUn[uix1].Loc >= 0) and (MyUn[uix1].Master = uix) and
     667              (MyUn[uix1].Home >= 0) then
     668              CityNeedsOptimize[MyUn[uix1].Home] := true;
     669        end
     670      end;
     671
     672      procedure CityOptimizer_AfterRemoveUnit;
     673      begin
     674        if MyRO.Government <> gAnarchy then
     675          OptimizeCities(false);
     676      end;
     677
     678      procedure CityOptimizer_EndOfTurn;
     679      // all cities should already be optimized here -- only check this
     680      var
     681        cix: integer;
     682      begin
    619683{$IFOPT O-}
    620 if MyRO.Government<>gAnarchy then
    621   begin
    622   fillchar(CityNeedsOptimize,MyRO.nCity-1,0); //false
    623   for cix:=0 to MyRO.nCity-1 do
    624     if (MyCity[cix].Loc>=0) and (MyCity[cix].Flags and chCaptured=0) then
    625       CityNeedsOptimize[cix]:=true;
    626   OptimizeCities(true); // check all cities
    627   end;
     684        if MyRO.Government <> gAnarchy then
     685        begin
     686          FillChar(CityNeedsOptimize, MyRO.nCity - 1, 0); // false
     687          for cix := 0 to MyRO.nCity - 1 do
     688            if (MyCity[cix].Loc >= 0) and (MyCity[cix].Flags and chCaptured = 0)
     689            then
     690              CityNeedsOptimize[cix] := true;
     691          OptimizeCities(true); // check all cities
     692        end;
    628693{$ENDIF}
    629 end;
    630 
     694      end;
    631695
    632696initialization
    633 assert(nImp<128);
     697
     698assert(nImp < 128);
    634699CalculateAdvValues;
    635700
    636701end.
    637 
  • trunk/LocalPlayer/Diagram.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit Diagram;
    43
     
    2221    procedure ToggleBtnClick(Sender: TObject);
    2322    procedure PlayerClick(Sender: TObject);
    24     procedure FormKeyDown(Sender: TObject; var Key: word;
    25       Shift: TShiftState);
     23    procedure FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
    2624
    2725  public
     
    3129
    3230  private
    33     Kind:(dkChart,dkShip);
    34     Player,Mode: integer;
     31    Kind: (dkChart, dkShip);
     32    Player, Mode: integer;
    3533  end;
    3634
     
    3836  DiaDlg: TDiaDlg;
    3937
    40 procedure PaintColonyShip(canvas: TCanvas; Player,Left,Width,Top: integer);
    41 
     38procedure PaintColonyShip(canvas: TCanvas; Player, Left, Width, Top: integer);
    4239
    4340implementation
    4441
    4542uses
    46 Protocol, ScreenTools, ClientTools,Term,Tribes;
     43  Protocol, ScreenTools, ClientTools, Term, Tribes;
    4744
    4845{$R *.DFM}
    4946
    5047const
    51 Border=24;
    52 RoundPixels: array[0..nStat-1] of integer=(0,0,0,5,5,5);
    53 
    54 yArea=48;
    55 xComp: array[0..5] of integer=(-60,-28,4,4,36,68);
    56 yComp: array[0..5] of integer=(-40,-40,-79,-1,-40,-40);
    57 xPow: array[0..3] of integer=(-116,-116,-116,-116);
    58 yPow: array[0..3] of integer=(-28,0,-44,16);
    59 xHab: array[0..1] of integer=(23,23);
    60 yHab: array[0..1] of integer=(-81,1);
    61 
    62 procedure PaintColonyShip(canvas: TCanvas; Player,Left,Width,Top: integer);
    63 var
    64 i,x,r,nComp,nPow,nHab: integer;
    65 begin
    66 with canvas do
    67   begin
    68   Brush.Color:=$000000;
    69   FillRect(Rect(Left,Top,Left+Width,Top+200));
    70   Brush.Style:=bsClear;
    71   Frame(Canvas,Left-1,Top-1,Left+Width,Top+200,MainTexture.clBevelShade,MainTexture.clBevelLight);
    72   RFrame(Canvas,Left-2,Top-2,Left+Width+1,Top+200+1,MainTexture.clBevelShade,MainTexture.clBevelLight);
    73 
    74   // stars
    75   RandSeed:=Player*11111;
    76   for i:=1 to Width-16 do
     48  Border = 24;
     49  RoundPixels: array [0 .. nStat - 1] of integer = (0, 0, 0, 5, 5, 5);
     50
     51  yArea = 48;
     52  xComp: array [0 .. 5] of integer = (-60, -28, 4, 4, 36, 68);
     53  yComp: array [0 .. 5] of integer = (-40, -40, -79, -1, -40, -40);
     54  xPow: array [0 .. 3] of integer = (-116, -116, -116, -116);
     55  yPow: array [0 .. 3] of integer = (-28, 0, -44, 16);
     56  xHab: array [0 .. 1] of integer = (23, 23);
     57  yHab: array [0 .. 1] of integer = (-81, 1);
     58
     59procedure PaintColonyShip(canvas: TCanvas; Player, Left, Width, Top: integer);
     60var
     61  i, x, r, nComp, nPow, nHab: integer;
     62begin
     63  with canvas do
     64  begin
     65    Brush.Color := $000000;
     66    FillRect(Rect(Left, Top, Left + Width, Top + 200));
     67    Brush.Style := bsClear;
     68    Frame(canvas, Left - 1, Top - 1, Left + Width, Top + 200,
     69      MainTexture.clBevelShade, MainTexture.clBevelLight);
     70    RFrame(canvas, Left - 2, Top - 2, Left + Width + 1, Top + 200 + 1,
     71      MainTexture.clBevelShade, MainTexture.clBevelLight);
     72
     73    // stars
     74    RandSeed := Player * 11111;
     75    for i := 1 to Width - 16 do
    7776    begin
    78     x:=Random((Width-16)*200);
    79     r:=Random(13)+28;
    80     Pixels[x div 200+8,x mod 200+Top]:=(r*r*r*r div 10001)*$10101;
     77      x := Random((Width - 16) * 200);
     78      r := Random(13) + 28;
     79      Pixels[x div 200 + 8, x mod 200 + Top] :=
     80        (r * r * r * r div 10001) * $10101;
    8181    end;
    8282
    83   nComp:=MyRO.Ship[Player].Parts[spComp];
    84   nPow:=MyRO.Ship[Player].Parts[spPow];
    85   nHab:=MyRO.Ship[Player].Parts[spHab];
    86   if nComp>6 then nComp:=6;
    87   if nPow>4 then nPow:=4;
    88   if nHab>2 then nHab:=2;
    89   for i:=0 to nHab-1 do
    90     Sprite(canvas,HGrSystem2,Left+Width div 2+xHab[i],Top+100+yHab[i],
    91       80,80,34,1);
    92   for i:=0 to nComp-1 do
    93     Sprite(canvas,HGrSystem2,Left+Width div 2+xComp[i],Top+100+yComp[i],
    94       32,80,1,1);
    95   if nComp>0 then
    96     for i:=3 downto nPow do
    97       Sprite(canvas,HGrSystem2,Left+Width div 2+xPow[i]+40,Top+100+yPow[i],
    98         16,27,1,82);
    99   for i:=nPow-1 downto 0 do
    100     Sprite(canvas,HGrSystem2,Left+Width div 2+xPow[i],Top+100+yPow[i],
    101       56,28,58,82);
    102   if (nComp<3) and (nHab>=1) then
    103     Sprite(canvas,HGrSystem2,Left+Width div 2+xComp[2]+32-16,Top+100+7+yComp[2],
    104       16,27,1,82);
    105   if (nComp>=3) and (nHab<1) then
    106     Sprite(canvas,HGrSystem2,Left+Width div 2+xComp[2]+32,Top+100+7+yComp[2],
    107       16,27,18,82);
    108   if (nComp<4) and (nHab>=2) then
    109     Sprite(canvas,HGrSystem2,Left+Width div 2+xComp[3]+32-16,Top+100+46+yComp[3],
    110       16,27,1,82);
    111   if (nComp>=4) and (nHab<2) then
    112     Sprite(canvas,HGrSystem2,Left+Width div 2+xComp[3]+32,Top+100+46+yComp[3],
    113       16,27,18,82);
    114   if (nComp<>6) and (nComp<>2) and not ((nComp=0) and (nPow<1)) then
    115     Sprite(canvas,HGrSystem2,Left+Width div 2+xComp[nComp],Top+100+7+yComp[nComp],
    116       16,27,18,82);
    117   if (nComp<>6) and (nComp<>3) and not ((nComp=0) and (nPow<2)) then
    118     Sprite(canvas,HGrSystem2,Left+Width div 2+xComp[nComp],Top+100+46+yComp[nComp],
    119       16,27,18,82);
    120   if nComp=2 then
    121     Sprite(canvas,HGrSystem2,Left+Width div 2+xComp[3],Top+100+7+yComp[3],
    122       16,27,18,82);
    123   if nComp=3 then
    124     Sprite(canvas,HGrSystem2,Left+Width div 2+xComp[4],Top+100+7+yComp[4],
    125       16,27,18,82);
     83    nComp := MyRO.Ship[Player].Parts[spComp];
     84    nPow := MyRO.Ship[Player].Parts[spPow];
     85    nHab := MyRO.Ship[Player].Parts[spHab];
     86    if nComp > 6 then
     87      nComp := 6;
     88    if nPow > 4 then
     89      nPow := 4;
     90    if nHab > 2 then
     91      nHab := 2;
     92    for i := 0 to nHab - 1 do
     93      Sprite(canvas, HGrSystem2, Left + Width div 2 + xHab[i],
     94        Top + 100 + yHab[i], 80, 80, 34, 1);
     95    for i := 0 to nComp - 1 do
     96      Sprite(canvas, HGrSystem2, Left + Width div 2 + xComp[i],
     97        Top + 100 + yComp[i], 32, 80, 1, 1);
     98    if nComp > 0 then
     99      for i := 3 downto nPow do
     100        Sprite(canvas, HGrSystem2, Left + Width div 2 + xPow[i] + 40,
     101          Top + 100 + yPow[i], 16, 27, 1, 82);
     102    for i := nPow - 1 downto 0 do
     103      Sprite(canvas, HGrSystem2, Left + Width div 2 + xPow[i],
     104        Top + 100 + yPow[i], 56, 28, 58, 82);
     105    if (nComp < 3) and (nHab >= 1) then
     106      Sprite(canvas, HGrSystem2, Left + Width div 2 + xComp[2] + 32 - 16,
     107        Top + 100 + 7 + yComp[2], 16, 27, 1, 82);
     108    if (nComp >= 3) and (nHab < 1) then
     109      Sprite(canvas, HGrSystem2, Left + Width div 2 + xComp[2] + 32,
     110        Top + 100 + 7 + yComp[2], 16, 27, 18, 82);
     111    if (nComp < 4) and (nHab >= 2) then
     112      Sprite(canvas, HGrSystem2, Left + Width div 2 + xComp[3] + 32 - 16,
     113        Top + 100 + 46 + yComp[3], 16, 27, 1, 82);
     114    if (nComp >= 4) and (nHab < 2) then
     115      Sprite(canvas, HGrSystem2, Left + Width div 2 + xComp[3] + 32,
     116        Top + 100 + 46 + yComp[3], 16, 27, 18, 82);
     117    if (nComp <> 6) and (nComp <> 2) and not((nComp = 0) and (nPow < 1)) then
     118      Sprite(canvas, HGrSystem2, Left + Width div 2 + xComp[nComp],
     119        Top + 100 + 7 + yComp[nComp], 16, 27, 18, 82);
     120    if (nComp <> 6) and (nComp <> 3) and not((nComp = 0) and (nPow < 2)) then
     121      Sprite(canvas, HGrSystem2, Left + Width div 2 + xComp[nComp],
     122        Top + 100 + 46 + yComp[nComp], 16, 27, 18, 82);
     123    if nComp = 2 then
     124      Sprite(canvas, HGrSystem2, Left + Width div 2 + xComp[3],
     125        Top + 100 + 7 + yComp[3], 16, 27, 18, 82);
     126    if nComp = 3 then
     127      Sprite(canvas, HGrSystem2, Left + Width div 2 + xComp[4],
     128        Top + 100 + 7 + yComp[4], 16, 27, 18, 82);
    126129  end
    127130end;
     
    129132procedure TDiaDlg.FormCreate(Sender: TObject);
    130133begin
    131 inherited;
    132 TitleHeight:=WideFrame+20;
    133 InnerHeight:=ClientHeight-TitleHeight-NarrowFrame;
    134 CaptionRight:=CloseBtn.Left;
    135 CaptionLeft:=ToggleBtn.Left+ToggleBtn.Width;
    136 InitButtons();
     134  inherited;
     135  TitleHeight := WideFrame + 20;
     136  InnerHeight := ClientHeight - TitleHeight - NarrowFrame;
     137  CaptionRight := CloseBtn.Left;
     138  CaptionLeft := ToggleBtn.Left + ToggleBtn.Width;
     139  InitButtons();
    137140end;
    138141
    139142procedure TDiaDlg.CloseBtnClick(Sender: TObject);
    140143begin
    141 Close;
     144  Close;
    142145end;
    143146
    144147procedure TDiaDlg.OffscreenPaint;
    145148type
    146 TLine=array[0..99999,0..2] of Byte;
    147 var
    148 p,T,max,x,y,y0,Stop,r,RoundRange,LineStep: integer;
    149 s: string;
    150 List: ^TChart;
     149  TLine = array [0 .. 99999, 0 .. 2] of Byte;
     150var
     151  p, T, max, x, y, y0, Stop, r, RoundRange, LineStep: integer;
     152  s: string;
     153  List: ^TChart;
    151154
    152155  function Round(T: integer): integer;
    153156  var
    154   n,i: integer;
    155   begin
    156   if T<RoundRange then n:=T else n:=RoundRange;
    157   result:=0;
    158   for i:=T-n to T do inc(result,List[i]);
    159   result:=result div (n+1);
     157    n, i: integer;
     158  begin
     159    if T < RoundRange then
     160      n := T
     161    else
     162      n := RoundRange;
     163    result := 0;
     164    for i := T - n to T do
     165      inc(result, List[i]);
     166    result := result div (n + 1);
    160167  end;
    161168
    162   procedure ShareBar(x,y:integer; Cap:string; val0,val1: integer);
    163   begin
    164   LoweredTextOut(offscreen.Canvas,-1,MainTexture,x-2,y,Cap);
    165   DLine(offscreen.Canvas,x-2,x+169,y+16,MainTexture.clTextShade,
    166     MainTexture.clTextLight);
    167   if val0>0 then s:=Format(Phrases.Lookup('SHARE'),[val0,val1])
    168   else s:='0';
    169   RisedTextOut(offscreen.Canvas,x+170-BiColorTextWidth(Offscreen.Canvas,s),y,s);
     169  procedure ShareBar(x, y: integer; Cap: string; val0, val1: integer);
     170  begin
     171    LoweredTextOut(offscreen.canvas, -1, MainTexture, x - 2, y, Cap);
     172    DLine(offscreen.canvas, x - 2, x + 169, y + 16, MainTexture.clTextShade,
     173      MainTexture.clTextLight);
     174    if val0 > 0 then
     175      s := Format(Phrases.Lookup('SHARE'), [val0, val1])
     176    else
     177      s := '0';
     178    RisedTextOut(offscreen.canvas,
     179      x + 170 - BiColorTextWidth(offscreen.canvas, s), y, s);
    170180  end;
    171181
    172182begin
    173 inherited;
    174 if Kind=dkChart then with offscreen.Canvas do
    175   begin
    176   Font.Assign(UniFont[ftTiny]);
    177   Font.Color:=$808080;
    178 
    179   RoundRange:=RoundPixels[Mode]*(MyRO.Turn-1) div (InnerWidth-2*Border);
    180 
    181   GetMem(List,4*(MyRO.Turn+2));
    182   if Mode=stExplore then max:=G.lx*G.ly
    183   else
     183  inherited;
     184  if Kind = dkChart then
     185    with offscreen.canvas do
    184186    begin
    185     max:=-1;
    186     for p:=0 to nPl-1 do
    187       if (G.Difficulty[p]>0)
    188         and (Server(sGetChart+Mode shl 4,me,p,List^)>=rExecuted) then
    189         for T:=0 to MyRO.Turn-1 do
    190           begin r:=Round(T); if r>max then max:=r; end;
     187      Font.Assign(UniFont[ftTiny]);
     188      Font.Color := $808080;
     189
     190      RoundRange := RoundPixels[Mode] * (MyRO.Turn - 1)
     191        div (InnerWidth - 2 * Border);
     192
     193      GetMem(List, 4 * (MyRO.Turn + 2));
     194      if Mode = stExplore then
     195        max := G.lx * G.ly
     196      else
     197      begin
     198        max := -1;
     199        for p := 0 to nPl - 1 do
     200          if (G.Difficulty[p] > 0) and
     201            (Server(sGetChart + Mode shl 4, me, p, List^) >= rExecuted) then
     202            for T := 0 to MyRO.Turn - 1 do
     203            begin
     204              r := Round(T);
     205              if r > max then
     206                max := r;
     207            end;
     208      end;
     209
     210      Brush.Color := $000000;
     211      FillRect(Rect(0, 0, InnerWidth, InnerHeight));
     212      Brush.Style := bsClear;
     213      Pen.Color := $606060;
     214      MoveTo(Border, InnerHeight - Border);
     215      LineTo(InnerWidth - Border, InnerHeight - Border);
     216      if MyRO.Turn >= 800 then
     217        LineStep := 200
     218      else if MyRO.Turn >= 400 then
     219        LineStep := 100
     220      else
     221        LineStep := 50;
     222      for T := 0 to (MyRO.Turn - 1) div LineStep do
     223      begin
     224        x := Border + (InnerWidth - 2 * Border) * T *
     225          LineStep div (MyRO.Turn - 1);
     226        MoveTo(x, Border);
     227        LineTo(x, InnerHeight - Border);
     228        s := IntToStr(abs(TurnToYear(T * LineStep)));
     229        Textout(x - TextWidth(s) div 2, Border - 16, s);
     230      end;
     231
     232      if max > 0 then
     233      begin
     234        for p := 0 to nPl - 1 do
     235          if (G.Difficulty[p] > 0) and
     236            (Server(sGetChart + Mode shl 4, me, p, List^) >= rExecuted) then
     237          begin
     238            Pen.Color := Tribe[p].Color;
     239            Stop := MyRO.Turn - 1;
     240            while (Stop > 0) and (List[Stop] = 0) do
     241              dec(Stop);
     242            for T := 0 to Stop do
     243            begin
     244              r := Round(T);
     245              x := Border + (InnerWidth - 2 * Border) * T div (MyRO.Turn - 1);
     246              y := InnerHeight - Border - (InnerHeight - 2 * Border) *
     247                r div max;
     248              if T = 0 then
     249                MoveTo(x, y)
     250                // else if Mode=stTerritory then
     251                // begin LineTo(x,y0); LineTo(x,y) end
     252              else if RoundPixels[Mode] = 0 then
     253              begin
     254                if (y <> y0) or (T = Stop) then
     255                  LineTo(x, y)
     256              end
     257              else
     258                LineTo(x, y);
     259              y0 := y;
     260            end;
     261          end;
     262      end;
     263      FreeMem(List);
     264    end
     265  else
     266    with offscreen.canvas do
     267    begin
     268      Font.Assign(UniFont[ftSmall]);
     269      FillOffscreen(0, 0, InnerWidth, InnerHeight);
     270
     271      PaintColonyShip(offscreen.canvas, Player, 8, InnerWidth - 16, yArea);
     272
     273      ShareBar(InnerWidth div 2 - 85, InnerHeight - 62,
     274        Phrases.Lookup('SHIPHAB'), MyRO.Ship[Player].Parts[spHab], 2);
     275      ShareBar(InnerWidth div 2 - 85, InnerHeight - 43,
     276        Phrases.Lookup('SHIPPOW'), MyRO.Ship[Player].Parts[spPow], 4);
     277      ShareBar(InnerWidth div 2 - 85, InnerHeight - 24,
     278        Phrases.Lookup('SHIPCOMP'), MyRO.Ship[Player].Parts[spComp], 6);
    191279    end;
    192 
    193   Brush.Color:=$000000;
    194   FillRect(Rect(0,0,InnerWidth,InnerHeight));
    195   Brush.Style:=bsClear;
    196   Pen.Color:=$606060;
    197   MoveTo(Border,InnerHeight-Border);
    198   LineTo(InnerWidth-Border,InnerHeight-Border);
    199   if MyRO.Turn>=800 then LineStep:=200
    200   else if MyRO.Turn>=400 then LineStep:=100
    201   else LineStep:=50;
    202   for T:=0 to (MyRO.Turn-1) div LineStep do
    203     begin
    204     x:=Border+(InnerWidth-2*Border)*T*LineStep div (MyRO.Turn-1);
    205     MoveTo(x,Border);
    206     LineTo(x,InnerHeight-Border);
    207     s:=IntToStr(abs(TurnToYear(T*LineStep)));
    208     Textout(x-TextWidth(s) div 2,Border-16,s);
    209     end;
    210 
    211   if max>0 then
    212     begin
    213     for p:=0 to nPl-1 do
    214       if (G.Difficulty[p]>0)
    215         and (Server(sGetChart+Mode shl 4,me,p,List^)>=rExecuted) then
    216         begin
    217         Pen.Color:=Tribe[p].Color;
    218         Stop:=MyRO.Turn-1;
    219         while (Stop>0) and (List[Stop]=0) do dec(Stop);
    220         for T:=0 to Stop do
    221           begin
    222           r:=Round(T);
    223           x:=Border+(InnerWidth-2*Border)*T div (MyRO.Turn-1);
    224           y:=InnerHeight-Border-(InnerHeight-2*Border)*r div max;
    225           if T=0 then MoveTo(x,y)
    226 //          else if Mode=stTerritory then
    227 //            begin LineTo(x,y0); LineTo(x,y) end
    228           else if RoundPixels[Mode]=0 then
    229             begin
    230             if (y<>y0) or (T=Stop) then LineTo(x,y)
    231             end
    232           else LineTo(x,y);
    233           y0:=y;
    234           end;
    235         end;
    236     end;
    237   FreeMem(List);
     280  MarkUsedOffscreen(InnerWidth, InnerHeight);
     281end; // OffscreenPaint
     282
     283procedure TDiaDlg.FormPaint(Sender: TObject);
     284var
     285  s: string;
     286begin
     287  inherited;
     288  canvas.Font.Assign(UniFont[ftNormal]);
     289  if Kind = dkChart then
     290    s := Phrases.Lookup('DIAGRAM', Mode)
     291  else
     292    s := Tribe[Player].TPhrase('SHORTNAME');
     293  LoweredTextOut(canvas, -1, MainTexture,
     294    (ClientWidth - BiColorTextWidth(canvas, s)) div 2, 31, s);
     295end;
     296
     297procedure TDiaDlg.FormShow(Sender: TObject);
     298begin
     299  if WindowMode = wmModal then
     300  begin { center on screen }
     301    Left := (Screen.Width - Width) div 2;
     302    Top := (Screen.Height - Height) div 2;
     303  end;
     304  OffscreenPaint;
     305end;
     306
     307procedure TDiaDlg.ShowNewContent_Charts(NewMode: integer);
     308begin
     309  Kind := dkChart;
     310  Mode := stPop;
     311  ToggleBtn.ButtonIndex := 15;
     312  ToggleBtn.Hint := Phrases.Lookup('BTN_PAGE');
     313  Caption := Phrases.Lookup('TITLE_DIAGRAMS');
     314  inherited ShowNewContent(NewMode);
     315end;
     316
     317procedure TDiaDlg.ShowNewContent_Ship(NewMode, p: integer);
     318begin
     319  Kind := dkShip;
     320  if p < 0 then
     321  begin
     322    Player := me;
     323    while MyRO.Ship[Player].Parts[spComp] + MyRO.Ship[Player].Parts[spPow] +
     324      MyRO.Ship[Player].Parts[spHab] = 0 do
     325      Player := (Player + 1) mod nPl;
    238326  end
    239 else with offscreen.Canvas do
    240   begin
    241   Font.Assign(UniFont[ftSmall]);
    242   FillOffscreen(0,0,InnerWidth,InnerHeight);
    243 
    244   PaintColonyShip(offscreen.Canvas,Player,8,InnerWidth-16,yArea);
    245 
    246   ShareBar(InnerWidth div 2-85,InnerHeight-62,Phrases.Lookup('SHIPHAB'),
    247     MyRO.Ship[Player].Parts[spHab],2);
    248   ShareBar(InnerWidth div 2-85,InnerHeight-43,Phrases.Lookup('SHIPPOW'),
    249     MyRO.Ship[Player].Parts[spPow],4);
    250   ShareBar(InnerWidth div 2-85,InnerHeight-24,Phrases.Lookup('SHIPCOMP'),
    251     MyRO.Ship[Player].Parts[spComp],6);
    252   end;
    253 MarkUsedOffscreen(InnerWidth,InnerHeight);
    254 end; // OffscreenPaint
    255 
    256 procedure TDiaDlg.FormPaint(Sender: TObject);
    257 var
    258 s: string;
    259 begin
    260 inherited;
    261 Canvas.Font.Assign(UniFont[ftNormal]);
    262 if Kind=dkChart then s:=Phrases.Lookup('DIAGRAM',Mode)
    263 else s:=Tribe[Player].TPhrase('SHORTNAME');
    264 LoweredTextOut(Canvas, -1, MainTexture,
    265   (ClientWidth-BiColorTextWidth(Canvas,s)) div 2, 31, s);
    266 end;
    267 
    268 procedure TDiaDlg.FormShow(Sender: TObject);
    269 begin
    270 if WindowMode=wmModal then
    271   begin {center on screen}
    272   Left:=(Screen.Width-Width) div 2;
    273   Top:=(Screen.Height-Height) div 2;
    274   end;
    275 OffscreenPaint;
    276 end;
    277 
    278 procedure TDiaDlg.ShowNewContent_Charts(NewMode: integer);
    279 begin
    280 Kind:=dkChart;
    281 Mode:=stPop;
    282 ToggleBtn.ButtonIndex:=15;
    283 ToggleBtn.Hint:=Phrases.Lookup('BTN_PAGE');
    284 Caption:=Phrases.Lookup('TITLE_DIAGRAMS');
    285 inherited ShowNewContent(NewMode);
    286 end;
    287 
    288 procedure TDiaDlg.ShowNewContent_Ship(NewMode,p: integer);
    289 begin
    290 Kind:=dkShip;
    291 if p<0 then
    292   begin
    293   Player:=me;
    294   while MyRO.Ship[Player].Parts[spComp]+MyRO.Ship[Player].Parts[spPow]
    295     +MyRO.Ship[Player].Parts[spHab]=0 do
    296     Player:=(Player+1) mod nPl;
     327  else
     328    Player := p;
     329  ToggleBtn.ButtonIndex := 28;
     330  ToggleBtn.Hint := Phrases.Lookup('BTN_SELECT');
     331  Caption := Phrases.Lookup('TITLE_SHIPS');
     332  inherited ShowNewContent(NewMode);
     333end;
     334
     335procedure TDiaDlg.ToggleBtnClick(Sender: TObject);
     336var
     337  p1: integer;
     338  m: TMenuItem;
     339begin
     340  if Kind = dkChart then
     341  begin
     342    Mode := (Mode + 1) mod nStat;
     343    OffscreenPaint;
     344    Invalidate;
    297345  end
    298 else Player:=p;
    299 ToggleBtn.ButtonIndex:=28;
    300 ToggleBtn.Hint:=Phrases.Lookup('BTN_SELECT');
    301 Caption:=Phrases.Lookup('TITLE_SHIPS');
    302 inherited ShowNewContent(NewMode);
    303 end;
    304 
    305 procedure TDiaDlg.ToggleBtnClick(Sender: TObject);
    306 var
    307 p1: integer;
    308 m: TMenuItem;
    309 begin
    310 if Kind=dkChart then
    311   begin
    312   Mode:=(Mode+1) mod nStat;
    313   OffscreenPaint;
    314   Invalidate;
     346  else
     347  begin
     348    EmptyMenu(Popup.Items);
     349    for p1 := 0 to nPl - 1 do
     350      if MyRO.Ship[p1].Parts[spComp] + MyRO.Ship[p1].Parts[spPow] +
     351        MyRO.Ship[p1].Parts[spHab] > 0 then
     352      begin
     353        m := TMenuItem.Create(Popup);
     354        m.RadioItem := true;
     355        m.Caption := Tribe[p1].TPhrase('SHORTNAME');
     356        m.Tag := p1;
     357        m.OnClick := PlayerClick;
     358        if p1 = Player then
     359          m.Checked := true;
     360        Popup.Items.Add(m);
     361      end;
     362    Popup.Popup(Left + ToggleBtn.Left, Top + ToggleBtn.Top + ToggleBtn.Height);
    315363  end
    316 else
    317   begin
    318   EmptyMenu(Popup.Items);
    319   for p1:=0 to nPl-1 do
    320     if MyRO.Ship[p1].Parts[spComp]+MyRO.Ship[p1].Parts[spPow]
    321       +MyRO.Ship[p1].Parts[spHab]>0 then
    322       begin
    323       m:=TMenuItem.Create(Popup);
    324       m.RadioItem:=true;
    325       m.Caption:=Tribe[p1].TPhrase('SHORTNAME');
    326       m.Tag:=p1;
    327       m.OnClick:=PlayerClick;
    328       if p1=Player then m.Checked:=true;
    329       Popup.Items.Add(m);
    330       end;
    331   Popup.Popup(Left+ToggleBtn.Left, Top+ToggleBtn.Top+ToggleBtn.Height);
    332   end
    333364end;
    334365
    335366procedure TDiaDlg.PlayerClick(Sender: TObject);
    336367begin
    337 ShowNewContent_Ship(FWindowMode, TComponent(Sender).Tag);
     368  ShowNewContent_Ship(FWindowMode, TComponent(Sender).Tag);
    338369end;
    339370
     
    341372  Shift: TShiftState);
    342373begin
    343 if (Key=VK_F6) and (Kind=dkChart) then // my key
    344   ToggleBtnClick(nil)
    345 else if (Key=VK_F8) and (Kind=dkShip) then // my other key
    346 else inherited
     374  if (Key = VK_F6) and (Kind = dkChart) then // my key
     375    ToggleBtnClick(nil)
     376  else if (Key = VK_F8) and (Kind = dkShip) then // my other key
     377  else
     378    inherited
    347379end;
    348380
    349381end.
    350 
  • trunk/LocalPlayer/Diplomacy.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit Diplomacy;
    43
     
    76uses Protocol;
    87
    9 function DipCommandToString(pSender, pTarget, Treaty, OppCommand, Command: integer;
    10   const OppOffer, Offer: TOffer): string;
     8function DipCommandToString(pSender, pTarget, Treaty, OppCommand,
     9  Command: integer; const OppOffer, Offer: TOffer): string;
    1110
    1211implementation
    1312
    1413uses
    15 ScreenTools,Tribes,SysUtils;
     14  ScreenTools, Tribes, SysUtils;
    1615
    1716function DipCommandToString;
     
    1918  function PriceToString(p, Price: integer): string;
    2019  begin
    21   case Price and opMask of
    22     opChoose:
    23       result:=Phrases.Lookup('PRICE_CHOOSE');
    24     opCivilReport:
    25       result:=Tribe[p].TPhrase('PRICE_CIVIL');
    26     opMilReport:
    27       result:=Tribe[p].TPhrase('PRICE_MIL');
    28     opMap:
    29       result:=Tribe[p].TPhrase('PRICE_MAP');
    30     opTreaty:
    31       {if Price-opTreaty<Treaty then
    32         case Treaty of
     20    case Price and opMask of
     21      opChoose:
     22        result := Phrases.Lookup('PRICE_CHOOSE');
     23      opCivilReport:
     24        result := Tribe[p].TPhrase('PRICE_CIVIL');
     25      opMilReport:
     26        result := Tribe[p].TPhrase('PRICE_MIL');
     27      opMap:
     28        result := Tribe[p].TPhrase('PRICE_MAP');
     29      opTreaty:
     30        { if Price-opTreaty<Treaty then
     31          case Treaty of
    3332          trPeace: result:=Phrases.Lookup('FRENDTREATY_PEACE');
    3433          trFriendlyContact: result:=Phrases.Lookup('FRENDTREATY_FRIENDLY');
    3534          trAlliance: result:=Phrases.Lookup('FRENDTREATY_ALLIANCE');
    3635          end
    37       else} result:=Phrases.Lookup('TREATY',Price-opTreaty);
    38     opShipParts:
    39       case Price shr 16 and $f of
    40         0: result:=Format(Phrases.Lookup('PRICE_SHIPCOMP'),[Price and $FFFF]);
    41         1: result:=Format(Phrases.Lookup('PRICE_SHIPPOW'),[Price and $FFFF]);
    42         2: result:=Format(Phrases.Lookup('PRICE_SHIPHAB'),[Price and $FFFF]);
    43         end;
    44     opMoney:
    45       result:=Format('%d%%c',[Price-opMoney]);
    46     opTribute:
    47       result:=Format(Phrases.Lookup('PRICE_TRIBUTE'),[Price-opTribute]);
    48     opTech:
    49       result:=Phrases.Lookup('ADVANCES',Price-opTech);
    50     opAllTech:
    51       result:=Tribe[p].TPhrase('PRICE_ALLTECH');
    52     opModel:
    53       result:=Tribe[p].ModelName[Price-opModel];
    54     opAllModel:
    55       result:=Tribe[p].TPhrase('PRICE_ALLMODEL');
    56 {    opCity:
    57       result:=Format(TPhrase('PRICE_CITY',p),[CityName(Price-opCity)]);}
     36          else } result := Phrases.Lookup('TREATY', Price - opTreaty);
     37      opShipParts:
     38        case Price shr 16 and $F of
     39          0:
     40            result := Format(Phrases.Lookup('PRICE_SHIPCOMP'),
     41              [Price and $FFFF]);
     42          1:
     43            result := Format(Phrases.Lookup('PRICE_SHIPPOW'),
     44              [Price and $FFFF]);
     45          2:
     46            result := Format(Phrases.Lookup('PRICE_SHIPHAB'),
     47              [Price and $FFFF]);
     48        end;
     49      opMoney:
     50        result := Format('%d%%c', [Price - opMoney]);
     51      opTribute:
     52        result := Format(Phrases.Lookup('PRICE_TRIBUTE'), [Price - opTribute]);
     53      opTech:
     54        result := Phrases.Lookup('ADVANCES', Price - opTech);
     55      opAllTech:
     56        result := Tribe[p].TPhrase('PRICE_ALLTECH');
     57      opModel:
     58        result := Tribe[p].ModelName[Price - opModel];
     59      opAllModel:
     60        result := Tribe[p].TPhrase('PRICE_ALLMODEL');
     61      { opCity:
     62        result:=Format(TPhrase('PRICE_CITY',p),[CityName(Price-opCity)]); }
    5863    end
    5964  end;
    6065
    6166var
    62 i: integer;
    63 sAdd,sDeliver, sCost: string;
    64 DoIntro: boolean;
     67  i: integer;
     68  sAdd, sDeliver, sCost: string;
     69  DoIntro: boolean;
    6570begin
    66 DoIntro:= OppCommand=scDipStart;
    67 case Command of
    68   scDipCancelTreaty:
    69     begin
    70     case Treaty of
    71       trPeace: result:=Phrases.Lookup('FRCANCELTREATY_PEACE');
    72       trFriendlyContact: result:=Phrases.Lookup('FRCANCELTREATY_FRIENDLY');
    73       trAlliance: result:=Phrases.Lookup('FRCANCELTREATY_ALLIANCE');
    74       end;
    75     DoIntro:=false;
    76     end;
    77   scDipNotice: result:=Phrases.Lookup('FRNOTICE');
    78   scDipAccept:
    79     begin
    80     if (OppOffer.nDeliver+OppOffer.nCost=1)
    81       and (OppOffer.Price[0] and opMask=opTreaty)
    82       and (integer(OppOffer.Price[0]-opTreaty)>Treaty) then // simple treaty offer
    83       {if OppOffer.Price[0]-opTreaty=trCeaseFire then
    84         result:=Tribe[pTarget].TPhrase('FRACCEPTCEASEFIRE')
    85       else} result:=Tribe[pTarget].TPhrase('FRACCEPTTREATY')
    86     else if OppOffer.nDeliver=0 then
    87       result:=Tribe[pSender].TPhrase('FRACCEPTDEMAND_STRONG')
    88     else if OppOffer.nCost=0 then
    89       result:=Tribe[pSender].TPhrase('FRACCEPTPRESENT')
    90     else result:=Tribe[pSender].TPhrase('FRACCEPTOFFER');
    91     end;
    92   scDipBreak:
    93     begin
    94     result:=Tribe[pTarget].TPhrase('FRBREAK');
    95     DoIntro:=false;
    96     end;
    97   scDipOffer:
    98     begin
    99     result:='';
    100     if (OppCommand=scDipOffer) and ((OppOffer.nDeliver>0) or (OppOffer.nCost>0))
    101       and (Offer.nCost+Offer.nDeliver<=2) then
    102       begin // respond to made offer before making own one
    103       if (OppOffer.nDeliver+OppOffer.nCost=1)
    104         and (OppOffer.Price[0] and opMask=opTreaty)
    105         and (integer(OppOffer.Price[0]-opTreaty)>Treaty) then // simple treaty offer
    106         result:=Tribe[pSender].TPhrase('FRNOTACCEPTTREATY')+'\'
    107       else if OppOffer.nDeliver=0 then
    108         result:=Tribe[pSender].TPhrase('FRNOTACCEPTDEMAND_STRONG')+'\'
    109       else if OppOffer.nCost=0 then
    110         result:=Tribe[pSender].TPhrase('FRNOTACCEPTPRESENT')+'\';
    111       end;
    112 
    113     sDeliver:='';
    114     for i:=0 to Offer.nDeliver-1 do
    115       begin
    116       sAdd:=PriceToString(pSender,Offer.Price[i]);
    117       if i=0 then sDeliver:=sAdd
    118       else sDeliver:=Format(Phrases.Lookup('PRICE_CONCAT'),[sDeliver,sAdd])
    119       end;
    120     sCost:='';
    121     for i:=0 to Offer.nCost-1 do
    122       begin
    123       sAdd:=PriceToString(pTarget,Offer.Price[Offer.nDeliver+i]);
    124       if i=0 then sCost:=sAdd
    125       else sCost:=Format(Phrases.Lookup('PRICE_CONCAT'),[sCost,sAdd])
    126       end;
    127 
    128     if (Offer.nDeliver=0) and (Offer.nCost=0) then
    129       begin // no offer made
    130       if (OppCommand=scDipOffer) and ((OppOffer.nDeliver=0) and (OppOffer.nCost=0)) then
    131         result:=Tribe[pTarget].TPhrase('FRBYE')
    132       else
    133         begin
    134         if (result='') and (OppCommand=scDipOffer)
    135           and ((OppOffer.nDeliver>0) or (OppOffer.nCost>0)) then
     71  DoIntro := OppCommand = scDipStart;
     72  case Command of
     73    scDipCancelTreaty:
     74      begin
     75        case Treaty of
     76          trPeace:
     77            result := Phrases.Lookup('FRCANCELTREATY_PEACE');
     78          trFriendlyContact:
     79            result := Phrases.Lookup('FRCANCELTREATY_FRIENDLY');
     80          trAlliance:
     81            result := Phrases.Lookup('FRCANCELTREATY_ALLIANCE');
     82        end;
     83        DoIntro := false;
     84      end;
     85    scDipNotice:
     86      result := Phrases.Lookup('FRNOTICE');
     87    scDipAccept:
     88      begin
     89        if (OppOffer.nDeliver + OppOffer.nCost = 1) and
     90          (OppOffer.Price[0] and opMask = opTreaty) and
     91          (integer(OppOffer.Price[0] - opTreaty) > Treaty) then
     92        // simple treaty offer
     93          { if OppOffer.Price[0]-opTreaty=trCeaseFire then
     94            result:=Tribe[pTarget].TPhrase('FRACCEPTCEASEFIRE')
     95            else } result := Tribe[pTarget].TPhrase('FRACCEPTTREATY')
     96        else if OppOffer.nDeliver = 0 then
     97          result := Tribe[pSender].TPhrase('FRACCEPTDEMAND_STRONG')
     98        else if OppOffer.nCost = 0 then
     99          result := Tribe[pSender].TPhrase('FRACCEPTPRESENT')
     100        else
     101          result := Tribe[pSender].TPhrase('FRACCEPTOFFER');
     102      end;
     103    scDipBreak:
     104      begin
     105        result := Tribe[pTarget].TPhrase('FRBREAK');
     106        DoIntro := false;
     107      end;
     108    scDipOffer:
     109      begin
     110        result := '';
     111        if (OppCommand = scDipOffer) and
     112          ((OppOffer.nDeliver > 0) or (OppOffer.nCost > 0)) and
     113          (Offer.nCost + Offer.nDeliver <= 2) then
     114        begin // respond to made offer before making own one
     115          if (OppOffer.nDeliver + OppOffer.nCost = 1) and
     116            (OppOffer.Price[0] and opMask = opTreaty) and
     117            (integer(OppOffer.Price[0] - opTreaty) > Treaty) then
     118          // simple treaty offer
     119            result := Tribe[pSender].TPhrase('FRNOTACCEPTTREATY') + '\'
     120          else if OppOffer.nDeliver = 0 then
     121            result := Tribe[pSender].TPhrase('FRNOTACCEPTDEMAND_STRONG') + '\'
     122          else if OppOffer.nCost = 0 then
     123            result := Tribe[pSender].TPhrase('FRNOTACCEPTPRESENT') + '\';
     124        end;
     125
     126        sDeliver := '';
     127        for i := 0 to Offer.nDeliver - 1 do
     128        begin
     129          sAdd := PriceToString(pSender, Offer.Price[i]);
     130          if i = 0 then
     131            sDeliver := sAdd
     132          else
     133            sDeliver := Format(Phrases.Lookup('PRICE_CONCAT'), [sDeliver, sAdd])
     134        end;
     135        sCost := '';
     136        for i := 0 to Offer.nCost - 1 do
     137        begin
     138          sAdd := PriceToString(pTarget, Offer.Price[Offer.nDeliver + i]);
     139          if i = 0 then
     140            sCost := sAdd
     141          else
     142            sCost := Format(Phrases.Lookup('PRICE_CONCAT'), [sCost, sAdd])
     143        end;
     144
     145        if (Offer.nDeliver = 0) and (Offer.nCost = 0) then
     146        begin // no offer made
     147          if (OppCommand = scDipOffer) and
     148            ((OppOffer.nDeliver = 0) and (OppOffer.nCost = 0)) then
     149            result := Tribe[pTarget].TPhrase('FRBYE')
     150          else
    136151          begin
    137           if (OppOffer.nDeliver=1) and (OppOffer.Price[0]=opChoose)
    138             and not Phrases2FallenBackToEnglish then
    139             result:=Tribe[pSender].TString(Phrases2.Lookup('FRNOTACCEPTANYOFFER'))+' '
    140           else if (OppOffer.nCost=1) and (OppOffer.Price[OppOffer.nDeliver]=opChoose)
    141             and not Phrases2FallenBackToEnglish then
    142             result:=Tribe[pSender].TString(Phrases2.Lookup('FRNOTACCEPTANYWANT'))+' '
    143           else result:=Tribe[pSender].TPhrase('FRNOTACCEPTOFFER')+' ';
    144           end;
    145         result:=result+Phrases.Lookup('FRDONE');
    146         DoIntro:=false
     152            if (result = '') and (OppCommand = scDipOffer) and
     153              ((OppOffer.nDeliver > 0) or (OppOffer.nCost > 0)) then
     154            begin
     155              if (OppOffer.nDeliver = 1) and (OppOffer.Price[0] = opChoose) and
     156                not Phrases2FallenBackToEnglish then
     157                result := Tribe[pSender].TString
     158                  (Phrases2.Lookup('FRNOTACCEPTANYOFFER')) + ' '
     159              else if (OppOffer.nCost = 1) and
     160                (OppOffer.Price[OppOffer.nDeliver] = opChoose) and not Phrases2FallenBackToEnglish
     161              then
     162                result := Tribe[pSender].TString
     163                  (Phrases2.Lookup('FRNOTACCEPTANYWANT')) + ' '
     164              else
     165                result := Tribe[pSender].TPhrase('FRNOTACCEPTOFFER') + ' ';
     166            end;
     167            result := result + Phrases.Lookup('FRDONE');
     168            DoIntro := false
     169          end
    147170        end
    148       end
    149     else if (Offer.nDeliver+Offer.nCost=1)
    150       and (Offer.Price[0] and opMask=opTreaty)
    151       and (integer(Offer.Price[0]-opTreaty)>Treaty) then // simple treaty offer
    152       begin
    153       case Offer.Price[0]-opTreaty of
    154         //trCeaseFire: result:=result+Tribe[pTarget].TPhrase('FRCEASEFIRE');
    155         trPeace: result:=result+Tribe[pTarget].TPhrase('FRPEACE');
    156         trFriendlyContact: result:=result+Tribe[pTarget].TPhrase('FRFRIENDLY');
    157         trAlliance: result:=result+Tribe[pTarget].TPhrase('FRALLIANCE');
     171        else if (Offer.nDeliver + Offer.nCost = 1) and
     172          (Offer.Price[0] and opMask = opTreaty) and
     173          (integer(Offer.Price[0] - opTreaty) > Treaty) then
     174        // simple treaty offer
     175        begin
     176          case Offer.Price[0] - opTreaty of
     177            // trCeaseFire: result:=result+Tribe[pTarget].TPhrase('FRCEASEFIRE');
     178            trPeace:
     179              result := result + Tribe[pTarget].TPhrase('FRPEACE');
     180            trFriendlyContact:
     181              result := result + Tribe[pTarget].TPhrase('FRFRIENDLY');
     182            trAlliance:
     183              result := result + Tribe[pTarget].TPhrase('FRALLIANCE');
     184          end
    158185        end
    159       end
    160     else if Offer.nDeliver=0 then // demand
    161       begin
    162       if (Treaty>=trFriendlyContact) and not Phrases2FallenBackToEnglish then
    163         result:=result+Format(Tribe[pTarget].TString(Phrases2.Lookup('FRDEMAND_SOFT')),[sCost])
    164       else
    165         begin
    166         result:=result+Format(Tribe[pTarget].TPhrase('FRDEMAND_STRONG'),[sCost]);
    167         DoIntro:=false
     186        else if Offer.nDeliver = 0 then // demand
     187        begin
     188          if (Treaty >= trFriendlyContact) and not Phrases2FallenBackToEnglish
     189          then
     190            result := result +
     191              Format(Tribe[pTarget].TString(Phrases2.Lookup('FRDEMAND_SOFT')
     192              ), [sCost])
     193          else
     194          begin
     195            result := result +
     196              Format(Tribe[pTarget].TPhrase('FRDEMAND_STRONG'), [sCost]);
     197            DoIntro := false
     198          end
    168199        end
    169       end
    170     else if Offer.nCost=0 then // present
    171       result:=result+Format(Tribe[pTarget].TPhrase('FRPRESENT'),[sDeliver])
    172     else if (Offer.nDeliver=1) and (Offer.Price[0]=opChoose) then
    173       result:=result+Format(Phrases.Lookup('FRDELCHOICE'),[sCost])
    174     else if (Offer.nCost=1) and (Offer.Price[Offer.nDeliver]=opChoose) then
    175       result:=result+Format(Phrases.Lookup('FRCOSTCHOICE'),[sDeliver])
    176     else result:=result+Format(Phrases.Lookup('FROFFER'),[sDeliver,sCost]);
    177     end;
     200        else if Offer.nCost = 0 then // present
     201          result := result + Format(Tribe[pTarget].TPhrase('FRPRESENT'),
     202            [sDeliver])
     203        else if (Offer.nDeliver = 1) and (Offer.Price[0] = opChoose) then
     204          result := result + Format(Phrases.Lookup('FRDELCHOICE'), [sCost])
     205        else if (Offer.nCost = 1) and (Offer.Price[Offer.nDeliver] = opChoose)
     206        then
     207          result := result + Format(Phrases.Lookup('FRCOSTCHOICE'), [sDeliver])
     208        else
     209          result := result + Format(Phrases.Lookup('FROFFER'),
     210            [sDeliver, sCost]);
     211      end;
    178212  end;
    179 if DoIntro then
    180   if Treaty<trPeace then
    181     result:=Tribe[pSender].TPhrase('FRSTART_NOTREATY')+' '+result
    182   else result:=Tribe[pSender].TPhrase('FRSTART_PEACE')+' '+result
     213  if DoIntro then
     214    if Treaty < trPeace then
     215      result := Tribe[pSender].TPhrase('FRSTART_NOTREATY') + ' ' + result
     216    else
     217      result := Tribe[pSender].TPhrase('FRSTART_PEACE') + ' ' + result
    183218end;
    184219
    185220end.
    186 
  • trunk/LocalPlayer/Draft.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit Draft;
    43
     
    65
    76uses
    8   Protocol,ClientTools,Term,ScreenTools,PVSB,BaseWin,
    9 
    10   Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,ExtCtrls,ButtonA,
     7  Protocol, ClientTools, Term, ScreenTools, PVSB, BaseWin,
     8
     9  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, ExtCtrls,
     10  ButtonA,
    1111  ButtonB, ButtonBase, Area;
    1212
     
    2828    procedure FormDestroy(Sender: TObject);
    2929  public
    30     procedure ShowNewContent(NewMode: integer); 
     30    procedure ShowNewContent(NewMode: integer);
    3131  protected
    3232    procedure OffscreenPaint; override;
    3333  private
    34     Domain,MaxLines,Lines,Cut,yDomain,yFeature,yWeight,yTotal,yView,IncCap,
    35       DecCap: integer;
    36     code: array[0..nFeature-1] of integer;
    37     Template,Back: TBitmap;
    38     function IsFeatureInList(d,i: integer): boolean;
     34    Domain, MaxLines, Lines, Cut, yDomain, yFeature, yWeight, yTotal, yView,
     35      IncCap, DecCap: integer;
     36    code: array [0 .. nFeature - 1] of integer;
     37    Template, Back: TBitmap;
     38    function IsFeatureInList(d, i: integer): boolean;
    3939    procedure SetDomain(d: integer);
    4040  end;
     
    4545implementation
    4646
    47 uses Help,Tribes,Directories;
     47uses Help, Tribes, Directories;
    4848
    4949{$R *.DFM}
    5050
    5151const
    52 MaxLines0=11; LinePitch=20;
    53 xDomain=30; yDomain0=464; DomainPitch=40;
    54 xFeature=38; yFeature0=42;
    55 xWeight=100; yWeight0=271;
    56 xTotal=20; xTotal2=34; yTotal0=354;
    57 xView=17; yView0=283;
     52  MaxLines0 = 11;
     53  LinePitch = 20;
     54  xDomain = 30;
     55  yDomain0 = 464;
     56  DomainPitch = 40;
     57  xFeature = 38;
     58  yFeature0 = 42;
     59  xWeight = 100;
     60  yWeight0 = 271;
     61  xTotal = 20;
     62  xTotal2 = 34;
     63  yTotal0 = 354;
     64  xView = 17;
     65  yView0 = 283;
    5866
    5967procedure TDraftDlg.FormCreate(Sender: TObject);
    6068begin
    61 inherited;
    62 InitButtons();
    63 HelpContext:='CLASSES';
    64 Caption:=Phrases.Lookup('TITLE_DRAFT');
    65 OKBtn.Caption:=Phrases.Lookup('BTN_OK');
    66 
    67 if not Phrases2FallenBackToEnglish then
    68   begin
    69   GroundArea.Hint:=Phrases2.Lookup('DRAFTDOMAIN',0);
    70   SeaArea.Hint:=Phrases2.Lookup('DRAFTDOMAIN',1);
    71   AirArea.Hint:=Phrases2.Lookup('DRAFTDOMAIN',2);
     69  inherited;
     70  InitButtons();
     71  HelpContext := 'CLASSES';
     72  Caption := Phrases.Lookup('TITLE_DRAFT');
     73  OKBtn.Caption := Phrases.Lookup('BTN_OK');
     74
     75  if not Phrases2FallenBackToEnglish then
     76  begin
     77    GroundArea.Hint := Phrases2.Lookup('DRAFTDOMAIN', 0);
     78    SeaArea.Hint := Phrases2.Lookup('DRAFTDOMAIN', 1);
     79    AirArea.Hint := Phrases2.Lookup('DRAFTDOMAIN', 2);
    7280  end
    73 else
    74   begin
    75   GroundArea.Hint:=Phrases.Lookup('DOMAIN',0);
    76   SeaArea.Hint:=Phrases.Lookup('DOMAIN',1);
    77   AirArea.Hint:=Phrases.Lookup('DOMAIN',2);
    78   end;
    79 
    80 Back:=TBitmap.Create;
    81 Back.PixelFormat:=pf24bit;
    82 Back.Width:=ClientWidth; Back.Height:=ClientHeight;
    83 Template:=TBitmap.Create;
    84 LoadGraphicFile(Template, HomeDir+'Graphics\MiliRes', gfNoGamma);
    85 Template.PixelFormat:=pf8bit;
     81  else
     82  begin
     83    GroundArea.Hint := Phrases.Lookup('DOMAIN', 0);
     84    SeaArea.Hint := Phrases.Lookup('DOMAIN', 1);
     85    AirArea.Hint := Phrases.Lookup('DOMAIN', 2);
     86  end;
     87
     88  Back := TBitmap.Create;
     89  Back.PixelFormat := pf24bit;
     90  Back.Width := ClientWidth;
     91  Back.Height := ClientHeight;
     92  Template := TBitmap.Create;
     93  LoadGraphicFile(Template, HomeDir + 'Graphics\MiliRes', gfNoGamma);
     94  Template.PixelFormat := pf8bit;
    8695end;
    8796
    8897procedure TDraftDlg.FormDestroy(Sender: TObject);
    8998begin
    90 Template.Free;
     99  Template.Free;
    91100end;
    92101
    93102procedure TDraftDlg.CloseBtnClick(Sender: TObject);
    94103begin
    95 ModalResult:=mrCancel;
     104  ModalResult := mrCancel;
    96105end;
    97106
     
    100109  function DomainAvailable(d: integer): boolean;
    101110  begin
    102   result:=(upgrade[d,0].Preq=preNone)
    103     or (MyRO.Tech[upgrade[d,0].Preq]>=tsApplicable);
     111    result := (upgrade[d, 0].Preq = preNone) or
     112      (MyRO.Tech[upgrade[d, 0].Preq] >= tsApplicable);
    104113  end;
    105114
    106115  procedure PaintTotalBars;
    107116  var
    108   i,y,dx,num,w: integer;
    109   s: string;
    110   begin
    111   with offscreen.Canvas do
    112     begin
    113     // strength bar
    114     y:=yTotal;
    115     DarkGradient(Offscreen.Canvas,xTotal-6,y+1,184,2);
    116     DarkGradient(Offscreen.Canvas,xTotal2+172,y+1,95,2);
    117     RisedTextOut(Offscreen.Canvas,xTotal-2,y,Phrases.Lookup('UNITSTRENGTH'));
    118     RisedTextOut(Offscreen.Canvas,xTotal+112+30,y,'x'+IntToStr(MyRO.DevModel.MStrength));
    119     RisedTextOut(Offscreen.Canvas,xTotal2+148+30,y,'=');
    120     s:=IntToStr(MyRO.DevModel.Attack)+'/'+IntToStr(MyRO.DevModel.Defense);
    121     RisedTextOut(Offscreen.Canvas,xTotal2+170+64+30-BiColorTextWidth(Offscreen.Canvas,s),y,s);
    122 
    123     // transport bar
    124     if MyRO.DevModel.MTrans>0 then
     117    i, y, dx, num, w: integer;
     118    s: string;
     119  begin
     120    with offscreen.Canvas do
     121    begin
     122      // strength bar
     123      y := yTotal;
     124      DarkGradient(offscreen.Canvas, xTotal - 6, y + 1, 184, 2);
     125      DarkGradient(offscreen.Canvas, xTotal2 + 172, y + 1, 95, 2);
     126      RisedTextOut(offscreen.Canvas, xTotal - 2, y,
     127        Phrases.Lookup('UNITSTRENGTH'));
     128      RisedTextOut(offscreen.Canvas, xTotal + 112 + 30, y,
     129        'x' + IntToStr(MyRO.DevModel.MStrength));
     130      RisedTextOut(offscreen.Canvas, xTotal2 + 148 + 30, y, '=');
     131      s := IntToStr(MyRO.DevModel.Attack) + '/' +
     132        IntToStr(MyRO.DevModel.Defense);
     133      RisedTextOut(offscreen.Canvas, xTotal2 + 170 + 64 + 30 -
     134        BiColorTextWidth(offscreen.Canvas, s), y, s);
     135
     136      // transport bar
     137      if MyRO.DevModel.MTrans > 0 then
    125138      begin
    126       y:=yTotal+19;
    127       DarkGradient(Offscreen.Canvas,xTotal-6,y+1,184,1);
    128       DarkGradient(Offscreen.Canvas,xTotal2+172,y+1,95,1);
    129       RisedTextOut(Offscreen.Canvas,xTotal-2,y,Phrases.Lookup('UNITTRANSPORT'));
    130       RisedTextOut(Offscreen.Canvas,xTotal+112+30,y,'x'+IntToStr(MyRO.DevModel.MTrans));
    131       RisedTextOut(Offscreen.Canvas,xTotal2+148+30,y,'=');
    132 
    133       Font.Color:=$000000;
    134       dx:=-237-30;
    135       for i:=mcFirstNonCap-1 downto 3 do
    136         if i in [mcSeaTrans,mcCarrier,mcAirTrans] then
     139        y := yTotal + 19;
     140        DarkGradient(offscreen.Canvas, xTotal - 6, y + 1, 184, 1);
     141        DarkGradient(offscreen.Canvas, xTotal2 + 172, y + 1, 95, 1);
     142        RisedTextOut(offscreen.Canvas, xTotal - 2, y,
     143          Phrases.Lookup('UNITTRANSPORT'));
     144        RisedTextOut(offscreen.Canvas, xTotal + 112 + 30, y,
     145          'x' + IntToStr(MyRO.DevModel.MTrans));
     146        RisedTextOut(offscreen.Canvas, xTotal2 + 148 + 30, y, '=');
     147
     148        Font.Color := $000000;
     149        dx := -237 - 30;
     150        for i := mcFirstNonCap - 1 downto 3 do
     151          if i in [mcSeaTrans, mcCarrier, mcAirTrans] then
    137152          begin
    138           num:=MyRO.DevModel.Cap[i]*MyRO.DevModel.MTrans;
    139           if num>0 then
     153            num := MyRO.DevModel.Cap[i] * MyRO.DevModel.MTrans;
     154            if num > 0 then
    140155            begin
    141             inc(dx,15);
    142             Brush.Color:=$C0C0C0;
    143             FrameRect(Rect(xTotal2-3-dx,y+2,xTotal2+11-dx,y+16));
    144             Brush.Style:=bsClear;
    145             Sprite(Offscreen,HGrSystem,xTotal2-1-dx,y+4,10,10,66+i mod 11 *11,137+i div 11 *11);
    146             if num>1 then
     156              inc(dx, 15);
     157              Brush.Color := $C0C0C0;
     158              FrameRect(Rect(xTotal2 - 3 - dx, y + 2,
     159                xTotal2 + 11 - dx, y + 16));
     160              Brush.Style := bsClear;
     161              Sprite(offscreen, HGrSystem, xTotal2 - 1 - dx, y + 4, 10, 10,
     162                66 + i mod 11 * 11, 137 + i div 11 * 11);
     163              if num > 1 then
    147164              begin
    148               s:=IntToStr(num);
    149               w:=TextWidth(s);
    150               inc(dx,w+1);
    151               Brush.Color:=$FFFFFF;
    152               FillRect(Rect(xTotal2-3-dx,y+2,xTotal2+w-1-dx,y+16));
    153               Brush.Style:=bsClear;
    154               Textout(xTotal2-3-dx+1,y,s);
     165                s := IntToStr(num);
     166                w := TextWidth(s);
     167                inc(dx, w + 1);
     168                Brush.Color := $FFFFFF;
     169                FillRect(Rect(xTotal2 - 3 - dx, y + 2,
     170                  xTotal2 + w - 1 - dx, y + 16));
     171                Brush.Style := bsClear;
     172                Textout(xTotal2 - 3 - dx + 1, y, s);
    155173              end;
    156174            end;
     
    158176      end;
    159177
    160     // speed bar
    161     y:=yTotal+38;
    162     LoweredTextOut(offscreen.Canvas,-1,MainTexture,xTotal-2,y,Phrases.Lookup('UNITSPEED'));
    163     DLine(offscreen.Canvas,xTotal-2,xTotal+174,y+16,MainTexture.clBevelShade,
    164       MainTexture.clBevelLight);
    165     DLine(offscreen.Canvas,xTotal2+176,xTotal2+263,y+16,MainTexture.clBevelShade,
    166       MainTexture.clBevelLight);
    167     s:=MovementToString(MyRO.DevModel.Speed);
    168     RisedTextOut(offscreen.Canvas,xTotal2+170+64+30-TextWidth(s),y,s);
    169 
    170     // cost bar
    171     y:=yTotal+57;
    172     LoweredTextOut(offscreen.Canvas,-1,MainTexture,xTotal-2,y,Phrases.Lookup('UNITCOST'));
    173     LoweredTextOut(Offscreen.Canvas,-1,MainTexture,xTotal+112+30,y,'x'+IntToStr(MyRO.DevModel.MCost));
    174     LoweredTextOut(Offscreen.Canvas,-1,MainTexture,xTotal2+148+30,y,'=');
    175     DLine(offscreen.Canvas,xTotal-2,xTotal+174,y+16,MainTexture.clBevelShade,
    176       MainTexture.clBevelLight);
    177     DLine(offscreen.Canvas,xTotal2+176,xTotal2+263,y+16,MainTexture.clBevelShade,
    178       MainTexture.clBevelLight);
    179     s:=IntToStr(MyRO.DevModel.Cost);
    180     RisedTextOut(offscreen.Canvas,xTotal2+170+64+30-12-TextWidth(s),y,s);
    181     Sprite(offscreen,HGrSystem,xTotal2+170+54+30,y+4,10,10,88,115);
    182 
    183     if G.Difficulty[me]<>2 then
     178      // speed bar
     179      y := yTotal + 38;
     180      LoweredTextOut(offscreen.Canvas, -1, MainTexture, xTotal - 2, y,
     181        Phrases.Lookup('UNITSPEED'));
     182      DLine(offscreen.Canvas, xTotal - 2, xTotal + 174, y + 16,
     183        MainTexture.clBevelShade, MainTexture.clBevelLight);
     184      DLine(offscreen.Canvas, xTotal2 + 176, xTotal2 + 263, y + 16,
     185        MainTexture.clBevelShade, MainTexture.clBevelLight);
     186      s := MovementToString(MyRO.DevModel.Speed);
     187      RisedTextOut(offscreen.Canvas, xTotal2 + 170 + 64 + 30 -
     188        TextWidth(s), y, s);
     189
     190      // cost bar
     191      y := yTotal + 57;
     192      LoweredTextOut(offscreen.Canvas, -1, MainTexture, xTotal - 2, y,
     193        Phrases.Lookup('UNITCOST'));
     194      LoweredTextOut(offscreen.Canvas, -1, MainTexture, xTotal + 112 + 30, y,
     195        'x' + IntToStr(MyRO.DevModel.MCost));
     196      LoweredTextOut(offscreen.Canvas, -1, MainTexture,
     197        xTotal2 + 148 + 30, y, '=');
     198      DLine(offscreen.Canvas, xTotal - 2, xTotal + 174, y + 16,
     199        MainTexture.clBevelShade, MainTexture.clBevelLight);
     200      DLine(offscreen.Canvas, xTotal2 + 176, xTotal2 + 263, y + 16,
     201        MainTexture.clBevelShade, MainTexture.clBevelLight);
     202      s := IntToStr(MyRO.DevModel.Cost);
     203      RisedTextOut(offscreen.Canvas, xTotal2 + 170 + 64 + 30 - 12 -
     204        TextWidth(s), y, s);
     205      Sprite(offscreen, HGrSystem, xTotal2 + 170 + 54 + 30, y + 4, 10,
     206        10, 88, 115);
     207
     208      if G.Difficulty[me] <> 2 then
    184209      begin // corrected cost bar
    185       y:=yTotal+76;
    186       LoweredTextOut(offscreen.Canvas,-1,MainTexture,xTotal-2,y,
    187         Phrases.Lookup('COSTDIFF'+char(48+G.Difficulty[me])));
    188       LoweredTextOut(Offscreen.Canvas,-1,MainTexture,xTotal2+148+30,y,'=');
    189       DLine(offscreen.Canvas,xTotal-2,xTotal+174,y+16,MainTexture.clBevelShade,
    190         MainTexture.clBevelLight);
    191       DLine(offscreen.Canvas,xTotal2+176,xTotal2+263,y+16,MainTexture.clBevelShade,
    192         MainTexture.clBevelLight);
    193       s:=IntToStr(MyRO.DevModel.Cost*BuildCostMod[G.Difficulty[me]] div 12);
    194       RisedTextOut(offscreen.Canvas,xTotal2+170+64+30-12-TextWidth(s),y,s);
    195       Sprite(offscreen,HGrSystem,xTotal2+170+54+30,y+4,10,10,88,115);
     210        y := yTotal + 76;
     211        LoweredTextOut(offscreen.Canvas, -1, MainTexture, xTotal - 2, y,
     212          Phrases.Lookup('COSTDIFF' + char(48 + G.Difficulty[me])));
     213        LoweredTextOut(offscreen.Canvas, -1, MainTexture,
     214          xTotal2 + 148 + 30, y, '=');
     215        DLine(offscreen.Canvas, xTotal - 2, xTotal + 174, y + 16,
     216          MainTexture.clBevelShade, MainTexture.clBevelLight);
     217        DLine(offscreen.Canvas, xTotal2 + 176, xTotal2 + 263, y + 16,
     218          MainTexture.clBevelShade, MainTexture.clBevelLight);
     219        s := IntToStr(MyRO.DevModel.Cost * BuildCostMod
     220          [G.Difficulty[me]] div 12);
     221        RisedTextOut(offscreen.Canvas, xTotal2 + 170 + 64 + 30 - 12 -
     222          TextWidth(s), y, s);
     223        Sprite(offscreen, HGrSystem, xTotal2 + 170 + 54 + 30, y + 4, 10,
     224          10, 88, 115);
    196225      end;
    197226    end;
     
    199228
    200229var
    201 i,j,x,d,n,TextColor,CapWeight,DomainCount: integer;
    202 begin
    203 inherited;
    204 
    205 ClientHeight:=Template.Height-Cut;
    206 if ClientHeight>hMainTexture then // assemble background from 2 texture tiles
    207   begin
    208   bitblt(Back.Canvas.Handle,0,0,ClientWidth,64,MainTexture.Image.Canvas.Handle,
    209     (wMainTexture-ClientWidth) div 2,hMainTexture-64,SRCCOPY);
    210   bitblt(Back.Canvas.Handle,0,64,ClientWidth,ClientHeight-64,
    211     MainTexture.Image.Canvas.Handle,(wMainTexture-ClientWidth) div 2,0,SRCCOPY);
     230  i, j, x, d, n, TextColor, CapWeight, DomainCount: integer;
     231begin
     232  inherited;
     233
     234  ClientHeight := Template.Height - Cut;
     235  if ClientHeight > hMainTexture then
     236  // assemble background from 2 texture tiles
     237  begin
     238    bitblt(Back.Canvas.Handle, 0, 0, ClientWidth, 64,
     239      MainTexture.Image.Canvas.Handle, (wMainTexture - ClientWidth) div 2,
     240      hMainTexture - 64, SRCCOPY);
     241    bitblt(Back.Canvas.Handle, 0, 64, ClientWidth, ClientHeight - 64,
     242      MainTexture.Image.Canvas.Handle, (wMainTexture - ClientWidth) div 2,
     243      0, SRCCOPY);
    212244  end
    213 else bitblt(Back.Canvas.Handle,0,0,ClientWidth,ClientHeight,MainTexture.Image.Canvas.Handle,
    214   (wMainTexture-ClientWidth) div 2,(hMainTexture-ClientHeight) div 2,SRCCOPY);
    215 ImageOp_B(Back,Template,0,0,0,0,Template.Width,64);
    216 ImageOp_B(Back,Template,0,64,0,64+Cut,Template.Width,Template.Height-64-Cut);
    217 
    218 bitblt(offscreen.canvas.handle,0,0,ClientWidth,ClientHeight,Back.Canvas.handle,0,0,SRCCOPY);
    219 
    220 offscreen.Canvas.Font.Assign(UniFont[ftCaption]);
    221 RisedTextout(offscreen.Canvas,10,7,Caption);
    222 offscreen.Canvas.Font.Assign(UniFont[ftSmall]);
    223 
    224 with MyRO.DevModel do
    225   begin
    226   DomainCount:=0;
    227   for d:=0 to nDomains-1 do
    228     if DomainAvailable(d) then
    229       inc(DomainCount);
    230   if DomainCount>1 then
    231     begin
    232     for d:=0 to nDomains-1 do
     245  else
     246    bitblt(Back.Canvas.Handle, 0, 0, ClientWidth, ClientHeight,
     247      MainTexture.Image.Canvas.Handle, (wMainTexture - ClientWidth) div 2,
     248      (hMainTexture - ClientHeight) div 2, SRCCOPY);
     249  ImageOp_B(Back, Template, 0, 0, 0, 0, Template.Width, 64);
     250  ImageOp_B(Back, Template, 0, 64, 0, 64 + Cut, Template.Width,
     251    Template.Height - 64 - Cut);
     252
     253  bitblt(offscreen.Canvas.Handle, 0, 0, ClientWidth, ClientHeight,
     254    Back.Canvas.Handle, 0, 0, SRCCOPY);
     255
     256  offscreen.Canvas.Font.Assign(UniFont[ftCaption]);
     257  RisedTextOut(offscreen.Canvas, 10, 7, Caption);
     258  offscreen.Canvas.Font.Assign(UniFont[ftSmall]);
     259
     260  with MyRO.DevModel do
     261  begin
     262    DomainCount := 0;
     263    for d := 0 to nDomains - 1 do
    233264      if DomainAvailable(d) then
     265        inc(DomainCount);
     266    if DomainCount > 1 then
     267    begin
     268      for d := 0 to nDomains - 1 do
     269        if DomainAvailable(d) then
    234270        begin
    235         x:=xDomain+d*DomainPitch;
    236         if d=Domain then
    237           ImageOp_BCC(Offscreen,Templates,x,yDomain,142,246+37*d,36,36,0,$00C0FF)
    238         else ImageOp_BCC(Offscreen,Templates,x,yDomain,142,246+37*d,36,36,0,$606060);
     271          x := xDomain + d * DomainPitch;
     272          if d = Domain then
     273            ImageOp_BCC(offscreen, Templates, x, yDomain, 142, 246 + 37 * d, 36,
     274              36, 0, $00C0FF)
     275          else
     276            ImageOp_BCC(offscreen, Templates, x, yDomain, 142, 246 + 37 * d, 36,
     277              36, 0, $606060);
    239278        end;
    240     Frame(Offscreen.Canvas,xDomain-11,yDomain-3,xDomain+2*DomainPitch+46,
    241       yDomain+38,$B0B0B0,$FFFFFF);
    242     RFrame(Offscreen.Canvas,xDomain-12,yDomain-4,xDomain+2*DomainPitch+47,
    243       yDomain+39,$FFFFFF,$B0B0B0);
    244     end;
    245   GroundArea.Top:=yDomain;
    246   GroundArea.Visible:=DomainAvailable(dGround);
    247   SeaArea.Top:=yDomain;
    248   SeaArea.Visible:=DomainAvailable(dSea);
    249   AirArea.Top:=yDomain;
    250   AirArea.Visible:=DomainAvailable(dAir);
    251 
    252   PaintTotalBars;
    253 
    254   // display weight
    255   with offscreen.Canvas do
    256     begin
    257     for i:=0 to MaxWeight-1 do
    258       if i<Weight then
    259         ImageOp_BCC(Offscreen,Templates,xWeight+20*i,
    260           yWeight,123,400,18,20,0,$949494)
    261       else ImageOp_BCC(Offscreen,Templates,xWeight+20*i,
    262         yWeight,105,400,18,20,0,$949494);
    263     end;
    264 
    265   with offscreen.Canvas do for i:=0 to Lines-1 do
    266     begin
    267     if not (code[i] in AutoFeature) then
     279      Frame(offscreen.Canvas, xDomain - 11, yDomain - 3,
     280        xDomain + 2 * DomainPitch + 46, yDomain + 38, $B0B0B0, $FFFFFF);
     281      RFrame(offscreen.Canvas, xDomain - 12, yDomain - 4,
     282        xDomain + 2 * DomainPitch + 47, yDomain + 39, $FFFFFF, $B0B0B0);
     283    end;
     284    GroundArea.Top := yDomain;
     285    GroundArea.Visible := DomainAvailable(dGround);
     286    SeaArea.Top := yDomain;
     287    SeaArea.Visible := DomainAvailable(dSea);
     288    AirArea.Top := yDomain;
     289    AirArea.Visible := DomainAvailable(dAir);
     290
     291    PaintTotalBars;
     292
     293    // display weight
     294    with offscreen.Canvas do
     295    begin
     296      for i := 0 to MaxWeight - 1 do
     297        if i < Weight then
     298          ImageOp_BCC(offscreen, Templates, xWeight + 20 * i, yWeight, 123, 400,
     299            18, 20, 0, $949494)
     300        else
     301          ImageOp_BCC(offscreen, Templates, xWeight + 20 * i, yWeight, 105, 400,
     302            18, 20, 0, $949494);
     303    end;
     304
     305    with offscreen.Canvas do
     306      for i := 0 to Lines - 1 do
    268307      begin
    269       // paint +/- butttons
    270       if code[i]<mcFirstNonCap then
     308        if not(code[i] in AutoFeature) then
    271309        begin
    272         Dump(offscreen,HGrSystem,xFeature-21,yFeature+2+LinePitch*i,
    273           12,12,169,172);
    274         Dump(offscreen,HGrSystem,xFeature-9,yFeature+2+LinePitch*i,
    275           12,12,169,159);
    276         RFrame(offscreen.Canvas,xFeature-(21+1),yFeature+2+LinePitch*i-1,
    277           xFeature-(21-24),yFeature+2+LinePitch*i+12,
    278           MainTexture.clBevelShade,MainTexture.clBevelLight);
    279         end
    280       else
    281         begin
    282         Dump(offscreen,HGrSystem,xFeature-9,yFeature+2+LinePitch*i,
    283           12,12,169,185+13*MyRO.DevModel.Cap[code[i]]);
    284         RFrame(offscreen.Canvas,xFeature-(9+1),yFeature+2+LinePitch*i-1,
    285           xFeature-(21-24),yFeature+2+LinePitch*i+12,
    286           MainTexture.clBevelShade,MainTexture.clBevelLight);
    287         end;
    288 
    289       // paint cost
    290       LightGradient(offscreen.Canvas,xFeature+34,yFeature+LinePitch*i,50,
    291         GrExt[HGrSystem].Data.Canvas.Pixels[187,137]);
    292       if (Domain=dGround) and (code[i]=mcDefense) then CapWeight:=2
    293       else CapWeight:=Feature[code[i]].Weight;
    294       n:=CapWeight+Feature[code[i]].Cost;
    295       d:=6;
    296       while (n-1)*d*2>48-10 do dec(d);
    297       for j:=0 to n-1 do
    298         if j<CapWeight then
    299           Sprite(offscreen,HGrSystem,xFeature+54+(j*2+1-n)*d,
    300             yFeature+2+LinePitch*i+1,10,10,88,126)
    301           else Sprite(offscreen,HGrSystem,xFeature+54+(j*2+1-n)*d,
    302             yFeature+2+LinePitch*i+1,10,10,88,115);
    303       end; // if not (code[i] in AutoFeature)
    304     DarkGradient(offscreen.Canvas,xFeature+17,yFeature+LinePitch*i,16,1);
    305     Frame(offscreen.canvas,xFeature+18,yFeature+1+LinePitch*i,
    306       xFeature+20-2+13,yFeature+2+1-2+13+LinePitch*i,$C0C0C0,$C0C0C0);
    307     Sprite(offscreen,HGrSystem,xFeature+20,yFeature+2+1+LinePitch*i,
    308       10,10,66+code[i] mod 11 *11,137+code[i] div 11 *11);
    309 
    310     if MyRO.DevModel.Cap[code[i]]>0 then TextColor:=MainTexture.clLitText
    311     else TextColor:=-1;
    312 
    313     if code[i]<mcFirstNonCap then
    314       LoweredTextOut(offscreen.Canvas,TextColor,MainTexture,xFeature+7,
    315         yFeature+LinePitch*i-1,IntToStr(MyRO.DevModel.Cap[code[i]]));
    316     LoweredTextOut(offscreen.Canvas,TextColor,MainTexture,xFeature+88,
    317       yFeature+LinePitch*i-1,Phrases.Lookup('FEATURES',code[i]));
    318     end;
    319   end;
    320 
    321 // free features
    322 j:=0;
    323 for i:=0 to nFeature-1 do
    324   if (i in AutoFeature)
    325     and (1 shl Domain and Feature[i].Domains<>0) and (Feature[i].Preq<>preNA)
    326     and ((Feature[i].Preq=preSun) and (MyRO.Wonder[woSun].EffectiveOwner=me)
    327       or (Feature[i].Preq>=0) and (MyRO.Tech[Feature[i].Preq]>=tsApplicable))
    328     and not ((Feature[i].Preq=adSteamEngine)
    329       and (MyRO.Tech[adNuclearPower]>=tsApplicable)) then
    330     begin
    331     DarkGradient(offscreen.Canvas,xWeight+4,yWeight+32+LinePitch*j,16,1);
    332     Frame(offscreen.canvas,xWeight+5,yWeight+33+LinePitch*j,
    333       xWeight+18,yWeight+47+LinePitch*j,$C0C0C0,$C0C0C0);
    334     Sprite(offscreen,HGrSystem,xWeight+7,yWeight+36+LinePitch*j,
    335       10,10,66+i mod 11 *11,137+i div 11 *11);
    336     LoweredTextOut(offscreen.Canvas,-1,MainTexture,xWeight+26,
    337       yWeight+31+LinePitch*j,Phrases.Lookup('FEATURES',i));
    338     inc(j);
    339     end;
    340 
    341 with Tribe[me].ModelPicture[MyRO.nModel] do
    342   begin
    343   FrameImage(offscreen.canvas,BigImp,xView+4,yView+4,xSizeBig,ySizeBig,0,0);
    344   Sprite(offscreen,HGr,xView,yView,64,44,pix mod 10 *65+1,pix div 10*49+1);
    345   end;
    346 MarkUsedOffscreen(ClientWidth,ClientHeight);
    347 end;{MainPaint}
     310          // paint +/- butttons
     311          if code[i] < mcFirstNonCap then
     312          begin
     313            Dump(offscreen, HGrSystem, xFeature - 21, yFeature + 2 + LinePitch *
     314              i, 12, 12, 169, 172);
     315            Dump(offscreen, HGrSystem, xFeature - 9, yFeature + 2 + LinePitch *
     316              i, 12, 12, 169, 159);
     317            RFrame(offscreen.Canvas, xFeature - (21 + 1),
     318              yFeature + 2 + LinePitch * i - 1, xFeature - (21 - 24),
     319              yFeature + 2 + LinePitch * i + 12, MainTexture.clBevelShade,
     320              MainTexture.clBevelLight);
     321          end
     322          else
     323          begin
     324            Dump(offscreen, HGrSystem, xFeature - 9, yFeature + 2 + LinePitch *
     325              i, 12, 12, 169, 185 + 13 * MyRO.DevModel.Cap[code[i]]);
     326            RFrame(offscreen.Canvas, xFeature - (9 + 1),
     327              yFeature + 2 + LinePitch * i - 1, xFeature - (21 - 24),
     328              yFeature + 2 + LinePitch * i + 12, MainTexture.clBevelShade,
     329              MainTexture.clBevelLight);
     330          end;
     331
     332          // paint cost
     333          LightGradient(offscreen.Canvas, xFeature + 34,
     334            yFeature + LinePitch * i, 50, GrExt[HGrSystem].Data.Canvas.Pixels
     335            [187, 137]);
     336          if (Domain = dGround) and (code[i] = mcDefense) then
     337            CapWeight := 2
     338          else
     339            CapWeight := Feature[code[i]].Weight;
     340          n := CapWeight + Feature[code[i]].Cost;
     341          d := 6;
     342          while (n - 1) * d * 2 > 48 - 10 do
     343            dec(d);
     344          for j := 0 to n - 1 do
     345            if j < CapWeight then
     346              Sprite(offscreen, HGrSystem, xFeature + 54 + (j * 2 + 1 - n) * d,
     347                yFeature + 2 + LinePitch * i + 1, 10, 10, 88, 126)
     348            else
     349              Sprite(offscreen, HGrSystem, xFeature + 54 + (j * 2 + 1 - n) * d,
     350                yFeature + 2 + LinePitch * i + 1, 10, 10, 88, 115);
     351        end; // if not (code[i] in AutoFeature)
     352        DarkGradient(offscreen.Canvas, xFeature + 17,
     353          yFeature + LinePitch * i, 16, 1);
     354        Frame(offscreen.Canvas, xFeature + 18, yFeature + 1 + LinePitch * i,
     355          xFeature + 20 - 2 + 13, yFeature + 2 + 1 - 2 + 13 + LinePitch * i,
     356          $C0C0C0, $C0C0C0);
     357        Sprite(offscreen, HGrSystem, xFeature + 20, yFeature + 2 + 1 + LinePitch
     358          * i, 10, 10, 66 + code[i] mod 11 * 11, 137 + code[i] div 11 * 11);
     359
     360        if MyRO.DevModel.Cap[code[i]] > 0 then
     361          TextColor := MainTexture.clLitText
     362        else
     363          TextColor := -1;
     364
     365        if code[i] < mcFirstNonCap then
     366          LoweredTextOut(offscreen.Canvas, TextColor, MainTexture, xFeature + 7,
     367            yFeature + LinePitch * i - 1, IntToStr(MyRO.DevModel.Cap[code[i]]));
     368        LoweredTextOut(offscreen.Canvas, TextColor, MainTexture, xFeature + 88,
     369          yFeature + LinePitch * i - 1, Phrases.Lookup('FEATURES', code[i]));
     370      end;
     371  end;
     372
     373  // free features
     374  j := 0;
     375  for i := 0 to nFeature - 1 do
     376    if (i in AutoFeature) and (1 shl Domain and Feature[i].Domains <> 0) and
     377      (Feature[i].Preq <> preNA) and
     378      ((Feature[i].Preq = preSun) and (MyRO.Wonder[woSun].EffectiveOwner = me)
     379      or (Feature[i].Preq >= 0) and (MyRO.Tech[Feature[i].Preq] >= tsApplicable)
     380      ) and not((Feature[i].Preq = adSteamEngine) and
     381      (MyRO.Tech[adNuclearPower] >= tsApplicable)) then
     382    begin
     383      DarkGradient(offscreen.Canvas, xWeight + 4, yWeight + 32 + LinePitch
     384        * j, 16, 1);
     385      Frame(offscreen.Canvas, xWeight + 5, yWeight + 33 + LinePitch * j,
     386        xWeight + 18, yWeight + 47 + LinePitch * j, $C0C0C0, $C0C0C0);
     387      Sprite(offscreen, HGrSystem, xWeight + 7, yWeight + 36 + LinePitch * j,
     388        10, 10, 66 + i mod 11 * 11, 137 + i div 11 * 11);
     389      LoweredTextOut(offscreen.Canvas, -1, MainTexture, xWeight + 26,
     390        yWeight + 31 + LinePitch * j, Phrases.Lookup('FEATURES', i));
     391      inc(j);
     392    end;
     393
     394  with Tribe[me].ModelPicture[MyRO.nModel] do
     395  begin
     396    FrameImage(offscreen.Canvas, BigImp, xView + 4, yView + 4, xSizeBig,
     397      ySizeBig, 0, 0);
     398    Sprite(offscreen, HGr, xView, yView, 64, 44, pix mod 10 * 65 + 1,
     399      pix div 10 * 49 + 1);
     400  end;
     401  MarkUsedOffscreen(ClientWidth, ClientHeight);
     402end; { MainPaint }
    348403
    349404procedure TDraftDlg.SetDomain(d: integer);
     
    351406  function Prio(fix: integer): integer;
    352407  var
    353   FeaturePreq: integer;
    354   begin
    355   FeaturePreq:=Feature[fix].Preq;
    356   assert(FeaturePreq<>preNA);
    357   if fix<mcFirstNonCap then result:=10000+fix
    358   else if FeaturePreq=preNone then result:=20000
    359   else if FeaturePreq<0 then result:=40000
    360   else result:=30000+AdvValue[FeaturePreq];
    361   if not (fix in AutoFeature) then inc(result,90000);
     408    FeaturePreq: integer;
     409  begin
     410    FeaturePreq := Feature[fix].Preq;
     411    assert(FeaturePreq <> preNA);
     412    if fix < mcFirstNonCap then
     413      result := 10000 + fix
     414    else if FeaturePreq = preNone then
     415      result := 20000
     416    else if FeaturePreq < 0 then
     417      result := 40000
     418    else
     419      result := 30000 + AdvValue[FeaturePreq];
     420    if not(fix in AutoFeature) then
     421      inc(result, 90000);
    362422  end;
    363423
    364424var
    365 i,j,x: integer;
    366 begin
    367 Domain:=d;
    368 Lines:=0;
    369 for i:=0 to nFeature-1 do
    370   if IsFeatureInList(Domain,i) then
    371     begin code[Lines]:=i; inc(Lines) end;
    372 yFeature:=yFeature0+(MaxLines-Lines)*LinePitch div 2;
    373 
    374 // sort features
    375 for i:=0 to Lines-2 do for j:=i+1 to Lines-1 do
    376   if Prio(code[i])>Prio(code[j]) then
    377     begin // exchange
    378     x:=code[i];
    379     code[i]:=code[j];
    380     code[j]:=x
    381     end;
    382 end;
    383 
    384 function TDraftDlg.IsFeatureInList(d,i: integer): boolean;
    385 begin
    386 result:= not (i in AutoFeature)
    387   and (1 shl d and Feature[i].Domains<>0) and (Feature[i].Preq<>preNA)
    388   and ((Feature[i].Preq=preNone)
    389     or (Feature[i].Preq=preSun) and (MyRO.Wonder[woSun].EffectiveOwner=me)
    390     or (Feature[i].Preq>=0) and (MyRO.Tech[Feature[i].Preq]>=tsApplicable));
     425  i, j, x: integer;
     426begin
     427  Domain := d;
     428  Lines := 0;
     429  for i := 0 to nFeature - 1 do
     430    if IsFeatureInList(Domain, i) then
     431    begin
     432      code[Lines] := i;
     433      inc(Lines)
     434    end;
     435  yFeature := yFeature0 + (MaxLines - Lines) * LinePitch div 2;
     436
     437  // sort features
     438  for i := 0 to Lines - 2 do
     439    for j := i + 1 to Lines - 1 do
     440      if Prio(code[i]) > Prio(code[j]) then
     441      begin // exchange
     442        x := code[i];
     443        code[i] := code[j];
     444        code[j] := x
     445      end;
     446end;
     447
     448function TDraftDlg.IsFeatureInList(d, i: integer): boolean;
     449begin
     450  result := not(i in AutoFeature) and (1 shl d and Feature[i].Domains <> 0) and
     451    (Feature[i].Preq <> preNA) and
     452    ((Feature[i].Preq = preNone) or (Feature[i].Preq = preSun) and
     453    (MyRO.Wonder[woSun].EffectiveOwner = me) or (Feature[i].Preq >= 0) and
     454    (MyRO.Tech[Feature[i].Preq] >= tsApplicable));
    391455end;
    392456
    393457procedure TDraftDlg.FormShow(Sender: TObject);
    394458var
    395 count,d,i: integer;
    396 begin
    397 Domain:=dGround;
    398 while (Domain<dAir) and (upgrade[Domain,0].Preq<>preNone)
    399   and (MyRO.Tech[upgrade[Domain,0].Preq]<tsApplicable) do inc(Domain);
    400 
    401 // count max number of features in any domain
    402 MaxLines:=0;
    403 for d:=0 to nDomains-1 do
    404   if (upgrade[d,0].Preq=preNone)
    405     or (MyRO.Tech[upgrade[d,0].Preq]>=tsApplicable) then
    406     begin
    407     count:=0;
    408     for i:=0 to nFeature-1 do
    409       if IsFeatureInList(d,i) then
    410         inc(count);
    411     if count>MaxLines then
    412       MaxLines:=count;
    413     end;
    414 Cut:=(MaxLines0-MaxLines)*LinePitch;
    415 OKBtn.Top:=477-Cut;
    416 yDomain:=yDomain0-Cut;
    417 yWeight:=yWeight0-Cut;
    418 yTotal:=yTotal0-Cut;
    419 yView:=yView0-Cut;
    420 
    421 if WindowMode=wmModal then
    422   begin {center on screen}
    423   Left:=(Screen.Width-Template.Width) div 2;
    424   Top:=(Screen.Height-(Template.Height-Cut)) div 2;
    425   end;
    426 
    427 SetDomain(Domain);
    428 Server(sCreateDevModel,me,Domain,nil^);
    429 MyModel[MyRO.nModel]:=MyRO.DevModel;
    430 InitMyModel(MyRO.nModel,false);
    431 OffscreenPaint;
    432 IncCap:=-1; DecCap:=-1;
     459  count, d, i: integer;
     460begin
     461  Domain := dGround;
     462  while (Domain < dAir) and (upgrade[Domain, 0].Preq <> preNone) and
     463    (MyRO.Tech[upgrade[Domain, 0].Preq] < tsApplicable) do
     464    inc(Domain);
     465
     466  // count max number of features in any domain
     467  MaxLines := 0;
     468  for d := 0 to nDomains - 1 do
     469    if (upgrade[d, 0].Preq = preNone) or
     470      (MyRO.Tech[upgrade[d, 0].Preq] >= tsApplicable) then
     471    begin
     472      count := 0;
     473      for i := 0 to nFeature - 1 do
     474        if IsFeatureInList(d, i) then
     475          inc(count);
     476      if count > MaxLines then
     477        MaxLines := count;
     478    end;
     479  Cut := (MaxLines0 - MaxLines) * LinePitch;
     480  OKBtn.Top := 477 - Cut;
     481  yDomain := yDomain0 - Cut;
     482  yWeight := yWeight0 - Cut;
     483  yTotal := yTotal0 - Cut;
     484  yView := yView0 - Cut;
     485
     486  if WindowMode = wmModal then
     487  begin { center on screen }
     488    Left := (Screen.Width - Template.Width) div 2;
     489    Top := (Screen.Height - (Template.Height - Cut)) div 2;
     490  end;
     491
     492  SetDomain(Domain);
     493  Server(sCreateDevModel, me, Domain, nil^);
     494  MyModel[MyRO.nModel] := MyRO.DevModel;
     495  InitMyModel(MyRO.nModel, false);
     496  OffscreenPaint;
     497  IncCap := -1;
     498  DecCap := -1;
    433499end;
    434500
    435501procedure TDraftDlg.ShowNewContent(NewMode: integer);
    436502begin
    437 inherited ShowNewContent(NewMode);
     503  inherited ShowNewContent(NewMode);
    438504end;
    439505
     
    441507  Shift: TShiftState; x, y: integer);
    442508var
    443 i,d: integer;
    444 begin
    445 if Button=mbLeft then
    446   begin
    447   for d:=0 to nDomains-1 do
    448     if (d<>Domain) and ((upgrade[d,0].Preq=preNone)
    449       or (MyRO.Tech[upgrade[d,0].Preq]>=tsApplicable))
    450       and (x>=xDomain+d*DomainPitch) and (x<xDomain+d*DomainPitch+36)
    451       and (y>=yDomain) and (y<yDomain+36) then
     509  i, d: integer;
     510begin
     511  if Button = mbLeft then
     512  begin
     513    for d := 0 to nDomains - 1 do
     514      if (d <> Domain) and ((upgrade[d, 0].Preq = preNone) or
     515        (MyRO.Tech[upgrade[d, 0].Preq] >= tsApplicable)) and
     516        (x >= xDomain + d * DomainPitch) and
     517        (x < xDomain + d * DomainPitch + 36) and (y >= yDomain) and
     518        (y < yDomain + 36) then
    452519      begin
    453       SetDomain(d);
    454       Server(sCreateDevModel,me,Domain,nil^);
    455       MyModel[MyRO.nModel]:=MyRO.DevModel;
    456       InitMyModel(MyRO.nModel,false);
    457       SmartUpdateContent;
     520        SetDomain(d);
     521        Server(sCreateDevModel, me, Domain, nil^);
     522        MyModel[MyRO.nModel] := MyRO.DevModel;
     523        InitMyModel(MyRO.nModel, false);
     524        SmartUpdateContent;
    458525      end;
    459526
    460   if (y>=yFeature) and (y<yFeature+LinePitch*Lines) then
    461     begin
    462     i:=(y-yFeature) div LinePitch;
    463     if (x>=xFeature-21) and (x<ClientWidth) and (ssShift in Shift) then
    464       HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkFeature, code[i])
    465     else if not (code[i] in AutoFeature) then
     527    if (y >= yFeature) and (y < yFeature + LinePitch * Lines) then
     528    begin
     529      i := (y - yFeature) div LinePitch;
     530      if (x >= xFeature - 21) and (x < ClientWidth) and (ssShift in Shift) then
     531        HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkFeature, code[i])
     532      else if not(code[i] in AutoFeature) then
    466533      begin
    467       if (code[i]<mcFirstNonCap) and (x>=xFeature-21) and (x<xFeature-21+12) then
     534        if (code[i] < mcFirstNonCap) and (x >= xFeature - 21) and
     535          (x < xFeature - 21 + 12) then
    468536        begin
    469         IncCap:=code[i];
    470         Dump(offscreen,HGrSystem,xFeature-21,yFeature+2+LinePitch*i,12,12,182,172);
    471         SmartInvalidate;
     537          IncCap := code[i];
     538          Dump(offscreen, HGrSystem, xFeature - 21, yFeature + 2 + LinePitch *
     539            i, 12, 12, 182, 172);
     540          SmartInvalidate;
    472541        end
    473       else if (x>=xFeature-9) and (x<xFeature-9+12) then
     542        else if (x >= xFeature - 9) and (x < xFeature - 9 + 12) then
    474543        begin
    475         DecCap:=code[i];
    476         if code[i]<mcFirstNonCap then
    477           Dump(offscreen,HGrSystem,xFeature-9,yFeature+2+LinePitch*i,12,12,182,159)
    478         else Dump(offscreen,HGrSystem,xFeature-9,yFeature+2+LinePitch*i,
    479           12,12,182,185+13*MyRO.DevModel.Cap[code[i]]);
    480         SmartInvalidate;
     544          DecCap := code[i];
     545          if code[i] < mcFirstNonCap then
     546            Dump(offscreen, HGrSystem, xFeature - 9, yFeature + 2 + LinePitch *
     547              i, 12, 12, 182, 159)
     548          else
     549            Dump(offscreen, HGrSystem, xFeature - 9, yFeature + 2 + LinePitch *
     550              i, 12, 12, 182, 185 + 13 * MyRO.DevModel.Cap[code[i]]);
     551          SmartInvalidate;
    481552        end;
    482553      end
     
    488559  Shift: TShiftState; x, y: integer);
    489560var
    490 NewValue: integer;
    491 begin
    492 if IncCap>=0 then
    493   begin
    494   NewValue:=MyRO.DevModel.Cap[IncCap]+1;
    495   Server(sSetDevModelCap+NewValue shl 4,me,IncCap,nil^);
    496   MyModel[MyRO.nModel]:=MyRO.DevModel;
    497   InitMyModel(MyRO.nModel,false);
    498   SmartUpdateContent;
    499   IncCap:=-1;
     561  NewValue: integer;
     562begin
     563  if IncCap >= 0 then
     564  begin
     565    NewValue := MyRO.DevModel.Cap[IncCap] + 1;
     566    Server(sSetDevModelCap + NewValue shl 4, me, IncCap, nil^);
     567    MyModel[MyRO.nModel] := MyRO.DevModel;
     568    InitMyModel(MyRO.nModel, false);
     569    SmartUpdateContent;
     570    IncCap := -1;
    500571  end
    501 else if DecCap>=0 then
    502   begin
    503   if (DecCap>=mcFirstNonCap) or (MyRO.DevModel.Cap[DecCap]>0) then
    504     begin
    505     NewValue:=MyRO.DevModel.Cap[DecCap]-1;
    506     if DecCap>=mcFirstNonCap then NewValue:=-NewValue;
    507     Server(sSetDevModelCap+NewValue shl 4,me,DecCap,nil^);
    508     MyModel[MyRO.nModel]:=MyRO.DevModel;
    509     InitMyModel(MyRO.nModel,false);
    510     end;
    511   SmartUpdateContent;
    512   DecCap:=-1;
     572  else if DecCap >= 0 then
     573  begin
     574    if (DecCap >= mcFirstNonCap) or (MyRO.DevModel.Cap[DecCap] > 0) then
     575    begin
     576      NewValue := MyRO.DevModel.Cap[DecCap] - 1;
     577      if DecCap >= mcFirstNonCap then
     578        NewValue := -NewValue;
     579      Server(sSetDevModelCap + NewValue shl 4, me, DecCap, nil^);
     580      MyModel[MyRO.nModel] := MyRO.DevModel;
     581      InitMyModel(MyRO.nModel, false);
     582    end;
     583    SmartUpdateContent;
     584    DecCap := -1;
    513585  end;
    514586end;
     
    516588procedure TDraftDlg.OKBtnClick(Sender: TObject);
    517589begin
    518 ModalResult:=mrOK;
     590  ModalResult := mrOK;
    519591end;
    520592
    521593end.
    522 
  • trunk/LocalPlayer/Enhance.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit Enhance;
    43
     
    65
    76uses
    8   ScreenTools,BaseWin,Protocol,ClientTools,Term,
     7  ScreenTools, BaseWin, Protocol, ClientTools, Term,
    98
    109  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
     
    3130    procedure TerrClick(Sender: TObject);
    3231    procedure JobClick(Sender: TObject);
    33     procedure FormKeyDown(Sender: TObject; var Key: Word;
    34       Shift: TShiftState);
     32    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    3533  public
    3634    procedure ShowNewContent(NewMode: integer; TerrType: integer = -1);
     
    5149procedure TEnhanceDlg.FormCreate(Sender: TObject);
    5250var
    53 TerrType: integer;
    54 m: TMenuItem;
    55 begin
    56 inherited;
    57 CaptionRight:=CloseBtn.Left;
    58 CaptionLeft:=ToggleBtn.Left+ToggleBtn.Width;
    59 InitButtons();
    60 HelpContext:='MACRO';
    61 Caption:=Phrases.Lookup('TITLE_ENHANCE');
    62 ToggleBtn.Hint:=Phrases.Lookup('BTN_SELECT');
    63 
    64 for TerrType:=fGrass to fMountains do if TerrType<>fJungle then
     51  TerrType: integer;
     52  m: TMenuItem;
     53begin
     54  inherited;
     55  CaptionRight := CloseBtn.Left;
     56  CaptionLeft := ToggleBtn.Left + ToggleBtn.Width;
     57  InitButtons();
     58  HelpContext := 'MACRO';
     59  Caption := Phrases.Lookup('TITLE_ENHANCE');
     60  ToggleBtn.Hint := Phrases.Lookup('BTN_SELECT');
     61
     62  for TerrType := fGrass to fMountains do
     63    if TerrType <> fJungle then
     64    begin
     65      m := TMenuItem.Create(Popup);
     66      m.RadioItem := true;
     67      if TerrType = fGrass then
     68        m.Caption := Format(Phrases.Lookup('TWOTERRAINS'),
     69          [Phrases.Lookup('TERRAIN', fGrass), Phrases.Lookup('TERRAIN',
     70          fGrass + 12)])
     71      else if TerrType = fForest then
     72        m.Caption := Format(Phrases.Lookup('TWOTERRAINS'),
     73          [Phrases.Lookup('TERRAIN', fForest), Phrases.Lookup('TERRAIN',
     74          fJungle)])
     75      else
     76        m.Caption := Phrases.Lookup('TERRAIN', TerrType);
     77      m.Tag := TerrType;
     78      m.OnClick := TerrClick;
     79      Popup.Items.Add(m);
     80    end;
     81end;
     82
     83procedure TEnhanceDlg.FormPaint(Sender: TObject);
     84var
     85  i: integer;
     86begin
     87  inherited;
     88  BtnFrame(Canvas, Rect(job1.Left, job1.Top, job7.Left + job7.Width,
     89    job1.Top + job1.Height), MainTexture);
     90  BtnFrame(Canvas, Rect(job3.Left, job3.Top, job9.Left + job9.Width,
     91    job3.Top + job3.Height), MainTexture);
     92  for i := 0 to ControlCount - 1 do
     93    if Controls[i] is TButtonC then
     94      BitBlt(Canvas.Handle, Controls[i].Left + 2, Controls[i].Top - 11, 8, 8,
     95        GrExt[HGrSystem].Data.Canvas.Handle, 121 + Controls[i].Tag mod 7 * 9,
     96        1 + Controls[i].Tag div 7 * 9, SRCCOPY);
     97end;
     98
     99procedure TEnhanceDlg.FormShow(Sender: TObject);
     100begin
     101  OffscreenPaint;
     102end;
     103
     104procedure TEnhanceDlg.ShowNewContent(NewMode, TerrType: integer);
     105begin
     106  if (TerrType < fGrass) or (TerrType > fMountains) then
     107    Page := fGrass
     108  else
     109    Page := TerrType;
     110  inherited ShowNewContent(NewMode);
     111end;
     112
     113procedure TEnhanceDlg.OffscreenPaint;
     114var
     115  i, stage, TerrType, TileImp, x, EndStage, Cost, LastJob: integer;
     116  s: string;
     117  Done: Set of jNone .. jTrans;
     118  TypeChanged: boolean;
     119begin
     120  OffscreenUser := self;
     121  offscreen.Canvas.Font.Assign(UniFont[ftSmall]);
     122  FillOffscreen(0, 0, InnerWidth, InnerHeight);
     123
     124  EndStage := 0;
     125  while (EndStage < 5) and (MyData.EnhancementJobs[Page, EndStage] <> jNone) do
     126    inc(EndStage);
     127  x := InnerWidth div 2 - xxt - (xxt + 3) * EndStage;
     128
     129  TerrType := Page;
     130  TileImp := 0;
     131  Done := [];
     132  Cost := 0;
     133  for stage := 0 to EndStage do
    65134  begin
    66   m:=TMenuItem.Create(Popup);
    67   m.RadioItem:=true;
    68   if TerrType=fGrass then
    69     m.Caption:=Format(Phrases.Lookup('TWOTERRAINS'),
    70       [Phrases.Lookup('TERRAIN',fGrass), Phrases.Lookup('TERRAIN',fGrass+12)])
    71   else if TerrType=fForest then
    72     m.Caption:=Format(Phrases.Lookup('TWOTERRAINS'),
    73       [Phrases.Lookup('TERRAIN',fForest), Phrases.Lookup('TERRAIN',fJungle)])
    74   else m.Caption:=Phrases.Lookup('TERRAIN',TerrType);
    75   m.Tag:=TerrType;
    76   m.OnClick:=TerrClick;
    77   Popup.Items.Add(m);
    78   end;
    79 end;
    80 
    81 procedure TEnhanceDlg.FormPaint(Sender: TObject);
    82 var
    83 i: integer;
    84 begin
    85 inherited;
    86 BtnFrame(Canvas,Rect(job1.Left,job1.Top,job7.Left+job7.Width,job1.Top+job1.Height),MainTexture);
    87 BtnFrame(Canvas,Rect(job3.Left,job3.Top,job9.Left+job9.Width,job3.Top+job3.Height),MainTexture);
    88 for i:=0 to ControlCount-1 do if Controls[i] is TButtonC then
    89   BitBlt(Canvas.Handle,Controls[i].Left+2,Controls[i].Top-11,8,8,
    90     GrExt[HGrSystem].Data.Canvas.Handle,121+Controls[i].Tag mod 7 *9,
    91     1+Controls[i].Tag div 7 *9,SRCCOPY);
    92 end;
    93 
    94 procedure TEnhanceDlg.FormShow(Sender: TObject);
    95 begin
    96 OffscreenPaint;
    97 end;
    98 
    99 procedure TEnhanceDlg.ShowNewContent(NewMode,TerrType: integer);
    100 begin
    101 if (TerrType<fGrass) or (TerrType>fMountains) then Page:=fGrass
    102 else Page:=TerrType;
    103 inherited ShowNewContent(NewMode);
    104 end;
    105 
    106 procedure TEnhanceDlg.OffscreenPaint;
    107 var
    108 i,stage,TerrType,TileImp,x,EndStage,Cost,LastJob: integer;
    109 s: string;
    110 Done: Set of jNone..jTrans;
    111 TypeChanged: boolean;
    112 begin
    113 OffscreenUser:=self;
    114 offscreen.Canvas.Font.Assign(UniFont[ftSmall]);
    115 FillOffscreen(0,0,InnerWidth,InnerHeight);
    116 
    117 EndStage:=0;
    118 while (EndStage<5) and (MyData.EnhancementJobs[Page,EndStage]<>jNone) do
    119   inc(EndStage);
    120 x:=InnerWidth div 2-xxt-(xxt+3)*EndStage;
    121 
    122 TerrType:=Page;
    123 TileImp:=0;
    124 Done:=[];
    125 Cost:=0;
    126 for stage:=0 to EndStage do
    127   begin
    128   if stage>0 then
    129     begin
    130     Sprite(offscreen,HGrSystem,x-10,66,14,14,80,1);
    131     case MyData.EnhancementJobs[Page,stage-1] of
    132       jRoad:
    133         begin
    134         inc(Cost,Terrain[TerrType].MoveCost*RoadWork);
    135         TileImp:=TileImp or fRoad;
    136         end;
    137       jRR:
    138         begin
    139         inc(Cost,Terrain[TerrType].MoveCost*RRWork);
    140         TileImp:=TileImp or fRR;
    141         end;
    142       jIrr:
    143         begin
    144         inc(Cost,Terrain[TerrType].IrrClearWork);
    145         TileImp:=TileImp and not fTerImp or tiIrrigation;
    146         end;
    147       jFarm:
    148         begin
    149         inc(Cost,Terrain[TerrType].IrrClearWork*FarmWork);
    150         TileImp:=TileImp and not fTerImp or tiFarm;
    151         end;
    152       jMine:
    153         begin
    154         inc(Cost,Terrain[TerrType].MineAfforestWork);
    155         TileImp:=TileImp and not fTerImp or tiMine;
    156         end;
    157       jClear:
    158         begin
    159         inc(Cost,Terrain[TerrType].IrrClearWork);
    160         TerrType:=Terrain[TerrType].ClearTerrain;
    161         end;
    162       jAfforest:
    163         begin
    164         inc(Cost,Terrain[TerrType].MineAfforestWork);
    165         TerrType:=Terrain[TerrType].AfforestTerrain;
    166         end;
    167       jTrans:
    168         begin
    169         inc(Cost,Terrain[TerrType].TransWork);
    170         TerrType:=Terrain[TerrType].TransTerrain;
    171         end;
     135    if stage > 0 then
     136    begin
     137      Sprite(offscreen, HGrSystem, x - 10, 66, 14, 14, 80, 1);
     138      case MyData.EnhancementJobs[Page, stage - 1] of
     139        jRoad:
     140          begin
     141            inc(Cost, Terrain[TerrType].MoveCost * RoadWork);
     142            TileImp := TileImp or fRoad;
     143          end;
     144        jRR:
     145          begin
     146            inc(Cost, Terrain[TerrType].MoveCost * RRWork);
     147            TileImp := TileImp or fRR;
     148          end;
     149        jIrr:
     150          begin
     151            inc(Cost, Terrain[TerrType].IrrClearWork);
     152            TileImp := TileImp and not fTerImp or tiIrrigation;
     153          end;
     154        jFarm:
     155          begin
     156            inc(Cost, Terrain[TerrType].IrrClearWork * FarmWork);
     157            TileImp := TileImp and not fTerImp or tiFarm;
     158          end;
     159        jMine:
     160          begin
     161            inc(Cost, Terrain[TerrType].MineAfforestWork);
     162            TileImp := TileImp and not fTerImp or tiMine;
     163          end;
     164        jClear:
     165          begin
     166            inc(Cost, Terrain[TerrType].IrrClearWork);
     167            TerrType := Terrain[TerrType].ClearTerrain;
     168          end;
     169        jAfforest:
     170          begin
     171            inc(Cost, Terrain[TerrType].MineAfforestWork);
     172            TerrType := Terrain[TerrType].AfforestTerrain;
     173          end;
     174        jTrans:
     175          begin
     176            inc(Cost, Terrain[TerrType].TransWork);
     177            TerrType := Terrain[TerrType].TransTerrain;
     178          end;
    172179      end;
    173     include(Done,MyData.EnhancementJobs[Page,stage-1]);
    174     end;
    175 
    176   if TerrType<fForest then
    177     Sprite(offscreen,HGrTerrain,x,64-yyt,xxt*2,yyt*2,1+TerrType*(xxt*2+1),1+yyt)
    178   else
    179     begin
    180     Sprite(offscreen,HGrTerrain,x,64-yyt,xxt*2,yyt*2,1+2*(xxt*2+1),1+yyt+2*(yyt*3+1));
    181     Sprite(offscreen,HGrTerrain,x,64-yyt,xxt*2,yyt*2,1+7*(xxt*2+1),1+yyt+2*(2+TerrType-fForest)*(yyt*3+1));
    182     end;
    183   if TileImp and fTerImp=tiFarm then
    184     Sprite(offscreen,HGrTerrain,x,64-yyt,xxt*2,yyt*2,1+(xxt*2+1),1+yyt+12*(yyt*3+1))
    185   else if TileImp and fTerImp=tiIrrigation then
    186     Sprite(offscreen,HGrTerrain,x,64-yyt,xxt*2,yyt*2,1,1+yyt+12*(yyt*3+1));
    187   if TileImp and fRR<>0 then
    188     begin
    189     Sprite(offscreen,HGrTerrain,x,64-yyt,xxt*2,yyt*2,1+6*(xxt*2+1),1+yyt+10*(yyt*3+1));
    190     Sprite(offscreen,HGrTerrain,x,64-yyt,xxt*2,yyt*2,1+2*(xxt*2+1),1+yyt+10*(yyt*3+1));
     180      include(Done, MyData.EnhancementJobs[Page, stage - 1]);
     181    end;
     182
     183    if TerrType < fForest then
     184      Sprite(offscreen, HGrTerrain, x, 64 - yyt, xxt * 2, yyt * 2,
     185        1 + TerrType * (xxt * 2 + 1), 1 + yyt)
     186    else
     187    begin
     188      Sprite(offscreen, HGrTerrain, x, 64 - yyt, xxt * 2, yyt * 2,
     189        1 + 2 * (xxt * 2 + 1), 1 + yyt + 2 * (yyt * 3 + 1));
     190      Sprite(offscreen, HGrTerrain, x, 64 - yyt, xxt * 2, yyt * 2,
     191        1 + 7 * (xxt * 2 + 1), 1 + yyt + 2 * (2 + TerrType - fForest) *
     192        (yyt * 3 + 1));
     193    end;
     194    if TileImp and fTerImp = tiFarm then
     195      Sprite(offscreen, HGrTerrain, x, 64 - yyt, xxt * 2, yyt * 2,
     196        1 + (xxt * 2 + 1), 1 + yyt + 12 * (yyt * 3 + 1))
     197    else if TileImp and fTerImp = tiIrrigation then
     198      Sprite(offscreen, HGrTerrain, x, 64 - yyt, xxt * 2, yyt * 2, 1,
     199        1 + yyt + 12 * (yyt * 3 + 1));
     200    if TileImp and fRR <> 0 then
     201    begin
     202      Sprite(offscreen, HGrTerrain, x, 64 - yyt, xxt * 2, yyt * 2,
     203        1 + 6 * (xxt * 2 + 1), 1 + yyt + 10 * (yyt * 3 + 1));
     204      Sprite(offscreen, HGrTerrain, x, 64 - yyt, xxt * 2, yyt * 2,
     205        1 + 2 * (xxt * 2 + 1), 1 + yyt + 10 * (yyt * 3 + 1));
    191206    end
    192   else if TileImp and fRoad<>0 then
    193     begin
    194     Sprite(offscreen,HGrTerrain,x,64-yyt,xxt*2,yyt*2,1+6*(xxt*2+1),1+yyt+9*(yyt*3+1));
    195     Sprite(offscreen,HGrTerrain,x,64-yyt,xxt*2,yyt*2,1+2*(xxt*2+1),1+yyt+9*(yyt*3+1));
    196     end;
    197   if TileImp and fTerImp=tiMine then
    198     Sprite(offscreen,HGrTerrain,x,64-yyt,xxt*2,yyt*2,1+2*(xxt*2+1),1+yyt+12*(yyt*3+1));
    199   inc(x,xxt*2+6)
    200   end;
    201 
    202 for i:=0 to Popup.Items.Count-1 do
    203   if Popup.Items[i].Tag=Page then
    204     s:=Popup.Items[i].Caption;
    205 if Cost>0 then s:=Format(Phrases.Lookup('ENHANCE'),[s,MovementToString(Cost)]);
    206 LoweredTextOut(offscreen.Canvas,-1,MainTexture,
    207   (InnerWidth-BiColorTextWidth(Offscreen.Canvas,s)) div 2,12,s);
    208 
    209 if EndStage>0 then LastJob:=MyData.EnhancementJobs[Page,EndStage-1]
    210 else LastJob:=jNone;
    211 if jRoad in Done then job1.ButtonIndex:=3 else job1.ButtonIndex:=2;
    212 if jRR in Done then job2.ButtonIndex:=3 else job2.ButtonIndex:=2;
    213 if jIrr in Done then job4.ButtonIndex:=3 else job4.ButtonIndex:=2;
    214 if jFarm in Done then job5.ButtonIndex:=3 else job5.ButtonIndex:=2;
    215 if jMine in Done then job7.ButtonIndex:=3 else job7.ButtonIndex:=2;
    216 if LastJob=jClear then job3.ButtonIndex:=3 else job3.ButtonIndex:=2;
    217 if LastJob=jAfforest then job6.ButtonIndex:=3 else job6.ButtonIndex:=2;
    218 if LastJob=jTrans then job9.ButtonIndex:=3 else job9.ButtonIndex:=2;
    219 
    220 TypeChanged:= LastJob in [jClear, jAfforest, jTrans];
    221 job1.Visible:=(jRoad in Done) or not TypeChanged;
    222 job2.Visible:=(jRR in Done) or not TypeChanged;
    223 job4.Visible:=(jIrr in Done) or not TypeChanged and (Terrain[TerrType].IrrEff>0);
    224 job5.Visible:=(jFarm in Done) or not TypeChanged and (Terrain[TerrType].IrrEff>0);
    225 job7.Visible:=(jMine in Done) or not TypeChanged and (Terrain[TerrType].MineEff>0);
    226 job3.Visible:=not TypeChanged and (Terrain[TerrType].ClearTerrain>=0)
    227   and ((TerrType<>fDesert) or (MyRO.Wonder[woGardens].EffectiveOwner=me))
    228   or (LastJob=jClear);
    229 job6.Visible:=not TypeChanged and (Terrain[TerrType].AfforestTerrain>=0)
    230   or (LastJob=jAfforest);
    231 job9.Visible:=not TypeChanged and (Terrain[TerrType].TransTerrain>=0)
    232   or (LastJob=jTrans);
    233 
    234 MarkUsedOffscreen(InnerWidth,InnerHeight);
    235 end; {OffscreenPaint}
     207    else if TileImp and fRoad <> 0 then
     208    begin
     209      Sprite(offscreen, HGrTerrain, x, 64 - yyt, xxt * 2, yyt * 2,
     210        1 + 6 * (xxt * 2 + 1), 1 + yyt + 9 * (yyt * 3 + 1));
     211      Sprite(offscreen, HGrTerrain, x, 64 - yyt, xxt * 2, yyt * 2,
     212        1 + 2 * (xxt * 2 + 1), 1 + yyt + 9 * (yyt * 3 + 1));
     213    end;
     214    if TileImp and fTerImp = tiMine then
     215      Sprite(offscreen, HGrTerrain, x, 64 - yyt, xxt * 2, yyt * 2,
     216        1 + 2 * (xxt * 2 + 1), 1 + yyt + 12 * (yyt * 3 + 1));
     217    inc(x, xxt * 2 + 6)
     218  end;
     219
     220  for i := 0 to Popup.Items.Count - 1 do
     221    if Popup.Items[i].Tag = Page then
     222      s := Popup.Items[i].Caption;
     223  if Cost > 0 then
     224    s := Format(Phrases.Lookup('ENHANCE'), [s, MovementToString(Cost)]);
     225  LoweredTextOut(offscreen.Canvas, -1, MainTexture,
     226    (InnerWidth - BiColorTextWidth(offscreen.Canvas, s)) div 2, 12, s);
     227
     228  if EndStage > 0 then
     229    LastJob := MyData.EnhancementJobs[Page, EndStage - 1]
     230  else
     231    LastJob := jNone;
     232  if jRoad in Done then
     233    job1.ButtonIndex := 3
     234  else
     235    job1.ButtonIndex := 2;
     236  if jRR in Done then
     237    job2.ButtonIndex := 3
     238  else
     239    job2.ButtonIndex := 2;
     240  if jIrr in Done then
     241    job4.ButtonIndex := 3
     242  else
     243    job4.ButtonIndex := 2;
     244  if jFarm in Done then
     245    job5.ButtonIndex := 3
     246  else
     247    job5.ButtonIndex := 2;
     248  if jMine in Done then
     249    job7.ButtonIndex := 3
     250  else
     251    job7.ButtonIndex := 2;
     252  if LastJob = jClear then
     253    job3.ButtonIndex := 3
     254  else
     255    job3.ButtonIndex := 2;
     256  if LastJob = jAfforest then
     257    job6.ButtonIndex := 3
     258  else
     259    job6.ButtonIndex := 2;
     260  if LastJob = jTrans then
     261    job9.ButtonIndex := 3
     262  else
     263    job9.ButtonIndex := 2;
     264
     265  TypeChanged := LastJob in [jClear, jAfforest, jTrans];
     266  job1.Visible := (jRoad in Done) or not TypeChanged;
     267  job2.Visible := (jRR in Done) or not TypeChanged;
     268  job4.Visible := (jIrr in Done) or not TypeChanged and
     269    (Terrain[TerrType].IrrEff > 0);
     270  job5.Visible := (jFarm in Done) or not TypeChanged and
     271    (Terrain[TerrType].IrrEff > 0);
     272  job7.Visible := (jMine in Done) or not TypeChanged and
     273    (Terrain[TerrType].MineEff > 0);
     274  job3.Visible := not TypeChanged and (Terrain[TerrType].ClearTerrain >= 0) and
     275    ((TerrType <> fDesert) or (MyRO.Wonder[woGardens].EffectiveOwner = me)) or
     276    (LastJob = jClear);
     277  job6.Visible := not TypeChanged and (Terrain[TerrType].AfforestTerrain >= 0)
     278    or (LastJob = jAfforest);
     279  job9.Visible := not TypeChanged and (Terrain[TerrType].TransTerrain >= 0) or
     280    (LastJob = jTrans);
     281
     282  MarkUsedOffscreen(InnerWidth, InnerHeight);
     283end; { OffscreenPaint }
    236284
    237285procedure TEnhanceDlg.CloseBtnClick(Sender: TObject);
    238286begin
    239 Close
     287  Close
    240288end;
    241289
    242290procedure TEnhanceDlg.ToggleBtnClick(Sender: TObject);
    243291var
    244 i: integer;
    245 begin
    246 for i:=0 to Popup.Items.Count-1 do
    247   Popup.Items[i].Checked:= Popup.Items[i].Tag=Page;
    248 Popup.Popup(Left+ToggleBtn.Left, Top+ToggleBtn.Top+ToggleBtn.Height);
     292  i: integer;
     293begin
     294  for i := 0 to Popup.Items.Count - 1 do
     295    Popup.Items[i].Checked := Popup.Items[i].Tag = Page;
     296  Popup.Popup(Left + ToggleBtn.Left, Top + ToggleBtn.Top + ToggleBtn.Height);
    249297end;
    250298
    251299procedure TEnhanceDlg.TerrClick(Sender: TObject);
    252300begin
    253 Page:=TComponent(Sender).Tag;
    254 SmartUpdateContent
     301  Page := TComponent(Sender).Tag;
     302  SmartUpdateContent
    255303end;
    256304
    257305procedure TEnhanceDlg.JobClick(Sender: TObject);
    258306var
    259 stage, NewJob: integer;
    260 Done: Set of jNone..jTrans;
     307  stage, NewJob: integer;
     308  Done: Set of jNone .. jTrans;
    261309
    262310  procedure RemoveJob(j: integer);
    263311  begin // remove job
    264   stage:=0;
    265   while (stage<5) and (MyData.EnhancementJobs[Page,stage]<>jNone) do
    266     begin
    267     if (MyData.EnhancementJobs[Page,stage]=j) or (j=jRoad)
    268       and (MyData.EnhancementJobs[Page,stage]=jRR)
    269       or (j=jIrr) and (MyData.EnhancementJobs[Page,stage]=jFarm) then
     312    stage := 0;
     313    while (stage < 5) and (MyData.EnhancementJobs[Page, stage] <> jNone) do
     314    begin
     315      if (MyData.EnhancementJobs[Page, stage] = j) or (j = jRoad) and
     316        (MyData.EnhancementJobs[Page, stage] = jRR) or (j = jIrr) and
     317        (MyData.EnhancementJobs[Page, stage] = jFarm) then
    270318      begin
    271       if stage<4 then
    272         move(MyData.EnhancementJobs[Page,stage+1],
    273           MyData.EnhancementJobs[Page,stage],4-stage);
    274       MyData.EnhancementJobs[Page,4]:=jNone
     319        if stage < 4 then
     320          move(MyData.EnhancementJobs[Page, stage + 1],
     321            MyData.EnhancementJobs[Page, stage], 4 - stage);
     322        MyData.EnhancementJobs[Page, 4] := jNone
    275323      end
    276     else inc(stage);
    277     end;
    278   end;
    279 
    280 begin
    281 NewJob:=TButtonC(Sender).Tag;
    282 Done:=[];
    283 stage:=0;
    284 while (stage<5) and (MyData.EnhancementJobs[Page,stage]<>jNone) do
     324      else
     325        inc(stage);
     326    end;
     327  end;
     328
     329begin
     330  NewJob := TButtonC(Sender).Tag;
     331  Done := [];
     332  stage := 0;
     333  while (stage < 5) and (MyData.EnhancementJobs[Page, stage] <> jNone) do
    285334  begin
    286   include(Done, MyData.EnhancementJobs[Page,stage]);
    287   inc(stage);
    288   end;
    289 if NewJob in Done then RemoveJob(NewJob)
    290 else
     335    include(Done, MyData.EnhancementJobs[Page, stage]);
     336    inc(stage);
     337  end;
     338  if NewJob in Done then
     339    RemoveJob(NewJob)
     340  else
    291341  begin // add job
    292   if NewJob in [jMine,jAfforest] then RemoveJob(jIrr);
    293   if NewJob in [jIrr,jFarm,jTrans] then RemoveJob(jMine);
    294   if (NewJob=jRR) and not (jRoad in Done) then
    295     begin MyData.EnhancementJobs[Page,stage]:=jRoad; inc(stage) end;
    296   if (NewJob=jFarm) and not (jIrr in Done) then
    297     begin MyData.EnhancementJobs[Page,stage]:=jIrr; inc(stage) end;
    298   MyData.EnhancementJobs[Page,stage]:=NewJob
    299   end;
    300 SmartUpdateContent
     342    if NewJob in [jMine, jAfforest] then
     343      RemoveJob(jIrr);
     344    if NewJob in [jIrr, jFarm, jTrans] then
     345      RemoveJob(jMine);
     346    if (NewJob = jRR) and not(jRoad in Done) then
     347    begin
     348      MyData.EnhancementJobs[Page, stage] := jRoad;
     349      inc(stage)
     350    end;
     351    if (NewJob = jFarm) and not(jIrr in Done) then
     352    begin
     353      MyData.EnhancementJobs[Page, stage] := jIrr;
     354      inc(stage)
     355    end;
     356    MyData.EnhancementJobs[Page, stage] := NewJob
     357  end;
     358  SmartUpdateContent
    301359end;
    302360
     
    304362  Shift: TShiftState);
    305363begin
    306 if Key=VK_ESCAPE then Close
    307 else if Key=VK_F1 then
    308   HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkText, HelpDlg.TextIndex('MACRO'))
     364  if Key = VK_ESCAPE then
     365    Close
     366  else if Key = VK_F1 then
     367    HelpDlg.ShowNewContent(FWindowMode or wmPersistent, hkText,
     368      HelpDlg.TextIndex('MACRO'))
    309369end;
    310370
    311371end.
    312 
  • trunk/LocalPlayer/Help.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit Help;
    43
     
    65
    76uses
    8   Protocol,ScreenTools,BaseWin,StringTables,
     7  Protocol, ScreenTools, BaseWin, StringTables,
    98
    109  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
     
    1211
    1312const
    14 MaxHist=16;
    15 
    16 {link categories}
    17 hkNoLink=0;hkAdv=1;hkImp=2;hkTer=3;hkFeature=4;hkInternet=5;hkModel=6;hkMisc=7;
    18 hkCrossLink=$40;
    19 hkText=$80;
    20 
    21 liInvalid=$3FFF; // link index indicates invalid link
    22 
    23 {link indices for category hkMisc}
    24 miscMain=0; miscCredits=1; miscGovList=2; miscJobList=3; miscSearchResult=7;
    25 
    26 fJungle=8; // pseudo terrain
    27 
     13  MaxHist = 16;
     14
     15  { link categories }
     16  hkNoLink = 0;
     17  hkAdv = 1;
     18  hkImp = 2;
     19  hkTer = 3;
     20  hkFeature = 4;
     21  hkInternet = 5;
     22  hkModel = 6;
     23  hkMisc = 7;
     24  hkCrossLink = $40;
     25  hkText = $80;
     26
     27  liInvalid = $3FFF; // link index indicates invalid link
     28
     29  { link indices for category hkMisc }
     30  miscMain = 0;
     31  miscCredits = 1;
     32  miscGovList = 2;
     33  miscJobList = 3;
     34  miscSearchResult = 7;
     35
     36  fJungle = 8; // pseudo terrain
    2837
    2938type
    30   THyperText=class(TStringList)
    31     procedure AddLine(s: String =''; Format: integer =0; Picpix: integer =0;
    32       LinkCategory: integer =0; LinkIndex: integer =0);
     39  THyperText = class(TStringList)
     40    procedure AddLine(s: String = ''; Format: integer = 0; Picpix: integer = 0;
     41      LinkCategory: integer = 0; LinkIndex: integer = 0);
    3342    procedure LF;
    34     end;
     43  end;
    3544
    3645  THelpDlg = class(TFramedDlg)
     
    3948    TopBtn: TButtonB;
    4049    SearchBtn: TButtonB;
    41     procedure FormCreate(Sender:TObject);
    42     procedure FormDestroy(Sender:TObject);
    43     procedure FormPaint(Sender:TObject);
    44     procedure CloseBtnClick(Sender:TObject);
    45     procedure PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; x,
    46       y: integer);
     50    procedure FormCreate(Sender: TObject);
     51    procedure FormDestroy(Sender: TObject);
     52    procedure FormPaint(Sender: TObject);
     53    procedure CloseBtnClick(Sender: TObject);
     54    procedure PaintBox1MouseMove(Sender: TObject; Shift: TShiftState;
     55      x, y: integer);
    4756    procedure PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
    4857      Shift: TShiftState; x, y: integer);
     
    5059    procedure TopBtnClick(Sender: TObject);
    5160    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    52     procedure FormKeyDown(Sender: TObject; var Key: Word;
    53       Shift: TShiftState);
     61    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    5462    procedure SearchBtnClick(Sender: TObject);
    5563  public
    5664    Difficulty: integer;
    57     procedure ShowNewContent(NewMode,Category,Index: integer);
     65    procedure ShowNewContent(NewMode, Category, Index: integer);
    5866    procedure ClearHistory;
    5967    function TextIndex(Item: string): integer;
     
    6169    procedure OffscreenPaint; override;
    6270  private
    63     Kind,no,Sel,nHist,CaptionColor: integer;
    64     hADVHELP, hIMPHELP, hFEATUREHELP, hGOVHELP, hSPECIALMODEL, hJOBHELP: integer;
     71    Kind, no, Sel, nHist, CaptionColor: integer;
     72    hADVHELP, hIMPHELP, hFEATUREHELP, hGOVHELP, hSPECIALMODEL,
     73      hJOBHELP: integer;
    6574    SearchContent, NewSearchContent: string;
    6675    CaptionFont: TFont;
    6776    MainText, SearchResult: THyperText;
    6877    HelpText: TStringTable;
    69     ExtPic,TerrIcon: TBitmap;
    70     sb:TPVScrollbar;
    71     x0: array[-2..18] of integer;
    72     HistKind: array[0..MaxHist-1] of integer;
    73     HistNo: array[0..MaxHist-1] of integer;
    74     HistPos: array[0..MaxHist-1] of integer;
    75     HistSearchContent: array[0..MaxHist-1] of shortstring;
     78    ExtPic, TerrIcon: TBitmap;
     79    sb: TPVScrollbar;
     80    x0: array [-2 .. 18] of integer;
     81    HistKind: array [0 .. MaxHist - 1] of integer;
     82    HistNo: array [0 .. MaxHist - 1] of integer;
     83    HistPos: array [0 .. MaxHist - 1] of integer;
     84    HistSearchContent: array [0 .. MaxHist - 1] of shortstring;
    7685    procedure line(ca: TCanvas; i: integer; lit: boolean);
    7786    procedure Prepare(sbPos: integer = 0);
    78     procedure WaterSign(x0,y0,iix: integer);
     87    procedure WaterSign(x0, y0, iix: integer);
    7988    procedure Search(SearchString: string);
    80     procedure OnScroll(var m:TMessage); message WM_VSCROLL;
    81     procedure OnMouseWheel(var m:TMessage); message WM_MOUSEWHEEL;
    82     procedure OnMouseLeave(var Msg:TMessage); message CM_MOUSELEAVE;
     89    procedure OnScroll(var m: TMessage); message WM_VSCROLL;
     90    procedure OnMouseWheel(var m: TMessage); message WM_MOUSEWHEEL;
     91    procedure OnMouseLeave(var Msg: TMessage); message CM_MOUSELEAVE;
    8392  end;
    8493
     
    8998
    9099uses
    91 Directories,ClientTools,Term,Tribes,ShellAPI, Inp,Messg;
     100  Directories, ClientTools, Term, Tribes, ShellAPI, Inp, Messg;
    92101
    93102{$R *.DFM}
    94103
    95104type
    96 THelpLineInfo=packed record
    97   Format, Picpix: Byte;
    98   Link: Word;
     105  THelpLineInfo = packed record
     106    Format, Picpix: Byte;
     107    Link: Word;
    99108  end;
    100109
     
    102111  LinkCategory: integer; LinkIndex: integer);
    103112var
    104 HelpLineInfo: THelpLineInfo;
     113  HelpLineInfo: THelpLineInfo;
    105114begin
    106 if LinkIndex<0 then LinkIndex:=liInvalid;
    107 HelpLineInfo.Format:=Format;
    108 HelpLineInfo.Picpix:=Picpix;
    109 HelpLineInfo.Link:=LinkCategory shl 8+LinkIndex;
    110 AddObject(s,TObject(HelpLineInfo));
     115  if LinkIndex < 0 then
     116    LinkIndex := liInvalid;
     117  HelpLineInfo.Format := Format;
     118  HelpLineInfo.Picpix := Picpix;
     119  HelpLineInfo.Link := LinkCategory shl 8 + LinkIndex;
     120  AddObject(s, TObject(HelpLineInfo));
    111121end;
    112122
    113123procedure THyperText.LF;
    114124begin
    115 AddLine;
     125  AddLine;
    116126end;
    117127
    118 
    119128const
    120 {text formats}
    121 pkNormal=0;pkCaption=1;pkSmallIcon=2;pkBigIcon=3;pkAdvIcon=4;pkTer=5;
    122 pkBigTer=6;pkFeature=7;pkDot=8;pkNormal_Dot=9;pkDomain=10;pkSection=11;
    123 pkBigFeature=12;pkExp=13;pkAITStat=14;pkExternal=15;pkModel=16;pkNormal_64=17;
    124 pkIllu=18;pkLogo=19;pkTerImp=20;pkRightIcon=21;pkAdvIcon_AsPreq=22;
    125 pkSmallIcon_AsPreq=23;pkSpecialIcon=24;pkGov=25;
    126 
    127 nSeeAlso=14;
    128 SeeAlso: array[0..nSeeAlso-1] of record Kind,no,SeeKind,SeeNo: integer end=
    129 ((Kind:hkImp;no:imWalls;SeeKind:hkFeature;SeeNo:mcArtillery),
    130 (Kind:hkImp;no:imHydro;SeeKind:hkImp;SeeNo:woHoover),
    131 (Kind:hkImp;no:imWalls;SeeKind:hkImp;SeeNo:imGrWall),
    132 (Kind:hkImp;no:imHighways;SeeKind:hkAdv;SeeNo:adWheel),
    133 (Kind:hkImp;no:imCathedral;SeeKind:hkImp;SeeNo:woBach),
    134 (Kind:hkImp;no:imBank;SeeKind:hkImp;SeeNo:imStockEx),
    135 (Kind:hkImp;no:imShipComp;SeeKind:hkImp;SeeNo:imSpacePort),
    136 (Kind:hkImp;no:imShipPow;SeeKind:hkImp;SeeNo:imSpacePort),
    137 (Kind:hkImp;no:imShipHab;SeeKind:hkImp;SeeNo:imSpacePort),
    138 (Kind:hkFeature;no:mcSub;SeeKind:hkFeature;SeeNo:mcRadar),
    139 (Kind:hkFeature;no:mcDefense;SeeKind:hkAdv;SeeNo:adSteel),
    140 (Kind:hkFeature;no:mcSE;SeeKind:hkFeature;SeeNo:mcNP),
    141 (Kind:hkAdv;no:adWheel;SeeKind:hkImp;SeeNo:imHighways),
    142 (Kind:hkAdv;no:adSteel;SeeKind:hkFeature;SeeNo:mcDefense));
    143 
    144 nTerrainHelp=14;
    145 TerrainHelp: array[0..nTerrainHelp-1] of integer=
    146 (fGrass,fGrass+12,fPrairie,fForest,fJungle,fHills,fMountains,fSwamp,fTundra,fArctic,
    147 fDesert,3*12{DeadLands},fShore,fOcean);
    148 
    149 nJobHelp=8;
    150 JobHelp: array[0..nJobHelp-1] of integer=
    151   (jRoad,jRR,jCanal,jIrr,jFarm,jMine,jFort,jBase);
    152 
    153 
    154 procedure THelpDlg.FormCreate(Sender:TObject);
     129  { text formats }
     130  pkNormal = 0;
     131  pkCaption = 1;
     132  pkSmallIcon = 2;
     133  pkBigIcon = 3;
     134  pkAdvIcon = 4;
     135  pkTer = 5;
     136  pkBigTer = 6;
     137  pkFeature = 7;
     138  pkDot = 8;
     139  pkNormal_Dot = 9;
     140  pkDomain = 10;
     141  pkSection = 11;
     142  pkBigFeature = 12;
     143  pkExp = 13;
     144  pkAITStat = 14;
     145  pkExternal = 15;
     146  pkModel = 16;
     147  pkNormal_64 = 17;
     148  pkIllu = 18;
     149  pkLogo = 19;
     150  pkTerImp = 20;
     151  pkRightIcon = 21;
     152  pkAdvIcon_AsPreq = 22;
     153  pkSmallIcon_AsPreq = 23;
     154  pkSpecialIcon = 24;
     155  pkGov = 25;
     156
     157  nSeeAlso = 14;
     158  SeeAlso: array [0 .. nSeeAlso - 1] of record Kind, no, SeeKind,
     159    SeeNo: integer end = ((Kind: hkImp; no: imWalls; SeeKind: hkFeature;
     160    SeeNo: mcArtillery), (Kind: hkImp; no: imHydro; SeeKind: hkImp;
     161    SeeNo: woHoover), (Kind: hkImp; no: imWalls; SeeKind: hkImp;
     162    SeeNo: imGrWall), (Kind: hkImp; no: imHighways; SeeKind: hkAdv;
     163    SeeNo: adWheel), (Kind: hkImp; no: imCathedral; SeeKind: hkImp;
     164    SeeNo: woBach), (Kind: hkImp; no: imBank; SeeKind: hkImp; SeeNo: imStockEx),
     165    (Kind: hkImp; no: imShipComp; SeeKind: hkImp; SeeNo: imSpacePort),
     166    (Kind: hkImp; no: imShipPow; SeeKind: hkImp; SeeNo: imSpacePort),
     167    (Kind: hkImp; no: imShipHab; SeeKind: hkImp; SeeNo: imSpacePort),
     168    (Kind: hkFeature; no: mcSub; SeeKind: hkFeature; SeeNo: mcRadar),
     169    (Kind: hkFeature; no: mcDefense; SeeKind: hkAdv; SeeNo: adSteel),
     170    (Kind: hkFeature; no: mcSE; SeeKind: hkFeature; SeeNo: mcNP), (Kind: hkAdv;
     171    no: adWheel; SeeKind: hkImp; SeeNo: imHighways), (Kind: hkAdv; no: adSteel;
     172    SeeKind: hkFeature; SeeNo: mcDefense));
     173
     174  nTerrainHelp = 14;
     175  TerrainHelp: array [0 .. nTerrainHelp - 1] of integer = (fGrass, fGrass + 12,
     176    fPrairie, fForest, fJungle, fHills, fMountains, fSwamp, fTundra, fArctic,
     177    fDesert, 3 * 12 { DeadLands } , fShore, fOcean);
     178
     179  nJobHelp = 8;
     180  JobHelp: array [0 .. nJobHelp - 1] of integer = (jRoad, jRR, jCanal, jIrr,
     181    jFarm, jMine, jFort, jBase);
     182
     183procedure THelpDlg.FormCreate(Sender: TObject);
    155184begin
    156 inherited;
    157 CaptionLeft:=BackBtn.Left+BackBtn.Width;
    158 CaptionRight:=SearchBtn.Left;
    159 inc(ModalFrameIndent,29);
    160 MainText:=THyperText.Create;
    161 SearchResult:=THyperText.Create;
    162 CreatePVSB(sb,Handle,36,551,36+432);
    163 
    164 HelpText:=TStringTable.Create;
    165 HelpText.LoadFromFile(LocalizedFilePath('Help\help.txt'));
    166 hADVHELP:=HelpText.Gethandle('ADVHELP');
    167 hIMPHELP:=HelpText.Gethandle('IMPHELP');
    168 hFEATUREHELP:=HelpText.Gethandle('FEATUREHELP');
    169 hGOVHELP:=HelpText.Gethandle('GOVHELP');
    170 hSPECIALMODEL:=HelpText.Gethandle('SPECIALMODEL');
    171 hJOBHELP:=HelpText.Gethandle('JOBHELP');
    172 
    173 CaptionFont:=Font.Create;
    174 CaptionFont.Assign(UniFont[ftNormal]);
    175 CaptionFont.Style:=CaptionFont.Style+[fsItalic,fsBold];
    176 InitButtons();
    177 
    178 TopBtn.Hint:=Phrases.Lookup('BTN_CONTENTS');
    179 BackBtn.Hint:=Phrases.Lookup('BTN_BACK');
    180 SearchBtn.Hint:=Phrases.Lookup('BTN_SEARCH');
    181 
    182 ExtPic:=TBitmap.Create;
    183 TerrIcon:=TBitmap.Create;
    184 TerrIcon.PixelFormat:=pf24bit;
    185 TerrIcon.Width:=xSizeBig; TerrIcon.Height:=ySizeBig;
    186 SearchContent:='';
    187 nHist:=-1;
     185  inherited;
     186  CaptionLeft := BackBtn.Left + BackBtn.Width;
     187  CaptionRight := SearchBtn.Left;
     188  inc(ModalFrameIndent, 29);
     189  MainText := THyperText.Create;
     190  SearchResult := THyperText.Create;
     191  CreatePVSB(sb, Handle, 36, 551, 36 + 432);
     192
     193  HelpText := TStringTable.Create;
     194  HelpText.LoadFromFile(LocalizedFilePath('Help\help.txt'));
     195  hADVHELP := HelpText.Gethandle('ADVHELP');
     196  hIMPHELP := HelpText.Gethandle('IMPHELP');
     197  hFEATUREHELP := HelpText.Gethandle('FEATUREHELP');
     198  hGOVHELP := HelpText.Gethandle('GOVHELP');
     199  hSPECIALMODEL := HelpText.Gethandle('SPECIALMODEL');
     200  hJOBHELP := HelpText.Gethandle('JOBHELP');
     201
     202  CaptionFont := Font.Create;
     203  CaptionFont.Assign(UniFont[ftNormal]);
     204  CaptionFont.Style := CaptionFont.Style + [fsItalic, fsBold];
     205  InitButtons();
     206
     207  TopBtn.Hint := Phrases.Lookup('BTN_CONTENTS');
     208  BackBtn.Hint := Phrases.Lookup('BTN_BACK');
     209  SearchBtn.Hint := Phrases.Lookup('BTN_SEARCH');
     210
     211  ExtPic := TBitmap.Create;
     212  TerrIcon := TBitmap.Create;
     213  TerrIcon.PixelFormat := pf24bit;
     214  TerrIcon.Width := xSizeBig;
     215  TerrIcon.Height := ySizeBig;
     216  SearchContent := '';
     217  nHist := -1;
    188218end;
    189219
    190220procedure THelpDlg.ClearHistory;
    191221begin
    192 nHist:=-1;
     222  nHist := -1;
    193223end;
    194224
    195 procedure THelpDlg.FormDestroy(Sender:TObject);
     225procedure THelpDlg.FormDestroy(Sender: TObject);
    196226begin
    197 MainText.Free;
    198 SearchResult.Free;
    199 ExtPic.Free;
    200 TerrIcon.Free;
    201 HelpText.Free;
    202 //CaptionFont.Free;
     227  MainText.Free;
     228  SearchResult.Free;
     229  ExtPic.Free;
     230  TerrIcon.Free;
     231  HelpText.Free;
     232  // CaptionFont.Free;
    203233end;
    204234
    205 procedure THelpDlg.CloseBtnClick(Sender:TObject);
     235procedure THelpDlg.CloseBtnClick(Sender: TObject);
    206236begin
    207 Close
     237  Close
    208238end;
    209239
    210 procedure THelpDlg.OnScroll(var m:TMessage);
     240procedure THelpDlg.OnScroll(var m: TMessage);
    211241begin
    212 if ProcessPVSB(sb,m) then
    213   begin Sel:=-1; SmartUpdateContent(true) end
    214 end;
    215 
    216 procedure THelpDlg.OnMouseWheel(var m:TMessage);
    217 begin
    218 if ProcessMouseWheel(sb,m) then
    219   begin
    220   Sel:=-1;
    221   SmartUpdateContent(true);
    222   PaintBox1MouseMove(nil, [], m.lParam and $FFFF-Left, m.lParam shr 16-Top);
     242  if ProcessPVSB(sb, m) then
     243  begin
     244    Sel := -1;
     245    SmartUpdateContent(true)
    223246  end
    224247end;
    225248
    226 procedure THelpDlg.OnMouseLeave(var Msg:TMessage);
     249procedure THelpDlg.OnMouseWheel(var m: TMessage);
    227250begin
    228 if Sel<>-1 then
    229   begin
    230   line(Canvas,Sel,false);
    231   Sel:=-1
     251  if ProcessMouseWheel(sb, m) then
     252  begin
     253    Sel := -1;
     254    SmartUpdateContent(true);
     255    PaintBox1MouseMove(nil, [], m.lParam and $FFFF - Left,
     256      m.lParam shr 16 - Top);
    232257  end
    233258end;
    234259
    235 procedure THelpDlg.FormPaint(Sender:TObject);
     260procedure THelpDlg.OnMouseLeave(var Msg: TMessage);
    236261begin
    237 inherited;
    238 Canvas.Font.Assign(UniFont[ftNormal]);
     262  if Sel <> -1 then
     263  begin
     264    line(Canvas, Sel, false);
     265    Sel := -1
     266  end
     267end;
     268
     269procedure THelpDlg.FormPaint(Sender: TObject);
     270begin
     271  inherited;
     272  Canvas.Font.Assign(UniFont[ftNormal]);
    239273end;
    240274
    241275procedure THelpDlg.line(ca: TCanvas; i: integer; lit: boolean);
    242276var
    243 TextColor,x,y: integer;
    244 TextSize: TSize;
    245 s: string;
     277  TextColor, x, y: integer;
     278  TextSize: TSize;
     279  s: string;
    246280begin
    247 s:=MainText[sb.si.npos+i];
    248 if s='' then exit;
    249 x:=x0[i]; y:=2+i*24;
    250 if ca=Canvas then
    251   begin x:=x+SideFrame; y:=y+WideFrame end;
    252 if THelpLineInfo(MainText.Objects[sb.si.npos+i]).Format
    253   in [pkCaption,pkBigTer,pkRightIcon,pkBigFeature] then
    254   begin
    255   ca.Font.Assign(CaptionFont);
    256 {  ca.brush.color:=CaptionColor;
    257   ca.FillRect(rect(x,i*24,x+24,i*24+24));
    258   ca.brush.color:=$FFFFFF;
    259   ca.FrameRect(rect(x+1,i*24+1,x+24-1,i*24+24-1));
    260   ca.Brush.Style:=bsClear;}
    261   BitBlt(ca.handle,x,y-4,24,24,GrExt[HGrSystem].Data.Canvas.Handle,1,146,SRCCOPY);
    262   BiColorTextOut(ca,$FFFFFF,$7F007F,x+10-ca.Textwidth(s[1]) div 2,y-3,s[1]);
    263   BiColorTextOut(ca,CaptionColor,$7F007F,x+24,y-3,copy(s,2,255));
    264   ca.Font.Assign(UniFont[ftNormal]);
    265   end
    266 else if THelpLineInfo(MainText.Objects[sb.si.npos+i]).Format=pkSection then
    267   begin
    268   ca.Font.Assign(CaptionFont);
    269   BiColorTextOut(ca,CaptionColor,$7F007F,x,y-3,s);
    270   ca.Font.Assign(UniFont[ftNormal]);
    271   end
    272 else
    273   begin
    274   if (Kind=hkMisc) and (no=miscMain) then
     281  s := MainText[sb.si.npos + i];
     282  if s = '' then
     283    exit;
     284  x := x0[i];
     285  y := 2 + i * 24;
     286  if ca = Canvas then
     287  begin
     288    x := x + SideFrame;
     289    y := y + WideFrame
     290  end;
     291  if THelpLineInfo(MainText.Objects[sb.si.npos + i]).Format
     292    in [pkCaption, pkBigTer, pkRightIcon, pkBigFeature] then
     293  begin
    275294    ca.Font.Assign(CaptionFont);
    276   TextColor:=Colors.Canvas.Pixels[clkMisc,cliPaperText];
    277   if ca=Canvas then
    278     begin
    279     TextSize.cx:=BiColorTextWidth(ca,s);
    280     TextSize.cy:=ca.TextHeight(s);
    281     if y+TextSize.cy>=WideFrame+InnerHeight then
    282       TextSize.cy:=WideFrame+InnerHeight-y;
    283     FillSeamless(ca,x,y,TextSize.cx,TextSize.cy,-SideFrame,sb.si.npos*24-WideFrame,
    284       Paper);
    285     end;
    286   BiColorTextOut(ca,TextColor,$7F007F,x,y,s);
    287   if lit then with ca do
    288     begin
    289     assert(ca=Canvas);
    290     pen.color:=TextColor;
    291     moveto(x+1,y+TextSize.cy-2);
    292     lineto(x+TextSize.cx,y+TextSize.cy-2);
    293     end;
    294   if (Kind=hkMisc) and (no=miscMain) then
     295    { ca.brush.color:=CaptionColor;
     296      ca.FillRect(rect(x,i*24,x+24,i*24+24));
     297      ca.brush.color:=$FFFFFF;
     298      ca.FrameRect(rect(x+1,i*24+1,x+24-1,i*24+24-1));
     299      ca.Brush.Style:=bsClear; }
     300    BitBlt(ca.Handle, x, y - 4, 24, 24, GrExt[HGrSystem].Data.Canvas.Handle, 1,
     301      146, SRCCOPY);
     302    BiColorTextOut(ca, $FFFFFF, $7F007F, x + 10 - ca.Textwidth(s[1]) div 2,
     303      y - 3, s[1]);
     304    BiColorTextOut(ca, CaptionColor, $7F007F, x + 24, y - 3, copy(s, 2, 255));
    295305    ca.Font.Assign(UniFont[ftNormal]);
    296306  end
     307  else if THelpLineInfo(MainText.Objects[sb.si.npos + i]).Format = pkSection
     308  then
     309  begin
     310    ca.Font.Assign(CaptionFont);
     311    BiColorTextOut(ca, CaptionColor, $7F007F, x, y - 3, s);
     312    ca.Font.Assign(UniFont[ftNormal]);
     313  end
     314  else
     315  begin
     316    if (Kind = hkMisc) and (no = miscMain) then
     317      ca.Font.Assign(CaptionFont);
     318    TextColor := Colors.Canvas.Pixels[clkMisc, cliPaperText];
     319    if ca = Canvas then
     320    begin
     321      TextSize.cx := BiColorTextWidth(ca, s);
     322      TextSize.cy := ca.TextHeight(s);
     323      if y + TextSize.cy >= WideFrame + InnerHeight then
     324        TextSize.cy := WideFrame + InnerHeight - y;
     325      FillSeamless(ca, x, y, TextSize.cx, TextSize.cy, -SideFrame,
     326        sb.si.npos * 24 - WideFrame, Paper);
     327    end;
     328    BiColorTextOut(ca, TextColor, $7F007F, x, y, s);
     329    if lit then
     330      with ca do
     331      begin
     332        assert(ca = Canvas);
     333        pen.color := TextColor;
     334        moveto(x + 1, y + TextSize.cy - 2);
     335        lineto(x + TextSize.cx, y + TextSize.cy - 2);
     336      end;
     337    if (Kind = hkMisc) and (no = miscMain) then
     338      ca.Font.Assign(UniFont[ftNormal]);
     339  end
    297340end;
    298341
    299 procedure THelpDlg.WaterSign(x0,y0,iix: integer);
     342procedure THelpDlg.WaterSign(x0, y0, iix: integer);
    300343const
    301 nHeaven=28;
    302 maxsum=9*9*255 *75 div 100;
     344  nHeaven = 28;
     345  maxsum = 9 * 9 * 255 * 75 div 100;
    303346type
    304 TLine=array[0..649,0..2] of Byte;
     347  TLine = array [0 .. 649, 0 .. 2] of Byte;
    305348var
    306 x,y,dx,dy,xSrc,ySrc,sum,xx: integer;
    307 Heaven: array[0..nHeaven] of integer;
    308 PaintLine,CoalLine: ^TLine;
    309 ImpLine: array[-1..1] of ^TLine;
     349  x, y, dx, dy, xSrc, ySrc, sum, xx: integer;
     350  Heaven: array [0 .. nHeaven] of integer;
     351  PaintLine, CoalLine: ^TLine;
     352  ImpLine: array [-1 .. 1] of ^TLine;
    310353begin
    311 // assume eiffel tower has free common heaven
    312 for dy:=0 to nHeaven-1 do
    313   Heaven[dy]:=BigImp.Canvas.Pixels[woEiffel mod 7 *xSizeBig,
    314     (SystemIconLines+woEiffel div 7)*ySizeBig+dy];
    315 
    316 xSrc:=iix mod 7 *xSizeBig;
    317 ySrc:=(iix div 7+1) *ySizeBig;
    318 for y:=0 to ySizeBig*2-1 do if (y0+y>=0) and (y0+y<InnerHeight) then
    319   begin
    320   PaintLine:=OffScreen.ScanLine[y0+y];
    321   CoalLine:=Templates.ScanLine[yCoal+y];
    322   for dy:=-1 to 1 do
    323     if ((y+dy) shr 1>=0) and ((y+dy) shr 1<ySizeBig) then
    324       ImpLine[dy]:=BigImp.ScanLine[ySrc+(y+dy) shr 1];
    325   for x:=0 to xSizeBig*2-1 do
    326     begin
    327     sum:=0;
    328     for dx:=-1 to 1 do
     354  // assume eiffel tower has free common heaven
     355  for dy := 0 to nHeaven - 1 do
     356    Heaven[dy] := BigImp.Canvas.Pixels[woEiffel mod 7 * xSizeBig,
     357      (SystemIconLines + woEiffel div 7) * ySizeBig + dy];
     358
     359  xSrc := iix mod 7 * xSizeBig;
     360  ySrc := (iix div 7 + 1) * ySizeBig;
     361  for y := 0 to ySizeBig * 2 - 1 do
     362    if (y0 + y >= 0) and (y0 + y < InnerHeight) then
     363    begin
     364      PaintLine := OffScreen.ScanLine[y0 + y];
     365      CoalLine := Templates.ScanLine[yCoal + y];
     366      for dy := -1 to 1 do
     367        if ((y + dy) shr 1 >= 0) and ((y + dy) shr 1 < ySizeBig) then
     368          ImpLine[dy] := BigImp.ScanLine[ySrc + (y + dy) shr 1];
     369      for x := 0 to xSizeBig * 2 - 1 do
    329370      begin
    330       xx:=xSrc+(x+dx) shr 1;
    331       for dy:=-1 to 1 do
    332         if ((y+dy) shr 1<0) or ((y+dy) shr 1>=ySizeBig)
    333           or ((x+dx) shr 1<0) or ((x+dx) shr 1>=xSizeBig)
    334           or ((y+dy) shr 1<nHeaven)
    335           and (ImpLine[dy,xx,0] shl 16+ImpLine[dy,xx,1] shl 8+ImpLine[dy,xx,2]=Heaven[(y+dy) shr 1]) then
    336           sum:=sum+9*255
    337         else sum:=sum+ImpLine[dy,xx,0]+5*ImpLine[dy,xx,1]+3*ImpLine[dy,xx,2];
     371        sum := 0;
     372        for dx := -1 to 1 do
     373        begin
     374          xx := xSrc + (x + dx) shr 1;
     375          for dy := -1 to 1 do
     376            if ((y + dy) shr 1 < 0) or ((y + dy) shr 1 >= ySizeBig) or
     377              ((x + dx) shr 1 < 0) or ((x + dx) shr 1 >= xSizeBig) or
     378              ((y + dy) shr 1 < nHeaven) and
     379              (ImpLine[dy, xx, 0] shl 16 + ImpLine[dy, xx, 1] shl 8 +
     380              ImpLine[dy, xx, 2] = Heaven[(y + dy) shr 1]) then
     381              sum := sum + 9 * 255
     382            else
     383              sum := sum + ImpLine[dy, xx, 0] + 5 * ImpLine[dy, xx, 1] + 3 *
     384                ImpLine[dy, xx, 2];
     385        end;
     386        if sum < maxsum then
     387        begin // no saturation
     388          sum := 1 shl 22 - (maxsum - sum) * (256 - CoalLine[xCoal + x, 0] * 2);
     389          PaintLine[x0 + x, 0] := PaintLine[x0 + x, 0] * sum shr 22;
     390          PaintLine[x0 + x, 1] := PaintLine[x0 + x, 1] * sum shr 22;
     391          PaintLine[x0 + x, 2] := PaintLine[x0 + x, 2] * sum shr 22;
     392        end
     393      end
     394    end;
     395end;
     396
     397procedure THelpDlg.OffscreenPaint;
     398
     399  procedure PaintTerrIcon(x, y, xSrc, ySrc: integer);
     400  begin
     401    Frame(OffScreen.Canvas, x - 1, y - 1, x + xSizeBig, y + ySizeBig,
     402      $000000, $000000);
     403    if 2 * yyt < 40 then
     404    begin
     405      Sprite(OffScreen, HGrTerrain, x, y, 56, 2 * yyt, xSrc, ySrc);
     406      Sprite(OffScreen, HGrTerrain, x, y + 2 * yyt, 56, 40 - 2 * yyt,
     407        xSrc, ySrc);
     408    end
     409    else
     410      Sprite(OffScreen, HGrTerrain, x, y, 56, 40, xSrc, ySrc);
     411    Sprite(OffScreen, HGrTerrain, x, y, xxt, yyt, xSrc + xxt, ySrc + yyt);
     412    Sprite(OffScreen, HGrTerrain, x, y + yyt, xxt, 40 - yyt, xSrc + xxt, ySrc);
     413    Sprite(OffScreen, HGrTerrain, x + xxt, y, 56 - xxt, yyt, xSrc, ySrc + yyt);
     414    Sprite(OffScreen, HGrTerrain, x + xxt, y + yyt, 56 - xxt, 40 - yyt,
     415      xSrc, ySrc);
     416  end;
     417
     418var
     419  i, j, yl, srcno, ofs, cnt, y: integer;
     420  s: string;
     421  HelpLineInfo: THelpLineInfo;
     422begin
     423  inherited;
     424  CaptionColor := Colors.Canvas.Pixels[clkMisc, cliPaperCaption];
     425  FillSeamless(OffScreen.Canvas, 0, 0, InnerWidth, InnerHeight, 0,
     426    sb.si.npos * 24, Paper);
     427  with OffScreen.Canvas do
     428  begin
     429    Font.Assign(UniFont[ftNormal]);
     430    for i := -sb.si.npos to InnerHeight div 24 do
     431      if sb.si.npos + i < MainText.Count then
     432      begin
     433        HelpLineInfo := THelpLineInfo(MainText.Objects[sb.si.npos + i]);
     434        if HelpLineInfo.Format = pkExternal then
     435        begin
     436          yl := ExtPic.Height;
     437          if 4 + i * 24 + yl > InnerHeight then
     438            yl := InnerHeight - (4 + i * 24);
     439          BitBlt(Handle, 8, 4 + i * 24, ExtPic.Width, yl, ExtPic.Canvas.Handle,
     440            0, 0, SRCCOPY);
     441        end
    338442      end;
    339     if sum<maxsum then
    340       begin // no saturation
    341       sum:=1 shl 22 - (maxsum-sum)*(256-CoalLine[xCoal+x,0]*2);
    342       PaintLine[x0+x,0]:=PaintLine[x0+x,0]*sum shr 22;
    343       PaintLine[x0+x,1]:=PaintLine[x0+x,1]*sum shr 22;
    344       PaintLine[x0+x,2]:=PaintLine[x0+x,2]*sum shr 22;
     443    for i := -2 to InnerHeight div 24 do
     444      if (sb.si.npos + i >= 0) and (sb.si.npos + i < MainText.Count) then
     445      begin
     446        HelpLineInfo := THelpLineInfo(MainText.Objects[sb.si.npos + i]);
     447        if HelpLineInfo.Link <> 0 then
     448        begin
     449          if (Kind = hkMisc) and (no = miscSearchResult) then
     450            Sprite(OffScreen, HGrSystem, 18, 9 + i * 24, 8, 8, 90, 16)
     451          else if HelpLineInfo.Format in [pkSmallIcon_AsPreq, pkAdvIcon_AsPreq]
     452          then
     453            Sprite(OffScreen, HGrSystem, 12, i * 24 + 5, 14, 14, 65, 20)
     454          else if HelpLineInfo.Link and (hkCrossLink shl 8) <> 0 then
     455            Sprite(OffScreen, HGrSystem, 12, i * 24 + 5, 14, 14, 80, 1)
     456          else if not((Kind = hkMisc) and (no = miscMain)) then
     457            Sprite(OffScreen, HGrSystem, 10, i * 24 + 6, 14, 14, 65, 1);
     458          x0[i] := 24;
     459        end
     460        else
     461          x0[i] := 0;
     462        case HelpLineInfo.Format of
     463          pkLogo:
     464            begin
     465              Server(sGetVersion, 0, 0, j);
     466              s := Format('%d.%d.%d', [j shr 16 and $FF, j shr 8 and $FF,
     467                j and $FF]);
     468              PaintLogo(OffScreen.Canvas, (InnerWidth - 122) div 2, i * 24 + 1,
     469                GrExt[HGrSystem].Data.Canvas.Pixels[95, 1], $000000);
     470              Font.Assign(UniFont[ftSmall]);
     471              BiColorTextOut(OffScreen.Canvas, $000000, $7F007F,
     472                (InnerWidth - Textwidth(s)) div 2, i * 24 + 26, s);
     473              Font.Assign(UniFont[ftNormal]);
     474            end;
     475          pkSmallIcon, pkSmallIcon_AsPreq:
     476            begin
     477              Frame(OffScreen.Canvas, 8 - 1 + x0[i], 2 - 1 + i * 24,
     478                8 + xSizeSmall + x0[i], 2 + 20 + i * 24, $000000, $000000);
     479              if HelpLineInfo.Picpix = imPalace then
     480                BitBlt(OffScreen.Canvas.Handle, 8 + x0[i], 2 + i * 24,
     481                  xSizeSmall, ySizeSmall, SmallImp.Canvas.Handle,
     482                  0 * xSizeSmall, 1 * ySizeSmall, SRCCOPY)
     483              else
     484                BitBlt(OffScreen.Canvas.Handle, 8 + x0[i], 2 + i * 24,
     485                  xSizeSmall, ySizeSmall, SmallImp.Canvas.Handle,
     486                  HelpLineInfo.Picpix mod 7 * xSizeSmall,
     487                  (HelpLineInfo.Picpix + SystemIconLines * 7) div 7 *
     488                  ySizeSmall, SRCCOPY);
     489              x0[i] := x0[i] + (8 + 8 + 36);
     490            end;
     491          pkBigIcon:
     492            begin
     493              FrameImage(OffScreen.Canvas, BigImp, x0[i] + 12, i * 24 - 7, 56,
     494                40, HelpLineInfo.Picpix mod 7 * xSizeBig,
     495                HelpLineInfo.Picpix div 7 * ySizeBig);
     496              x0[i] := 64 + 8 + 8 + x0[i];
     497            end;
     498          pkSpecialIcon:
     499            begin
     500              case HelpLineInfo.Picpix of
     501                0:
     502                  FrameImage(OffScreen.Canvas, GrExt[HGrSystem2].Data,
     503                    12 + x0[i], -7 + i * 24, 56, 40, 137, 127);
     504                1:
     505                  begin
     506                    PaintTerrIcon(12 + x0[i], -7 + i * 24,
     507                      1 + 3 * (xxt * 2 + 1), 1 + yyt);
     508                    if 2 * yyt < 40 then
     509                      Sprite(OffScreen, HGrTerrain, 12 + x0[i], -7 + 4 + i * 24,
     510                        56, 2 * yyt, 1 + 3 * (xxt * 2 + 1) + xxt - 28,
     511                        1 + yyt + 1 * (yyt * 3 + 1))
     512                    else
     513                      Sprite(OffScreen, HGrTerrain, 12 + x0[i],
     514                        -7 + 4 + i * 24 - 4, 56, 40, 1 + 3 * (xxt * 2 + 1) + xxt
     515                        - 28, 1 + yyt + 1 * (yyt * 3 + 1) + yyt - 20);
     516                  end;
     517                2:
     518                  begin
     519                    PaintTerrIcon(12 + x0[i], -7 + i * 24,
     520                      1 + 7 * (xxt * 2 + 1), 1 + yyt + 4 * (yyt * 3 + 1));
     521                    if 2 * yyt < 40 then
     522                      Sprite(OffScreen, HGrTerrain, 12 + x0[i], -7 + 4 + i * 24,
     523                        56, 32, 1 + 4 * (xxt * 2 + 1) + xxt - 28,
     524                        1 + yyt + 12 * (yyt * 3 + 1) + yyt - 16)
     525                    else
     526                      Sprite(OffScreen, HGrTerrain, 12 + x0[i], -7 + 4 + i * 24,
     527                        56, 32, 1 + 4 * (xxt * 2 + 1) + xxt - 28,
     528                        1 + yyt + 12 * (yyt * 3 + 1) + yyt - 16)
     529                  end;
     530              end;
     531              x0[i] := 64 + 8 + 8 + x0[i];
     532            end;
     533          pkDomain:
     534            begin
     535              Frame(OffScreen.Canvas, 8 - 1 + x0[i], 2 - 1 + i * 24,
     536                8 + 36 + x0[i], 2 + 20 + i * 24, $000000, $000000);
     537              Dump(OffScreen, HGrSystem, 8 + x0[i], 2 + i * 24, 36, 20,
     538                75 + HelpLineInfo.Picpix * 37, 295);
     539              x0[i] := x0[i] + (8 + 8 + 36);
     540            end;
     541          pkAdvIcon, pkAdvIcon_AsPreq:
     542            begin
     543              Frame(OffScreen.Canvas, 8 - 1 + x0[i], 2 - 1 + i * 24,
     544                8 + xSizeSmall + x0[i], 2 + ySizeSmall + i * 24,
     545                $000000, $000000);
     546              if AdvIcon[HelpLineInfo.Picpix] < 84 then
     547                BitBlt(OffScreen.Canvas.Handle, 8 + x0[i], 2 + i * 24,
     548                  xSizeSmall, ySizeSmall, SmallImp.Canvas.Handle,
     549                  (AdvIcon[HelpLineInfo.Picpix] + SystemIconLines * 7) mod 7 *
     550                  xSizeSmall, (AdvIcon[HelpLineInfo.Picpix] + SystemIconLines *
     551                  7) div 7 * ySizeSmall, SRCCOPY)
     552              else
     553                Dump(OffScreen, HGrSystem, 8 + x0[i], 2 + i * 24, 36, 20,
     554                  1 + (AdvIcon[HelpLineInfo.Picpix] - 84) mod 8 * 37,
     555                  295 + (AdvIcon[HelpLineInfo.Picpix] - 84) div 8 * 21);
     556              j := AdvValue[HelpLineInfo.Picpix] div 1000;
     557              BitBlt(Handle, x0[i] + 4, 4 + i * 24, 14, 14,
     558                GrExt[HGrSystem].Mask.Canvas.Handle, 127 + j * 15, 85, SRCAND);
     559              Sprite(OffScreen, HGrSystem, x0[i] + 3, 3 + i * 24, 14, 14,
     560                127 + j * 15, 85);
     561              x0[i] := x0[i] + (8 + 8 + 36);
     562            end;
     563          pkRightIcon:
     564            begin
     565              if Imp[HelpLineInfo.Picpix].Kind <> ikWonder then
     566                ImpImage(OffScreen.Canvas, InnerWidth - (40 + xSizeBig), i * 24,
     567                  HelpLineInfo.Picpix, gDespotism)
     568              else
     569                WaterSign(InnerWidth - (40 + 2 * xSizeBig), i * 24 - 8,
     570                  HelpLineInfo.Picpix + 7);
     571              x0[i] := x0[i] + 8;
     572            end;
     573          pkIllu:
     574            WaterSign(8, i * 24 - 8, HelpLineInfo.Picpix);
     575          pkBigFeature:
     576            begin
     577              cnt := 0;
     578              for j := nDomains - 1 downto 0 do
     579                if 1 shl j and Feature[HelpLineInfo.Picpix].Domains <> 0 then
     580                begin
     581                  inc(cnt);
     582                  Dump(OffScreen, HGrSystem, InnerWidth - 38 - 38 * cnt,
     583                    i * 24 + 1, 36, 20, 75 + j * 37, 295);
     584                  Frame(OffScreen.Canvas, InnerWidth - 39 - 38 * cnt, i * 24,
     585                    InnerWidth - 2 - 38 * cnt, i * 24 + 21, $000000, $000000);
     586                end;
     587              DarkGradient(OffScreen.Canvas, InnerWidth - 38 - 38 * cnt,
     588                i * 24 + 23, cnt * 38 - 2, 1);
     589              ofs := InnerWidth - (39 + 7) - 19 * cnt;
     590              with OffScreen.Canvas do
     591              begin
     592                Brush.color := $C0C0C0;
     593                FrameRect(Rect(ofs, 1 + 23 + i * 24, ofs + 14,
     594                  15 + 23 + i * 24));
     595                Brush.Style := bsClear;
     596                Sprite(OffScreen, HGrSystem, ofs + 2, 3 + 23 + i * 24, 10, 10,
     597                  66 + HelpLineInfo.Picpix mod 11 * 11,
     598                  137 + HelpLineInfo.Picpix div 11 * 11);
     599              end;
     600              x0[i] := x0[i] + 8;
     601            end;
     602          pkTer, pkBigTer:
     603            begin
     604              if HelpLineInfo.Format = pkBigTer then
     605                y := i * 24 - 3 + yyt
     606              else
     607                y := i * 24 + 13;
     608              if HelpLineInfo.Picpix >= 3 * 12 then
     609                srcno := 2 * 9 + 6
     610              else if HelpLineInfo.Picpix mod 12 = fJungle then
     611                srcno := 18 * 9
     612              else if HelpLineInfo.Picpix mod 12 < fJungle then
     613                srcno := HelpLineInfo.Picpix mod 12
     614              else
     615                srcno := 27 + (HelpLineInfo.Picpix mod 12 - 9) * 18;
     616              if HelpLineInfo.Format = pkTer then
     617              begin
     618                ofs := x0[i] + 8;
     619                x0[i] := 2 * xxt + 8 + ofs;
     620              end
     621              else
     622              begin
     623                ofs := InnerWidth - (2 * xxt + 38);
     624                x0[i] := x0[i] + 8;
     625              end;
     626              if srcno >= fJungle then
     627              begin
     628                Sprite(OffScreen, HGrTerrain, ofs + 4, y - yyt + 2, xxt * 2 - 8,
     629                  yyt * 2 - 4, 5 + 2 * (xxt * 2 + 1),
     630                  3 + yyt + 2 * (yyt * 3 + 1));
     631                Sprite(OffScreen, HGrTerrain, ofs, y - 2 * yyt, xxt * 2,
     632                  yyt * 3 - 2, 1 + srcno mod 9 * (xxt * 2 + 1),
     633                  1 + srcno div 9 * (yyt * 3 + 1));
     634              end
     635              else
     636                Sprite(OffScreen, HGrTerrain, ofs + 4, y - yyt + 2, xxt * 2 - 8,
     637                  yyt * 2 - 4, 5 + srcno mod 9 * (xxt * 2 + 1),
     638                  3 + yyt + srcno div 9 * (yyt * 3 + 1));
     639              if HelpLineInfo.Picpix >= 3 * 12 then { rare resource }
     640                Sprite(OffScreen, HGrTerrain, ofs, y - 2 * yyt, xxt * 2,
     641                  yyt * 3, 1 + 8 * (xxt * 2 + 1),
     642                  1 + (HelpLineInfo.Picpix - 2 * 12) * (yyt * 3 + 1))
     643              else if HelpLineInfo.Picpix >= 12 then { special tile }
     644              begin
     645                if HelpLineInfo.Picpix mod 12 = fJungle then
     646                  srcno := 17 * 9 + 8
     647                else if HelpLineInfo.Picpix mod 12 < fJungle then
     648                  srcno := HelpLineInfo.Picpix mod 12
     649                else
     650                  srcno := 18 + 8 + (HelpLineInfo.Picpix mod 12 - 9) * 18;
     651                srcno := srcno + HelpLineInfo.Picpix div 12 * 9;
     652                Sprite(OffScreen, HGrTerrain, ofs, y - 2 * yyt, xxt * 2,
     653                  yyt * 3, 1 + srcno mod 9 * (xxt * 2 + 1),
     654                  1 + srcno div 9 * (yyt * 3 + 1));
     655              end;
     656            end;
     657          pkTerImp:
     658            begin
     659              ofs := 8;
     660              if HelpLineInfo.Picpix = 5 then
     661              begin // display mine on hills
     662                Sprite(OffScreen, HGrTerrain, ofs + 4, i * 24 + 13 - yyt,
     663                  xxt * 2 - 8, yyt * 2 - 4, 5 + 2 * (xxt * 2 + 1),
     664                  3 + yyt + 2 * (yyt * 3 + 1));
     665                srcno := 45
     666              end
     667              else
     668                srcno := fPrairie; // display on prairie
     669              Sprite(OffScreen, HGrTerrain, ofs + 4, i * 24 + 13 - yyt,
     670                xxt * 2 - 8, yyt * 2 - 4, 5 + srcno mod 9 * (xxt * 2 + 1),
     671                3 + yyt + srcno div 9 * (yyt * 3 + 1));
     672              if HelpLineInfo.Picpix = 12 then { river }
     673                Sprite(OffScreen, HGrTerrain, ofs, i * 24 + 11 - yyt, xxt * 2,
     674                  yyt * 2, 1 + 5 * (xxt * 2 + 1), 1 + yyt + 13 * (yyt * 3 + 1))
     675              else if HelpLineInfo.Picpix >= 3 then { improvement 2 }
     676              begin
     677                if HelpLineInfo.Picpix = 6 then
     678                  Sprite(OffScreen, HGrTerrain, ofs, i * 24 + 11 - 2 * yyt,
     679                    xxt * 2, yyt * 3, 1 + 7 * (xxt * 2 + 1),
     680                    1 + 12 * (yyt * 3 + 1));
     681                Sprite(OffScreen, HGrTerrain, ofs, i * 24 + 11 - 2 * yyt,
     682                  xxt * 2, yyt * 3, 1 + (HelpLineInfo.Picpix - 3) *
     683                  (xxt * 2 + 1), 1 + 12 * (yyt * 3 + 1))
     684              end
     685              else { improvement 1 }
     686              begin
     687                Sprite(OffScreen, HGrTerrain, ofs, i * 24 + 11 - 2 * yyt,
     688                  xxt * 2, yyt * 3, 1 + 2 * (xxt * 2 + 1),
     689                  1 + (9 + HelpLineInfo.Picpix) * (yyt * 3 + 1));
     690                Sprite(OffScreen, HGrTerrain, ofs, i * 24 + 11 - 2 * yyt,
     691                  xxt * 2, yyt * 3, 1 + 5 * (xxt * 2 + 1),
     692                  1 + (9 + HelpLineInfo.Picpix) * (yyt * 3 + 1))
     693              end;
     694              x0[i] := x0[i] + 8;
     695            end;
     696          pkModel:
     697            begin
     698              FrameImage(OffScreen.Canvas, BigImp, x0[i] + 12, i * 24 - 7,
     699                56, 40, 0, 0);
     700              Sprite(OffScreen, HGrStdUnits, x0[i] + 8, i * 24 - 11, 64, 44,
     701                1 + HelpLineInfo.Picpix mod 10 * 65,
     702                1 + HelpLineInfo.Picpix div 10 * 49);
     703              x0[i] := 64 + 8 + 8 + x0[i];
     704            end;
     705          pkFeature:
     706            begin
     707              DarkGradient(OffScreen.Canvas, x0[i] + 8 - 1,
     708                7 + i * 24 - 3, 16, 1);
     709              Frame(OffScreen.Canvas, x0[i] + 8, 7 + i * 24 - 2, x0[i] + 8 + 13,
     710                7 + i * 24 - 2 + 13, $C0C0C0, $C0C0C0);
     711              Sprite(OffScreen, HGrSystem, x0[i] + 8 + 2, 7 + i * 24, 10, 10,
     712                66 + HelpLineInfo.Picpix mod 11 * 11,
     713                137 + HelpLineInfo.Picpix div 11 * 11);
     714              x0[i] := x0[i] + 8 + 8 + 2 + 13;
     715            end;
     716          pkExp:
     717            begin
     718              Frame(OffScreen.Canvas, 20 - 1, 8 - 4 + i * 24, 20 + 12,
     719                8 + 11 + i * 24, $000000, $000000);
     720              Dump(OffScreen, HGrSystem, 20, 8 - 3 + i * 24, 12, 14,
     721                121 + HelpLineInfo.Picpix * 13, 28);
     722              x0[i] := 20 + 8 + 11;
     723            end;
     724          pkAITStat:
     725            begin
     726              Sprite(OffScreen, HGrSystem, 20, 6 + i * 24, 14, 14,
     727                1 + HelpLineInfo.Picpix * 15, 316);
     728              x0[i] := 20 + 8 + 11;
     729            end;
     730          pkGov:
     731            begin
     732              Frame(OffScreen.Canvas, 8 - 1 + x0[i], 2 - 1 + i * 24,
     733                8 + xSizeSmall + x0[i], 2 + 20 + i * 24, $000000, $000000);
     734              BitBlt(OffScreen.Canvas.Handle, 8 + x0[i], 2 + i * 24, xSizeSmall,
     735                ySizeSmall, SmallImp.Canvas.Handle, (HelpLineInfo.Picpix - 1) *
     736                xSizeSmall, ySizeSmall, SRCCOPY);
     737              x0[i] := x0[i] + (8 + 8 + 36);
     738            end;
     739          pkDot:
     740            begin
     741              Sprite(OffScreen, HGrSystem, x0[i] + 18, 9 + i * 24, 8,
     742                8, 81, 16);
     743              x0[i] := 20 + 8 + 4;
     744            end;
     745          pkNormal_Dot:
     746            x0[i] := 20 + 8 + 4;
     747          pkNormal_64:
     748            x0[i] := 64 + 8 + 8;
     749        else
     750          x0[i] := x0[i] + 8
     751        end;
     752        line(OffScreen.Canvas, i, false)
     753      end
     754  end;
     755  MarkUsedOffscreen(InnerWidth, InnerHeight + 13 + 48);
     756end; { OffscreenPaint }
     757
     758procedure THelpDlg.Prepare(sbPos: integer = 0);
     759var
     760  i, j, special, Domain, Headline, TerrType, TerrSubType: integer;
     761  s: string;
     762  ps: pchar;
     763  List: THyperText;
     764  CheckSeeAlso: boolean;
     765
     766  procedure AddAdv(i: integer);
     767  begin
     768    MainText.AddLine(Phrases.Lookup('ADVANCES', i), pkAdvIcon, i,
     769      hkAdv + hkCrossLink, i);
     770  end;
     771
     772  procedure AddPreqAdv(i: integer);
     773  begin
     774    MainText.AddLine(Phrases.Lookup('ADVANCES', i), pkAdvIcon_AsPreq, i,
     775      hkAdv + hkCrossLink, i);
     776  end;
     777
     778  procedure AddImp(i: integer);
     779  begin
     780    MainText.AddLine(Phrases.Lookup('IMPROVEMENTS', i), pkSmallIcon, i,
     781      hkImp + hkCrossLink, i);
     782  end;
     783
     784  procedure AddPreqImp(i: integer);
     785  begin
     786    MainText.AddLine(Phrases.Lookup('IMPROVEMENTS', i), pkSmallIcon_AsPreq, i,
     787      hkImp + hkCrossLink, i);
     788  end;
     789
     790  procedure AddTer(i: integer);
     791  begin
     792    if MainText.Count > 1 then
     793    begin
     794      MainText.LF;
     795    end;
     796    MainText.AddLine(Phrases.Lookup('TERRAIN', i), pkTer, i, hkTer, i);
     797  end;
     798
     799  procedure AddFeature(i: integer);
     800  begin
     801    MainText.AddLine(Phrases.Lookup('FEATURES', i), pkFeature, i,
     802      hkFeature + hkCrossLink, i);
     803  end;
     804
     805  procedure AddModel(i: integer);
     806  var
     807    pix: integer;
     808    Name: string;
     809  begin
     810    if MainText.Count > 1 then
     811      MainText.LF;
     812    FindStdModelPicture(SpecialModelPictureCode[i], pix, Name);
     813    MainText.AddLine(Name, pkModel, pix, hkModel + hkCrossLink, i)
     814  end;
     815
     816  procedure AddStandardBlock(Item: string);
     817  var
     818    i: integer;
     819  begin
     820    with MainText do
     821    begin
     822      if Item = 'LOGO' then
     823      begin
     824        AddLine('', pkLogo);
     825        LF;
     826      end
     827      else if Item = 'TECHFORMULA' then
     828      begin
     829        i := Difficulty;
     830        if i = 0 then
     831          i := 2;
     832        AddLine(Format(HelpText.Lookup('TECHFORMULA'), [TechFormula_M[i],
     833          TechFormula_D[i]]))
     834      end
     835      else if Item = 'EXPERIENCE' then
     836        for i := 0 to nExp - 1 do
     837          AddLine(Phrases.Lookup('EXPERIENCE', i), pkExp, i)
     838      else if Item = 'MODERN' then
     839        for i := 1 to 3 do
     840        begin
     841          LF;
     842          AddLine(Phrases.Lookup('TERRAIN', 3 * 12 + i), pkTer, 3 * 12 + i);
     843        end
     844      else if Item = 'SAVED' then
     845        AddLine(DataDir + 'Saved', pkNormal)
     846      else if Item = 'AITSTAT' then
     847        for i := 0 to 3 do
     848          AddLine(Phrases2.Lookup('AITSTAT', i), pkAITStat, i)
     849    end
     850  end;
     851
     852  procedure DecodeItem(s: string; var Category, Index: integer);
     853  var
     854    i: integer;
     855  begin
     856    if (length(s) > 0) and (s[1] = ':') then
     857    begin
     858      Category := hkMisc;
     859      Index := 0;
     860      for i := 3 to length(s) do
     861        Index := Index * 10 + ord(s[i]) - 48;
     862      case s[2] of
     863        'A':
     864          Category := hkAdv;
     865        'B':
     866          Category := hkImp;
     867        'T':
     868          Category := hkTer;
     869        'F':
     870          Category := hkFeature;
     871        'E':
     872          Category := hkInternet;
     873        'S':
     874          Category := hkModel;
     875        'C':
     876          Index := miscCredits;
     877        'J':
     878          Index := miscJobList;
     879        'G':
     880          Index := miscGovList;
     881      end;
     882      if (Category <> hkMisc) and (Index = 0) then
     883        Index := 200;
     884    end
     885    else
     886    begin
     887      Category := hkText;
     888      Index := HelpText.Gethandle(copy(s, 1, 255));
     889    end;
     890  end;
     891
     892  procedure AddText(s: string);
     893  var
     894    i, p, l, ofs, CurrentFormat, FollowFormat, Picpix, LinkCategory, LinkIndex,
     895      RightMargin: integer;
     896    Name: string;
     897  begin
     898    RightMargin := InnerWidth - 16 - GetSystemMetrics(SM_CXVSCROLL);
     899    FollowFormat := pkNormal;
     900    while s <> '' do
     901    begin
     902      Picpix := 0;
     903      LinkCategory := 0;
     904      LinkIndex := 0;
     905      if s[1] = '$' then
     906      begin // window caption
     907        p := 1;
     908        repeat
     909          inc(p)
     910        until (p > length(s)) or (s[p] = '\');
     911        Caption := copy(s, 2, p - 2);
     912        Delete(s, 1, p);
     913      end
     914      else if s[1] = '&' then
     915      begin // standard block
     916        p := 1;
     917        repeat
     918          inc(p)
     919        until (p > length(s)) or (s[p] = '\');
     920        AddStandardBlock(copy(s, 2, p - 2));
     921        Delete(s, 1, p);
     922      end
     923      else if s[1] = '@' then
     924      begin // image
     925        if (length(s) >= 2) and (s[2] = '@') then
     926        begin // generate from icon
     927          Picpix := 0;
     928          p := 3;
     929          while (p <= length(s)) and (s[p] <> '\') do
     930          begin
     931            Picpix := Picpix * 10 + ord(s[p]) - 48;
     932            inc(p)
     933          end;
     934          if (Picpix < 0) or (Picpix >= nImp) then
     935            Picpix := 0;
     936          MainText.AddLine('', pkIllu, Picpix);
     937          MainText.LF;
     938          MainText.LF;
     939        end
     940        else
     941        begin // external image
     942          p := 1;
     943          repeat
     944            inc(p)
     945          until (p > length(s)) or (s[p] = '\');
     946          if LoadLocalizedGraphicFile(ExtPic, 'Help\' + copy(s, 2, p - 2)) then
     947          begin
     948            MainText.AddLine('', pkExternal);
     949            for i := 0 to (ExtPic.Height - 12) div 24 do
     950              MainText.LF;
     951          end;
     952        end;
     953        Delete(s, 1, p);
     954      end
     955      else
     956      begin
     957        case s[1] of
     958          ':', ';':
     959            begin // link
     960              p := 1;
     961              repeat
     962                inc(p)
     963              until (p > length(s)) or (s[p] = '\') or (s[p] = ' ');
     964              DecodeItem(copy(s, 2, p - 2), LinkCategory, LinkIndex);
     965              CurrentFormat := 0;
     966              if (LinkCategory <> hkText) and (LinkIndex < 200) then
     967              // show icon
     968                case LinkCategory of
     969                  hkAdv:
     970                    begin
     971                      CurrentFormat := pkAdvIcon;
     972                      Picpix := LinkIndex
     973                    end;
     974                  hkImp:
     975                    begin
     976                      CurrentFormat := pkSmallIcon;
     977                      Picpix := LinkIndex
     978                    end;
     979                  hkTer:
     980                    begin
     981                      CurrentFormat := pkTer;
     982                      Picpix := LinkIndex
     983                    end;
     984                  hkFeature:
     985                    begin
     986                      CurrentFormat := pkFeature;
     987                      Picpix := LinkIndex
     988                    end;
     989                  hkModel:
     990                    begin
     991                      CurrentFormat := pkModel;
     992                      FindStdModelPicture(SpecialModelPictureCode[LinkIndex],
     993                        Picpix, Name);
     994                    end;
     995                end;
     996              if s[1] = ':' then
     997                LinkCategory := LinkCategory + hkCrossLink;
     998              if (p > length(s)) or (s[p] = ' ') then
     999                Delete(s, 1, p)
     1000              else
     1001                Delete(s, 1, p - 1)
     1002            end;
     1003          '!': // highlited
     1004            if (length(s) >= 2) and (s[2] = '!') then
     1005            begin
     1006              if MainText.Count > 1 then
     1007                MainText.LF;
     1008              FollowFormat := pkCaption;
     1009              CurrentFormat := pkCaption;
     1010              Delete(s, 1, 2);
     1011            end
     1012            else
     1013            begin
     1014              FollowFormat := pkSection;
     1015              CurrentFormat := pkSection;
     1016              Delete(s, 1, 1);
     1017            end;
     1018          '-':
     1019            begin // list
     1020              FollowFormat := pkNormal_Dot;
     1021              CurrentFormat := pkDot;
     1022              Delete(s, 1, 1);
     1023            end;
     1024        else
     1025          CurrentFormat := FollowFormat;
     1026        end;
     1027        if FollowFormat = pkNormal_Dot then
     1028          ofs := 20 + 4 + 8
     1029        else
     1030          ofs := 8;
     1031        p := 0;
     1032        repeat
     1033          repeat
     1034            inc(p)
     1035          until (p > length(s)) or (s[p] = ' ') or (s[p] = '\');
     1036          if (BiColorTextWidth(OffScreen.Canvas, copy(s, 1, p - 1)) <=
     1037            RightMargin - ofs) then
     1038            l := p - 1
     1039          else
     1040            Break;
     1041        until (p >= length(s)) or (s[l + 1] = '\');
     1042        MainText.AddLine(copy(s, 1, l), CurrentFormat, Picpix, LinkCategory,
     1043          LinkIndex);
     1044        if (l < length(s)) and (s[l + 1] = '\') then
     1045          FollowFormat := pkNormal;
     1046        Delete(s, 1, l + 1);
    3451047      end
    3461048    end
    3471049  end;
    348 end;
    349 
    350 procedure THelpDlg.OffscreenPaint;
    351 
    352   procedure PaintTerrIcon(x,y,xSrc,ySrc: integer);
    353   begin
    354   Frame(offscreen.canvas,x-1,y-1,x+xSizeBig,y+ySizeBig,$000000,$000000);
    355   if 2*yyt<40 then
    356     begin
    357     Sprite(Offscreen, HGrTerrain, x, y, 56, 2*yyt, xSrc, ySrc);
    358     Sprite(Offscreen, HGrTerrain, x, y+2*yyt, 56, 40-2*yyt, xSrc, ySrc);
    359     end
    360   else Sprite(Offscreen, HGrTerrain, x, y, 56, 40, xSrc, ySrc);
    361   Sprite(Offscreen, HGrTerrain, x, y, xxt, yyt, xSrc+xxt, ySrc+yyt);
    362   Sprite(Offscreen, HGrTerrain, x, y+yyt, xxt, 40-yyt, xSrc+xxt, ySrc);
    363   Sprite(Offscreen, HGrTerrain, x+xxt, y, 56-xxt, yyt, xSrc, ySrc+yyt);
    364   Sprite(Offscreen, HGrTerrain, x+xxt, y+yyt, 56-xxt, 40-yyt, xSrc, ySrc);
    365   end;
    366 
    367 var
    368 i,j,yl,srcno,ofs,cnt,y: integer;
    369 s: string;
    370 HelpLineInfo: THelpLineInfo;
    371 begin
    372 inherited;
    373 CaptionColor:=Colors.Canvas.Pixels[clkMisc,cliPaperCaption];
    374 FillSeamless(offscreen.Canvas,0,0,InnerWidth,InnerHeight,0,sb.si.npos*24,
    375   Paper);
    376 with offscreen.Canvas do
    377   begin
    378   Font.Assign(UniFont[ftNormal]);
    379   for i:=-sb.si.npos to InnerHeight div 24 do
    380     if sb.si.npos+i<MainText.Count then
    381       begin
    382       HelpLineInfo:=THelpLineInfo(MainText.Objects[sb.si.npos+i]);
    383       if HelpLineInfo.Format=pkExternal then
    384         begin
    385         yl:=ExtPic.Height;
    386         if 4+i*24+yl>InnerHeight then yl:=InnerHeight-(4+i*24);
    387         BitBlt(Handle,8,4+i*24,ExtPic.Width,yl,
    388           ExtPic.Canvas.Handle,0,0,SRCCOPY);
    389         end
    390       end;
    391   for i:=-2 to InnerHeight div 24 do
    392     if (sb.si.npos+i>=0) and (sb.si.npos+i<MainText.Count) then
    393       begin
    394       HelpLineInfo:=THelpLineInfo(MainText.Objects[sb.si.npos+i]);
    395       if HelpLineInfo.Link<>0 then
    396         begin
    397         if (Kind=hkMisc) and (no=miscSearchResult) then
    398           Sprite(offscreen,HGrSystem,18,9+i*24,8,8,90,16)
    399         else if HelpLineInfo.Format in [pkSmallIcon_AsPreq,pkAdvIcon_AsPreq] then
    400           Sprite(offscreen,HGrSystem,12,i*24+5,14,14,65,20)
    401         else if HelpLineInfo.Link and (hkCrossLink shl 8)<>0 then
    402           Sprite(offscreen,HGrSystem,12,i*24+5,14,14,80,1)
    403         else if not ((Kind=hkMisc) and (no=miscMain)) then
    404           Sprite(offscreen,HGrSystem,10,i*24+6,14,14,65,1);
    405         x0[i]:=24;
    406         end
    407       else x0[i]:=0;
    408       case HelpLineInfo.Format of
    409         pkLogo:
    410           begin
    411           Server(sGetVersion,0,0,j);
    412           s:=Format('%d.%d.%d',[j shr 16 and $FF, j shr 8 and $FF, j and $FF]);
    413           PaintLogo(offscreen.canvas,(InnerWidth-122) div 2,i*24+1,
    414             GrExt[HGrSystem].Data.Canvas.Pixels[95,1],$000000);
    415           Font.Assign(UniFont[ftSmall]);
    416           BiColorTextout(offscreen.Canvas,$000000,$7F007F,
    417             (InnerWidth-TextWidth(s)) div 2,i*24+26,s);
    418           Font.Assign(UniFont[ftNormal]);
    419           end;
    420         pkSmallIcon,pkSmallIcon_AsPreq:
    421           begin
    422           Frame(offscreen.Canvas,8-1+x0[i],2-1+i*24,8+xSizeSmall+x0[i],2+20+i*24,
    423             $000000,$000000);
    424           if HelpLineInfo.Picpix=imPalace then
    425             BitBlt(offscreen.Canvas.Handle,8+x0[i],2+i*24,xSizeSmall,ySizeSmall,SmallImp.Canvas.Handle,
    426               0*xSizeSmall,1*ySizeSmall,SRCCOPY)
    427           else BitBlt(offscreen.Canvas.Handle,8+x0[i],2+i*24,xSizeSmall,ySizeSmall,SmallImp.Canvas.Handle,
    428             HelpLineInfo.Picpix mod 7*xSizeSmall,(HelpLineInfo.Picpix+SystemIconLines*7) div 7*ySizeSmall,SRCCOPY);
    429           x0[i]:=x0[i]+(8+8+36);
    430           end;
    431         pkBigIcon:
    432           begin
    433           FrameImage(offscreen.canvas,BigImp,x0[i]+12,i*24-7,56,40,
    434             HelpLineInfo.Picpix mod 7*xSizeBig,
    435             HelpLineInfo.Picpix div 7*ySizeBig);
    436           x0[i]:=64+8+8+x0[i];
    437           end;
    438         pkSpecialIcon:
    439           begin
    440           case HelpLineInfo.Picpix of
    441             0:
    442               FrameImage(Offscreen.Canvas, GrExt[HGrSystem2].Data, 12+x0[i],-7+i*24, 56, 40, 137, 127);
    443             1:
    444               begin
    445               PaintTerrIcon(12+x0[i],-7+i*24, 1+3*(xxt*2+1), 1+yyt);
    446               if 2*yyt<40 then
    447                 Sprite(Offscreen, HGrTerrain, 12+x0[i],-7+4+i*24, 56, 2*yyt, 1+3*(xxt*2+1)+xxt-28, 1+yyt+1*(yyt*3+1))
    448               else Sprite(Offscreen, HGrTerrain, 12+x0[i],-7+4+i*24-4, 56, 40, 1+3*(xxt*2+1)+xxt-28, 1+yyt+1*(yyt*3+1)+yyt-20);
    449               end;
    450             2:
    451               begin
    452               PaintTerrIcon(12+x0[i],-7+i*24, 1+7*(xxt*2+1), 1+yyt+4*(yyt*3+1));
    453               if 2*yyt<40 then
    454                 Sprite(Offscreen, HGrTerrain, 12+x0[i],-7+4+i*24, 56, 32, 1+4*(xxt*2+1)+xxt-28, 1+yyt+12*(yyt*3+1)+yyt-16)
    455               else Sprite(Offscreen, HGrTerrain, 12+x0[i],-7+4+i*24, 56, 32, 1+4*(xxt*2+1)+xxt-28, 1+yyt+12*(yyt*3+1)+yyt-16)
    456               end;
    457             end;
    458           x0[i]:=64+8+8+x0[i];
    459           end;
    460         pkDomain:
    461           begin
    462           Frame(offscreen.Canvas,8-1+x0[i],2-1+i*24,8+36+x0[i],2+20+i*24,
    463             $000000,$000000);
    464           Dump(offscreen,HGrSystem,8+x0[i],2+i*24,36,20,
    465             75+HelpLineInfo.Picpix*37,295);
    466           x0[i]:=x0[i]+(8+8+36);
    467           end;
    468         pkAdvIcon,pkAdvIcon_AsPreq:
    469           begin
    470           Frame(offscreen.Canvas,8-1+x0[i],2-1+i*24,8+xSizeSmall+x0[i],2+ySizeSmall+i*24,
    471             $000000,$000000);
    472           if AdvIcon[HelpLineInfo.Picpix]<84 then
    473             BitBlt(offscreen.Canvas.Handle,8+x0[i],2+i*24,xSizeSmall,ySizeSmall,
    474               SmallImp.Canvas.Handle, (AdvIcon[HelpLineInfo.Picpix]+SystemIconLines*7) mod 7*xSizeSmall,
    475               (AdvIcon[HelpLineInfo.Picpix]+SystemIconLines*7) div 7*ySizeSmall,SRCCOPY)
    476           else
    477             Dump(offscreen,HGrSystem,8+x0[i],2+i*24,36,20,
    478               1+(AdvIcon[HelpLineInfo.Picpix]-84) mod 8*37,
    479               295+(AdvIcon[HelpLineInfo.Picpix]-84) div 8*21);
    480           j:=AdvValue[HelpLineInfo.Picpix] div 1000;
    481           BitBlt(Handle,x0[i]+4,4+i*24,14,14,
    482             GrExt[HGrSystem].Mask.Canvas.Handle,127+j*15,85,SRCAND);
    483           Sprite(offscreen,HGrSystem,x0[i]+3,3+i*24,14,14,127+j*15,85);
    484           x0[i]:=x0[i]+(8+8+36);
    485           end;
    486         pkRightIcon:
    487           begin
    488           if Imp[HelpLineInfo.Picpix].Kind<>ikWonder then
    489             ImpImage(Offscreen.Canvas,InnerWidth-(40+xSizeBig),i*24,
    490               HelpLineInfo.Picpix, gDespotism)
    491           else WaterSign(InnerWidth-(40+2*xSizeBig),i*24-8,HelpLineInfo.Picpix+7);
    492           x0[i]:=x0[i]+8;
    493           end;
    494         pkIllu:
    495           WaterSign(8,i*24-8,HelpLineInfo.Picpix);
    496         pkBigFeature:
    497           begin
    498           cnt:=0;
    499           for j:=nDomains-1 downto 0 do
    500             if 1 shl j and Feature[HelpLineInfo.Picpix].Domains<>0 then
    501               begin
    502               inc(cnt);
    503               Dump(offscreen,HGrSystem,InnerWidth-38-38*cnt,i*24+1,36,20,75+j*37,295);
    504               Frame(offscreen.canvas,InnerWidth-39-38*cnt,i*24,
    505                 InnerWidth-2-38*cnt,i*24+21,
    506                 $000000,$000000);
    507               end;
    508           DarkGradient(offscreen.Canvas,InnerWidth-38-38*cnt,i*24+23,cnt*38-2,1);
    509           ofs:=InnerWidth-(39+7)-19*cnt;
    510           with offscreen.Canvas do
    511             begin
    512             Brush.Color:=$C0C0C0;
    513             FrameRect(Rect(ofs,1+23+i*24,ofs+14,15+23+i*24));
    514             Brush.Style:=bsClear;
    515             Sprite(offscreen,HGrSystem,ofs+2,3+23+i*24,10,10,
    516               66+HelpLineInfo.Picpix mod 11 *11,
    517               137+HelpLineInfo.Picpix div 11 *11);
    518             end;
    519           x0[i]:=x0[i]+8;
    520           end;
    521         pkTer,pkBigTer:
    522           begin
    523           if HelpLineInfo.Format=pkBigTer then
    524             y:=i*24-3+yyt
    525           else y:=i*24+13;
    526           if HelpLineInfo.Picpix>=3*12 then srcno:=2*9+6
    527           else if HelpLineInfo.Picpix mod 12=fJungle then srcno:=18*9
    528           else if HelpLineInfo.Picpix mod 12<fJungle then
    529             srcno:=HelpLineInfo.Picpix mod 12
    530           else srcno:=27+(HelpLineInfo.Picpix mod 12-9)*18;
    531           if HelpLineInfo.Format=pkTer then
    532             begin ofs:=x0[i]+8; x0[i]:=2*xxt+8+ofs; end
    533           else begin ofs:=InnerWidth-(2*xxt+38); x0[i]:=x0[i]+8; end;
    534           if srcno>=fJungle then
    535             begin
    536             Sprite(offscreen,HGrTerrain,ofs+4,y-yyt+2,xxt*2-8,yyt*2-4,
    537               5+2*(xxt*2+1),3+yyt+2*(yyt*3+1));
    538             Sprite(offscreen,HGrTerrain,ofs,y-2*yyt,xxt*2,yyt*3-2,
    539               1+srcno mod 9 *(xxt*2+1),1+srcno div 9 *(yyt*3+1));
    540             end
    541           else Sprite(offscreen,HGrTerrain,ofs+4,y-yyt+2,xxt*2-8,yyt*2-4,
    542             5+srcno mod 9 *(xxt*2+1),3+yyt+srcno div 9 *(yyt*3+1));
    543           if HelpLineInfo.Picpix>=3*12 then {rare resource}
    544             Sprite(offscreen,HGrTerrain,ofs,y-2*yyt,xxt*2,yyt*3,
    545               1+8*(xxt*2+1), 1+(HelpLineInfo.Picpix-2*12)*(yyt*3+1))
    546           else if HelpLineInfo.Picpix>=12 then {special tile}
    547             begin
    548             if HelpLineInfo.Picpix mod 12=fJungle then srcno:=17*9+8
    549             else if HelpLineInfo.Picpix mod 12<fJungle then
    550               srcno:=HelpLineInfo.Picpix mod 12
    551             else srcno:=18+8+(HelpLineInfo.Picpix mod 12-9)*18;
    552             srcno:=srcno+HelpLineInfo.Picpix div 12*9;
    553             Sprite(offscreen,HGrTerrain,ofs,y-2*yyt,xxt*2,yyt*3,
    554               1+srcno mod 9 *(xxt*2+1),1+srcno div 9 *(yyt*3+1));
    555             end;
    556           end;
    557         pkTerImp:
    558           begin
    559           ofs:=8;
    560           if HelpLineInfo.Picpix=5 then
    561             begin // display mine on hills
    562             Sprite(offscreen,HGrTerrain,ofs+4,i*24+13-yyt,xxt*2-8,yyt*2-4,
    563               5+2*(xxt*2+1),3+yyt+2*(yyt*3+1));
    564             srcno:=45
    565             end
    566           else srcno:=fPrairie; // display on prairie
    567           Sprite(offscreen,HGrTerrain,ofs+4,i*24+13-yyt,xxt*2-8,yyt*2-4,
    568             5+srcno mod 9 *(xxt*2+1),3+yyt+srcno div 9 *(yyt*3+1));
    569           if HelpLineInfo.Picpix=12 then {river}
    570             Sprite(offscreen,HGrTerrain,ofs,i*24+11-yyt,xxt*2,yyt*2,1+5*(xxt*2+1),
    571               1+yyt+13*(yyt*3+1))
    572           else if HelpLineInfo.Picpix>=3 then {improvement 2}
    573             begin
    574             if HelpLineInfo.Picpix=6 then
    575               Sprite(offscreen,HGrTerrain,ofs,i*24+11-2*yyt,xxt*2,yyt*3,
    576                 1+7 *(xxt*2+1),1+12 *(yyt*3+1));
    577             Sprite(offscreen,HGrTerrain,ofs,i*24+11-2*yyt,xxt*2,yyt*3,
    578               1+(HelpLineInfo.Picpix-3)*(xxt*2+1), 1+12*(yyt*3+1))
    579             end
    580           else {improvement 1}
    581             begin
    582             Sprite(offscreen,HGrTerrain,ofs,i*24+11-2*yyt,xxt*2,yyt*3,
    583               1+2*(xxt*2+1),1+(9+HelpLineInfo.Picpix)*(yyt*3+1));
    584             Sprite(offscreen,HGrTerrain,ofs,i*24+11-2*yyt,xxt*2,yyt*3,
    585               1+5*(xxt*2+1),1+(9+HelpLineInfo.Picpix)*(yyt*3+1))
    586             end;
    587           x0[i]:=x0[i]+8;
    588           end;
    589         pkModel:
    590           begin
    591           FrameImage(offscreen.canvas,BigImp,x0[i]+12,i*24-7,56,40,0,0);
    592           Sprite(offscreen,HGrStdUnits,x0[i]+8,i*24-11,64,44,
    593             1+HelpLineInfo.Picpix mod 10 *65,1+HelpLineInfo.Picpix div 10 *49);
    594           x0[i]:=64+8+8+x0[i];
    595           end;
    596         pkFeature:
    597           begin
    598           DarkGradient(offscreen.Canvas,x0[i]+8-1,7+i*24-3,16,1);
    599           Frame(offscreen.canvas,x0[i]+8,7+i*24-2,x0[i]+8+13,7+i*24-2+13,
    600             $C0C0C0,$C0C0C0);
    601           Sprite(offscreen,HGrSystem,x0[i]+8+2,7+i*24,10,10,
    602             66+HelpLineInfo.Picpix mod 11*11,137+HelpLineInfo.Picpix div 11*11);
    603           x0[i]:=x0[i]+8+8+2+13;
    604           end;
    605         pkExp:
    606           begin
    607           Frame(offscreen.Canvas,20-1,8-4+i*24,20+12,8+11+i*24,$000000,$000000);
    608           Dump(offscreen,HGrSystem,20,8-3+i*24,12,14,121+HelpLineInfo.Picpix*13,28);
    609           x0[i]:=20+8+11;
    610           end;
    611         pkAITStat:
    612           begin
    613           Sprite(offscreen,HGrSystem,20,6+i*24,14,14,1+HelpLineInfo.Picpix*15,316);
    614           x0[i]:=20+8+11;
    615           end;
    616         pkGov:
    617           begin
    618           Frame(offscreen.Canvas,8-1+x0[i],2-1+i*24,8+xSizeSmall+x0[i],2+20+i*24,
    619             $000000,$000000);
    620           BitBlt(offscreen.Canvas.Handle,8+x0[i],2+i*24,xSizeSmall,ySizeSmall,SmallImp.Canvas.Handle,
    621             (HelpLineInfo.Picpix-1)*xSizeSmall,ySizeSmall,SRCCOPY);
    622           x0[i]:=x0[i]+(8+8+36);
    623           end;
    624         pkDot:
    625           begin
    626           Sprite(offscreen,HGrSystem,x0[i]+18,9+i*24,8,8,81,16);
    627           x0[i]:=20+8+4;
    628           end;
    629         pkNormal_Dot:
    630           x0[i]:=20+8+4;
    631         pkNormal_64:
    632           x0[i]:=64+8+8;
    633         else x0[i]:=x0[i]+8
    634         end;
    635       line(offscreen.Canvas,i,false)
    636       end
    637   end;
    638 MarkUsedOffscreen(InnerWidth,InnerHeight+13+48);
    639 end; {OffscreenPaint}
    640 
    641 procedure THelpDlg.Prepare(sbPos: integer = 0);
    642 var
    643 i,j,special,Domain,Headline,TerrType,TerrSubType: integer;
    644 s: string;
    645 ps: pchar;
    646 List: THyperText;
    647 CheckSeeAlso: boolean;
    648 
    649   procedure AddAdv(i: integer);
    650   begin
    651   MainText.AddLine(Phrases.Lookup('ADVANCES',i),pkAdvIcon,i,hkAdv+hkCrossLink,i);
    652   end;
    653 
    654   procedure AddPreqAdv(i: integer);
    655   begin
    656   MainText.AddLine(Phrases.Lookup('ADVANCES',i),pkAdvIcon_AsPreq,i,hkAdv+hkCrossLink,i);
    657   end;
    658 
    659   procedure AddImp(i: integer);
    660   begin
    661   MainText.AddLine(Phrases.Lookup('IMPROVEMENTS',i),pkSmallIcon,i,hkImp+hkCrossLink,i);
    662   end;
    663 
    664   procedure AddPreqImp(i: integer);
    665   begin
    666   MainText.AddLine(Phrases.Lookup('IMPROVEMENTS',i),pkSmallIcon_AsPreq,i,hkImp+hkCrossLink,i);
    667   end;
    668 
    669   procedure AddTer(i: integer);
    670   begin
    671   if MainText.Count>1 then begin MainText.LF; end;
    672   MainText.AddLine(Phrases.Lookup('TERRAIN',i),pkTer,i,hkTer,i);
    673   end;
    674 
    675   procedure AddFeature(i: integer);
    676   begin
    677   MainText.AddLine(Phrases.Lookup('FEATURES',i),pkFeature,i,hkFeature+hkCrossLink,i);
    678   end;
    679 
    680   procedure AddModel(i: integer);
    681   var
    682   pix: integer;
    683   Name: string;
    684   begin
    685   if MainText.Count>1 then MainText.LF;
    686   FindStdModelPicture(SpecialModelPictureCode[i],pix,Name);
    687   MainText.AddLine(Name,pkModel,pix,hkModel+hkCrossLink,i)
    688   end;
    689 
    690   procedure AddStandardBlock(Item: string);
    691   var
    692   i: integer;
    693   begin
    694   with MainText do
    695     begin
    696     if Item='LOGO' then
    697       begin AddLine('',pkLogo); LF; end
    698     else if Item='TECHFORMULA' then
    699       begin
    700       i:=Difficulty;
    701       if i=0 then i:=2;
    702       AddLine(Format(HelpText.Lookup('TECHFORMULA'),[TechFormula_M[i],TechFormula_D[i]]))
    703       end
    704     else if Item='EXPERIENCE' then
    705       for i:=0 to nExp-1 do AddLine(Phrases.Lookup('EXPERIENCE',i),pkExp,i)
    706     else if Item='MODERN' then
    707       for i:=1 to 3 do
    708         begin
    709         LF;
    710         AddLine(Phrases.Lookup('TERRAIN',3*12+i),pkTer,3*12+i);
    711         end
    712     else if Item='SAVED' then
    713       AddLine(DataDir+'Saved',pkNormal)
    714     else if Item='AITSTAT' then
    715       for i:=0 to 3 do AddLine(Phrases2.Lookup('AITSTAT',i),pkAITStat,i)
    716     end
    717   end;
    718 
    719   procedure DecodeItem(s: string; var Category, Index: integer);
    720   var
    721   i: integer;
    722   begin
    723   if (length(s)>0) and (s[1]=':') then
    724     begin
    725     Category:=hkMisc;
    726     Index:=0;
    727     for i:=3 to Length(s) do Index:=Index*10+ord(s[i])-48;
    728     case s[2] of
    729       'A': Category:=hkAdv;
    730       'B': Category:=hkImp;
    731       'T': Category:=hkTer;
    732       'F': Category:=hkFeature;
    733       'E': Category:=hkInternet;
    734       'S': Category:=hkModel;
    735       'C': Index:=miscCredits;
    736       'J': Index:=miscJobList;
    737       'G': Index:=miscGovList;
    738       end;
    739     if (Category<>hkMisc) and (Index=0) then
    740       Index:=200;
    741     end
    742   else
    743     begin
    744     Category:=hkText;
    745     Index:=HelpText.GetHandle(copy(s,1,255));
    746     end;
    747   end;
    748 
    749   procedure AddText(s: string);
    750   var
    751   i,p,l,ofs,CurrentFormat,FollowFormat,Picpix,LinkCategory,LinkIndex,RightMargin: integer;
    752   Name: string;
    753   begin
    754   RightMargin:=InnerWidth-16-GetSystemMetrics(SM_CXVSCROLL);
    755   FollowFormat:=pkNormal;
    756   while s<>'' do
    757     begin
    758     Picpix:=0;
    759     LinkCategory:=0;
    760     LinkIndex:=0;
    761     if s[1]='$' then
    762       begin // window caption
    763       p:=1;
    764       repeat inc(p) until (p>Length(s)) or (s[p]='\');
    765       Caption:=Copy(s,2,p-2);
    766       Delete(s,1,p);
    767       end
    768     else if s[1]='&' then
    769       begin // standard block
    770       p:=1;
    771       repeat inc(p) until (p>Length(s)) or (s[p]='\');
    772       AddStandardBlock(Copy(s,2,p-2));
    773       Delete(s,1,p);
    774       end
    775     else if s[1]='@' then
    776       begin // image
    777       if (Length(s)>=2) and (s[2]='@') then
    778         begin // generate from icon
    779         Picpix:=0;
    780         p:=3;
    781         while (p<=Length(s)) and (s[p]<>'\') do
    782           begin Picpix:=Picpix*10+ord(s[p])-48; inc(p) end;
    783         if (Picpix<0) or (Picpix>=nImp) then Picpix:=0;
    784         MainText.AddLine('',pkIllu,Picpix);
    785         MainText.LF;
    786         MainText.LF;
    787         end
    788       else
    789         begin // external image
    790         p:=1;
    791         repeat inc(p) until (p>Length(s)) or (s[p]='\');
    792         if LoadLocalizedGraphicFile(ExtPic, 'Help\'+Copy(s,2,p-2)) then
    793           begin
    794           MainText.AddLine('',pkExternal);
    795           for i:=0 to (ExtPic.Height-12) div 24 do MainText.LF;
    796           end;
    797         end;
    798       Delete(s,1,p);
    799       end
    800     else
    801       begin
    802       case s[1] of
    803         ':',';':
    804           begin // link
    805           p:=1;
    806           repeat inc(p) until (p>Length(s)) or (s[p]='\') or (s[p]=' ');
    807           DecodeItem(copy(s,2,p-2), LinkCategory, LinkIndex);
    808           CurrentFormat:=0;
    809           if (LinkCategory<>hkText) and (LinkIndex<200) then // show icon
    810             case LinkCategory of
    811               hkAdv:
    812                 begin CurrentFormat:=pkAdvIcon; Picpix:=LinkIndex end;
    813               hkImp:
    814                 begin CurrentFormat:=pkSmallIcon; Picpix:=LinkIndex end;
    815               hkTer:
    816                 begin CurrentFormat:=pkTer; Picpix:=LinkIndex end;
    817               hkFeature:
    818                 begin CurrentFormat:=pkFeature; Picpix:=LinkIndex end;
    819               hkModel:
    820                 begin
    821                 CurrentFormat:=pkModel;
    822                 FindStdModelPicture(SpecialModelPictureCode[LinkIndex],Picpix,Name);
    823                 end;
    824               end;
    825           if s[1]=':' then LinkCategory:=LinkCategory+hkCrossLink;
    826           if (p>Length(s)) or (s[p]=' ') then Delete(s,1,p)
    827           else Delete(s,1,p-1)
    828           end;
    829         '!': // highlited
    830           if (length(s)>=2) and (s[2]='!') then
    831             begin
    832             if MainText.Count>1 then MainText.LF;
    833             FollowFormat:=pkCaption;
    834             CurrentFormat:=pkCaption;
    835             Delete(s,1,2);
    836             end
    837           else
    838             begin
    839             FollowFormat:=pkSection;
    840             CurrentFormat:=pkSection;
    841             Delete(s,1,1);
    842             end;
    843         '-':
    844           begin // list
    845           FollowFormat:=pkNormal_Dot;
    846           CurrentFormat:=pkDot;
    847           Delete(s,1,1);
    848           end;
    849         else CurrentFormat:=FollowFormat;
    850         end;
    851       if FollowFormat=pkNormal_Dot then ofs:=20+4+8
    852       else ofs:=8;
    853       p:=0;
    854       repeat
    855         repeat inc(p) until (p>Length(s)) or (s[p]=' ') or (s[p]='\');
    856         if (BiColorTextWidth(Offscreen.Canvas,Copy(s,1,p-1))<=RightMargin-ofs) then
    857           l:=p-1
    858         else Break;
    859       until (p>=Length(s)) or (s[l+1]='\');
    860       MainText.AddLine(Copy(s,1,l),CurrentFormat,Picpix,LinkCategory,LinkIndex);
    861       if (l<Length(s)) and (s[l+1]='\') then FollowFormat:=pkNormal;
    862       Delete(s,1,l+1);
    863       end
    864     end
    865   end;
    8661050
    8671051  procedure AddItem(Item: string);
    8681052  begin
    869   AddText(HelpText.Lookup(Item));
     1053    AddText(HelpText.Lookup(Item));
    8701054  end;
    8711055
    8721056  procedure AddModelText(i: integer);
    8731057  var
    874   pix: integer;
    875   s: string;
    876   begin
    877   with MainText do
    878     begin
    879     if Count>1 then begin LF; LF; end;
    880     FindStdModelPicture(SpecialModelPictureCode[i],pix,s);
    881     AddLine(s,pkSection);
    882     AddLine(Format(HelpText.Lookup('STRENGTH'),
    883       [SpecialModel[i].Attack,SpecialModel[i].Defense]),pkNormal_64);
    884     AddLine(Format(HelpText.Lookup('SPEED'),
    885       [MovementToString(SpecialModel[i].Speed)]),pkModel,pix);
    886     if Difficulty=0 then
    887       AddLine(Format(HelpText.Lookup('BUILDCOST'),
    888         [SpecialModel[i].Cost]),pkNormal_64)
    889     else AddLine(Format(HelpText.Lookup('BUILDCOST'),
    890       [SpecialModel[i].Cost*BuildCostMod[Difficulty] div 12]),pkNormal_64);
    891     s:=HelpText.LookupByHandle(hSPECIALMODEL,i);
    892     if (s<>'') and (s<>'*') then AddText(s);
    893     if SpecialModelPreq[i]>=0 then AddPreqAdv(SpecialModelPreq[i])
    894     else if SpecialModelPreq[i]=preLighthouse then AddPreqImp(woLighthouse)
    895     else if SpecialModelPreq[i]=preBuilder then AddPreqImp(woPyramids)
    896     else if SpecialModelPreq[i]=preLeo then AddPreqImp(woLeo);
    897     if SpecialModelPreq[i]<>preNone then
    898       MainText[Count-1]:=Format(HelpText.Lookup('REQUIRED'),[MainText[Count-1]]);
     1058    pix: integer;
     1059    s: string;
     1060  begin
     1061    with MainText do
     1062    begin
     1063      if Count > 1 then
     1064      begin
     1065        LF;
     1066        LF;
     1067      end;
     1068      FindStdModelPicture(SpecialModelPictureCode[i], pix, s);
     1069      AddLine(s, pkSection);
     1070      AddLine(Format(HelpText.Lookup('STRENGTH'), [SpecialModel[i].Attack,
     1071        SpecialModel[i].Defense]), pkNormal_64);
     1072      AddLine(Format(HelpText.Lookup('SPEED'),
     1073        [MovementToString(SpecialModel[i].Speed)]), pkModel, pix);
     1074      if Difficulty = 0 then
     1075        AddLine(Format(HelpText.Lookup('BUILDCOST'), [SpecialModel[i].Cost]),
     1076          pkNormal_64)
     1077      else
     1078        AddLine(Format(HelpText.Lookup('BUILDCOST'),
     1079          [SpecialModel[i].Cost * BuildCostMod[Difficulty] div 12]),
     1080          pkNormal_64);
     1081      s := HelpText.LookupByHandle(hSPECIALMODEL, i);
     1082      if (s <> '') and (s <> '*') then
     1083        AddText(s);
     1084      if SpecialModelPreq[i] >= 0 then
     1085        AddPreqAdv(SpecialModelPreq[i])
     1086      else if SpecialModelPreq[i] = preLighthouse then
     1087        AddPreqImp(woLighthouse)
     1088      else if SpecialModelPreq[i] = preBuilder then
     1089        AddPreqImp(woPyramids)
     1090      else if SpecialModelPreq[i] = preLeo then
     1091        AddPreqImp(woLeo);
     1092      if SpecialModelPreq[i] <> preNone then
     1093        MainText[Count - 1] := Format(HelpText.Lookup('REQUIRED'),
     1094          [MainText[Count - 1]]);
    8991095    end
    9001096  end;
     
    9021098  procedure AddJobList;
    9031099  var
    904   i,JobCost: integer;
    905   begin
    906   with MainText do
    907     begin
    908     for i:=0 to nJobHelp-1 do
     1100    i, JobCost: integer;
     1101  begin
     1102    with MainText do
     1103    begin
     1104      for i := 0 to nJobHelp - 1 do
    9091105      begin
    910       if i>0 then begin LF; LF end;
    911       AddLine(Phrases.Lookup('JOBRESULT',JobHelp[i]),pkSection);
    912       AddLine;
    913       AddLine('',pkTerImp,i);
    914       AddLine;
    915       AddText(HelpText.LookupByHandle(hJOBHELP,i));
    916       JobCost:=-1;
    917       case JobHelp[i] of
    918         jCanal: JobCost:=CanalWork;
    919         jFort: JobCost:=FortWork;
    920         jBase: JobCost:=BaseWork;
     1106        if i > 0 then
     1107        begin
     1108          LF;
     1109          LF
    9211110        end;
    922       if JobCost>=0 then
    923         AddText(Format(HelpText.Lookup('JOBCOST'),[MovementToString(JobCost)]))
    924       else AddText(HelpText.Lookup('JOBCOSTVAR'));
    925       if JobPreq[JobHelp[i]]<>preNone then
     1111        AddLine(Phrases.Lookup('JOBRESULT', JobHelp[i]), pkSection);
     1112        AddLine;
     1113        AddLine('', pkTerImp, i);
     1114        AddLine;
     1115        AddText(HelpText.LookupByHandle(hJOBHELP, i));
     1116        JobCost := -1;
     1117        case JobHelp[i] of
     1118          jCanal:
     1119            JobCost := CanalWork;
     1120          jFort:
     1121            JobCost := FortWork;
     1122          jBase:
     1123            JobCost := BaseWork;
     1124        end;
     1125        if JobCost >= 0 then
     1126          AddText(Format(HelpText.Lookup('JOBCOST'),
     1127            [MovementToString(JobCost)]))
     1128        else
     1129          AddText(HelpText.Lookup('JOBCOSTVAR'));
     1130        if JobPreq[JobHelp[i]] <> preNone then
    9261131        begin
    927         AddPreqAdv(JobPreq[JobHelp[i]]);
    928         MainText[Count-1]:=Format(HelpText.Lookup('REQUIRED'),[MainText[Count-1]]);
     1132          AddPreqAdv(JobPreq[JobHelp[i]]);
     1133          MainText[Count - 1] := Format(HelpText.Lookup('REQUIRED'),
     1134            [MainText[Count - 1]]);
    9291135        end
    9301136      end;
     
    9341140  procedure AddGraphicCredits;
    9351141  var
    936   i: integer;
    937   s: string;
    938   sr: TSearchRec;
    939   List, plus: tstringlist;
    940   begin
    941   List:=tstringlist.Create;
    942   plus:=tstringlist.Create;
    943   if FindFirst(HomeDir+'Graphics\*.credits.txt',$27,sr)=0 then
    944     repeat
    945       plus.LoadFromFile(HomeDir+'Graphics\'+sr.Name);
    946       List.AddStrings(plus);
    947     until FindNext(sr)<>0;
    948   FindClose(sr);
    949   plus.Free;
    950 
    951   List.Sort;
    952   i:=1;
    953   while i<List.Count do
    954     if List[i]=List[i-1] then List.Delete(i)
    955     else inc(i);
    956 
    957   for i:=0 to List.Count-1 do
    958     begin
    959     s:=List[i];
    960     while BiColorTextWidth(Offscreen.Canvas,s)>InnerWidth-16
    961       -GetSystemMetrics(SM_CXVSCROLL) do
    962       Delete(s,Length(s),1);
    963     MainText.AddLine(s);
     1142    i: integer;
     1143    s: string;
     1144    sr: TSearchRec;
     1145    List, plus: TStringList;
     1146  begin
     1147    List := TStringList.Create;
     1148    plus := TStringList.Create;
     1149    if FindFirst(HomeDir + 'Graphics\*.credits.txt', $27, sr) = 0 then
     1150      repeat
     1151        plus.LoadFromFile(HomeDir + 'Graphics\' + sr.Name);
     1152        List.AddStrings(plus);
     1153      until FindNext(sr) <> 0;
     1154    FindClose(sr);
     1155    plus.Free;
     1156
     1157    List.Sort;
     1158    i := 1;
     1159    while i < List.Count do
     1160      if List[i] = List[i - 1] then
     1161        List.Delete(i)
     1162      else
     1163        inc(i);
     1164
     1165    for i := 0 to List.Count - 1 do
     1166    begin
     1167      s := List[i];
     1168      while BiColorTextWidth(OffScreen.Canvas, s) > InnerWidth - 16 -
     1169        GetSystemMetrics(SM_CXVSCROLL) do
     1170        Delete(s, length(s), 1);
     1171      MainText.AddLine(s);
    9641172    end;
    965   List.Free;
     1173    List.Free;
    9661174  end;
    9671175
    9681176  procedure AddSoundCredits;
    9691177  var
    970   i: integer;
    971   s: string;
    972   List: tstringlist;
    973   begin
    974   List:=tstringlist.Create;
    975   List.LoadFromFile(HomeDir+'Sounds\sound.credits.txt');
    976   for i:=0 to List.Count-1 do
    977     begin
    978     s:=List[i];
    979     while BiColorTextWidth(Offscreen.Canvas,s)>InnerWidth-16
    980       -GetSystemMetrics(SM_CXVSCROLL) do
    981       Delete(s,Length(s),1);
    982     MainText.AddLine(s);
     1178    i: integer;
     1179    s: string;
     1180    List: TStringList;
     1181  begin
     1182    List := TStringList.Create;
     1183    List.LoadFromFile(HomeDir + 'Sounds\sound.credits.txt');
     1184    for i := 0 to List.Count - 1 do
     1185    begin
     1186      s := List[i];
     1187      while BiColorTextWidth(OffScreen.Canvas, s) > InnerWidth - 16 -
     1188        GetSystemMetrics(SM_CXVSCROLL) do
     1189        Delete(s, length(s), 1);
     1190      MainText.AddLine(s);
    9831191    end;
    984   List.Free;
     1192    List.Free;
    9851193  end;
    9861194
    9871195  procedure NextSection(Item: string);
    9881196  begin
    989   if MainText.Count>1 then
    990     if MainText.Count=Headline+1 then MainText.Delete(Headline)
    991     else MainText.LF;
    992   MainText.AddLine(HelpText.Lookup(Item),pkSection);
    993   Headline:=MainText.Count-1;
    994   end;
    995 
    996 begin {Prepare}
    997 with MainText do
    998   begin
    999   offscreen.Canvas.Font.Assign(UniFont[ftNormal]);
    1000   CheckSeeAlso:=false;
    1001   Clear;
    1002   Headline:=-1;
    1003   if (no>=200) or not (Kind in [hkAdv,hkImp,hkTer,hkFeature]) then
    1004     LF;
    1005   case Kind of
    1006     hkText: AddText(HelpText.LookupByHandle(no));
    1007     hkMisc:
    1008       begin
    1009       case no of
    1010         miscMain:
     1197    if MainText.Count > 1 then
     1198      if MainText.Count = Headline + 1 then
     1199        MainText.Delete(Headline)
     1200      else
     1201        MainText.LF;
     1202    MainText.AddLine(HelpText.Lookup(Item), pkSection);
     1203    Headline := MainText.Count - 1;
     1204  end;
     1205
     1206begin { Prepare }
     1207  with MainText do
     1208  begin
     1209    OffScreen.Canvas.Font.Assign(UniFont[ftNormal]);
     1210    CheckSeeAlso := false;
     1211    Clear;
     1212    Headline := -1;
     1213    if (no >= 200) or not(Kind in [hkAdv, hkImp, hkTer, hkFeature]) then
     1214      LF;
     1215    case Kind of
     1216      hkText:
     1217        AddText(HelpText.LookupByHandle(no));
     1218      hkMisc:
     1219        begin
     1220          case no of
     1221            miscMain:
     1222              begin
     1223                Caption := HelpText.Lookup('HELPTITLE_MAIN');
     1224                AddLine(HelpText.Lookup('HELPTITLE_QUICKSTART'), pkSpecialIcon,
     1225                  0, { pkBigIcon,22, } hkText, HelpText.Gethandle('QUICK'));
     1226                LF;
     1227                AddLine(HelpText.Lookup('HELPTITLE_CONCEPTS'), pkBigIcon, 6,
     1228                  hkText, HelpText.Gethandle('CONCEPTS'));
     1229                LF;
     1230                AddLine(HelpText.Lookup('HELPTITLE_TERLIST'), pkSpecialIcon, 1,
     1231                  hkTer, 200);
     1232                LF;
     1233                AddLine(HelpText.Lookup('HELPTITLE_JOBLIST'), pkSpecialIcon, 2,
     1234                  hkMisc, miscJobList);
     1235                LF;
     1236                AddLine(HelpText.Lookup('HELPTITLE_TECHLIST'), pkBigIcon, 39,
     1237                  hkAdv, 200);
     1238                LF;
     1239                FindStdModelPicture(SpecialModelPictureCode[6], i, s);
     1240                AddLine(HelpText.Lookup('HELPTITLE_MODELLIST'), pkModel, i,
     1241                  hkModel, 0);
     1242                LF;
     1243                AddLine(HelpText.Lookup('HELPTITLE_FEATURELIST'), pkBigIcon, 28,
     1244                  hkFeature, 200);
     1245                LF;
     1246                AddLine(HelpText.Lookup('HELPTITLE_IMPLIST'), pkBigIcon,
     1247                  7 * SystemIconLines + imCourt, hkImp, 200);
     1248                LF;
     1249                AddLine(HelpText.Lookup('HELPTITLE_UNIQUELIST'), pkBigIcon,
     1250                  7 * SystemIconLines + imStockEx, hkImp, 201);
     1251                LF;
     1252                AddLine(HelpText.Lookup('HELPTITLE_WONDERLIST'), pkBigIcon,
     1253                  7 * SystemIconLines, hkImp, 202);
     1254                LF;
     1255                AddLine(HelpText.Lookup('HELPTITLE_GOVLIST'), pkBigIcon,
     1256                  gDemocracy + 6, hkMisc, miscGovList);
     1257                LF;
     1258                AddLine(HelpText.Lookup('HELPTITLE_KEYS'), pkBigIcon, 2, hkText,
     1259                  HelpText.Gethandle('HOTKEYS'));
     1260                LF;
     1261                AddLine(HelpText.Lookup('HELPTITLE_ABOUT'), pkBigIcon, 1,
     1262                  hkText, HelpText.Gethandle('ABOUT'));
     1263                LF;
     1264                AddLine(HelpText.Lookup('HELPTITLE_CREDITS'), pkBigIcon, 22,
     1265                  hkMisc, miscCredits);
     1266              end;
     1267            miscCredits:
     1268              begin
     1269                AddItem('CREDITS');
     1270                LF;
     1271                AddGraphicCredits;
     1272                NextSection('CRED_CAPSOUND');
     1273                AddSoundCredits;
     1274                NextSection('CRED_CAPAI');
     1275                Server(sGetAICredits, 0, 0, ps);
     1276                AddText(ps);
     1277                NextSection('CRED_CAPLANG');
     1278                AddItem('AUTHOR');
     1279              end;
     1280            miscJobList:
     1281              begin
     1282                Caption := HelpText.Lookup('HELPTITLE_JOBLIST');
     1283                AddJobList;
     1284                LF;
     1285                AddItem('TERIMPEXCLUDE');
     1286                LF;
     1287                AddItem('TERIMPCITY');
     1288              end;
     1289            miscGovList:
     1290              begin
     1291                Caption := HelpText.Lookup('HELPTITLE_GOVLIST');
     1292                for i := 1 to nGov do
     1293                begin
     1294                  AddLine(Phrases.Lookup('GOVERNMENT', i mod nGov), pkSection);
     1295                  LF;
     1296                  if i = nGov then
     1297                    AddLine('', pkBigIcon, 7 * SystemIconLines + imPalace)
     1298                  else
     1299                    AddLine('', pkBigIcon, i + 6);
     1300                  LF;
     1301                  AddText(HelpText.LookupByHandle(hGOVHELP, i mod nGov));
     1302                  if i mod nGov >= 2 then
     1303                  begin
     1304                    AddPreqAdv(GovPreq[i mod nGov]);
     1305                    MainText[Count - 1] := Format(HelpText.Lookup('REQUIRED'),
     1306                      [MainText[Count - 1]]);
     1307                  end;
     1308                  if i < nGov then
     1309                  begin
     1310                    LF;
     1311                    LF;
     1312                  end
     1313                end
     1314              end;
     1315            miscSearchResult:
     1316              begin
     1317                Caption := HelpText.Lookup('HELPTITLE_SEARCHRESULTS');
     1318                AddText(Format(HelpText.Lookup('MATCHES'), [SearchContent]));
     1319                MainText.AddStrings(SearchResult);
     1320              end
     1321          end; // case no
     1322        end;
     1323
     1324      hkAdv:
     1325        if no = 200 then
     1326        begin // complete advance list
     1327          Caption := HelpText.Lookup('HELPTITLE_TECHLIST');
     1328          List := THyperText.Create;
     1329          for j := 0 to 3 do
    10111330          begin
    1012           Caption:=HelpText.Lookup('HELPTITLE_MAIN');
    1013           AddLine(HelpText.Lookup('HELPTITLE_QUICKSTART'),pkSpecialIcon,0,{pkBigIcon,22,}hkText,HelpText.GetHandle('QUICK')); LF;
    1014           AddLine(HelpText.Lookup('HELPTITLE_CONCEPTS'),pkBigIcon,6,hkText,HelpText.GetHandle('CONCEPTS')); LF;
    1015           AddLine(HelpText.Lookup('HELPTITLE_TERLIST'),pkSpecialIcon,1,hkTer,200); LF;
    1016           AddLine(HelpText.Lookup('HELPTITLE_JOBLIST'),pkSpecialIcon,2,hkMisc,miscJobList); LF;
    1017           AddLine(HelpText.Lookup('HELPTITLE_TECHLIST'),pkBigIcon,39,hkAdv,200); LF;
    1018           FindStdModelPicture(SpecialModelPictureCode[6],i,s);
    1019           AddLine(HelpText.Lookup('HELPTITLE_MODELLIST'),pkModel,i,hkModel,0); LF;
    1020           AddLine(HelpText.Lookup('HELPTITLE_FEATURELIST'),pkBigIcon,28,hkFeature,200); LF;
    1021           AddLine(HelpText.Lookup('HELPTITLE_IMPLIST'),pkBigIcon,7*SystemIconLines+imCourt,hkImp,200); LF;
    1022           AddLine(HelpText.Lookup('HELPTITLE_UNIQUELIST'),pkBigIcon,7*SystemIconLines+imStockEx,hkImp,201); LF;
    1023           AddLine(HelpText.Lookup('HELPTITLE_WONDERLIST'),pkBigIcon,7*SystemIconLines,hkImp,202); LF;
    1024           AddLine(HelpText.Lookup('HELPTITLE_GOVLIST'),pkBigIcon,gDemocracy+6,hkMisc,miscGovList); LF;
    1025           AddLine(HelpText.Lookup('HELPTITLE_KEYS'),pkBigIcon,2,hkText,HelpText.GetHandle('HOTKEYS')); LF;
    1026           AddLine(HelpText.Lookup('HELPTITLE_ABOUT'),pkBigIcon,1,hkText,HelpText.GetHandle('ABOUT')); LF;
    1027           AddLine(HelpText.Lookup('HELPTITLE_CREDITS'),pkBigIcon,22,hkMisc,miscCredits);
     1331            if j > 0 then
     1332            begin
     1333              LF;
     1334              LF;
     1335            end;
     1336            AddLine(HelpText.Lookup('TECHAGE', j), pkSection);
     1337            if j = 1 then
     1338              AddLine(Phrases.Lookup('ADVANCES', adScience) + ' ' +
     1339                HelpText.Lookup('BASETECH'), pkAdvIcon, adScience, hkAdv,
     1340                adScience);
     1341            if j = 2 then
     1342              AddLine(Phrases.Lookup('ADVANCES', adMassProduction) + ' ' +
     1343                HelpText.Lookup('BASETECH'), pkAdvIcon, adMassProduction, hkAdv,
     1344                adMassProduction);
     1345            List.Clear;
     1346            for i := 0 to nAdv - 1 do
     1347              if (i <> adScience) and (i <> adMassProduction) and
     1348                (AdvValue[i] div 1000 = j) then
     1349                List.AddLine(Phrases.Lookup('ADVANCES', i), pkAdvIcon, i,
     1350                  hkAdv, i);
     1351            List.Sort;
     1352            AddStrings(List);
    10281353          end;
    1029         miscCredits:
     1354          List.Free
     1355        end
     1356        else // single advance
     1357        begin
     1358          Caption := Phrases.Lookup('ADVANCES', no);
     1359          LF;
     1360          AddLine(Phrases.Lookup('ADVANCES', no), pkCaption);
     1361          if no in FutureTech then
    10301362          begin
    1031           AddItem('CREDITS');
    1032           LF;
    1033           AddGraphicCredits;
    1034           NextSection('CRED_CAPSOUND');
    1035           AddSoundCredits;
    1036           NextSection('CRED_CAPAI');
    1037           Server(sGetAICredits,0,0,ps);
    1038           AddText(ps);
    1039           NextSection('CRED_CAPLANG');
    1040           AddItem('AUTHOR');
    1041           end;
    1042         miscJobList:
    1043           begin
    1044           Caption:=HelpText.Lookup('HELPTITLE_JOBLIST');
    1045           AddJobList;
    1046           LF;
    1047           AddItem('TERIMPEXCLUDE');
    1048           LF;
    1049           AddItem('TERIMPCITY');
    1050           end;
    1051         miscGovList:
    1052           begin
    1053           Caption:=HelpText.Lookup('HELPTITLE_GOVLIST');
    1054           for i:=1 to nGov do
    1055             begin
    1056             AddLine(Phrases.Lookup('GOVERNMENT',i mod nGov),pkSection);
     1363            AddLine(HelpText.Lookup('HELPSPEC_FUTURE'));
    10571364            LF;
    1058             if i=nGov then
    1059               AddLine('',pkBigIcon,7*SystemIconLines+imPalace)
    1060             else AddLine('',pkBigIcon,i+6);
    1061             LF;
    1062             AddText(HelpText.LookupByHandle(hGOVHELP,i mod nGov));
    1063             if i mod nGov>=2 then
     1365            if no = futResearchTechnology then
     1366              AddItem('FUTURETECHHELP100')
     1367            else
     1368              AddItem('FUTURETECHHELP25');
     1369          end
     1370          else
     1371            AddLine(HelpText.Lookup('HELPSPEC_ADV'));
     1372          if AdvPreq[no, 2] <> preNone then
     1373            NextSection('PREREQALT')
     1374          else
     1375            NextSection('PREREQ');
     1376          for i := 0 to 2 do
     1377            if AdvPreq[no, i] <> preNone then
     1378              AddPreqAdv(AdvPreq[no, i]);
     1379          NextSection('GOVALLOW');
     1380          for i := 2 to nGov - 1 do
     1381            if GovPreq[i] = no then
     1382              AddLine(Phrases.Lookup('GOVERNMENT', i), pkGov, i,
     1383                hkMisc + hkCrossLink, miscGovList);
     1384          NextSection('BUILDALLOW');
     1385          for i := 0 to 27 do
     1386            if Imp[i].Preq = no then
     1387              AddImp(i);
     1388          for i := 28 to nImp - 1 do
     1389            if (Imp[i].Preq = no) and (Imp[i].Kind <> ikCommon) then
     1390              AddImp(i);
     1391          for i := 28 to nImp - 1 do
     1392            if (Imp[i].Preq = no) and (Imp[i].Kind = ikCommon) then
     1393              AddImp(i);
     1394          NextSection('MODELALLOW');
     1395          for i := 0 to nSpecialModel - 1 do
     1396            if SpecialModelPreq[i] = no then
     1397              AddModel(i);
     1398          NextSection('FEATALLOW');
     1399          for i := 0 to nFeature - 1 do
     1400            if Feature[i].Preq = no then
     1401              AddFeature(i);
     1402          NextSection('FOLLOWADV');
     1403          for i := 0 to nAdv - 1 do
     1404            if (AdvPreq[i, 0] = no) or (AdvPreq[i, 1] = no) or
     1405              (AdvPreq[i, 2] = no) then
     1406              AddAdv(i);
     1407          NextSection('UPGRADEALLOW');
     1408          for Domain := 0 to nDomains - 1 do
     1409            for i := 1 to nUpgrade - 1 do
     1410              if upgrade[Domain, i].Preq = no then
    10641411              begin
    1065               AddPreqAdv(GovPreq[i mod nGov]);
    1066               MainText[Count-1]:=Format(HelpText.Lookup('REQUIRED'),[MainText[Count-1]]);
     1412                if upgrade[Domain, i].Strength > 0 then
     1413                  AddLine(Format(HelpText.Lookup('STRENGTHUP'),
     1414                    [Phrases.Lookup('DOMAIN', Domain), upgrade[Domain,
     1415                    i].Strength]), pkDomain, Domain);
     1416                if upgrade[Domain, i].Trans > 0 then
     1417                  AddLine(Format(HelpText.Lookup('TRANSUP'),
     1418                    [Phrases.Lookup('DOMAIN', Domain), upgrade[Domain, i].Trans]
     1419                    ), pkDomain, Domain);
     1420                if no in FutureTech then
     1421                  AddLine(Format(HelpText.Lookup('COSTUP'),
     1422                    [upgrade[Domain, i].Cost]), pkNormal_Dot)
     1423                else
     1424                  AddLine(Format(HelpText.Lookup('COSTMIN'),
     1425                    [upgrade[Domain, i].Cost]), pkNormal_Dot)
    10671426              end;
    1068             if i<nGov then begin LF; LF; end
    1069             end
    1070           end;
    1071         miscSearchResult:
    1072           begin
    1073           Caption:=HelpText.Lookup('HELPTITLE_SEARCHRESULTS');
    1074           AddText(Format(HelpText.Lookup('MATCHES'), [SearchContent]));
    1075           MainText.AddStrings(SearchResult);
    1076           end
    1077         end; // case no
    1078       end;
    1079 
    1080     hkAdv:
    1081       if no=200 then
    1082         begin // complete advance list
    1083         Caption:=HelpText.Lookup('HELPTITLE_TECHLIST');
    1084         List:=THyperText.Create;
    1085         for j:=0 to 3 do
    1086           begin
    1087           if j>0 then begin LF; LF; end;
    1088           AddLine(HelpText.Lookup('TECHAGE',j),pkSection);
    1089           if j=1 then
    1090             AddLine(Phrases.Lookup('ADVANCES',adScience)+' '
    1091               +HelpText.Lookup('BASETECH'),
    1092               pkAdvIcon,adScience,hkAdv,adScience);
    1093           if j=2 then
    1094             AddLine(Phrases.Lookup('ADVANCES',adMassProduction)+' '
    1095               +HelpText.Lookup('BASETECH'),
    1096               pkAdvIcon,adMassProduction,hkAdv,adMassProduction);
    1097           List.Clear;
    1098           for i:=0 to nAdv-1 do
    1099             if (i<>adScience) and (i<>adMassProduction) and (AdvValue[i] div 1000=j) then
    1100               List.AddLine(Phrases.Lookup('ADVANCES',i),pkAdvIcon,i,hkAdv,i);
     1427          NextSection('EXPIRATION');
     1428          for i := 0 to 27 do
     1429            if (Imp[i].Preq <> preNA) and (Imp[i].Expiration = no) then
     1430              AddImp(i);
     1431          NextSection('ADVEFFECT');
     1432          s := HelpText.LookupByHandle(hADVHELP, no);
     1433          if s <> '*' then
     1434            AddText(s);
     1435          NextSection('SEEALSO');
     1436          CheckSeeAlso := true
     1437        end;
     1438
     1439      hkImp:
     1440        if no = 200 then
     1441        begin // complete city improvement list
     1442          Caption := HelpText.Lookup('HELPTITLE_IMPLIST');
     1443          // AddLine(HelpText.Lookup('HELPTITLE_IMPLIST'),pkSection);
     1444          List := THyperText.Create;
     1445          for i := 28 to nImp - 1 do
     1446            if (i <> imTrGoods) and (Imp[i].Preq <> preNA) and
     1447              (Imp[i].Kind = ikCommon) then
     1448              List.AddLine(Phrases.Lookup('IMPROVEMENTS', i), pkSmallIcon,
     1449                i, hkImp, i);
    11011450          List.Sort;
    11021451          AddStrings(List);
     1452          List.Free
     1453        end
     1454        else if no = 201 then
     1455        begin // complete nat. project list
     1456          Caption := HelpText.Lookup('HELPTITLE_UNIQUELIST');
     1457          // AddLine(HelpText.Lookup('HELPTITLE_UNIQUELIST'),pkSection);
     1458          for i := 28 to nImp - 1 do
     1459            if (Imp[i].Preq <> preNA) and
     1460              ((Imp[i].Kind = ikNatLocal) or (Imp[i].Kind = ikNatGlobal)) then
     1461              AddLine(Phrases.Lookup('IMPROVEMENTS', i), pkSmallIcon, i,
     1462                hkImp, i);
     1463          { LF;
     1464            LF;
     1465            AddLine(HelpText.Lookup('HELPTITLE_SHIPPARTLIST'),pkSection);
     1466            for i:=28 to nImp-1 do
     1467            if (Imp[i].Preq<>preNA) and (Imp[i].Kind=ikShipPart) then
     1468            AddLine(Phrases.Lookup('IMPROVEMENTS',i),pkSmallIcon,i,hkImp,i); }
     1469        end
     1470        else if no = 202 then
     1471        begin // complete wonder list
     1472          Caption := HelpText.Lookup('HELPTITLE_WONDERLIST');
     1473          // AddLine(HelpText.Lookup('HELPTITLE_WONDERLIST'),pkSection);
     1474          for i := 0 to 27 do
     1475            if Imp[i].Preq <> preNA then
     1476              AddLine(Phrases.Lookup('IMPROVEMENTS', i), pkSmallIcon, i,
     1477                hkImp, i);
     1478        end
     1479        else
     1480        begin // single building
     1481          Caption := Phrases.Lookup('IMPROVEMENTS', no);
     1482          LF;
     1483          AddLine(Phrases.Lookup('IMPROVEMENTS', no), pkRightIcon, no);
     1484          case Imp[no].Kind of
     1485            ikWonder:
     1486              AddLine(HelpText.Lookup('HELPSPEC_WONDER'));
     1487            ikCommon:
     1488              AddLine(HelpText.Lookup('HELPSPEC_IMP'));
     1489            ikShipPart:
     1490              AddLine(HelpText.Lookup('HELPSPEC_SHIPPART'));
     1491          else
     1492            AddLine(HelpText.Lookup('HELPSPEC_NAT'))
    11031493          end;
    1104         List.Free
     1494          if Imp[no].Kind <> ikShipPart then
     1495          begin
     1496            NextSection('EFFECT');
     1497            AddText(HelpText.LookupByHandle(hIMPHELP, no));
     1498          end;
     1499          if no = woSun then
     1500          begin
     1501            AddFeature(mcFirst);
     1502            AddFeature(mcWill);
     1503            AddFeature(mcAcademy);
     1504          end;
     1505          if (no < 28) and not Phrases2FallenBackToEnglish then
     1506          begin
     1507            LF;
     1508            if Imp[no].Expiration >= 0 then
     1509              AddText(Phrases2.Lookup('HELP_WONDERMORALE1'))
     1510            else
     1511              AddText(Phrases2.Lookup('HELP_WONDERMORALE2'));
     1512          end;
     1513          if Imp[no].Preq <> preNone then
     1514          begin
     1515            NextSection('PREREQ');
     1516            AddPreqAdv(Imp[no].Preq);
     1517          end;
     1518          NextSection('COSTS');
     1519          if Difficulty = 0 then
     1520            s := Format(HelpText.Lookup('BUILDCOST'), [Imp[no].Cost])
     1521          else
     1522            s := Format(HelpText.Lookup('BUILDCOST'),
     1523              [Imp[no].Cost * BuildCostMod[Difficulty] div 12]);
     1524          AddLine(s);
     1525          if Imp[no].Maint > 0 then
     1526            AddLine(Format(HelpText.Lookup('MAINTCOST'), [Imp[no].Maint]));
     1527          j := 0;
     1528          for i := 0 to nImpReplacement - 1 do
     1529            if ImpReplacement[i].NewImp = no then
     1530            begin
     1531              if j = 0 then
     1532              begin
     1533                NextSection('REPLACE');
     1534                AddItem('REPLACETEXT');
     1535                j := 1
     1536              end;
     1537              AddImp(ImpReplacement[i].OldImp);
     1538            end;
     1539          if Imp[no].Kind = ikShipPart then
     1540          begin
     1541            LF;
     1542            if no = imShipComp then
     1543              i := 1
     1544            else if no = imShipPow then
     1545              i := 2
     1546            else { if no=imShipHab then }
     1547              i := 3;
     1548            AddLine(Format(HelpText.Lookup('RAREREQUIRED'),
     1549              [Phrases.Lookup('TERRAIN', 3 * 12 + i)]), pkTer, 3 * 12 + i);
     1550          end;
     1551          if (no < 28) and (Imp[no].Expiration >= 0) then
     1552          begin
     1553            NextSection('EXPIRATION');
     1554            s := Format(HelpText.Lookup('EXPWITH'),
     1555              [Phrases.Lookup('ADVANCES', Imp[no].Expiration)]);
     1556            if no = woPyramids then
     1557              s := s + ' ' + HelpText.Lookup('EXPSLAVE');
     1558            AddText(s);
     1559          end;
     1560          NextSection('SEEALSO');
     1561          if (no < 28) and (Imp[no].Expiration >= 0) then
     1562            AddImp(woEiffel);
     1563          for i := 0 to nImpReplacement - 1 do
     1564            if ImpReplacement[i].OldImp = no then
     1565              AddImp(ImpReplacement[i].NewImp);
     1566          if no = imSupermarket then
     1567            AddLine(HelpText.Lookup('HELPTITLE_JOBLIST'), pkNormal, 0,
     1568              hkMisc + hkCrossLink, miscJobList);
     1569          CheckSeeAlso := true
     1570        end;
     1571
     1572      hkTer:
     1573        if no = 200 then
     1574        begin // complete terrain type list
     1575          Caption := HelpText.Lookup('HELPTITLE_TERLIST');
     1576          // AddLine(HelpText.Lookup('HELPTITLE_TERLIST'),pkSection);
     1577          for i := 0 to nTerrainHelp - 1 do
     1578            AddTer(TerrainHelp[i]);
    11051579        end
    1106       else // single advance
    1107         begin
    1108         Caption:=Phrases.Lookup('ADVANCES',no);
    1109         LF;
    1110         AddLine(Phrases.Lookup('ADVANCES',no),pkCaption);
    1111         if no in FutureTech then
     1580        else
     1581        begin // sigle terrain type
     1582          TerrType := no mod 12;
     1583          if TerrType = fJungle then
     1584            TerrType := fForest;
     1585          TerrSubType := no div 12;
     1586          if no = 3 * 12 then
    11121587          begin
    1113           AddLine(HelpText.Lookup('HELPSPEC_FUTURE'));
    1114           LF;
    1115           if no=futResearchTechnology then
    1116             AddItem('FUTURETECHHELP100')
    1117           else AddItem('FUTURETECHHELP25');
    1118           end
    1119         else AddLine(HelpText.Lookup('HELPSPEC_ADV'));
    1120         if AdvPreq[no,2]<>preNone then NextSection('PREREQALT')
    1121         else NextSection('PREREQ');
    1122         for i:=0 to 2 do
    1123           if AdvPreq[no,i]<>preNone then AddPreqAdv(AdvPreq[no,i]);
    1124         NextSection('GOVALLOW');
    1125         for i:=2 to nGov-1 do
    1126           if GovPreq[i]=no then
    1127             AddLine(Phrases.Lookup('GOVERNMENT',i),pkGov,i,
    1128               hkMisc+hkCrossLink,miscGovList);
    1129         NextSection('BUILDALLOW');
    1130         for i:=0 to 27 do
    1131           if Imp[i].Preq=no then AddImp(i);
    1132         for i:=28 to nImp-1 do
    1133           if (Imp[i].Preq=no) and (Imp[i].Kind<>ikCommon) then AddImp(i);
    1134         for i:=28 to nImp-1 do
    1135           if (Imp[i].Preq=no) and (Imp[i].Kind=ikCommon) then AddImp(i);
    1136         NextSection('MODELALLOW');
    1137         for i:=0 to nSpecialModel-1 do
    1138           if SpecialModelPreq[i]=no then AddModel(i);
    1139         NextSection('FEATALLOW');
    1140         for i:=0 to nFeature-1 do if Feature[i].Preq=no then AddFeature(i);
    1141         NextSection('FOLLOWADV');
    1142         for i:=0 to nAdv-1 do
    1143           if (AdvPreq[i,0]=no) or (AdvPreq[i,1]=no) or (AdvPreq[i,2]=no) then
    1144             AddAdv(i);
    1145         NextSection('UPGRADEALLOW');
    1146         for Domain:=0 to nDomains-1 do for i:=1 to nUpgrade-1 do
    1147           if upgrade[Domain,i].Preq=no then
    1148             begin
    1149             if upgrade[Domain,i].Strength>0 then
    1150               AddLine(Format(HelpText.Lookup('STRENGTHUP'),
    1151                 [Phrases.Lookup('DOMAIN',Domain),upgrade[Domain,i].Strength]),
    1152                 pkDomain,Domain);
    1153             if upgrade[Domain,i].Trans>0 then
    1154               AddLine(Format(HelpText.Lookup('TRANSUP'),
    1155                 [Phrases.Lookup('DOMAIN',Domain),upgrade[Domain,i].Trans]),
    1156                 pkDomain,Domain);
    1157             if no in FutureTech then
    1158               AddLine(Format(HelpText.Lookup('COSTUP'),
    1159                 [upgrade[Domain,i].Cost]),pkNormal_Dot)
    1160             else
    1161               AddLine(Format(HelpText.Lookup('COSTMIN'),
    1162                 [upgrade[Domain,i].Cost]),pkNormal_Dot)
    1163             end;
    1164         NextSection('EXPIRATION');
    1165         for i:=0 to 27 do
    1166           if (Imp[i].Preq<>preNA) and (Imp[i].Expiration=no) then AddImp(i);
    1167         NextSection('ADVEFFECT');
    1168         s:=HelpText.LookupByHandle(hADVHELP,no);
    1169         if s<>'*' then AddText(s);
    1170         NextSection('SEEALSO');
    1171         CheckSeeAlso:=true
    1172         end;
    1173 
    1174     hkImp:
    1175       if no=200 then
    1176         begin // complete city improvement list
    1177         Caption:=HelpText.Lookup('HELPTITLE_IMPLIST');
    1178 //        AddLine(HelpText.Lookup('HELPTITLE_IMPLIST'),pkSection);
    1179         List:=THyperText.Create;
    1180         for i:=28 to nImp-1 do
    1181           if (i<>imTrGoods) and (Imp[i].Preq<>preNA) and (Imp[i].Kind=ikCommon) then
    1182             List.AddLine(Phrases.Lookup('IMPROVEMENTS',i),pkSmallIcon,i,hkImp,i);
    1183         List.Sort;
    1184         AddStrings(List);
    1185         List.Free
    1186         end
    1187       else if no=201 then
    1188         begin // complete nat. project list
    1189         Caption:=HelpText.Lookup('HELPTITLE_UNIQUELIST');
    1190 //        AddLine(HelpText.Lookup('HELPTITLE_UNIQUELIST'),pkSection);
    1191         for i:=28 to nImp-1 do
    1192           if (Imp[i].Preq<>preNA)
    1193             and ((Imp[i].Kind=ikNatLocal) or (Imp[i].Kind=ikNatGlobal)) then
    1194             AddLine(Phrases.Lookup('IMPROVEMENTS',i),pkSmallIcon,i,hkImp,i);
    1195 {        LF;
    1196         LF;
    1197         AddLine(HelpText.Lookup('HELPTITLE_SHIPPARTLIST'),pkSection);
    1198         for i:=28 to nImp-1 do
    1199           if (Imp[i].Preq<>preNA) and (Imp[i].Kind=ikShipPart) then
    1200             AddLine(Phrases.Lookup('IMPROVEMENTS',i),pkSmallIcon,i,hkImp,i);}
    1201         end
    1202       else if no=202 then
    1203         begin // complete wonder list
    1204         Caption:=HelpText.Lookup('HELPTITLE_WONDERLIST');
    1205 //        AddLine(HelpText.Lookup('HELPTITLE_WONDERLIST'),pkSection);
    1206         for i:=0 to 27 do if Imp[i].Preq<>preNA then
    1207           AddLine(Phrases.Lookup('IMPROVEMENTS',i),pkSmallIcon,i,hkImp,i);
    1208         end
    1209       else
    1210         begin // single building
    1211         Caption:=Phrases.Lookup('IMPROVEMENTS',no);
    1212         LF;
    1213         AddLine(Phrases.Lookup('IMPROVEMENTS',no),pkRightIcon,no);
    1214         case Imp[no].Kind of
    1215           ikWonder: AddLine(HelpText.Lookup('HELPSPEC_WONDER'));
    1216           ikCommon: AddLine(HelpText.Lookup('HELPSPEC_IMP'));
    1217           ikShipPart: AddLine(HelpText.Lookup('HELPSPEC_SHIPPART'));
    1218           else AddLine(HelpText.Lookup('HELPSPEC_NAT'))
     1588            TerrType := fDesert;
     1589            TerrSubType := 0
    12191590          end;
    1220         if Imp[no].Kind<>ikShipPart then
     1591          with Terrain[TerrType] do
    12211592          begin
    1222           NextSection('EFFECT');
    1223           AddText(HelpText.LookupByHandle(hIMPHELP,no));
    1224           end;
    1225         if no=woSun then
    1226           begin
    1227           AddFeature(mcFirst);
    1228           AddFeature(mcWill);
    1229           AddFeature(mcAcademy);
    1230           end;
    1231         if (no<28) and not Phrases2FallenBackToEnglish then
    1232           begin
    1233           LF;
    1234           if Imp[no].Expiration>=0 then
    1235             AddText(Phrases2.Lookup('HELP_WONDERMORALE1'))
    1236           else AddText(Phrases2.Lookup('HELP_WONDERMORALE2'));
    1237           end;
    1238         if Imp[no].Preq<>preNone then
    1239           begin
    1240           NextSection('PREREQ');
    1241           AddPreqAdv(Imp[no].Preq);
    1242           end;
    1243         NextSection('COSTS');
    1244         if Difficulty=0 then
    1245           s:=Format(HelpText.Lookup('BUILDCOST'),[Imp[no].Cost])
    1246         else s:=Format(HelpText.Lookup('BUILDCOST'),
    1247           [Imp[no].Cost*BuildCostMod[Difficulty] div 12]);
    1248         AddLine(s);
    1249         if Imp[no].Maint>0 then
    1250           AddLine(Format(HelpText.Lookup('MAINTCOST'),[Imp[no].Maint]));
    1251         j:=0;
    1252         for i:=0 to nImpReplacement-1 do if ImpReplacement[i].NewImp=no then
    1253           begin
    1254           if j=0 then
    1255             begin NextSection('REPLACE'); AddItem('REPLACETEXT'); j:=1 end;
    1256           AddImp(ImpReplacement[i].OldImp);
    1257           end;
    1258         if Imp[no].Kind=ikShipPart then
    1259           begin
    1260           LF;
    1261           if no=imShipComp then i:=1
    1262           else if no=imShipPow then i:=2
    1263           else {if no=imShipHab then} i:=3;
    1264           AddLine(Format(HelpText.Lookup('RAREREQUIRED'),
    1265             [Phrases.Lookup('TERRAIN',3*12+i)]),pkTer,3*12+i);
    1266           end;
    1267         if (no<28) and (Imp[no].Expiration>=0) then
    1268           begin
    1269           NextSection('EXPIRATION');
    1270           s:=Format(HelpText.Lookup('EXPWITH'),[Phrases.Lookup('ADVANCES',Imp[no].Expiration)]);
    1271           if no=woPyramids then s:=s+' '+HelpText.Lookup('EXPSLAVE');
    1272           AddText(s);
    1273           end;
    1274         NextSection('SEEALSO');
    1275         if (no<28) and (Imp[no].Expiration>=0) then AddImp(woEiffel);
    1276         for i:=0 to nImpReplacement-1 do if ImpReplacement[i].OldImp=no then
    1277           AddImp(ImpReplacement[i].NewImp);
    1278         if no=imSupermarket then
    1279           AddLine(HelpText.Lookup('HELPTITLE_JOBLIST'),pkNormal,0,hkMisc+hkCrossLink,miscJobList);
    1280         CheckSeeAlso:=true
    1281         end;
    1282 
    1283     hkTer:
    1284       if no=200 then
    1285         begin // complete terrain type list
    1286         Caption:=HelpText.Lookup('HELPTITLE_TERLIST');
    1287 //        AddLine(HelpText.Lookup('HELPTITLE_TERLIST'),pkSection);
    1288         for i:=0 to nTerrainHelp-1 do AddTer(TerrainHelp[i]);
    1289         end
    1290       else
    1291         begin // sigle terrain type
    1292         TerrType:=no mod 12;
    1293         if TerrType=fJungle then TerrType:=fForest;
    1294         TerrSubType:=no div 12;
    1295         if no=3*12 then
    1296           begin TerrType:=fDesert; TerrSubType:=0 end;
    1297         with Terrain[TerrType] do
    1298           begin
    1299           Caption:=Phrases.Lookup('TERRAIN',no);
    1300           LF;
    1301           AddLine(Phrases.Lookup('TERRAIN',no), pkBigTer, no);
    1302           AddLine(HelpText.Lookup('HELPSPEC_TER'));
    1303           LF;
    1304           if (ProdRes[TerrSubType]>0) or (MineEff>0) then
    1305             AddLine(Format(HelpText.Lookup('RESPROD'),[ProdRes[TerrSubType]]));
    1306           if (no<3*12) and (MineEff>0) then
    1307             MainText[Count-1]:=MainText[Count-1]+' '
    1308               +Format(HelpText.Lookup('MOREMINE'),[MineEff]);
    1309           if (FoodRes[TerrSubType]>0) or (IrrEff>0) then
    1310             AddLine(Format(HelpText.Lookup('RESFOOD'),[FoodRes[TerrSubType]]));
    1311           if (no<3*12) and (IrrEff>0) then
    1312             MainText[Count-1]:=MainText[Count-1]+' '
    1313               +Format(HelpText.Lookup('MOREIRR'),[IrrEff]);
    1314           if TradeRes[TerrSubType]>0 then
    1315             AddLine(Format(HelpText.Lookup('RESTRADE'),[TradeRes[TerrSubType]]));
    1316           if Defense>4 then
    1317             AddLine(Format(HelpText.Lookup('DEFBONUS'),[(Defense-4)*25]));
    1318           if (TerrType>=fGrass) and (TerrType<>fMountains) then
    1319             if MoveCost=2 then
    1320               AddLine(HelpText.Lookup('MOVEHEAVY'))
    1321             else AddLine(HelpText.Lookup('MOVEPLAIN'));
    1322           if no=3*12 then
    1323             begin
     1593            Caption := Phrases.Lookup('TERRAIN', no);
    13241594            LF;
    1325             AddText(HelpText.Lookup('DEADLANDS'));
    1326             end;
    1327           if (TerrType=fDesert) and (no<>fDesert+12) then
    1328             begin
     1595            AddLine(Phrases.Lookup('TERRAIN', no), pkBigTer, no);
     1596            AddLine(HelpText.Lookup('HELPSPEC_TER'));
    13291597            LF;
    1330             AddText(Format(HelpText.Lookup('HOSTILE'),[DesertThurst]));
    1331             end;
    1332           if TerrType=fArctic then
    1333             begin
    1334             LF;
    1335             AddText(Format(HelpText.Lookup('HOSTILE'),[ArcticThurst]));
    1336             end;
    1337           if (no<3*12) and (TransTerrain>=0) then
    1338             begin
    1339             LF;
    1340             i:=TransTerrain;
    1341             if (TerrType<>fGrass) and (i<>fGrass) then
    1342               i:=i+TerrSubType*12; // trafo to same special resource group
    1343             AddLine(Format(HelpText.Lookup('TRAFO'),
    1344               [Phrases.Lookup('TERRAIN',i)]),pkTer,i,hkTer+hkCrossLink,i);
    1345             if no=fSwamp+12 then
     1598            if (ProdRes[TerrSubType] > 0) or (MineEff > 0) then
     1599              AddLine(Format(HelpText.Lookup('RESPROD'),
     1600                [ProdRes[TerrSubType]]));
     1601            if (no < 3 * 12) and (MineEff > 0) then
     1602              MainText[Count - 1] := MainText[Count - 1] + ' ' +
     1603                Format(HelpText.Lookup('MOREMINE'), [MineEff]);
     1604            if (FoodRes[TerrSubType] > 0) or (IrrEff > 0) then
     1605              AddLine(Format(HelpText.Lookup('RESFOOD'),
     1606                [FoodRes[TerrSubType]]));
     1607            if (no < 3 * 12) and (IrrEff > 0) then
     1608              MainText[Count - 1] := MainText[Count - 1] + ' ' +
     1609                Format(HelpText.Lookup('MOREIRR'), [IrrEff]);
     1610            if TradeRes[TerrSubType] > 0 then
     1611              AddLine(Format(HelpText.Lookup('RESTRADE'),
     1612                [TradeRes[TerrSubType]]));
     1613            if Defense > 4 then
     1614              AddLine(Format(HelpText.Lookup('DEFBONUS'),
     1615                [(Defense - 4) * 25]));
     1616            if (TerrType >= fGrass) and (TerrType <> fMountains) then
     1617              if MoveCost = 2 then
     1618                AddLine(HelpText.Lookup('MOVEHEAVY'))
     1619              else
     1620                AddLine(HelpText.Lookup('MOVEPLAIN'));
     1621            if no = 3 * 12 then
     1622            begin
     1623              LF;
     1624              AddText(HelpText.Lookup('DEADLANDS'));
     1625            end;
     1626            if (TerrType = fDesert) and (no <> fDesert + 12) then
     1627            begin
     1628              LF;
     1629              AddText(Format(HelpText.Lookup('HOSTILE'), [DesertThurst]));
     1630            end;
     1631            if TerrType = fArctic then
     1632            begin
     1633              LF;
     1634              AddText(Format(HelpText.Lookup('HOSTILE'), [ArcticThurst]));
     1635            end;
     1636            if (no < 3 * 12) and (TransTerrain >= 0) then
     1637            begin
     1638              LF;
     1639              i := TransTerrain;
     1640              if (TerrType <> fGrass) and (i <> fGrass) then
     1641                i := i + TerrSubType * 12;
     1642              // trafo to same special resource group
     1643              AddLine(Format(HelpText.Lookup('TRAFO'),
     1644                [Phrases.Lookup('TERRAIN', i)]), pkTer, i,
     1645                hkTer + hkCrossLink, i);
     1646              if no = fSwamp + 12 then
    13461647              begin
     1648                LF;
     1649                AddLine(Format(HelpText.Lookup('TRAFO'),
     1650                  [Phrases.Lookup('TERRAIN', TransTerrain + 24)]), pkTer,
     1651                  TransTerrain + 24, hkTer + hkCrossLink, TransTerrain + 24);
     1652              end
     1653              else if i = fGrass then
     1654              begin
     1655                LF;
     1656                AddLine(Format(HelpText.Lookup('TRAFO'),
     1657                  [Phrases.Lookup('TERRAIN', fGrass + 12)]), pkTer, fGrass + 12,
     1658                  hkTer + hkCrossLink, fGrass + 12);
     1659              end
     1660            end;
     1661            NextSection('SPECIAL');
     1662            if no = 3 * 12 then
     1663            begin
    13471664              LF;
    1348               AddLine(Format(HelpText.Lookup('TRAFO'),
    1349                 [Phrases.Lookup('TERRAIN',TransTerrain+24)]),pkTer,TransTerrain+24,
    1350                 hkTer+hkCrossLink,TransTerrain+24);
    1351               end
    1352             else if i=fGrass then
     1665              for special := 1 to 3 do
    13531666              begin
    1354               LF;
    1355               AddLine(Format(HelpText.Lookup('TRAFO'),
    1356                 [Phrases.Lookup('TERRAIN',fGrass+12)]),pkTer,fGrass+12,
    1357                 hkTer+hkCrossLink,fGrass+12);
    1358               end
    1359             end;
    1360           NextSection('SPECIAL');
    1361           if no=3*12 then
    1362             begin
    1363             LF;
    1364             for special:=1 to 3 do
    1365               begin
    1366               if special>1 then LF;
    1367               AddLine(Phrases.Lookup('TERRAIN',3*12+special),pkTer,3*12+special);
     1667                if special > 1 then
     1668                  LF;
     1669                AddLine(Phrases.Lookup('TERRAIN', 3 * 12 + special), pkTer,
     1670                  3 * 12 + special);
    13681671              end
    13691672            end
    1370           else if (no<12) and (no<>fGrass) and (no<>fOcean) then
    1371             begin
    1372             LF;
    1373             for special:=1 to 2 do
    1374               if (no<>fArctic) and (no<>fSwamp) or (special<2) then
     1673            else if (no < 12) and (no <> fGrass) and (no <> fOcean) then
     1674            begin
     1675              LF;
     1676              for special := 1 to 2 do
     1677                if (no <> fArctic) and (no <> fSwamp) or (special < 2) then
    13751678                begin
    1376                 if special>1 then LF;
    1377                 AddLine(Phrases.Lookup('TERRAIN',no+special*12),pkTer,no+special*12);
    1378                 i:=FoodRes[special]-FoodRes[0];
    1379                 if i<>0 then
    1380                   MainText[Count-1]:=MainText[Count-1]+Format(HelpText.Lookup('SPECIALFOOD'),[i]);
    1381                 i:=ProdRes[special]-ProdRes[0];
    1382                 if i<>0 then
    1383                   MainText[Count-1]:=MainText[Count-1]+Format(HelpText.Lookup('SPECIALPROD'),[i]);
    1384                 i:=TradeRes[special]-TradeRes[0];
    1385                 if i<>0 then
    1386                   MainText[Count-1]:=MainText[Count-1]+Format(HelpText.Lookup('SPECIALTRADE'),[i]);
     1679                  if special > 1 then
     1680                    LF;
     1681                  AddLine(Phrases.Lookup('TERRAIN', no + special * 12), pkTer,
     1682                    no + special * 12);
     1683                  i := FoodRes[special] - FoodRes[0];
     1684                  if i <> 0 then
     1685                    MainText[Count - 1] := MainText[Count - 1] +
     1686                      Format(HelpText.Lookup('SPECIALFOOD'), [i]);
     1687                  i := ProdRes[special] - ProdRes[0];
     1688                  if i <> 0 then
     1689                    MainText[Count - 1] := MainText[Count - 1] +
     1690                      Format(HelpText.Lookup('SPECIALPROD'), [i]);
     1691                  i := TradeRes[special] - TradeRes[0];
     1692                  if i <> 0 then
     1693                    MainText[Count - 1] := MainText[Count - 1] +
     1694                      Format(HelpText.Lookup('SPECIALTRADE'), [i]);
    13871695                end;
    13881696            end;
    1389           if no=3*12 then
    1390             begin
    1391             LF;
    1392             AddText(HelpText.Lookup('RARE'));
    1393             end;
    1394           if (no<3*12) and (TerrType in [fDesert,fArctic]) then
    1395             begin
    1396             NextSection('SEEALSO');
    1397             AddImp(woGardens);
    1398             CheckSeeAlso:=true
     1697            if no = 3 * 12 then
     1698            begin
     1699              LF;
     1700              AddText(HelpText.Lookup('RARE'));
     1701            end;
     1702            if (no < 3 * 12) and (TerrType in [fDesert, fArctic]) then
     1703            begin
     1704              NextSection('SEEALSO');
     1705              AddImp(woGardens);
     1706              CheckSeeAlso := true
    13991707            end
    14001708          end
    14011709        end;
    14021710
    1403     hkFeature:
    1404       if no=200 then
     1711      hkFeature:
     1712        if no = 200 then
    14051713        begin // complete feature list
    1406         Caption:=HelpText.Lookup('HELPTITLE_FEATURELIST');
    1407         List:=THyperText.Create;
    1408         for special:=0 to 2 do
     1714          Caption := HelpText.Lookup('HELPTITLE_FEATURELIST');
     1715          List := THyperText.Create;
     1716          for special := 0 to 2 do
    14091717          begin
    1410           if special>0 then begin LF; LF end;
    1411           case special of
    1412             0: AddLine(HelpText.Lookup('HELPTITLE_FEATURE1LIST'),pkSection);
    1413             1: AddLine(HelpText.Lookup('HELPTITLE_FEATURE2LIST'),pkSection);
    1414             2: AddLine(HelpText.Lookup('HELPTITLE_FEATURE3LIST'),pkSection);
    1415             end;
    1416           List.Clear;
    1417           for i:=0 to nFeature-1 do if Feature[i].Preq<>preNA then
    1418             begin
    1419             if i<mcFirstNonCap then j:=0
    1420             else if i in AutoFeature then j:=2
    1421             else j:=1;
    1422             if j=special then
    1423               List.AddLine(Phrases.Lookup('FEATURES',i),pkFeature,i,hkFeature,i);
    1424             end;
    1425           List.Sort;
    1426           AddStrings(List);
     1718            if special > 0 then
     1719            begin
     1720              LF;
     1721              LF
     1722            end;
     1723            case special of
     1724              0:
     1725                AddLine(HelpText.Lookup('HELPTITLE_FEATURE1LIST'), pkSection);
     1726              1:
     1727                AddLine(HelpText.Lookup('HELPTITLE_FEATURE2LIST'), pkSection);
     1728              2:
     1729                AddLine(HelpText.Lookup('HELPTITLE_FEATURE3LIST'), pkSection);
     1730            end;
     1731            List.Clear;
     1732            for i := 0 to nFeature - 1 do
     1733              if Feature[i].Preq <> preNA then
     1734              begin
     1735                if i < mcFirstNonCap then
     1736                  j := 0
     1737                else if i in AutoFeature then
     1738                  j := 2
     1739                else
     1740                  j := 1;
     1741                if j = special then
     1742                  List.AddLine(Phrases.Lookup('FEATURES', i), pkFeature, i,
     1743                    hkFeature, i);
     1744              end;
     1745            List.Sort;
     1746            AddStrings(List);
    14271747          end;
    1428         List.Free
     1748          List.Free
    14291749        end
    1430       else
     1750        else
    14311751        begin // single feature
    1432         Caption:=Phrases.Lookup('FEATURES',no);
    1433         LF;
    1434         AddLine(Phrases.Lookup('FEATURES',no),pkBigFeature,no);
    1435         if no<mcFirstNonCap then
    1436           AddLine(HelpText.Lookup('HELPSPEC_CAP'))
    1437         else if no in AutoFeature then
    1438           AddLine(HelpText.Lookup('HELPSPEC_STANDARD'))
    1439         else AddLine(HelpText.Lookup('HELPSPEC_FEATURE'));
    1440         NextSection('EFFECT');
    1441         AddText(HelpText.LookupByHandle(hFEATUREHELP,no));
    1442         if (Feature[no].Weight<>0) or (Feature[no].Cost<>0) then
     1752          Caption := Phrases.Lookup('FEATURES', no);
     1753          LF;
     1754          AddLine(Phrases.Lookup('FEATURES', no), pkBigFeature, no);
     1755          if no < mcFirstNonCap then
     1756            AddLine(HelpText.Lookup('HELPSPEC_CAP'))
     1757          else if no in AutoFeature then
     1758            AddLine(HelpText.Lookup('HELPSPEC_STANDARD'))
     1759          else
     1760            AddLine(HelpText.Lookup('HELPSPEC_FEATURE'));
     1761          NextSection('EFFECT');
     1762          AddText(HelpText.LookupByHandle(hFEATUREHELP, no));
     1763          if (Feature[no].Weight <> 0) or (Feature[no].Cost <> 0) then
    14431764          begin
    1444           NextSection('COSTS');
    1445           s:=IntToStr(Feature[no].Cost);
    1446           if Feature[no].Cost>=0 then s:='+'+s;
    1447           AddLine(Format(HelpText.Lookup('COSTBASE'),[s]));
    1448           if Feature[no].Weight>0 then
    1449             begin
    1450             AddLine(Format(HelpText.Lookup('WEIGHT'),
    1451               ['+'+IntToStr(Feature[no].Weight)]));
    1452             if no=mcDefense then
    1453               AddLine(Format(HelpText.Lookup('WEIGHT'),['+2']),pkDomain,dGround);
     1765            NextSection('COSTS');
     1766            s := IntToStr(Feature[no].Cost);
     1767            if Feature[no].Cost >= 0 then
     1768              s := '+' + s;
     1769            AddLine(Format(HelpText.Lookup('COSTBASE'), [s]));
     1770            if Feature[no].Weight > 0 then
     1771            begin
     1772              AddLine(Format(HelpText.Lookup('WEIGHT'),
     1773                ['+' + IntToStr(Feature[no].Weight)]));
     1774              if no = mcDefense then
     1775                AddLine(Format(HelpText.Lookup('WEIGHT'), ['+2']),
     1776                  pkDomain, dGround);
    14541777            end
    14551778          end;
    1456         if Feature[no].Preq<>preNone then
     1779          if Feature[no].Preq <> preNone then
    14571780          begin
     1781            LF;
     1782            if Feature[no].Preq = preSun then
     1783              AddPreqImp(woSun) // sun tsu feature
     1784            else
     1785              AddPreqAdv(Feature[no].Preq);
     1786            MainText[Count - 1] := Format(HelpText.Lookup('REQUIRED'),
     1787              [MainText[Count - 1]]);
     1788          end;
     1789          NextSection('SEEALSO');
     1790          CheckSeeAlso := true
     1791        end;
     1792
     1793      hkModel:
     1794        begin
     1795          Caption := HelpText.Lookup('HELPTITLE_MODELLIST');
     1796          for i := 0 to nSpecialModel - 1 do
     1797            if i <> 2 then
     1798              AddModelText(i);
    14581799          LF;
    1459           if Feature[no].Preq=preSun then AddPreqImp(woSun) // sun tsu feature
    1460           else AddPreqAdv(Feature[no].Preq);
    1461           MainText[Count-1]:=Format(HelpText.Lookup('REQUIRED'),[MainText[Count-1]]);
     1800          AddItem('MODELNOTE');
     1801        end;
     1802
     1803    end;
     1804    if CheckSeeAlso then
     1805      for i := 0 to nSeeAlso - 1 do
     1806        if (SeeAlso[i].Kind = Kind) and (SeeAlso[i].no = no) then
     1807          case SeeAlso[i].SeeKind of
     1808            hkImp:
     1809              AddImp(SeeAlso[i].SeeNo);
     1810            hkAdv:
     1811              AddAdv(SeeAlso[i].SeeNo);
     1812            hkFeature:
     1813              AddFeature(SeeAlso[i].SeeNo);
    14621814          end;
    1463         NextSection('SEEALSO');
    1464         CheckSeeAlso:=true
    1465         end;
    1466 
    1467     hkModel:
    1468         begin
    1469         Caption:=HelpText.Lookup('HELPTITLE_MODELLIST');
    1470         for i:=0 to nSpecialModel-1 do if i<>2 then AddModelText(i);
    1471         LF;
    1472         AddItem('MODELNOTE');
    1473         end;
    1474 
     1815    if (Headline >= 0) and (Count = Headline + 1) then
     1816      Delete(Headline)
     1817    else
     1818      LF;
     1819
     1820    InitPVSB(sb, Count - 1, InnerHeight div 24);
     1821    if sbPos <> 0 then
     1822    begin
     1823      sb.si.npos := sbPos;
     1824      sb.si.FMask := SIF_POS;
     1825      SetScrollInfo(sb.h, SB_CTL, sb.si, true);
    14751826    end;
    1476   if CheckSeeAlso then
    1477     for i:=0 to nSeeAlso-1 do
    1478       if (SeeAlso[i].Kind=Kind) and (SeeAlso[i].no=no) then
    1479         case SeeAlso[i].SeeKind of
    1480           hkImp: AddImp(SeeAlso[i].SeeNo);
    1481           hkAdv: AddAdv(SeeAlso[i].SeeNo);
    1482           hkFeature: AddFeature(SeeAlso[i].SeeNo);
    1483           end;
    1484   if (Headline>=0) and (Count=Headline+1) then Delete(Headline)
    1485   else LF;
    1486 
    1487   InitPVSB(sb,Count-1,InnerHeight div 24);
    1488   if sbPos<>0 then
    1489     begin
    1490     sb.si.npos:=sbPos;
    1491     sb.si.FMask:=SIF_POS;
    1492     SetScrollInfo(sb.h,SB_CTL,sb.si,true);
    1493     end;
    1494   BackBtn.Visible:= nHist>0;
    1495   TopBtn.Visible:= (nHist>0) or (Kind<>hkMisc) or (no<>miscMain);
    1496   Sel:=-1;
     1827    BackBtn.Visible := nHist > 0;
     1828    TopBtn.Visible := (nHist > 0) or (Kind <> hkMisc) or (no <> miscMain);
     1829    Sel := -1;
    14971830  end; // with MainText
    1498 end; {Prepare}
     1831end; { Prepare }
    14991832
    15001833procedure THelpDlg.ShowNewContent(NewMode, Category, Index: integer);
    15011834begin
    1502 if (Category<>Kind) or (Index<>no)
    1503   or (Category=hkMisc) and (Index=miscSearchResult) then
    1504   begin
    1505   if nHist=MaxHist then
    1506     begin
    1507     move(HistKind[2],HistKind[1],4*(nHist-2));
    1508     move(HistNo[2],HistNo[1],4*(nHist-2));
    1509     move(HistPos[2],HistPos[1],4*(nHist-2));
    1510     move(HistSearchContent[2],HistSearchContent[1],sizeof(shortstring)*(nHist-2));
     1835  if (Category <> Kind) or (Index <> no) or (Category = hkMisc) and
     1836    (Index = miscSearchResult) then
     1837  begin
     1838    if nHist = MaxHist then
     1839    begin
     1840      move(HistKind[2], HistKind[1], 4 * (nHist - 2));
     1841      move(HistNo[2], HistNo[1], 4 * (nHist - 2));
     1842      move(HistPos[2], HistPos[1], 4 * (nHist - 2));
     1843      move(HistSearchContent[2], HistSearchContent[1],
     1844        sizeof(shortstring) * (nHist - 2));
    15111845    end
    1512   else inc(nHist);
    1513   if nHist>0 then
    1514     begin
    1515     HistKind[nHist-1]:=Kind;
    1516     HistNo[nHist-1]:=no;
    1517     HistPos[nHist-1]:=sb.si.npos;
    1518     HistSearchContent[nHist-1]:=SearchContent
     1846    else
     1847      inc(nHist);
     1848    if nHist > 0 then
     1849    begin
     1850      HistKind[nHist - 1] := Kind;
     1851      HistNo[nHist - 1] := no;
     1852      HistPos[nHist - 1] := sb.si.npos;
     1853      HistSearchContent[nHist - 1] := SearchContent
    15191854    end
    15201855  end;
    1521 Kind:=Category;
    1522 no:=Index;
    1523 SearchContent:=NewSearchContent;
    1524 Prepare;
    1525 OffscreenPaint;
    1526 inherited ShowNewContent(NewMode);
     1856  Kind := Category;
     1857  no := Index;
     1858  SearchContent := NewSearchContent;
     1859  Prepare;
     1860  OffscreenPaint;
     1861  inherited ShowNewContent(NewMode);
    15271862end;
    15281863
     
    15301865  x, y: integer);
    15311866var
    1532 i0,Sel0:integer;
     1867  i0, Sel0: integer;
    15331868begin
    1534 y:=y-WideFrame;
    1535 i0:=sb.si.npos;
    1536 Sel0:=Sel;
    1537 if (x>=SideFrame) and (x<SideFrame+InnerWidth) and (y>=0) and (y<InnerHeight)
    1538   and (y mod 24>=8) then
    1539   Sel:=y div 24
    1540 else Sel:=-1;
    1541 if (Sel+i0>=MainText.Count) or (Sel>=0)
    1542   and (THelpLineInfo(MainText.Objects[Sel+i0]).Link=0) then Sel:=-1;
    1543 if Sel<>Sel0 then
    1544   begin
    1545   if Sel0<>-1 then line(Canvas,Sel0,false);
    1546   if Sel<>-1 then line(Canvas,Sel,true)
     1869  y := y - WideFrame;
     1870  i0 := sb.si.npos;
     1871  Sel0 := Sel;
     1872  if (x >= SideFrame) and (x < SideFrame + InnerWidth) and (y >= 0) and
     1873    (y < InnerHeight) and (y mod 24 >= 8) then
     1874    Sel := y div 24
     1875  else
     1876    Sel := -1;
     1877  if (Sel + i0 >= MainText.Count) or (Sel >= 0) and
     1878    (THelpLineInfo(MainText.Objects[Sel + i0]).Link = 0) then
     1879    Sel := -1;
     1880  if Sel <> Sel0 then
     1881  begin
     1882    if Sel0 <> -1 then
     1883      line(Canvas, Sel0, false);
     1884    if Sel <> -1 then
     1885      line(Canvas, Sel, true)
    15471886  end
    15481887end;
    15491888
    1550 procedure THelpDlg.PaintBox1MouseDown(Sender: TObject;
    1551   Button: TMouseButton; Shift: TShiftState; x, y: integer);
     1889procedure THelpDlg.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
     1890  Shift: TShiftState; x, y: integer);
    15521891begin
    1553 if Sel>=0 then with THelpLineInfo(MainText.Objects[Sel+sb.si.npos]) do
    1554   if Link shr 8 and $3F=hkInternet then
    1555     case Link and $FF of
    1556       1: ShellExecute(Handle,'open',pchar(HomeDir+'AI Template\AI development manual.html'),'','',
    1557         SW_SHOWNORMAL);
    1558       2: ShellExecute(Handle,'open','http://c-evo.org','','',
    1559         SW_SHOWNORMAL);
    1560       3: ShellExecute(Handle,'open','http://c-evo.org/_sg/contact','','',
    1561         SW_SHOWNORMAL);
     1892  if Sel >= 0 then
     1893    with THelpLineInfo(MainText.Objects[Sel + sb.si.npos]) do
     1894      if Link shr 8 and $3F = hkInternet then
     1895        case Link and $FF of
     1896          1:
     1897            ShellExecute(Handle, 'open',
     1898              pchar(HomeDir + 'AI Template\AI development manual.html'), '', '',
     1899              SW_SHOWNORMAL);
     1900          2:
     1901            ShellExecute(Handle, 'open', 'http://c-evo.org', '', '',
     1902              SW_SHOWNORMAL);
     1903          3:
     1904            ShellExecute(Handle, 'open', 'http://c-evo.org/_sg/contact', '', '',
     1905              SW_SHOWNORMAL);
     1906        end
     1907      else
     1908      begin
     1909        if (Link >= $8000) and (Link and $3FFF = liInvalid) then
     1910          exit; // invalid link;
     1911        if Link >= $8000 then
     1912          ShowNewContent(FWindowMode, hkText, Link and $3FFF)
     1913        else
     1914          ShowNewContent(FWindowMode, Link shr 8 and $3F, Link and $FF);
    15621915      end
    1563   else
    1564     begin
    1565     if (Link>=$8000) and (Link and $3FFF=liInvalid) then
    1566       exit; // invalid link;
    1567     if Link>=$8000 then
    1568       ShowNewContent(FWindowMode, hkText, Link and $3FFF)
    1569     else ShowNewContent(FWindowMode, Link shr 8 and $3F, Link and $FF);
    1570     end
    15711916end;
    15721917
    15731918procedure THelpDlg.BackBtnClick(Sender: TObject);
    15741919begin
    1575 if nHist>0 then
    1576   begin
    1577   dec(nHist);
    1578   if (HistKind[nHist]=hkMisc) and (HistNo[nHist]=miscSearchResult)
    1579     and (HistSearchContent[nHist]<>SearchContent) then
    1580     begin
    1581     SearchContent:=HistSearchContent[nHist];
    1582     Search(SearchContent);
     1920  if nHist > 0 then
     1921  begin
     1922    dec(nHist);
     1923    if (HistKind[nHist] = hkMisc) and (HistNo[nHist] = miscSearchResult) and
     1924      (HistSearchContent[nHist] <> SearchContent) then
     1925    begin
     1926      SearchContent := HistSearchContent[nHist];
     1927      Search(SearchContent);
    15831928    end;
    1584   Kind:=HistKind[nHist];
    1585   no:=HistNo[nHist];
    1586   Prepare(HistPos[nHist]);
     1929    Kind := HistKind[nHist];
     1930    no := HistNo[nHist];
     1931    Prepare(HistPos[nHist]);
     1932    OffscreenPaint;
     1933    Invalidate;
     1934  end
     1935end;
     1936
     1937procedure THelpDlg.TopBtnClick(Sender: TObject);
     1938begin
     1939  nHist := 0;
     1940  Kind := hkMisc;
     1941  no := miscMain;
     1942  Prepare;
    15871943  OffscreenPaint;
    15881944  Invalidate;
    1589   end
    1590 end;
    1591 
    1592 procedure THelpDlg.TopBtnClick(Sender: TObject);
    1593 begin
    1594 nHist:=0;
    1595 Kind:=hkMisc;
    1596 no:=miscMain;
    1597 Prepare;
    1598 OffscreenPaint;
    1599 Invalidate;
    16001945end;
    16011946
    16021947procedure THelpDlg.FormClose(Sender: TObject; var Action: TCloseAction);
    16031948begin
    1604 ExtPic.Height:=0;
    1605 inherited;
     1949  ExtPic.Height := 0;
     1950  inherited;
    16061951end;
    16071952
    16081953function THelpDlg.TextIndex(Item: string): integer;
    16091954begin
    1610 result:=HelpText.GetHandle(Item)
     1955  result := HelpText.Gethandle(Item)
    16111956end;
    16121957
     
    16141959  Shift: TShiftState);
    16151960begin
    1616 if Key=VK_F1 then // my key
    1617 else inherited
     1961  if Key = VK_F1 then // my key
     1962  else
     1963    inherited
    16181964end;
    16191965
    16201966procedure THelpDlg.SearchBtnClick(Sender: TObject);
    16211967begin
    1622 InputDlg.Caption:=Phrases.Lookup('SEARCH');
    1623 InputDlg.EInput.Text:=SearchContent;
    1624 InputDlg.CenterToRect(BoundsRect);
    1625 InputDlg.ShowModal;
    1626 if (InputDlg.ModalResult=mrOK) and (length(InputDlg.EInput.Text)>=2) then
    1627   begin
    1628   Search(InputDlg.EInput.Text);
    1629   case SearchResult.Count of
    1630     0: SimpleMessage(Format(HelpText.Lookup('NOMATCHES'), [InputDlg.EInput.Text]));
    1631     1:
    1632       with THelpLineInfo(SearchResult.Objects[0]) do
    1633         if Link>=$8000 then
    1634           ShowNewContent(FWindowMode, hkText, Link and $3FFF)
    1635         else ShowNewContent(FWindowMode, Link shr 8 and $3F, Link and $FF);
     1968  InputDlg.Caption := Phrases.Lookup('SEARCH');
     1969  InputDlg.EInput.Text := SearchContent;
     1970  InputDlg.CenterToRect(BoundsRect);
     1971  InputDlg.ShowModal;
     1972  if (InputDlg.ModalResult = mrOK) and (length(InputDlg.EInput.Text) >= 2) then
     1973  begin
     1974    Search(InputDlg.EInput.Text);
     1975    case SearchResult.Count of
     1976      0:
     1977        SimpleMessage(Format(HelpText.Lookup('NOMATCHES'),
     1978          [InputDlg.EInput.Text]));
     1979      1:
     1980        with THelpLineInfo(SearchResult.Objects[0]) do
     1981          if Link >= $8000 then
     1982            ShowNewContent(FWindowMode, hkText, Link and $3FFF)
     1983          else
     1984            ShowNewContent(FWindowMode, Link shr 8 and $3F, Link and $FF);
    16361985    else
    16371986      begin
    1638       NewSearchContent:=InputDlg.EInput.Text;
    1639       ShowNewContent(FWindowMode, hkMisc, miscSearchResult);
     1987        NewSearchContent := InputDlg.EInput.Text;
     1988        ShowNewContent(FWindowMode, hkMisc, miscSearchResult);
    16401989      end
    16411990    end
     
    16451994procedure THelpDlg.Search(SearchString: string);
    16461995var
    1647 h, i, PrevHandle, PrevIndex, p, RightMargin: integer;
    1648 s: string;
    1649 mADVHELP, mIMPHELP, mFEATUREHELP: set of 0..255;
    1650 bGOVHELP, bSPECIALMODEL, bJOBHELP: boolean;
     1996  h, i, PrevHandle, PrevIndex, p, RightMargin: integer;
     1997  s: string;
     1998  mADVHELP, mIMPHELP, mFEATUREHELP: set of 0 .. 255;
     1999  bGOVHELP, bSPECIALMODEL, bJOBHELP: boolean;
    16512000begin
    1652 SearchResult.Clear;
    1653 mADVHELP:=[];
    1654 mIMPHELP:=[];
    1655 mFEATUREHELP:=[];
    1656 bGOVHELP:=false;
    1657 bSPECIALMODEL:=false;
    1658 bJOBHELP:=false;
    1659 
    1660 // search in generic reference
    1661 SearchString:=UpperCase(SearchString);
    1662 for i:=0 to 35+4 do
    1663   begin
    1664   s:=Phrases.Lookup('TERRAIN',i);
    1665   if pos(SearchString,UpperCase(s))>0 then
    1666     if i<36 then
    1667       SearchResult.AddLine(s+' '+HelpText.Lookup('HELPSPEC_TER'),pkNormal,0,
    1668         hkTer+hkCrossLink,i)
    1669     else
     2001  SearchResult.Clear;
     2002  mADVHELP := [];
     2003  mIMPHELP := [];
     2004  mFEATUREHELP := [];
     2005  bGOVHELP := false;
     2006  bSPECIALMODEL := false;
     2007  bJOBHELP := false;
     2008
     2009  // search in generic reference
     2010  SearchString := UpperCase(SearchString);
     2011  for i := 0 to 35 + 4 do
     2012  begin
     2013    s := Phrases.Lookup('TERRAIN', i);
     2014    if pos(SearchString, UpperCase(s)) > 0 then
     2015      if i < 36 then
     2016        SearchResult.AddLine(s + ' ' + HelpText.Lookup('HELPSPEC_TER'),
     2017          pkNormal, 0, hkTer + hkCrossLink, i)
     2018      else
    16702019      begin
    1671       SearchResult.AddLine(Phrases.Lookup('TERRAIN',36)+' '
    1672         +HelpText.Lookup('HELPSPEC_TER'),pkNormal,0,hkTer+hkCrossLink,36);
    1673       if i>36 then
    1674         SearchResult.AddLine(Phrases.Lookup('IMPROVEMENTS',imShipComp+i-37)
    1675           +' '+HelpText.Lookup('HELPSPEC_SHIPPART'),
    1676           pkNormal,0,hkImp+hkCrossLink,imShipComp+i-37);
    1677       break
     2020        SearchResult.AddLine(Phrases.Lookup('TERRAIN', 36) + ' ' +
     2021          HelpText.Lookup('HELPSPEC_TER'), pkNormal, 0,
     2022          hkTer + hkCrossLink, 36);
     2023        if i > 36 then
     2024          SearchResult.AddLine(Phrases.Lookup('IMPROVEMENTS',
     2025            imShipComp + i - 37) + ' ' + HelpText.Lookup('HELPSPEC_SHIPPART'),
     2026            pkNormal, 0, hkImp + hkCrossLink, imShipComp + i - 37);
     2027        Break
    16782028      end
    16792029  end;
    1680 for i:=0 to nJobHelp-1 do
    1681   if pos(SearchString,UpperCase(Phrases.Lookup('JOBRESULT',JobHelp[i])))>0 then
    1682     begin
    1683     SearchResult.AddLine(HelpText.Lookup('HELPTITLE_JOBLIST'),pkNormal,0,
    1684       hkMisc+hkCrossLink,miscJobList);
    1685     bJOBHELP:=true;
    1686     break
     2030  for i := 0 to nJobHelp - 1 do
     2031    if pos(SearchString, UpperCase(Phrases.Lookup('JOBRESULT', JobHelp[i]))) > 0
     2032    then
     2033    begin
     2034      SearchResult.AddLine(HelpText.Lookup('HELPTITLE_JOBLIST'), pkNormal, 0,
     2035        hkMisc + hkCrossLink, miscJobList);
     2036      bJOBHELP := true;
     2037      Break
    16872038    end;
    1688 for i:=0 to nAdv-1 do
    1689   begin
    1690   s:=Phrases.Lookup('ADVANCES',i);
    1691   if pos(SearchString,UpperCase(s))>0 then
    1692     begin
    1693     if i in FutureTech then s:=s+' '+HelpText.Lookup('HELPSPEC_FUTURE')
    1694     else s:=s+' '+HelpText.Lookup('HELPSPEC_ADV');
    1695     SearchResult.AddLine(s,pkNormal,0,hkAdv+hkCrossLink,i);
    1696     include(mADVHELP,i);
     2039  for i := 0 to nAdv - 1 do
     2040  begin
     2041    s := Phrases.Lookup('ADVANCES', i);
     2042    if pos(SearchString, UpperCase(s)) > 0 then
     2043    begin
     2044      if i in FutureTech then
     2045        s := s + ' ' + HelpText.Lookup('HELPSPEC_FUTURE')
     2046      else
     2047        s := s + ' ' + HelpText.Lookup('HELPSPEC_ADV');
     2048      SearchResult.AddLine(s, pkNormal, 0, hkAdv + hkCrossLink, i);
     2049      include(mADVHELP, i);
    16972050    end
    16982051  end;
    1699 for i:=0 to nSpecialModel-1 do
    1700   begin
    1701   FindStdModelPicture(SpecialModelPictureCode[i],h,s);
    1702   if pos(SearchString,UpperCase(s))>0 then
    1703     begin
    1704     SearchResult.AddLine(HelpText.Lookup('HELPTITLE_MODELLIST'),pkNormal,0,
    1705       hkModel+hkCrossLink,0);
    1706     bSPECIALMODEL:=true;
    1707     break
     2052  for i := 0 to nSpecialModel - 1 do
     2053  begin
     2054    FindStdModelPicture(SpecialModelPictureCode[i], h, s);
     2055    if pos(SearchString, UpperCase(s)) > 0 then
     2056    begin
     2057      SearchResult.AddLine(HelpText.Lookup('HELPTITLE_MODELLIST'), pkNormal, 0,
     2058        hkModel + hkCrossLink, 0);
     2059      bSPECIALMODEL := true;
     2060      Break
    17082061    end;
    17092062  end;
    1710 for i:=0 to nFeature-1 do
    1711   begin
    1712   s:=Phrases.Lookup('FEATURES',i);
    1713   if pos(SearchString,UpperCase(s))>0 then
    1714     begin
    1715     if i<mcFirstNonCap then s:=s+' '+HelpText.Lookup('HELPSPEC_CAP')
    1716     else if i in AutoFeature then s:=s+' '+HelpText.Lookup('HELPSPEC_STANDARD')
    1717     else s:=s+' '+HelpText.Lookup('HELPSPEC_FEATURE');
    1718     SearchResult.AddLine(s,pkNormal,0,hkFeature+hkCrossLink,i);
    1719     include(mFEATUREHELP,i);
     2063  for i := 0 to nFeature - 1 do
     2064  begin
     2065    s := Phrases.Lookup('FEATURES', i);
     2066    if pos(SearchString, UpperCase(s)) > 0 then
     2067    begin
     2068      if i < mcFirstNonCap then
     2069        s := s + ' ' + HelpText.Lookup('HELPSPEC_CAP')
     2070      else if i in AutoFeature then
     2071        s := s + ' ' + HelpText.Lookup('HELPSPEC_STANDARD')
     2072      else
     2073        s := s + ' ' + HelpText.Lookup('HELPSPEC_FEATURE');
     2074      SearchResult.AddLine(s, pkNormal, 0, hkFeature + hkCrossLink, i);
     2075      include(mFEATUREHELP, i);
    17202076    end
    17212077  end;
    1722 for i:=0 to nImp-1 do
    1723   begin
    1724   s:=Phrases.Lookup('IMPROVEMENTS',i);
    1725   if pos(SearchString,UpperCase(s))>0 then
    1726     begin
    1727     case Imp[i].Kind of
    1728       ikWonder: s:=s+' '+HelpText.Lookup('HELPSPEC_WONDER');
    1729       ikCommon: s:=s+' '+HelpText.Lookup('HELPSPEC_IMP');
    1730       ikShipPart: s:=s+' '+HelpText.Lookup('HELPSPEC_SHIPPART');
    1731       else s:=s+' '+HelpText.Lookup('HELPSPEC_NAT')
     2078  for i := 0 to nImp - 1 do
     2079  begin
     2080    s := Phrases.Lookup('IMPROVEMENTS', i);
     2081    if pos(SearchString, UpperCase(s)) > 0 then
     2082    begin
     2083      case Imp[i].Kind of
     2084        ikWonder:
     2085          s := s + ' ' + HelpText.Lookup('HELPSPEC_WONDER');
     2086        ikCommon:
     2087          s := s + ' ' + HelpText.Lookup('HELPSPEC_IMP');
     2088        ikShipPart:
     2089          s := s + ' ' + HelpText.Lookup('HELPSPEC_SHIPPART');
     2090      else
     2091        s := s + ' ' + HelpText.Lookup('HELPSPEC_NAT')
    17322092      end;
    1733     SearchResult.AddLine(s,pkNormal,0,hkImp+hkCrossLink,i);
    1734     include(mIMPHELP,i);
     2093      SearchResult.AddLine(s, pkNormal, 0, hkImp + hkCrossLink, i);
     2094      include(mIMPHELP, i);
    17352095    end
    17362096  end;
    1737 for i:=0 to nGov-1 do
    1738   if pos(SearchString,UpperCase(Phrases.Lookup('GOVERNMENT',i)))>0 then
    1739     begin
    1740     SearchResult.AddLine(HelpText.Lookup('HELPTITLE_GOVLIST'),pkNormal,0,
    1741       hkMisc+hkCrossLink,miscGovList);
    1742     bGOVHELP:=true;
    1743     break
     2097  for i := 0 to nGov - 1 do
     2098    if pos(SearchString, UpperCase(Phrases.Lookup('GOVERNMENT', i))) > 0 then
     2099    begin
     2100      SearchResult.AddLine(HelpText.Lookup('HELPTITLE_GOVLIST'), pkNormal, 0,
     2101        hkMisc + hkCrossLink, miscGovList);
     2102      bGOVHELP := true;
     2103      Break
    17442104    end;
    17452105
    1746 // full text search
    1747 h:=-1;
    1748 repeat
    1749   PrevHandle:=h;
    1750   PrevIndex:=i;
    1751   if not HelpText.Search(SearchString, h, i) then
    1752     break;
    1753   if h=hADVHELP then
    1754     begin
    1755     if (i>=0) and ((i<>PrevIndex) or (h<>PrevHandle)) and not (i in mADVHELP) then
     2106  // full text search
     2107  h := -1;
     2108  repeat
     2109    PrevHandle := h;
     2110    PrevIndex := i;
     2111    if not HelpText.Search(SearchString, h, i) then
     2112      Break;
     2113    if h = hADVHELP then
     2114    begin
     2115      if (i >= 0) and ((i <> PrevIndex) or (h <> PrevHandle)) and
     2116        not(i in mADVHELP) then
    17562117      begin
    1757       s:=Phrases.Lookup('ADVANCES',i);
    1758       if i in FutureTech then s:=s+' '+HelpText.Lookup('HELPSPEC_FUTURE')
    1759       else s:=s+' '+HelpText.Lookup('HELPSPEC_ADV');
    1760       SearchResult.AddLine(s,pkNormal,0,hkAdv+hkCrossLink,i)
     2118        s := Phrases.Lookup('ADVANCES', i);
     2119        if i in FutureTech then
     2120          s := s + ' ' + HelpText.Lookup('HELPSPEC_FUTURE')
     2121        else
     2122          s := s + ' ' + HelpText.Lookup('HELPSPEC_ADV');
     2123        SearchResult.AddLine(s, pkNormal, 0, hkAdv + hkCrossLink, i)
    17612124      end
    17622125    end
    1763   else if h=hIMPHELP then
    1764     begin
    1765     if (i>=0) and ((i<>PrevIndex) or (h<>PrevHandle)) and not (i in mIMPHELP) then
     2126    else if h = hIMPHELP then
     2127    begin
     2128      if (i >= 0) and ((i <> PrevIndex) or (h <> PrevHandle)) and
     2129        not(i in mIMPHELP) then
    17662130      begin
    1767       s:=Phrases.Lookup('IMPROVEMENTS',i);
    1768       case Imp[i].Kind of
    1769         ikWonder: s:=s+' '+HelpText.Lookup('HELPSPEC_WONDER');
    1770         ikCommon: s:=s+' '+HelpText.Lookup('HELPSPEC_IMP');
    1771         ikShipPart: s:=s+' '+HelpText.Lookup('HELPSPEC_SHIPPART');
    1772         else s:=s+' '+HelpText.Lookup('HELPSPEC_NAT')
     2131        s := Phrases.Lookup('IMPROVEMENTS', i);
     2132        case Imp[i].Kind of
     2133          ikWonder:
     2134            s := s + ' ' + HelpText.Lookup('HELPSPEC_WONDER');
     2135          ikCommon:
     2136            s := s + ' ' + HelpText.Lookup('HELPSPEC_IMP');
     2137          ikShipPart:
     2138            s := s + ' ' + HelpText.Lookup('HELPSPEC_SHIPPART');
     2139        else
     2140          s := s + ' ' + HelpText.Lookup('HELPSPEC_NAT')
    17732141        end;
    1774       SearchResult.AddLine(s,pkNormal,0,hkImp+hkCrossLink,i)
     2142        SearchResult.AddLine(s, pkNormal, 0, hkImp + hkCrossLink, i)
    17752143      end
    17762144    end
    1777   else if h=hFEATUREHELP then
    1778     begin
    1779     if (i>=0) and ((i<>PrevIndex) or (h<>PrevHandle)) and not (i in mFEATUREHELP) then
     2145    else if h = hFEATUREHELP then
     2146    begin
     2147      if (i >= 0) and ((i <> PrevIndex) or (h <> PrevHandle)) and
     2148        not(i in mFEATUREHELP) then
    17802149      begin
    1781       s:=Phrases.Lookup('FEATURES',i);
    1782       if i<mcFirstNonCap then s:=s+' '+HelpText.Lookup('HELPSPEC_CAP')
    1783       else if i in AutoFeature then s:=s+' '+HelpText.Lookup('HELPSPEC_STANDARD')
    1784       else s:=s+' '+HelpText.Lookup('HELPSPEC_FEATURE');
    1785       SearchResult.AddLine(s,pkNormal,0,hkFeature+hkCrossLink,i);
     2150        s := Phrases.Lookup('FEATURES', i);
     2151        if i < mcFirstNonCap then
     2152          s := s + ' ' + HelpText.Lookup('HELPSPEC_CAP')
     2153        else if i in AutoFeature then
     2154          s := s + ' ' + HelpText.Lookup('HELPSPEC_STANDARD')
     2155        else
     2156          s := s + ' ' + HelpText.Lookup('HELPSPEC_FEATURE');
     2157        SearchResult.AddLine(s, pkNormal, 0, hkFeature + hkCrossLink, i);
    17862158      end
    17872159    end
    1788   else if h=hGOVHELP then
    1789     begin
    1790     if (i>=0) and (h<>PrevHandle) and not bGOVHELP then
    1791       SearchResult.AddLine(HelpText.Lookup('HELPTITLE_GOVLIST'),pkNormal,0,
    1792         hkMisc+hkCrossLink,miscGovList)
     2160    else if h = hGOVHELP then
     2161    begin
     2162      if (i >= 0) and (h <> PrevHandle) and not bGOVHELP then
     2163        SearchResult.AddLine(HelpText.Lookup('HELPTITLE_GOVLIST'), pkNormal, 0,
     2164          hkMisc + hkCrossLink, miscGovList)
    17932165    end
    1794   else if h=hSPECIALMODEL then
    1795     begin
    1796     if (i>=0) and (h<>PrevHandle) and not bSPECIALMODEL then
    1797       SearchResult.AddLine(HelpText.Lookup('HELPTITLE_MODELLIST'),pkNormal,0,
    1798         hkModel+hkCrossLink,0)
     2166    else if h = hSPECIALMODEL then
     2167    begin
     2168      if (i >= 0) and (h <> PrevHandle) and not bSPECIALMODEL then
     2169        SearchResult.AddLine(HelpText.Lookup('HELPTITLE_MODELLIST'), pkNormal,
     2170          0, hkModel + hkCrossLink, 0)
    17992171    end
    1800   else if h=hJOBHELP then
    1801     begin
    1802     if (i>=0) and (h<>PrevHandle) and not bJOBHELP then
    1803       SearchResult.AddLine(HelpText.Lookup('HELPTITLE_JOBLIST'),pkNormal,0,
    1804         hkMisc+hkCrossLink,miscJobList)
     2172    else if h = hJOBHELP then
     2173    begin
     2174      if (i >= 0) and (h <> PrevHandle) and not bJOBHELP then
     2175        SearchResult.AddLine(HelpText.Lookup('HELPTITLE_JOBLIST'), pkNormal, 0,
     2176          hkMisc + hkCrossLink, miscJobList)
    18052177    end
    1806   else if {(h<>hMAIN) and} (h<>PrevHandle) then
    1807     begin
    1808     s:=HelpText.LookupByHandle(h);
    1809     p:=pos('$',s);
    1810     if p>0 then
     2178    else if { (h<>hMAIN) and } (h <> PrevHandle) then
     2179    begin
     2180      s := HelpText.LookupByHandle(h);
     2181      p := pos('$', s);
     2182      if p > 0 then
    18112183      begin
    1812       s:=copy(s,p+1,maxint);
    1813       p:=pos('\',s);
    1814       if p>0 then
    1815         s:=copy(s,1,p-1);
    1816       SearchResult.AddLine(s, pkNormal, 0, hkText+hkCrossLink, h);
     2184        s := copy(s, p + 1, maxint);
     2185        p := pos('\', s);
     2186        if p > 0 then
     2187          s := copy(s, 1, p - 1);
     2188        SearchResult.AddLine(s, pkNormal, 0, hkText + hkCrossLink, h);
    18172189      end
    18182190    end
    1819 until false;
    1820 
    1821 // cut lines to fit to window
    1822 RightMargin:=InnerWidth-16-GetSystemMetrics(SM_CXVSCROLL);
    1823 Offscreen.Canvas.Font.Assign(UniFont[ftNormal]);
    1824 for i:=0 to SearchResult.Count-1 do
    1825   begin
    1826   while BiColorTextWidth(Offscreen.Canvas, SearchResult[i])>RightMargin-32 do
    1827     SearchResult[i]:=copy(SearchResult[i], 1, length(SearchResult[i])-1)
    1828   end;
    1829 end;
     2191    until false;
     2192
     2193    // cut lines to fit to window
     2194    RightMargin := InnerWidth - 16 - GetSystemMetrics(SM_CXVSCROLL);
     2195    OffScreen.Canvas.Font.Assign(UniFont[ftNormal]);
     2196    for i := 0 to SearchResult.Count - 1 do
     2197    begin
     2198      while BiColorTextWidth(OffScreen.Canvas, SearchResult[i]) >
     2199        RightMargin - 32 do
     2200        SearchResult[i] := copy(SearchResult[i], 1, length(SearchResult[i]) - 1)
     2201    end;
     2202  end;
    18302203
    18312204end.
    1832 
  • trunk/LocalPlayer/IsoEngine.pas

    r2 r6  
    11{$INCLUDE switches}
    2 
    32unit IsoEngine;
    43
     
    65
    76uses
    8   Protocol,ClientTools,ScreenTools,Tribes,
    9   {$IFNDEF SCR}Term,{$ENDIF}
    10 
    11   Windows,SysUtils,Classes,Graphics;
     7  Protocol, ClientTools, ScreenTools, Tribes,
     8{$IFNDEF SCR}Term, {$ENDIF}
     9  Windows, SysUtils, Classes, Graphics;
    1210
    1311type
    14 TInitEnemyModelEvent=function(emix: integer): boolean;
    15 
    16 TIsoMap = class
    17   constructor Create;
    18   procedure SetOutput(Output: TBitmap);
    19   procedure SetPaintBounds(Left, Top, Right, Bottom: integer);
    20   procedure Paint(x,y,Loc,nx,ny,CityLoc,CityOwner:integer; UseBlink: boolean = false; CityAllowClick: boolean = false);
    21   procedure PaintUnit(x,y:integer;const UnitInfo:TUnitInfo;Status:integer);
    22   procedure PaintCity(x,y:integer;const CityInfo:TCityInfo; accessory: boolean = true);
    23   procedure BitBlt(Src: TBitmap; x,y,Width,Height,xSrc,ySrc,Rop: integer);
    24 
    25   procedure AttackBegin(const ShowMove: TShowMove);
    26   procedure AttackEffect(const ShowMove: TShowMove);
    27   procedure AttackEnd;
    28 
    29 protected
    30   FOutput: TBitmap;
    31   FLeft, FTop, FRight, FBottom, RealTop, RealBottom, AttLoc, DefLoc, DefHealth, FAdviceLoc: integer;
    32   OutDC, DataDC, MaskDC: Cardinal;
    33   function Connection4(Loc,Mask,Value:integer):integer;
    34   function Connection8(Loc,Mask:integer):integer;
    35   function OceanConnection(Loc: integer): integer;
    36   procedure PaintShore(x,y,Loc:integer);
    37   procedure PaintTileExtraTerrain(x,y,Loc: integer);
    38   procedure PaintTileObjects(x,y,Loc,CityLoc,CityOwner:integer; UseBlink: boolean);
    39   procedure PaintGrid(x,y,nx,ny: integer);
    40   procedure FillRect(x,y,Width,Height,Color: integer);
    41   procedure Textout(x,y,Color: integer; const s: string);
    42   procedure Sprite(HGr,xDst,yDst,Width,Height,xGr,yGr: integer);
    43   procedure TSprite(xDst,yDst,grix: integer; PureBlack: boolean = false);
    44 
    45 public
    46   property AdviceLoc: integer read FAdviceLoc write FAdviceLoc;
     12  TInitEnemyModelEvent = function(emix: integer): boolean;
     13
     14  TIsoMap = class
     15    constructor Create;
     16    procedure SetOutput(Output: TBitmap);
     17    procedure SetPaintBounds(Left, Top, Right, Bottom: integer);
     18    procedure Paint(x, y, Loc, nx, ny, CityLoc, CityOwner: integer;
     19      UseBlink: boolean = false; CityAllowClick: boolean = false);
     20    procedure PaintUnit(x, y: integer; const UnitInfo: TUnitInfo;
     21      Status: integer);
     22    procedure PaintCity(x, y: integer; const CityInfo: TCityInfo;
     23      accessory: boolean = true);
     24    procedure BitBlt(Src: TBitmap; x, y, Width, Height, xSrc, ySrc,
     25      Rop: integer);
     26
     27    procedure AttackBegin(const ShowMove: TShowMove);
     28    procedure AttackEffect(const ShowMove: TShowMove);
     29    procedure AttackEnd;
     30
     31  protected
     32    FOutput: TBitmap;
     33    FLeft, FTop, FRight, FBottom, RealTop, RealBottom, AttLoc, DefLoc,
     34      DefHealth, FAdviceLoc: integer;
     35    OutDC, DataDC, MaskDC: Cardinal;
     36    function Connection4(Loc, Mask, Value: integer): integer;
     37    function Connection8(Loc, Mask: integer): integer;
     38    function OceanConnection(Loc: integer): integer;
     39    procedure PaintShore(x, y, Loc: integer);
     40    procedure PaintTileExtraTerrain(x, y, Loc: integer);
     41    procedure PaintTileObjects(x, y, Loc, CityLoc, CityOwner: integer;
     42      UseBlink: boolean);
     43    procedure PaintGrid(x, y, nx, ny: integer);
     44    procedure FillRect(x, y, Width, Height, Color: integer);
     45    procedure Textout(x, y, Color: integer; const s: string);
     46    procedure Sprite(HGr, xDst, yDst, Width, Height, xGr, yGr: integer);
     47    procedure TSprite(xDst, yDst, grix: integer; PureBlack: boolean = false);
     48
     49  public
     50    property AdviceLoc: integer read FAdviceLoc write FAdviceLoc;
    4751  end;
    4852
    49 
    5053const
    51 // options switched by buttons
    52 moPolitical=0; moCityNames=1; moGreatWall=4; moGrid=5; moBareTerrain=6;
    53 
    54 // other options
    55 moEditMode=16; moLocCodes=17;
    56 
     54  // options switched by buttons
     55  moPolitical = 0;
     56  moCityNames = 1;
     57  moGreatWall = 4;
     58  moGrid = 5;
     59  moBareTerrain = 6;
     60
     61  // other options
     62  moEditMode = 16;
     63  moLocCodes = 17;
    5764
    5865var
    59 NoMap: TIsoMap;
    60 Options: integer;
    61 pDebugMap: integer; //-1 for off
    62 
     66  NoMap: TIsoMap;
     67  Options: integer;
     68  pDebugMap: integer; // -1 for off
    6369
    6470function IsJungle(y: integer): boolean;
     
    6874procedure Reset;
    6975
    70 
    7176implementation
    7277
    7378const
    74 ShoreDither=fGrass;
    75 TerrainIconLines=21;
     79  ShoreDither = fGrass;
     80  TerrainIconLines = 21;
    7681
    7782var
    78 BordersOK: integer;
    79 OnInitEnemyModel: TInitEnemyModelEvent;
    80 LandPatch,OceanPatch, Borders: TBitmap;
    81 TSpriteSize: array[0..TerrainIconLines*9-1] of TRect;
    82 DebugMap: ^TTileList;
    83 CitiesPictures: array[2..3,0..3] of TCityPicture;
    84 FoW, ShowLoc, ShowCityNames, ShowObjects, ShowBorder, ShowMyBorder,
    85   ShowGrWall, ShowDebug: boolean;
     83  BordersOK: integer;
     84  OnInitEnemyModel: TInitEnemyModelEvent;
     85  LandPatch, OceanPatch, Borders: TBitmap;
     86  TSpriteSize: array [0 .. TerrainIconLines * 9 - 1] of TRect;
     87  DebugMap: ^TTileList;
     88  CitiesPictures: array [2 .. 3, 0 .. 3] of TCityPicture;
     89  FoW, ShowLoc, ShowCityNames, ShowObjects, ShowBorder, ShowMyBorder,
     90    ShowGrWall, ShowDebug: boolean;
    8691
    8792function IsJungle(y: integer): boolean;
    8893begin
    89 result:= (y>(G.ly-2) div 4) and (G.ly-1-y>(G.ly-2) div 4)
     94  result := (y > (G.ly - 2) div 4) and (G.ly - 1 - y > (G.ly - 2) div 4)
    9095end;
    9196
    9297procedure Init(InitEnemyModelHandler: TInitEnemyModelEvent);
    9398begin
    94 OnInitEnemyModel:=InitEnemyModelHandler;
    95 if NoMap<>nil then
    96   NoMap.Free;
    97 NoMap:=TIsoMap.Create;
     99  OnInitEnemyModel := InitEnemyModelHandler;
     100  if NoMap <> nil then
     101    NoMap.Free;
     102  NoMap := TIsoMap.Create;
    98103end;
    99104
    100105function ApplyTileSize(xxtNew, yytNew: integer): boolean;
    101106type
    102 TLine=array[0..INFIN,0..2] of Byte;
     107  TLine = array [0 .. INFIN, 0 .. 2] of Byte;
    103108var
    104 i,x,y,xSrc,ySrc,HGrTerrainNew,HGrCitiesNew,age,size:integer;
    105 LandMore,OceanMore,DitherMask,Mask24: TBitmap;
    106 MaskLine: array[0..32*3-1] of ^TLine; // 32 = assumed maximum for yyt
    107 Border: boolean;
     109  i, x, y, xSrc, ySrc, HGrTerrainNew, HGrCitiesNew, age, size: integer;
     110  LandMore, OceanMore, DitherMask, Mask24: TBitmap;
     111  MaskLine: array [0 .. 32 * 3 - 1] of ^TLine; // 32 = assumed maximum for yyt
     112  Border: boolean;
    108113begin
    109 result:=false;
    110 HGrTerrainNew:=LoadGraphicSet(Format('Terrain%dx%d',[xxtNew*2,yytNew*2]));
    111 if HGrTerrainNew<0 then
    112   exit;
    113 HGrCitiesNew:=LoadGraphicSet(Format('Cities%dx%d',[xxtNew*2,yytNew*2]));
    114 if HGrCitiesNew<0 then
    115   exit;
    116 xxt:=xxtNew; yyt:=yytNew;
    117 HGrTerrain:=HGrTerrainNew;
    118 HGrCities:=HGrCitiesNew;
    119 result:=true;
    120 
    121 // prepare age 2+3 cities
    122 for age:=2 to 3 do
    123   for size:=0 to 3 do with CitiesPictures[age,size] do
    124     FindPosition(HGrCities,size*(xxt*2+1),(age-2)*(yyt*3+1),xxt*2-1,yyt*3-1,
    125       $00FFFF,xShield,yShield);
    126 
    127 {prepare dithered ground tiles}
    128 if LandPatch<>nil then
    129   LandPatch.Free;
    130 LandPatch:=TBitmap.Create;
    131 LandPatch.PixelFormat:=pf24bit;
    132 LandPatch.Canvas.Brush.Color:=0;
    133 LandPatch.Width:=xxt*18; LandPatch.Height:=yyt*9;
    134 if OceanPatch<>nil then
    135   OceanPatch.Free;
    136 OceanPatch:=TBitmap.Create;
    137 OceanPatch.PixelFormat:=pf24bit;
    138 OceanPatch.Canvas.Brush.Color:=0;
    139 OceanPatch.Width:=xxt*8; OceanPatch.Height:=yyt*4;
    140 LandMore:=TBitmap.Create;
    141 LandMore.PixelFormat:=pf24bit;
    142 LandMore.Canvas.Brush.Color:=0;
    143 LandMore.Width:=xxt*18; LandMore.Height:=yyt*9;
    144 OceanMore:=TBitmap.Create;
    145 OceanMore.PixelFormat:=pf24bit;
    146 OceanMore.Canvas.Brush.Color:=0;
    147 OceanMore.Width:=xxt*8; OceanMore.Height:=yyt*4;
    148 DitherMask:=TBitmap.Create;
    149 DitherMask.PixelFormat:=pf24bit;
    150 DitherMask.Width:=xxt*2; DitherMask.Height:=yyt*2;
    151 BitBlt(DitherMask.Canvas.Handle,0,0,xxt*2,yyt*2,
    152   GrExt[HGrTerrain].Mask.Canvas.Handle,1+7*(xxt*2+1),1+yyt+15*(yyt*3+1),SRCAND);
    153 
    154 for x:=-1 to 6 do
     114  result := false;
     115  HGrTerrainNew := LoadGraphicSet(Format('Terrain%dx%d',
     116    [xxtNew * 2, yytNew * 2]));
     117  if HGrTerrainNew < 0 then
     118    exit;
     119  HGrCitiesNew := LoadGraphicSet(Format('Cities%dx%d',
     120    [xxtNew * 2, yytNew * 2]));
     121  if HGrCitiesNew < 0 then
     122    exit;
     123  xxt := xxtNew;
     124  yyt := yytNew;
     125  HGrTerrain := HGrTerrainNew;
     126  HGrCities := HGrCitiesNew;
     127  result := true;
     128
     129  // prepare age 2+3 cities
     130  for age := 2 to 3 do
     131    for size := 0 to 3 do
     132      with CitiesPictures[age, size] do
     133        FindPosition(HGrCities, size * (xxt * 2 + 1), (age - 2) * (yyt * 3 + 1),
     134          xxt * 2 - 1, yyt * 3 - 1, $00FFFF, xShield, yShield);
     135
     136  { prepare dithered ground tiles }
     137  if LandPatch <> nil then
     138    LandPatch.Free;
     139  LandPatch := TBitmap.Create;
     140  LandPatch.PixelFormat := pf24bit;
     141  LandPatch.Canvas.Brush.Color := 0;
     142  LandPatch.Width := xxt * 18;
     143  LandPatch.Height := yyt * 9;
     144  if OceanPatch <> nil then
     145    OceanPatch.Free;
     146  OceanPatch := TBitmap.Create;
     147  OceanPatch.PixelFormat := pf24bit;
     148  OceanPatch.Canvas.Brush.Color := 0;
     149  OceanPatch.Width := xxt * 8;
     150  OceanPatch.Height := yyt * 4;
     151  LandMore := TBitmap.Create;
     152  LandMore.PixelFormat := pf24bit;
     153  LandMore.Canvas.Brush.Color := 0;
     154  LandMore.Width := xxt * 18;
     155  LandMore.Height := yyt * 9;
     156  OceanMore := TBitmap.Create;
     157  OceanMore.PixelFormat := pf24bit;
     158  OceanMore.Canvas.Brush.Color := 0;
     159  OceanMore.Width := xxt * 8;
     160  OceanMore.Height := yyt * 4;
     161  DitherMask := TBitmap.Create;
     162  DitherMask.PixelFormat := pf24bit;
     163  DitherMask.Width := xxt * 2;
     164  DitherMask.Height := yyt * 2;
     165  BitBlt(DitherMask.Canvas.Handle, 0, 0, xxt * 2, yyt * 2,
     166    GrExt[HGrTerrain].Mask.Canvas.Handle, 1 + 7 * (xxt * 2 + 1),
     167    1 + yyt + 15 * (yyt * 3 + 1), SRCAND);
     168
     169  for x := -1 to 6 do
    155170  begin
    156   if x=-1 then begin xSrc:=ShoreDither*(xxt*2+1)+1; ySrc:=1+yyt end
    157   else if x=6 then begin xSrc:=1+(xxt*2+1)*2; ySrc:=1+yyt+(yyt*3+1)*2 end
    158   else begin xSrc:=(x+2)*(xxt*2+1)+1; ySrc:=1+yyt end;
    159   for y:=-1 to 6 do
    160     BitBlt(LandPatch.Canvas.Handle,(x+2)*(xxt*2),(y+2)*yyt,xxt*2,yyt,
    161       GrExt[HGrTerrain].Data.Canvas.Handle,xSrc,ySrc,SRCCOPY);
    162   for y:=-2 to 6 do
    163     BitBlt(LandPatch.Canvas.Handle,(x+2)*(xxt*2),(y+2)*yyt,xxt,yyt,
    164       GrExt[HGrTerrain].Data.Canvas.Handle,xSrc+xxt,ySrc+yyt,SRCPAINT);
    165   for y:=-2 to 6 do
    166     BitBlt(LandPatch.Canvas.Handle,(x+2)*(xxt*2)+xxt,(y+2)*yyt,xxt,yyt,
    167       GrExt[HGrTerrain].Data.Canvas.Handle,xSrc,ySrc+yyt,SRCPAINT);
    168   for y:=-2 to 6 do
    169     BitBlt(LandPatch.Canvas.Handle,(x+2)*(xxt*2),(y+2)*yyt,xxt,yyt,
    170       DitherMask.Canvas.Handle,xxt,yyt,SRCAND);
    171   for y:=-2 to 6 do
    172     BitBlt(LandPatch.Canvas.Handle,(x+2)*(xxt*2)+xxt,(y+2)*yyt,xxt,yyt,
    173       DitherMask.Canvas.Handle,0,yyt,SRCAND);
     171    if x = -1 then
     172    begin
     173      xSrc := ShoreDither * (xxt * 2 + 1) + 1;
     174      ySrc := 1 + yyt
     175    end
     176    else if x = 6 then
     177    begin
     178      xSrc := 1 + (xxt * 2 + 1) * 2;
     179      ySrc := 1 + yyt + (yyt * 3 + 1) * 2
     180    end
     181    else
     182    begin
     183      xSrc := (x + 2) * (xxt * 2 + 1) + 1;
     184      ySrc := 1 + yyt
     185    end;
     186    for y := -1 to 6 do
     187      BitBlt(LandPatch.Canvas.Handle, (x + 2) * (xxt * 2), (y + 2) * yyt,
     188        xxt * 2, yyt, GrExt[HGrTerrain].Data.Canvas.Handle, xSrc, ySrc,
     189        SRCCOPY);
     190    for y := -2 to 6 do
     191      BitBlt(LandPatch.Canvas.Handle, (x + 2) * (xxt * 2), (y + 2) * yyt, xxt,
     192        yyt, GrExt[HGrTerrain].Data.Canvas.Handle, xSrc + xxt, ySrc + yyt,
     193        SRCPAINT);
     194    for y := -2 to 6 do
     195      BitBlt(LandPatch.Canvas.Handle, (x + 2) * (xxt * 2) + xxt, (y + 2) * yyt,
     196        xxt, yyt, GrExt[HGrTerrain].Data.Canvas.Handle, xSrc, ySrc + yyt,
     197        SRCPAINT);
     198    for y := -2 to 6 do
     199      BitBlt(LandPatch.Canvas.Handle, (x + 2) * (xxt * 2), (y + 2) * yyt, xxt,
     200        yyt, DitherMask.Canvas.Handle, xxt, yyt, SRCAND);
     201    for y := -2 to 6 do
     202      BitBlt(LandPatch.Canvas.Handle, (x + 2) * (xxt * 2) + xxt, (y + 2) * yyt,
     203        xxt, yyt, DitherMask.Canvas.Handle, 0, yyt, SRCAND);
    174204  end;
    175205
    176 for y:=-1 to 6 do
     206  for y := -1 to 6 do
    177207  begin
    178   if y=-1 then begin xSrc:=ShoreDither*(xxt*2+1)+1; ySrc:=1+yyt end
    179   else if y=6 then begin xSrc:=1+2*(xxt*2+1); ySrc:=1+yyt+2*(yyt*3+1) end
    180   else begin xSrc:=(y+2)*(xxt*2+1)+1; ySrc:=1+yyt end;
    181   for x:=-2 to 6 do
    182     BitBlt(LandMore.Canvas.Handle,(x+2)*(xxt*2),(y+2)*yyt,xxt*2,yyt,
    183       GrExt[HGrTerrain].Data.Canvas.Handle,xSrc,ySrc,SRCCOPY);
    184   BitBlt(LandMore.Canvas.Handle,xxt*2,(y+2)*yyt,xxt,yyt,
    185     GrExt[HGrTerrain].Data.Canvas.Handle,xSrc+xxt,ySrc+yyt,SRCPAINT);
    186   for x:=0 to 7 do
    187     BitBlt(LandMore.Canvas.Handle,(x+2)*(xxt*2)-xxt,(y+2)*yyt,xxt*2,yyt,
    188       GrExt[HGrTerrain].Data.Canvas.Handle,xSrc,ySrc+yyt,SRCPAINT);
    189   for x:=-2 to 6 do
    190     BitBlt(LandMore.Canvas.Handle,(x+2)*(xxt*2),(y+2)*yyt,xxt*2,yyt,
    191       DitherMask.Canvas.Handle,0,0,SRCAND);
     208    if y = -1 then
     209    begin
     210      xSrc := ShoreDither * (xxt * 2 + 1) + 1;
     211      ySrc := 1 + yyt
     212    end
     213    else if y = 6 then
     214    begin
     215      xSrc := 1 + 2 * (xxt * 2 + 1);
     216      ySrc := 1 + yyt + 2 * (yyt * 3 + 1)
     217    end
     218    else
     219    begin
     220      xSrc := (y + 2) * (xxt * 2 + 1) + 1;
     221      ySrc := 1 + yyt
     222    end;
     223    for x := -2 to 6 do
     224      BitBlt(LandMore.Canvas.Handle, (x + 2) * (xxt * 2), (y + 2) * yyt,
     225        xxt * 2, yyt, GrExt[HGrTerrain].Data.Canvas.Handle, xSrc, ySrc,
     226        SRCCOPY);
     227    BitBlt(LandMore.Canvas.Handle, xxt * 2, (y + 2) * yyt, xxt, yyt,
     228      GrExt[HGrTerrain].Data.Canvas.Handle, xSrc + xxt, ySrc + yyt, SRCPAINT);
     229    for x := 0 to 7 do
     230      BitBlt(LandMore.Canvas.Handle, (x + 2) * (xxt * 2) - xxt, (y + 2) * yyt,
     231        xxt * 2, yyt, GrExt[HGrTerrain].Data.Canvas.Handle, xSrc, ySrc + yyt,
     232        SRCPAINT);
     233    for x := -2 to 6 do
     234      BitBlt(LandMore.Canvas.Handle, (x + 2) * (xxt * 2), (y + 2) * yyt,
     235        xxt * 2, yyt, DitherMask.Canvas.Handle, 0, 0, SRCAND);
    192236  end;
    193237
    194 for x:=0 to 3 do for y:=0 to 3 do
     238  for x := 0 to 3 do
     239    for y := 0 to 3 do
     240    begin
     241      if (x = 1) and (y = 1) then
     242        xSrc := 1
     243      else
     244        xSrc := (x mod 2) * (xxt * 2 + 1) + 1;
     245      ySrc := 1 + yyt;
     246      if (x >= 1) = (y >= 2) then
     247        BitBlt(OceanPatch.Canvas.Handle, x * (xxt * 2), y * yyt, xxt * 2, yyt,
     248          GrExt[HGrTerrain].Data.Canvas.Handle, xSrc, ySrc, SRCCOPY);
     249      if (x >= 1) and ((y < 2) or (x >= 2)) then
     250      begin
     251        BitBlt(OceanPatch.Canvas.Handle, x * (xxt * 2), y * yyt, xxt, yyt,
     252          GrExt[HGrTerrain].Data.Canvas.Handle, xSrc + xxt, ySrc + yyt,
     253          SRCPAINT);
     254        BitBlt(OceanPatch.Canvas.Handle, x * (xxt * 2) + xxt, y * yyt, xxt, yyt,
     255          GrExt[HGrTerrain].Data.Canvas.Handle, xSrc, ySrc + yyt, SRCPAINT);
     256      end;
     257      BitBlt(OceanPatch.Canvas.Handle, x * (xxt * 2), y * yyt, xxt, yyt,
     258        DitherMask.Canvas.Handle, xxt, yyt, SRCAND);
     259      BitBlt(OceanPatch.Canvas.Handle, x * (xxt * 2) + xxt, y * yyt, xxt, yyt,
     260        DitherMask.Canvas.Handle, 0, yyt, SRCAND);
     261    end;
     262
     263  for y := 0 to 3 do
     264    for x := 0 to 3 do
     265    begin
     266      if (x = 1) and (y = 1) then
     267        xSrc := 1
     268      else
     269        xSrc := (y mod 2) * (xxt * 2 + 1) + 1;
     270      ySrc := 1 + yyt;
     271      if (x < 1) or (y >= 2) then
     272        BitBlt(OceanMore.Canvas.Handle, x * (xxt * 2), y * yyt, xxt * 2, yyt,
     273          GrExt[HGrTerrain].Data.Canvas.Handle, xSrc, ySrc, SRCCOPY);
     274      if (x = 1) and (y < 2) or (x >= 2) and (y >= 1) then
     275      begin
     276        BitBlt(OceanMore.Canvas.Handle, x * (xxt * 2), y * yyt, xxt, yyt,
     277          GrExt[HGrTerrain].Data.Canvas.Handle, xSrc + xxt, ySrc + yyt,
     278          SRCPAINT);
     279        BitBlt(OceanMore.Canvas.Handle, x * (xxt * 2) + xxt, y * yyt, xxt, yyt,
     280          GrExt[HGrTerrain].Data.Canvas.Handle, xSrc, ySrc + yyt, SRCPAINT);
     281      end;
     282      BitBlt(OceanMore.Canvas.Handle, x * (xxt * 2), y * yyt, xxt * 2, yyt,
     283        DitherMask.Canvas.Handle, 0, 0, SRCAND);
     284    end;
     285
     286  BitBlt(DitherMask.Canvas.Handle, 0, 0, xxt * 2, yyt * 2,
     287    DitherMask.Canvas.Handle, 0, 0, DSTINVERT); { invert dither mask }
     288  BitBlt(DitherMask.Canvas.Handle, 0, 0, xxt * 2, yyt * 2,
     289    GrExt[HGrTerrain].Mask.Canvas.Handle, 1, 1 + yyt, SRCPAINT);
     290
     291  for x := -1 to 6 do
     292    for y := -2 to 6 do
     293      BitBlt(LandPatch.Canvas.Handle, (x + 2) * (xxt * 2), (y + 2) * yyt,
     294        xxt * 2, yyt, DitherMask.Canvas.Handle, 0, 0, SRCAND);
     295
     296  for y := -1 to 6 do
     297    for x := -2 to 7 do
     298      BitBlt(LandMore.Canvas.Handle, (x + 2) * (xxt * 2) - xxt, (y + 2) * yyt,
     299        xxt * 2, yyt, DitherMask.Canvas.Handle, 0, yyt, SRCAND);
     300
     301  BitBlt(LandPatch.Canvas.Handle, 0, 0, (xxt * 2) * 9, yyt * 9,
     302    LandMore.Canvas.Handle, 0, 0, SRCPAINT);
     303
     304  for x := 0 to 3 do
     305    for y := 0 to 3 do
     306      BitBlt(OceanPatch.Canvas.Handle, x * (xxt * 2), y * yyt, xxt * 2, yyt,
     307        DitherMask.Canvas.Handle, 0, 0, SRCAND);
     308
     309  for y := 0 to 3 do
     310    for x := 0 to 4 do
     311      BitBlt(OceanMore.Canvas.Handle, x * (xxt * 2) - xxt, y * yyt, xxt * 2,
     312        yyt, DitherMask.Canvas.Handle, 0, yyt, SRCAND);
     313
     314  BitBlt(OceanPatch.Canvas.Handle, 0, 0, (xxt * 2) * 4, yyt * 4,
     315    OceanMore.Canvas.Handle, 0, 0, SRCPAINT);
     316
     317  with DitherMask.Canvas do
    195318  begin
    196   if (x=1) and (y=1) then xSrc:=1
    197   else xSrc:=(x mod 2)*(xxt*2+1)+1;
    198   ySrc:=1+yyt;
    199   if (x>=1)=(y>=2) then
    200     BitBlt(OceanPatch.Canvas.Handle,x*(xxt*2),y*yyt,xxt*2,yyt,
    201       GrExt[HGrTerrain].Data.Canvas.Handle,xSrc,ySrc,SRCCOPY);
    202   if (x>=1) and ((y<2) or (x>=2)) then
     319    Brush.Color := $FFFFFF;
     320    FillRect(Rect(0, 0, xxt * 2, yyt));
     321  end;
     322  BitBlt(DitherMask.Canvas.Handle, 0, 0, xxt * 2, yyt,
     323    GrExt[HGrTerrain].Mask.Canvas.Handle, 1, 1 + yyt, SRCCOPY);
     324
     325  for x := 0 to 6 do
     326    BitBlt(LandPatch.Canvas.Handle, (x + 2) * (xxt * 2), yyt, xxt * 2, yyt,
     327      DitherMask.Canvas.Handle, 0, 0, SRCAND);
     328
     329  BitBlt(DitherMask.Canvas.Handle, 0, 0, xxt * 2, yyt, DitherMask.Canvas.Handle,
     330    0, 0, DSTINVERT);
     331
     332  for y := 0 to 6 do
     333    BitBlt(LandPatch.Canvas.Handle, xxt * 2, (y + 2) * yyt, xxt * 2, yyt,
     334      DitherMask.Canvas.Handle, 0, 0, SRCAND);
     335
     336  LandMore.Free;
     337  OceanMore.Free;
     338  DitherMask.Free;
     339  // LandPatch.Savetofile('landpatch.bmp');
     340
     341  // reduce size of terrain icons
     342  Mask24 := TBitmap.Create;
     343  Mask24.Assign(GrExt[HGrTerrain].Mask);
     344  Mask24.PixelFormat := pf24bit;
     345  for ySrc := 0 to TerrainIconLines - 1 do
     346  begin
     347    for i := 0 to yyt * 3 - 1 do
     348      MaskLine[i] := Mask24.ScanLine[1 + ySrc * (yyt * 3 + 1) + i];
     349    for xSrc := 0 to 9 - 1 do
    203350    begin
    204     BitBlt(OceanPatch.Canvas.Handle,x*(xxt*2),y*yyt,xxt,yyt,
    205       GrExt[HGrTerrain].Data.Canvas.Handle,xSrc+xxt,ySrc+yyt,SRCPAINT);
    206     BitBlt(OceanPatch.Canvas.Handle,x*(xxt*2)+xxt,y*yyt,xxt,yyt,
    207       GrExt[HGrTerrain].Data.Canvas.Handle,xSrc,ySrc+yyt,SRCPAINT);
    208     end;
    209   BitBlt(OceanPatch.Canvas.Handle,x*(xxt*2),y*yyt,xxt,yyt,
    210     DitherMask.Canvas.Handle,xxt,yyt,SRCAND);
    211   BitBlt(OceanPatch.Canvas.Handle,x*(xxt*2)+xxt,y*yyt,xxt,yyt,
    212     DitherMask.Canvas.Handle,0,yyt,SRCAND);
    213   end;
    214 
    215 for y:=0 to 3 do for x:=0 to 3 do
    216   begin
    217   if (x=1) and (y=1) then xSrc:=1
    218   else xSrc:=(y mod 2)*(xxt*2+1)+1;
    219   ySrc:=1+yyt;
    220   if (x<1) or (y>=2) then
    221     BitBlt(OceanMore.Canvas.Handle,x*(xxt*2),y*yyt,xxt*2,yyt,
    222       GrExt[HGrTerrain].Data.Canvas.Handle,xSrc,ySrc,SRCCOPY);
    223   if (x=1) and (y<2) or (x>=2) and (y>=1) then
    224     begin
    225     BitBlt(OceanMore.Canvas.Handle,x*(xxt*2),y*yyt,xxt,yyt,
    226       GrExt[HGrTerrain].Data.Canvas.Handle,xSrc+xxt,ySrc+yyt,SRCPAINT);
    227     BitBlt(OceanMore.Canvas.Handle,x*(xxt*2)+xxt,y*yyt,xxt,yyt,
    228       GrExt[HGrTerrain].Data.Canvas.Handle,xSrc,ySrc+yyt,SRCPAINT);
    229     end;
    230   BitBlt(OceanMore.Canvas.Handle,x*(xxt*2),y*yyt,xxt*2,yyt,
    231     DitherMask.Canvas.Handle,0,0,SRCAND);
    232   end;
    233 
    234 BitBlt(DitherMask.Canvas.Handle,0,0,xxt*2,yyt*2,
    235   DitherMask.Canvas.Handle,0,0,DSTINVERT); {invert dither mask}
    236 BitBlt(DitherMask.Canvas.Handle,0,0,xxt*2,yyt*2,
    237   GrExt[HGrTerrain].Mask.Canvas.Handle,1,1+yyt,SRCPAINT);
    238 
    239 for x:=-1 to 6 do
    240   for y:=-2 to 6 do
    241     BitBlt(LandPatch.Canvas.Handle,(x+2)*(xxt*2),(y+2)*yyt,xxt*2,yyt,
    242       DitherMask.Canvas.Handle,0,0,SRCAND);
    243 
    244 for y:=-1 to 6 do
    245   for x:=-2 to 7 do
    246     BitBlt(LandMore.Canvas.Handle,(x+2)*(xxt*2)-xxt,(y+2)*yyt,xxt*2,yyt,
    247       DitherMask.Canvas.Handle,0,yyt,SRCAND);
    248 
    249 BitBlt(LandPatch.Canvas.Handle,0,0,(xxt*2)*9,yyt*9,LandMore.Canvas.Handle,0,0,
    250   SRCPAINT);
    251 
    252 for x:=0 to 3 do
    253   for y:=0 to 3 do
    254     BitBlt(OceanPatch.Canvas.Handle,x*(xxt*2),y*yyt,xxt*2,yyt,
    255       DitherMask.Canvas.Handle,0,0,SRCAND);
    256 
    257 for y:=0 to 3 do
    258   for x:=0 to 4 do
    259     BitBlt(OceanMore.Canvas.Handle,x*(xxt*2)-xxt,y*yyt,xxt*2,yyt,
    260       DitherMask.Canvas.Handle,0,yyt,SRCAND);
    261 
    262 BitBlt(OceanPatch.Canvas.Handle,0,0,(xxt*2)*4,yyt*4,OceanMore.Canvas.Handle,0,0,
    263   SRCPAINT);
    264 
    265 with DitherMask.Canvas do
    266   begin
    267   Brush.Color:=$FFFFFF;
    268   FillRect(Rect(0,0,xxt*2,yyt));
    269   end;
    270 BitBlt(DitherMask.Canvas.Handle,0,0,xxt*2,yyt,
    271   GrExt[HGrTerrain].Mask.Canvas.Handle,1,1+yyt,SRCCOPY);
    272 
    273 for x:=0 to 6 do
    274   BitBlt(LandPatch.Canvas.Handle,(x+2)*(xxt*2),yyt,xxt*2,yyt,
    275     DitherMask.Canvas.Handle,0,0,SRCAND);
    276 
    277 BitBlt(DitherMask.Canvas.Handle,0,0,xxt*2,yyt,
    278   DitherMask.Canvas.Handle,0,0,DSTINVERT);
    279 
    280 for y:=0 to 6 do
    281   BitBlt(LandPatch.Canvas.Handle,xxt*2,(y+2)*yyt,xxt*2,yyt,
    282     DitherMask.Canvas.Handle,0,0,SRCAND);
    283 
    284 LandMore.Free; OceanMore.Free; DitherMask.Free;
    285 //LandPatch.Savetofile('landpatch.bmp');
    286 
    287 // reduce size of terrain icons
    288 Mask24:=TBitmap.Create;
    289 Mask24.Assign(GrExt[HGrTerrain].Mask);
    290 Mask24.PixelFormat:=pf24bit;
    291 for ySrc:=0 to TerrainIconLines-1 do
    292   begin
    293   for i:=0 to yyt*3-1 do
    294     MaskLine[i]:=Mask24.ScanLine[1+ySrc*(yyt*3+1)+i];
    295   for xSrc:=0 to 9-1 do
    296     begin
    297     i:=ySrc*9+xSrc;
    298     TSpriteSize[i].Left:=0;
    299     repeat
    300       Border:=true;
    301       for y:=0 to yyt*3-1 do
    302         if MaskLine[y]^[1+xSrc*(xxt*2+1)+TSpriteSize[i].Left,0]=0 then
    303           Border:=false;
    304       if Border then inc(TSpriteSize[i].Left)
    305     until not Border or (TSpriteSize[i].Left=xxt*2-1);
    306     TSpriteSize[i].Top:=0;
    307     repeat
    308       Border:=true;
    309       for x:=0 to xxt*2-1 do
    310         if MaskLine[TSpriteSize[i].Top]^[1+xSrc*(xxt*2+1)+x,0]=0 then
    311           Border:=false;
    312